Vidalia
0.2.15
|
00001 /* 00002 ** This file is part of Vidalia, and is subject to the license terms in the 00003 ** LICENSE file, found in the top level directory of this distribution. If 00004 ** you did not receive the LICENSE file with this file, you may obtain it 00005 ** from the Vidalia source package distributed by the Vidalia Project at 00006 ** http://www.torproject.org/projects/vidalia.html. No part of Vidalia, 00007 ** including this file, may be copied, modified, propagated, or distributed 00008 ** except according to the terms described in the LICENSE file. 00009 */ 00010 00011 /* 00012 ** \file TorProcess.cpp 00013 ** \brief Starts and stops a Tor process 00014 */ 00015 00016 #include "TorProcess.h" 00017 #include "tcglobal.h" 00018 00019 #include "stringutil.h" 00020 00021 #include <QString> 00022 00023 /* Needed for _PROCESS_INFORMATION so that pid() works on Win32 */ 00024 #if defined (Q_OS_WIN32) 00025 #include <windows.h> 00026 #endif 00027 00028 00029 /** Default constructor */ 00030 TorProcess::TorProcess(QObject *parent) 00031 : QProcess(parent) 00032 { 00033 openStdout(); 00034 connect(this, SIGNAL(readyReadStandardOutput()), 00035 this, SLOT(onReadyRead())); 00036 connect(this, SIGNAL(error(QProcess::ProcessError)), 00037 this, SLOT(onError(QProcess::ProcessError))); 00038 } 00039 00040 /** Formats the Tor process arguments for logging. */ 00041 QString 00042 TorProcess::formatArguments(const QStringList &args) 00043 { 00044 QStringList out; 00045 foreach (QString arg, args) { 00046 out << (arg.contains(" ") || arg.isEmpty() ? string_escape(arg) : arg); 00047 } 00048 return out.join(" "); 00049 } 00050 00051 /** Attempts to start the Tor process using the location, executable, and 00052 * command-line arguments specified in Vidalia's settings. If Tor starts, the 00053 * signal started() will be emitted. If Tor fails to start, 00054 * startFailed(errmsg) will be emitted, with an appropriate error message. */ 00055 void 00056 TorProcess::start(const QString &app, const QStringList &args) 00057 { 00058 QString exe = app; 00059 #if defined(Q_OS_WIN32) 00060 /* If we're on Windows, QProcess::start requires that paths with spaces are 00061 * quoted before being passed to it. */ 00062 exe = "\"" + exe + "\""; 00063 #endif 00064 00065 /* Attempt to start Tor with the given command-line arguments */ 00066 QStringList env = QProcess::systemEnvironment(); 00067 #if !defined(Q_OS_WIN32) 00068 /* Add "/usr/sbin" to an existing $PATH 00069 * XXX What if they have no path? Would always just making one with 00070 * "/usr/sbin" smart? Should we add anything else? */ 00071 for (int i = 0; i < env.size(); i++) { 00072 QString envVar = env.at(i); 00073 if (envVar.startsWith("PATH=")) 00074 env.replace(i, envVar += ":/usr/sbin"); 00075 } 00076 #endif 00077 setEnvironment(env); 00078 00079 tc::debug("Starting Tor using '%1 %2'").arg(app).arg(formatArguments(args)); 00080 QProcess::start(exe, args, QIODevice::ReadOnly | QIODevice::Text); 00081 } 00082 00083 /** Stops the Tor process */ 00084 bool 00085 TorProcess::stop(QString *errmsg) 00086 { 00087 /* First, check if the process is already stopped before closing it 00088 * forcefully. */ 00089 if (state() == QProcess::NotRunning) { 00090 return true; 00091 } 00092 00093 tc::debug("Stopping the Tor process."); 00094 /* Tell the process to stop */ 00095 #if defined(Q_OS_WIN32) 00096 /* Tor on Windows doesn't understand a WM_CLOSE message (which is what 00097 * QProcess::terminate() sends it), so we have to kill it harshly. */ 00098 kill(); 00099 #else 00100 terminate(); 00101 00102 /* Wait for it to complete */ 00103 if (!waitForFinished(5000)) { 00104 tc::error("Tor failed to stop: %1").arg(errorString()); 00105 if (errmsg) { 00106 *errmsg = 00107 tr("Process %1 failed to stop. [%2]").arg(pid()).arg(errorString()); 00108 } 00109 return false; 00110 } 00111 #endif 00112 return true; 00113 } 00114 00115 /** Return the process ID for the current process. */ 00116 quint64 00117 TorProcess::pid() 00118 { 00119 #if defined(Q_OS_WIN32) 00120 return (quint64)((QProcess::pid())->dwProcessId); 00121 #else 00122 return QProcess::pid(); 00123 #endif 00124 } 00125 00126 /** Opens logging on stdout. When this is open, the log() signal will be 00127 * emitted when Tor prints a message to stdout. */ 00128 void 00129 TorProcess::openStdout() 00130 { 00131 setReadChannelMode(QProcess::MergedChannels); 00132 setReadChannel(QProcess::StandardOutput); 00133 } 00134 00135 /** Closes logging on stdout. When this is closed, the log() signal will not 00136 * be emitted when Tor prints a message to stdout. */ 00137 void 00138 TorProcess::closeStdout() 00139 { 00140 /* Close the stdout channel */ 00141 closeReadChannel(QProcess::StandardOutput); 00142 /* Read anything left waiting on the buffer */ 00143 onReadyRead(); 00144 } 00145 00146 /** Called when there is data to be read from stdout */ 00147 void 00148 TorProcess::onReadyRead() 00149 { 00150 int i, j; 00151 QString line; 00152 00153 while (canReadLine()) { 00154 line = readLine(); 00155 if (!line.isEmpty()) { 00156 /* Parse the log message and emit log() */ 00157 i = line.indexOf("["); 00158 j = line.indexOf("]"); 00159 if (i > 0 && j > i && line.length() >= j+2) { 00160 emit log(line.mid(i+1, j-i-1), line.mid(j+2)); 00161 } 00162 } 00163 } 00164 } 00165 00166 /** Called when the process encounters an error. If the error tells us that 00167 * the process failed to start, then we will emit the startFailed() signal and 00168 * an error message indicating why. */ 00169 void 00170 TorProcess::onError(QProcess::ProcessError error) 00171 { 00172 if (error == QProcess::FailedToStart) { 00173 tc::error("The Tor process failed to start: %1").arg(errorString()); 00174 /* Tor didn't start, so let everyone know why. */ 00175 emit startFailed(errorString()); 00176 } else { 00177 tc::error("Tor process error: %1").arg(errorString()); 00178 } 00179 } 00180 00181 /** Returns the version reported by the Tor executable specified in 00182 * <b>exe</b>, or a default-constructed QString on failure. */ 00183 QString 00184 TorProcess::version(const QString &exe) 00185 { 00186 QProcess tor; 00187 00188 tor.start(exe, QStringList() << "--version"); 00189 if (!tor.waitForStarted() || !tor.waitForFinished()) 00190 return QString(); 00191 00192 while (tor.canReadLine()) { 00193 QString line = tor.readLine(); 00194 if (line.startsWith("Tor version", Qt::CaseInsensitive)) { 00195 QStringList parts = line.split(" "); 00196 if (parts.size() >= 3) 00197 return parts.at(2); 00198 } 00199 } 00200 return QString(); 00201 } 00202