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