svcore  1.9
TempDirectory.cpp
Go to the documentation of this file.
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