annotate base/TempDirectory.cpp @ 661:a4faa1840384

* If a FileSource URL won't convert at all in strict mode, try again in tolerant mode (necessary for e.g. filenames with square brackets in them)
author Chris Cannam
date Tue, 19 Oct 2010 21:47:55 +0100
parents 29efe322ab47
children 27cdabba2d3e
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@150 17 #include "system/System.h"
Chris@130 18 #include "Exceptions.h"
Chris@81 19
Chris@81 20 #include <QDir>
Chris@81 21 #include <QFile>
Chris@81 22 #include <QMutexLocker>
Chris@170 23 #include <QSettings>
Chris@81 24
Chris@81 25 #include <iostream>
Chris@81 26 #include <cassert>
Chris@606 27 #include <unistd.h>
Chris@658 28 #include <time.h>
Chris@81 29
Chris@81 30 TempDirectory *
Chris@81 31 TempDirectory::m_instance = new TempDirectory;
Chris@81 32
Chris@81 33 TempDirectory *
Chris@145 34 TempDirectory::getInstance()
Chris@81 35 {
Chris@81 36 return m_instance;
Chris@81 37 }
Chris@81 38
Chris@81 39 TempDirectory::TempDirectory() :
Chris@81 40 m_tmpdir("")
Chris@81 41 {
Chris@81 42 }
Chris@81 43
Chris@81 44 TempDirectory::~TempDirectory()
Chris@81 45 {
Chris@81 46 std::cerr << "TempDirectory::~TempDirectory" << std::endl;
Chris@81 47
Chris@81 48 cleanup();
Chris@81 49 }
Chris@81 50
Chris@81 51 void
Chris@81 52 TempDirectory::cleanup()
Chris@81 53 {
Chris@81 54 cleanupDirectory("");
Chris@81 55 }
Chris@81 56
Chris@81 57 QString
Chris@460 58 TempDirectory::getContainingPath()
Chris@81 59 {
Chris@81 60 QMutexLocker locker(&m_mutex);
Chris@81 61
Chris@170 62 QSettings settings;
Chris@170 63 settings.beginGroup("TempDirectory");
Chris@170 64 QString svDirParent = settings.value("create-in", "$HOME").toString();
Chris@170 65 settings.endGroup();
Chris@170 66
Chris@520 67 #ifdef Q_OS_WIN32
Chris@520 68 char *homedrive = getenv("HOMEDRIVE");
Chris@520 69 char *homepath = getenv("HOMEPATH");
Chris@520 70 if (homedrive && homepath) {
Chris@520 71 svDirParent.replace("$HOME", QString("%1%2").arg(homedrive).arg(homepath));
Chris@520 72 } else {
Chris@520 73 svDirParent.replace("$HOME", QDir::home().absolutePath());
Chris@520 74 }
Chris@520 75 #else
Chris@170 76 svDirParent.replace("$HOME", QDir::home().absolutePath());
Chris@520 77 #endif
Chris@170 78
Chris@126 79 QString svDirBase = ".sv1";
Chris@170 80 QString svDir = QDir(svDirParent).filePath(svDirBase);
Chris@98 81 if (!QFileInfo(svDir).exists()) {
Chris@170 82 if (!QDir(svDirParent).mkdir(svDirBase)) {
Chris@170 83 throw DirectoryCreationFailed(QString("%1 directory in %2")
Chris@170 84 .arg(svDirBase).arg(svDirParent));
Chris@98 85 }
Chris@98 86 } else if (!QFileInfo(svDir).isDir()) {
Chris@170 87 throw DirectoryCreationFailed(QString("%1/%2 is not a directory")
Chris@170 88 .arg(svDirParent).arg(svDirBase));
Chris@98 89 }
Chris@98 90
Chris@98 91 cleanupAbandonedDirectories(svDir);
Chris@98 92
Chris@460 93 return svDir;
Chris@460 94 }
Chris@460 95
Chris@460 96 QString
Chris@460 97 TempDirectory::getPath()
Chris@460 98 {
Chris@460 99 if (m_tmpdir != "") return m_tmpdir;
Chris@460 100
Chris@460 101 return createTempDirectoryIn(getContainingPath());
Chris@98 102 }
Chris@98 103
Chris@98 104 QString
Chris@98 105 TempDirectory::createTempDirectoryIn(QString dir)
Chris@98 106 {
Chris@98 107 // Entered with mutex held.
Chris@98 108
Chris@98 109 QDir tempDirBase(dir);
Chris@81 110
Chris@81 111 // Generate a temporary directory. Qt4.1 doesn't seem to be able
Chris@81 112 // to do this for us, and mkdtemp is not standard. This method is
Chris@81 113 // based on the way glibc does mkdtemp.
Chris@81 114
Chris@81 115 static QString chars =
Chris@81 116 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Chris@81 117
Chris@81 118 QString suffix;
Chris@81 119 int padlen = 6, attempts = 100;
Chris@81 120 unsigned int r = time(0) ^ getpid();
Chris@81 121
Chris@81 122 for (int i = 0; i < padlen; ++i) {
Chris@81 123 suffix += "X";
Chris@81 124 }
Chris@81 125
Chris@81 126 for (int j = 0; j < attempts; ++j) {
Chris@81 127
Chris@81 128 unsigned int v = r;
Chris@81 129
Chris@81 130 for (int i = 0; i < padlen; ++i) {
Chris@81 131 suffix[i] = chars[v % 62];
Chris@81 132 v /= 62;
Chris@81 133 }
Chris@81 134
Chris@81 135 QString candidate = QString("sv_%1").arg(suffix);
Chris@81 136
Chris@86 137 if (tempDirBase.mkpath(candidate)) {
Chris@86 138 m_tmpdir = tempDirBase.filePath(candidate);
Chris@81 139 break;
Chris@81 140 }
Chris@81 141
Chris@81 142 r = r + 7777;
Chris@81 143 }
Chris@81 144
Chris@81 145 if (m_tmpdir == "") {
Chris@81 146 throw DirectoryCreationFailed(QString("temporary subdirectory in %1")
Chris@86 147 .arg(tempDirBase.canonicalPath()));
Chris@81 148 }
Chris@81 149
Chris@98 150 QString pidpath = QDir(m_tmpdir).filePath(QString("%1.pid").arg(getpid()));
Chris@98 151 QFile pidfile(pidpath);
Chris@98 152
Chris@98 153 if (!pidfile.open(QIODevice::WriteOnly)) {
Chris@98 154 throw DirectoryCreationFailed(QString("pid file creation in %1")
Chris@98 155 .arg(m_tmpdir));
Chris@98 156 } else {
Chris@98 157 pidfile.close();
Chris@98 158 }
Chris@98 159
Chris@81 160 return m_tmpdir;
Chris@81 161 }
Chris@81 162
Chris@81 163 QString
Chris@81 164 TempDirectory::getSubDirectoryPath(QString subdir)
Chris@81 165 {
Chris@81 166 QString tmpdirpath = getPath();
Chris@81 167
Chris@81 168 QMutexLocker locker(&m_mutex);
Chris@81 169
Chris@81 170 QDir tmpdir(tmpdirpath);
Chris@81 171 QFileInfo fi(tmpdir.filePath(subdir));
Chris@81 172
Chris@81 173 if (!fi.exists()) {
Chris@81 174 if (!tmpdir.mkdir(subdir)) {
Chris@81 175 throw DirectoryCreationFailed(fi.filePath());
Chris@81 176 } else {
Chris@81 177 return fi.filePath();
Chris@81 178 }
Chris@81 179 } else if (fi.isDir()) {
Chris@81 180 return fi.filePath();
Chris@81 181 } else {
Chris@81 182 throw DirectoryCreationFailed(fi.filePath());
Chris@81 183 }
Chris@81 184 }
Chris@81 185
Chris@81 186 void
Chris@81 187 TempDirectory::cleanupDirectory(QString tmpdir)
Chris@81 188 {
Chris@81 189 bool isRoot = false;
Chris@81 190
Chris@81 191 if (tmpdir == "") {
Chris@81 192
Chris@81 193 m_mutex.lock();
Chris@81 194
Chris@81 195 isRoot = true;
Chris@81 196 tmpdir = m_tmpdir;
Chris@81 197
Chris@81 198 if (tmpdir == "") {
Chris@81 199 m_mutex.unlock();
Chris@81 200 return;
Chris@81 201 }
Chris@81 202 }
Chris@81 203
Chris@81 204 QDir dir(tmpdir);
Chris@83 205 dir.setFilter(QDir::Dirs | QDir::Files);
Chris@81 206
Chris@81 207 for (unsigned int i = 0; i < dir.count(); ++i) {
Chris@81 208
Chris@83 209 if (dir[i] == "." || dir[i] == "..") continue;
Chris@81 210 QFileInfo fi(dir.filePath(dir[i]));
Chris@81 211
Chris@81 212 if (fi.isDir()) {
Chris@81 213 cleanupDirectory(fi.absoluteFilePath());
Chris@81 214 } else {
Chris@81 215 if (!QFile(fi.absoluteFilePath()).remove()) {
Chris@81 216 std::cerr << "WARNING: TempDirectory::cleanup: "
Chris@81 217 << "Failed to unlink file \""
Chris@81 218 << fi.absoluteFilePath().toStdString() << "\""
Chris@81 219 << std::endl;
Chris@81 220 }
Chris@81 221 }
Chris@81 222 }
Chris@81 223
Chris@81 224 QString dirname = dir.dirName();
Chris@81 225 if (dirname != "") {
Chris@81 226 if (!dir.cdUp()) {
Chris@81 227 std::cerr << "WARNING: TempDirectory::cleanup: "
Chris@81 228 << "Failed to cd to parent directory of "
Chris@81 229 << tmpdir.toStdString() << std::endl;
Chris@81 230 return;
Chris@81 231 }
Chris@81 232 if (!dir.rmdir(dirname)) {
Chris@81 233 std::cerr << "WARNING: TempDirectory::cleanup: "
Chris@81 234 << "Failed to remove directory "
Chris@81 235 << dirname.toStdString() << std::endl;
Chris@81 236 }
Chris@81 237 }
Chris@81 238
Chris@81 239 if (isRoot) {
Chris@81 240 m_tmpdir = "";
Chris@81 241 m_mutex.unlock();
Chris@81 242 }
Chris@81 243 }
Chris@98 244
Chris@98 245 void
Chris@98 246 TempDirectory::cleanupAbandonedDirectories(QString svDir)
Chris@98 247 {
Chris@98 248 QDir dir(svDir, "sv_*", QDir::Name, QDir::Dirs);
Chris@98 249
Chris@98 250 for (unsigned int i = 0; i < dir.count(); ++i) {
Chris@98 251
Chris@621 252 QString dirpath = dir.filePath(dir[i]);
Chris@621 253
Chris@621 254 QDir subdir(dirpath, "*.pid", QDir::Name, QDir::Files);
Chris@621 255
Chris@621 256 if (subdir.count() == 0) {
Chris@621 257 std::cerr << "INFO: Found temporary directory with no .pid file in it!\n(directory=\""
Chris@621 258 << dirpath.toStdString() << "\"). Removing it..." << std::endl;
Chris@621 259 cleanupDirectory(dirpath);
Chris@621 260 std::cerr << "...done." << std::endl;
Chris@621 261 continue;
Chris@621 262 }
Chris@98 263
Chris@98 264 for (unsigned int j = 0; j < subdir.count(); ++j) {
Chris@98 265
Chris@98 266 bool ok = false;
Chris@98 267 int pid = QFileInfo(subdir[j]).baseName().toInt(&ok);
Chris@98 268 if (!ok) continue;
Chris@98 269
Chris@98 270 if (GetProcessStatus(pid) == ProcessNotRunning) {
Chris@98 271 std::cerr << "INFO: Found abandoned temporary directory from "
Chris@327 272 << "a previous, defunct process\n(pid=" << pid
Chris@98 273 << ", directory=\""
Chris@621 274 << dirpath.toStdString()
Chris@98 275 << "\"). Removing it..." << std::endl;
Chris@621 276 cleanupDirectory(dirpath);
Chris@98 277 std::cerr << "...done." << std::endl;
Chris@98 278 break;
Chris@98 279 }
Chris@98 280 }
Chris@98 281 }
Chris@98 282 }
Chris@98 283
Chris@98 284
Chris@98 285