svcore
1.9
|
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 00002 00003 /* 00004 Sonic Visualiser 00005 An audio file viewer and annotation editor. 00006 Centre for Digital Music, Queen Mary, University of London. 00007 This file copyright 2006 Chris Cannam. 00008 00009 This program is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU General Public License as 00011 published by the Free Software Foundation; either version 2 of the 00012 License, or (at your option) any later version. See the file 00013 COPYING included with this distribution for more information. 00014 */ 00015 00016 #include "TempDirectory.h" 00017 #include "ResourceFinder.h" 00018 #include "system/System.h" 00019 #include "Exceptions.h" 00020 00021 #include <QDir> 00022 #include <QFile> 00023 #include <QMutexLocker> 00024 #include <QSettings> 00025 00026 #include <iostream> 00027 #include <cassert> 00028 #include <unistd.h> 00029 #include <time.h> 00030 00031 TempDirectory * 00032 TempDirectory::m_instance = new TempDirectory; 00033 00034 TempDirectory * 00035 TempDirectory::getInstance() 00036 { 00037 return m_instance; 00038 } 00039 00040 TempDirectory::TempDirectory() : 00041 m_tmpdir("") 00042 { 00043 } 00044 00045 TempDirectory::~TempDirectory() 00046 { 00047 SVDEBUG << "TempDirectory::~TempDirectory" << endl; 00048 00049 cleanup(); 00050 } 00051 00052 void 00053 TempDirectory::cleanup() 00054 { 00055 cleanupDirectory(""); 00056 } 00057 00058 QString 00059 TempDirectory::getContainingPath() 00060 { 00061 QMutexLocker locker(&m_mutex); 00062 00063 QSettings settings; 00064 settings.beginGroup("TempDirectory"); 00065 QString svDirParent = settings.value("create-in", "$HOME").toString(); 00066 settings.endGroup(); 00067 00068 QString svDir = ResourceFinder().getUserResourcePrefix(); 00069 if (svDirParent != "$HOME") { 00071 svDir.replace(QDir::home().absolutePath(), svDirParent); 00072 } 00073 00074 if (!QFileInfo(svDir).exists()) { 00075 if (!QDir(svDirParent).mkpath(svDir)) { 00076 throw DirectoryCreationFailed(QString("%1 directory in %2") 00077 .arg(svDir).arg(svDirParent)); 00078 } 00079 } else if (!QFileInfo(svDir).isDir()) { 00080 throw DirectoryCreationFailed(QString("%1 is not a directory") 00081 .arg(svDir)); 00082 } 00083 00084 cleanupAbandonedDirectories(svDir); 00085 00086 return svDir; 00087 } 00088 00089 QString 00090 TempDirectory::getPath() 00091 { 00092 if (m_tmpdir != "") return m_tmpdir; 00093 00094 return createTempDirectoryIn(getContainingPath()); 00095 } 00096 00097 QString 00098 TempDirectory::createTempDirectoryIn(QString dir) 00099 { 00100 // Entered with mutex held. 00101 00102 QDir tempDirBase(dir); 00103 00104 // Generate a temporary directory. Qt4.1 doesn't seem to be able 00105 // to do this for us, and mkdtemp is not standard. This method is 00106 // based on the way glibc does mkdtemp. 00107 00108 static QString chars = 00109 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 00110 00111 QString suffix; 00112 int padlen = 6, attempts = 100; 00113 unsigned int r = time(0) ^ getpid(); 00114 00115 for (int i = 0; i < padlen; ++i) { 00116 suffix += "X"; 00117 } 00118 00119 for (int j = 0; j < attempts; ++j) { 00120 00121 unsigned int v = r; 00122 00123 for (int i = 0; i < padlen; ++i) { 00124 suffix[i] = chars[v % 62]; 00125 v /= 62; 00126 } 00127 00128 QString candidate = QString("sv_%1").arg(suffix); 00129 00130 if (tempDirBase.mkpath(candidate)) { 00131 m_tmpdir = tempDirBase.filePath(candidate); 00132 break; 00133 } 00134 00135 r = r + 7777; 00136 } 00137 00138 if (m_tmpdir == "") { 00139 throw DirectoryCreationFailed(QString("temporary subdirectory in %1") 00140 .arg(tempDirBase.canonicalPath())); 00141 } 00142 00143 QString pidpath = QDir(m_tmpdir).filePath(QString("%1.pid").arg(getpid())); 00144 QFile pidfile(pidpath); 00145 00146 if (!pidfile.open(QIODevice::WriteOnly)) { 00147 throw DirectoryCreationFailed(QString("pid file creation in %1") 00148 .arg(m_tmpdir)); 00149 } else { 00150 pidfile.close(); 00151 } 00152 00153 return m_tmpdir; 00154 } 00155 00156 QString 00157 TempDirectory::getSubDirectoryPath(QString subdir) 00158 { 00159 QString tmpdirpath = getPath(); 00160 00161 QMutexLocker locker(&m_mutex); 00162 00163 QDir tmpdir(tmpdirpath); 00164 QFileInfo fi(tmpdir.filePath(subdir)); 00165 00166 if (!fi.exists()) { 00167 if (!tmpdir.mkdir(subdir)) { 00168 throw DirectoryCreationFailed(fi.filePath()); 00169 } else { 00170 return fi.filePath(); 00171 } 00172 } else if (fi.isDir()) { 00173 return fi.filePath(); 00174 } else { 00175 throw DirectoryCreationFailed(fi.filePath()); 00176 } 00177 } 00178 00179 void 00180 TempDirectory::cleanupDirectory(QString tmpdir) 00181 { 00182 bool isRoot = false; 00183 00184 if (tmpdir == "") { 00185 00186 m_mutex.lock(); 00187 00188 isRoot = true; 00189 tmpdir = m_tmpdir; 00190 00191 if (tmpdir == "") { 00192 m_mutex.unlock(); 00193 return; 00194 } 00195 } 00196 00197 QDir dir(tmpdir); 00198 dir.setFilter(QDir::Dirs | QDir::Files); 00199 00200 for (unsigned int i = 0; i < dir.count(); ++i) { 00201 00202 if (dir[i] == "." || dir[i] == "..") continue; 00203 QFileInfo fi(dir.filePath(dir[i])); 00204 00205 if (fi.isDir()) { 00206 cleanupDirectory(fi.absoluteFilePath()); 00207 } else { 00208 if (!QFile(fi.absoluteFilePath()).remove()) { 00209 cerr << "WARNING: TempDirectory::cleanup: " 00210 << "Failed to unlink file \"" 00211 << fi.absoluteFilePath() << "\"" 00212 << endl; 00213 } 00214 } 00215 } 00216 00217 QString dirname = dir.dirName(); 00218 if (dirname != "") { 00219 if (!dir.cdUp()) { 00220 cerr << "WARNING: TempDirectory::cleanup: " 00221 << "Failed to cd to parent directory of " 00222 << tmpdir << endl; 00223 return; 00224 } 00225 if (!dir.rmdir(dirname)) { 00226 cerr << "WARNING: TempDirectory::cleanup: " 00227 << "Failed to remove directory " 00228 << dirname << endl; 00229 } 00230 } 00231 00232 if (isRoot) { 00233 m_tmpdir = ""; 00234 m_mutex.unlock(); 00235 } 00236 } 00237 00238 void 00239 TempDirectory::cleanupAbandonedDirectories(QString svDir) 00240 { 00241 QDir dir(svDir, "sv_*", QDir::Name, QDir::Dirs); 00242 00243 for (unsigned int i = 0; i < dir.count(); ++i) { 00244 00245 QString dirpath = dir.filePath(dir[i]); 00246 00247 QDir subdir(dirpath, "*.pid", QDir::Name, QDir::Files); 00248 00249 if (subdir.count() == 0) { 00250 cerr << "INFO: Found temporary directory with no .pid file in it!\n(directory=\"" 00251 << dirpath << "\"). Removing it..." << endl; 00252 cleanupDirectory(dirpath); 00253 cerr << "...done." << endl; 00254 continue; 00255 } 00256 00257 for (unsigned int j = 0; j < subdir.count(); ++j) { 00258 00259 bool ok = false; 00260 int pid = QFileInfo(subdir[j]).baseName().toInt(&ok); 00261 if (!ok) continue; 00262 00263 if (GetProcessStatus(pid) == ProcessNotRunning) { 00264 cerr << "INFO: Found abandoned temporary directory from " 00265 << "a previous, defunct process\n(pid=" << pid 00266 << ", directory=\"" 00267 << dirpath 00268 << "\"). Removing it..." << endl; 00269 cleanupDirectory(dirpath); 00270 cerr << "...done." << endl; 00271 break; 00272 } 00273 } 00274 } 00275 } 00276 00277 00278