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 "system/System.h"
00018 #include "Exceptions.h"
00019 
00020 #include <QDir>
00021 #include <QFile>
00022 #include <QMutexLocker>
00023 #include <QSettings>
00024 
00025 #include <iostream>
00026 #include <cassert>
00027 
00028 TempDirectory *
00029 TempDirectory::m_instance = new TempDirectory;
00030 
00031 TempDirectory *
00032 TempDirectory::getInstance()
00033 {
00034     return m_instance;
00035 }
00036 
00037 TempDirectory::TempDirectory() :
00038     m_tmpdir("")
00039 {
00040 }
00041 
00042 TempDirectory::~TempDirectory()
00043 {
00044     std::cerr << "TempDirectory::~TempDirectory" << std::endl;
00045 
00046     cleanup();
00047 }
00048 
00049 void
00050 TempDirectory::cleanup()
00051 {
00052     cleanupDirectory("");
00053 }
00054 
00055 QString
00056 TempDirectory::getPath()
00057 {
00058     QMutexLocker locker(&m_mutex);
00059     
00060     if (m_tmpdir != "") return m_tmpdir;
00061 
00062     QSettings settings;
00063     settings.beginGroup("TempDirectory");
00064     QString svDirParent = settings.value("create-in", "$HOME").toString();
00065     settings.endGroup();
00066 
00067     svDirParent.replace("$HOME", QDir::home().absolutePath());
00068 
00069     QString svDirBase = ".sv1";
00070     QString svDir = QDir(svDirParent).filePath(svDirBase);
00071     if (!QFileInfo(svDir).exists()) {
00072         if (!QDir(svDirParent).mkdir(svDirBase)) {
00073             throw DirectoryCreationFailed(QString("%1 directory in %2")
00074                                           .arg(svDirBase).arg(svDirParent));
00075         }
00076     } else if (!QFileInfo(svDir).isDir()) {
00077         throw DirectoryCreationFailed(QString("%1/%2 is not a directory")
00078                                       .arg(svDirParent).arg(svDirBase));
00079     }
00080 
00081     cleanupAbandonedDirectories(svDir);
00082 
00083     return createTempDirectoryIn(svDir);
00084 }
00085 
00086 QString
00087 TempDirectory::createTempDirectoryIn(QString dir)
00088 {
00089     // Entered with mutex held.
00090 
00091     QDir tempDirBase(dir);
00092 
00093     // Generate a temporary directory.  Qt4.1 doesn't seem to be able
00094     // to do this for us, and mkdtemp is not standard.  This method is
00095     // based on the way glibc does mkdtemp.
00096 
00097     static QString chars =
00098         "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
00099 
00100     QString suffix;
00101     int padlen = 6, attempts = 100;
00102     unsigned int r = time(0) ^ getpid();
00103 
00104     for (int i = 0; i < padlen; ++i) {
00105         suffix += "X";
00106     }
00107     
00108     for (int j = 0; j < attempts; ++j) {
00109 
00110         unsigned int v = r;
00111         
00112         for (int i = 0; i < padlen; ++i) {
00113             suffix[i] = chars[v % 62];
00114             v /= 62;
00115         }
00116 
00117         QString candidate = QString("sv_%1").arg(suffix);
00118 
00119         if (tempDirBase.mkpath(candidate)) {
00120             m_tmpdir = tempDirBase.filePath(candidate);
00121             break;
00122         }
00123 
00124         r = r + 7777;
00125     }
00126 
00127     if (m_tmpdir == "") {
00128         throw DirectoryCreationFailed(QString("temporary subdirectory in %1")
00129                                       .arg(tempDirBase.canonicalPath()));
00130     }
00131 
00132     QString pidpath = QDir(m_tmpdir).filePath(QString("%1.pid").arg(getpid()));
00133     QFile pidfile(pidpath);
00134 
00135     if (!pidfile.open(QIODevice::WriteOnly)) {
00136         throw DirectoryCreationFailed(QString("pid file creation in %1")
00137                                       .arg(m_tmpdir));
00138     } else {
00139         pidfile.close();
00140     }
00141 
00142     return m_tmpdir;
00143 }
00144 
00145 QString
00146 TempDirectory::getSubDirectoryPath(QString subdir)
00147 {
00148     QString tmpdirpath = getPath();
00149     
00150     QMutexLocker locker(&m_mutex);
00151 
00152     QDir tmpdir(tmpdirpath);
00153     QFileInfo fi(tmpdir.filePath(subdir));
00154 
00155     if (!fi.exists()) {
00156         if (!tmpdir.mkdir(subdir)) {
00157             throw DirectoryCreationFailed(fi.filePath());
00158         } else {
00159             return fi.filePath();
00160         }
00161     } else if (fi.isDir()) {
00162         return fi.filePath();
00163     } else {
00164         throw DirectoryCreationFailed(fi.filePath());
00165     }
00166 }
00167 
00168 void
00169 TempDirectory::cleanupDirectory(QString tmpdir)
00170 {
00171     bool isRoot = false;
00172 
00173     if (tmpdir == "") {
00174 
00175         m_mutex.lock();
00176 
00177         isRoot = true;
00178         tmpdir = m_tmpdir;
00179 
00180         if (tmpdir == "") {
00181             m_mutex.unlock();
00182             return;
00183         }
00184     }
00185 
00186     QDir dir(tmpdir);
00187     dir.setFilter(QDir::Dirs | QDir::Files);
00188 
00189     for (unsigned int i = 0; i < dir.count(); ++i) {
00190 
00191         if (dir[i] == "." || dir[i] == "..") continue;
00192         QFileInfo fi(dir.filePath(dir[i]));
00193 
00194         if (fi.isDir()) {
00195             cleanupDirectory(fi.absoluteFilePath());
00196         } else {
00197             if (!QFile(fi.absoluteFilePath()).remove()) {
00198                 std::cerr << "WARNING: TempDirectory::cleanup: "
00199                           << "Failed to unlink file \""
00200                           << fi.absoluteFilePath().toStdString() << "\""
00201                           << std::endl;
00202             }
00203         }
00204     }
00205 
00206     QString dirname = dir.dirName();
00207     if (dirname != "") {
00208         if (!dir.cdUp()) {
00209             std::cerr << "WARNING: TempDirectory::cleanup: "
00210                       << "Failed to cd to parent directory of "
00211                       << tmpdir.toStdString() << std::endl;
00212             return;
00213         }
00214         if (!dir.rmdir(dirname)) {
00215             std::cerr << "WARNING: TempDirectory::cleanup: "
00216                       << "Failed to remove directory "
00217                       << dirname.toStdString() << std::endl;
00218         } 
00219     }
00220 
00221     if (isRoot) {
00222         m_tmpdir = "";
00223         m_mutex.unlock();
00224     }
00225 }
00226 
00227 void
00228 TempDirectory::cleanupAbandonedDirectories(QString svDir)
00229 {
00230     QDir dir(svDir, "sv_*", QDir::Name, QDir::Dirs);
00231 
00232     for (unsigned int i = 0; i < dir.count(); ++i) {
00233         
00234         QDir subdir(dir.filePath(dir[i]), "*.pid", QDir::Name, QDir::Files);
00235 
00236         for (unsigned int j = 0; j < subdir.count(); ++j) {
00237 
00238             bool ok = false;
00239             int pid = QFileInfo(subdir[j]).baseName().toInt(&ok);
00240             if (!ok) continue;
00241 
00242             if (GetProcessStatus(pid) == ProcessNotRunning) {
00243                 std::cerr << "INFO: Found abandoned temporary directory from "
00244                           << "a previous, defunct process\n(pid=" << pid
00245                           << ", directory=\""
00246                           << dir.filePath(dir[i]).toStdString()
00247                           << "\").  Removing it..." << std::endl;
00248                 cleanupDirectory(dir.filePath(dir[i]));
00249                 std::cerr << "...done." << std::endl;
00250                 break;
00251             }
00252         }
00253     }
00254 }
00255 
00256 
00257         

Generated on Wed Feb 20 15:45:28 2008 for SonicVisualiser by  doxygen 1.5.1