annotate base/TempDirectory.cpp @ 631:3a5ee4b6c9ad

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