annotate base/TempDirectory.cpp @ 706:579b2da21e7a

Make FileSource capable of handling resource files. Without this, we failed to open the silent resource file used as a placeholder in templates and thus failed to replace it with the proper file after loading the template -- the consequence was that (although the right audio file ended up being shown as the main model) any derived models were not regenerated
author Chris Cannam
date Fri, 07 Oct 2011 17:04:09 +0100
parents 1424aa29ae95
children b867c07478be
rev   line source
Chris@81 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@81 2
Chris@81 3 /*
Chris@81 4 Sonic Visualiser
Chris@81 5 An audio file viewer and annotation editor.
Chris@81 6 Centre for Digital Music, Queen Mary, University of London.
Chris@81 7 This file copyright 2006 Chris Cannam.
Chris@81 8
Chris@81 9 This program is free software; you can redistribute it and/or
Chris@81 10 modify it under the terms of the GNU General Public License as
Chris@81 11 published by the Free Software Foundation; either version 2 of the
Chris@81 12 License, or (at your option) any later version. See the file
Chris@81 13 COPYING included with this distribution for more information.
Chris@81 14 */
Chris@81 15
Chris@81 16 #include "TempDirectory.h"
Chris@680 17 #include "ResourceFinder.h"
Chris@150 18 #include "system/System.h"
Chris@130 19 #include "Exceptions.h"
Chris@81 20
Chris@81 21 #include <QDir>
Chris@81 22 #include <QFile>
Chris@81 23 #include <QMutexLocker>
Chris@170 24 #include <QSettings>
Chris@81 25
Chris@81 26 #include <iostream>
Chris@81 27 #include <cassert>
Chris@606 28 #include <unistd.h>
Chris@658 29 #include <time.h>
Chris@81 30
Chris@81 31 TempDirectory *
Chris@81 32 TempDirectory::m_instance = new TempDirectory;
Chris@81 33
Chris@81 34 TempDirectory *
Chris@145 35 TempDirectory::getInstance()
Chris@81 36 {
Chris@81 37 return m_instance;
Chris@81 38 }
Chris@81 39
Chris@81 40 TempDirectory::TempDirectory() :
Chris@81 41 m_tmpdir("")
Chris@81 42 {
Chris@81 43 }
Chris@81 44
Chris@81 45 TempDirectory::~TempDirectory()
Chris@81 46 {
Chris@690 47 SVDEBUG << "TempDirectory::~TempDirectory" << endl;
Chris@81 48
Chris@81 49 cleanup();
Chris@81 50 }
Chris@81 51
Chris@81 52 void
Chris@81 53 TempDirectory::cleanup()
Chris@81 54 {
Chris@81 55 cleanupDirectory("");
Chris@81 56 }
Chris@81 57
Chris@81 58 QString
Chris@460 59 TempDirectory::getContainingPath()
Chris@81 60 {
Chris@81 61 QMutexLocker locker(&m_mutex);
Chris@680 62
Chris@170 63 QSettings settings;
Chris@170 64 settings.beginGroup("TempDirectory");
Chris@170 65 QString svDirParent = settings.value("create-in", "$HOME").toString();
Chris@170 66 settings.endGroup();
Chris@170 67
Chris@680 68 QString svDir = ResourceFinder().getUserResourcePrefix();
Chris@680 69 if (svDirParent != "$HOME") {
Chris@680 70 //!!! iffy
Chris@680 71 svDir.replace(QDir::home().absolutePath(), svDirParent);
Chris@520 72 }
Chris@170 73
Chris@98 74 if (!QFileInfo(svDir).exists()) {
Chris@680 75 if (!QDir(svDirParent).mkdir(svDir)) {
Chris@170 76 throw DirectoryCreationFailed(QString("%1 directory in %2")
Chris@680 77 .arg(svDir).arg(svDirParent));
Chris@98 78 }
Chris@98 79 } else if (!QFileInfo(svDir).isDir()) {
Chris@680 80 throw DirectoryCreationFailed(QString("%1 is not a directory")
Chris@680 81 .arg(svDir));
Chris@98 82 }
Chris@98 83
Chris@98 84 cleanupAbandonedDirectories(svDir);
Chris@98 85
Chris@460 86 return svDir;
Chris@460 87 }
Chris@460 88
Chris@460 89 QString
Chris@460 90 TempDirectory::getPath()
Chris@460 91 {
Chris@460 92 if (m_tmpdir != "") return m_tmpdir;
Chris@460 93
Chris@460 94 return createTempDirectoryIn(getContainingPath());
Chris@98 95 }
Chris@98 96
Chris@98 97 QString
Chris@98 98 TempDirectory::createTempDirectoryIn(QString dir)
Chris@98 99 {
Chris@98 100 // Entered with mutex held.
Chris@98 101
Chris@98 102 QDir tempDirBase(dir);
Chris@81 103
Chris@81 104 // Generate a temporary directory. Qt4.1 doesn't seem to be able
Chris@81 105 // to do this for us, and mkdtemp is not standard. This method is
Chris@81 106 // based on the way glibc does mkdtemp.
Chris@81 107
Chris@81 108 static QString chars =
Chris@81 109 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Chris@81 110
Chris@81 111 QString suffix;
Chris@81 112 int padlen = 6, attempts = 100;
Chris@81 113 unsigned int r = time(0) ^ getpid();
Chris@81 114
Chris@81 115 for (int i = 0; i < padlen; ++i) {
Chris@81 116 suffix += "X";
Chris@81 117 }
Chris@81 118
Chris@81 119 for (int j = 0; j < attempts; ++j) {
Chris@81 120
Chris@81 121 unsigned int v = r;
Chris@81 122
Chris@81 123 for (int i = 0; i < padlen; ++i) {
Chris@81 124 suffix[i] = chars[v % 62];
Chris@81 125 v /= 62;
Chris@81 126 }
Chris@81 127
Chris@81 128 QString candidate = QString("sv_%1").arg(suffix);
Chris@81 129
Chris@86 130 if (tempDirBase.mkpath(candidate)) {
Chris@86 131 m_tmpdir = tempDirBase.filePath(candidate);
Chris@81 132 break;
Chris@81 133 }
Chris@81 134
Chris@81 135 r = r + 7777;
Chris@81 136 }
Chris@81 137
Chris@81 138 if (m_tmpdir == "") {
Chris@81 139 throw DirectoryCreationFailed(QString("temporary subdirectory in %1")
Chris@86 140 .arg(tempDirBase.canonicalPath()));
Chris@81 141 }
Chris@81 142
Chris@98 143 QString pidpath = QDir(m_tmpdir).filePath(QString("%1.pid").arg(getpid()));
Chris@98 144 QFile pidfile(pidpath);
Chris@98 145
Chris@98 146 if (!pidfile.open(QIODevice::WriteOnly)) {
Chris@98 147 throw DirectoryCreationFailed(QString("pid file creation in %1")
Chris@98 148 .arg(m_tmpdir));
Chris@98 149 } else {
Chris@98 150 pidfile.close();
Chris@98 151 }
Chris@98 152
Chris@81 153 return m_tmpdir;
Chris@81 154 }
Chris@81 155
Chris@81 156 QString
Chris@81 157 TempDirectory::getSubDirectoryPath(QString subdir)
Chris@81 158 {
Chris@81 159 QString tmpdirpath = getPath();
Chris@81 160
Chris@81 161 QMutexLocker locker(&m_mutex);
Chris@81 162
Chris@81 163 QDir tmpdir(tmpdirpath);
Chris@81 164 QFileInfo fi(tmpdir.filePath(subdir));
Chris@81 165
Chris@81 166 if (!fi.exists()) {
Chris@81 167 if (!tmpdir.mkdir(subdir)) {
Chris@81 168 throw DirectoryCreationFailed(fi.filePath());
Chris@81 169 } else {
Chris@81 170 return fi.filePath();
Chris@81 171 }
Chris@81 172 } else if (fi.isDir()) {
Chris@81 173 return fi.filePath();
Chris@81 174 } else {
Chris@81 175 throw DirectoryCreationFailed(fi.filePath());
Chris@81 176 }
Chris@81 177 }
Chris@81 178
Chris@81 179 void
Chris@81 180 TempDirectory::cleanupDirectory(QString tmpdir)
Chris@81 181 {
Chris@81 182 bool isRoot = false;
Chris@81 183
Chris@81 184 if (tmpdir == "") {
Chris@81 185
Chris@81 186 m_mutex.lock();
Chris@81 187
Chris@81 188 isRoot = true;
Chris@81 189 tmpdir = m_tmpdir;
Chris@81 190
Chris@81 191 if (tmpdir == "") {
Chris@81 192 m_mutex.unlock();
Chris@81 193 return;
Chris@81 194 }
Chris@81 195 }
Chris@81 196
Chris@81 197 QDir dir(tmpdir);
Chris@83 198 dir.setFilter(QDir::Dirs | QDir::Files);
Chris@81 199
Chris@81 200 for (unsigned int i = 0; i < dir.count(); ++i) {
Chris@81 201
Chris@83 202 if (dir[i] == "." || dir[i] == "..") continue;
Chris@81 203 QFileInfo fi(dir.filePath(dir[i]));
Chris@81 204
Chris@81 205 if (fi.isDir()) {
Chris@81 206 cleanupDirectory(fi.absoluteFilePath());
Chris@81 207 } else {
Chris@81 208 if (!QFile(fi.absoluteFilePath()).remove()) {
Chris@81 209 std::cerr << "WARNING: TempDirectory::cleanup: "
Chris@81 210 << "Failed to unlink file \""
Chris@686 211 << fi.absoluteFilePath() << "\""
Chris@81 212 << std::endl;
Chris@81 213 }
Chris@81 214 }
Chris@81 215 }
Chris@81 216
Chris@81 217 QString dirname = dir.dirName();
Chris@81 218 if (dirname != "") {
Chris@81 219 if (!dir.cdUp()) {
Chris@81 220 std::cerr << "WARNING: TempDirectory::cleanup: "
Chris@81 221 << "Failed to cd to parent directory of "
Chris@686 222 << tmpdir << std::endl;
Chris@81 223 return;
Chris@81 224 }
Chris@81 225 if (!dir.rmdir(dirname)) {
Chris@81 226 std::cerr << "WARNING: TempDirectory::cleanup: "
Chris@81 227 << "Failed to remove directory "
Chris@686 228 << dirname << std::endl;
Chris@81 229 }
Chris@81 230 }
Chris@81 231
Chris@81 232 if (isRoot) {
Chris@81 233 m_tmpdir = "";
Chris@81 234 m_mutex.unlock();
Chris@81 235 }
Chris@81 236 }
Chris@98 237
Chris@98 238 void
Chris@98 239 TempDirectory::cleanupAbandonedDirectories(QString svDir)
Chris@98 240 {
Chris@98 241 QDir dir(svDir, "sv_*", QDir::Name, QDir::Dirs);
Chris@98 242
Chris@98 243 for (unsigned int i = 0; i < dir.count(); ++i) {
Chris@98 244
Chris@621 245 QString dirpath = dir.filePath(dir[i]);
Chris@621 246
Chris@621 247 QDir subdir(dirpath, "*.pid", QDir::Name, QDir::Files);
Chris@621 248
Chris@621 249 if (subdir.count() == 0) {
Chris@621 250 std::cerr << "INFO: Found temporary directory with no .pid file in it!\n(directory=\""
Chris@686 251 << dirpath << "\"). Removing it..." << std::endl;
Chris@621 252 cleanupDirectory(dirpath);
Chris@621 253 std::cerr << "...done." << std::endl;
Chris@621 254 continue;
Chris@621 255 }
Chris@98 256
Chris@98 257 for (unsigned int j = 0; j < subdir.count(); ++j) {
Chris@98 258
Chris@98 259 bool ok = false;
Chris@98 260 int pid = QFileInfo(subdir[j]).baseName().toInt(&ok);
Chris@98 261 if (!ok) continue;
Chris@98 262
Chris@98 263 if (GetProcessStatus(pid) == ProcessNotRunning) {
Chris@98 264 std::cerr << "INFO: Found abandoned temporary directory from "
Chris@327 265 << "a previous, defunct process\n(pid=" << pid
Chris@98 266 << ", directory=\""
Chris@621 267 << dirpath.toStdString()
Chris@98 268 << "\"). Removing it..." << std::endl;
Chris@621 269 cleanupDirectory(dirpath);
Chris@98 270 std::cerr << "...done." << std::endl;
Chris@98 271 break;
Chris@98 272 }
Chris@98 273 }
Chris@98 274 }
Chris@98 275 }
Chris@98 276
Chris@98 277
Chris@98 278