annotate base/TempDirectory.cpp @ 1061:c1e43c8d2527 tonioni

Thread-local debug was causing crash on exit with Qt 5.4.x. But we introduced that because QDebug itself was crashing when used from multiple threads. Replace with simpler fstream version
author Chris Cannam
date Tue, 31 Mar 2015 10:36:52 +0100
parents cc27f35aa75c
children c811991a5efa
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@733 75 if (!QDir(svDirParent).mkpath(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@1038 113 unsigned int r = (unsigned int)(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@843 209 cerr << "WARNING: TempDirectory::cleanup: "
Chris@81 210 << "Failed to unlink file \""
Chris@686 211 << fi.absoluteFilePath() << "\""
Chris@843 212 << 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@843 220 cerr << "WARNING: TempDirectory::cleanup: "
Chris@81 221 << "Failed to cd to parent directory of "
Chris@843 222 << tmpdir << endl;
Chris@81 223 return;
Chris@81 224 }
Chris@81 225 if (!dir.rmdir(dirname)) {
Chris@843 226 cerr << "WARNING: TempDirectory::cleanup: "
Chris@81 227 << "Failed to remove directory "
Chris@843 228 << dirname << 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@843 250 cerr << "INFO: Found temporary directory with no .pid file in it!\n(directory=\""
Chris@843 251 << dirpath << "\"). Removing it..." << endl;
Chris@621 252 cleanupDirectory(dirpath);
Chris@843 253 cerr << "...done." << 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@843 264 cerr << "INFO: Found abandoned temporary directory from "
Chris@327 265 << "a previous, defunct process\n(pid=" << pid
Chris@98 266 << ", directory=\""
Chris@844 267 << dirpath
Chris@843 268 << "\"). Removing it..." << endl;
Chris@621 269 cleanupDirectory(dirpath);
Chris@843 270 cerr << "...done." << 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