torprocess.cpp

Go to the documentation of this file.
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 2695 2008-06-12 06:50:05Z edmanm $
00014 ** \brief Starts and stops a Tor process
00015 */
00016 
00017 #include <QString>
00018 #include <stringutil.h>
00019 #include "tcglobal.h"
00020 
00021 /* Needed for _PROCESS_INFORMATION so that pid() works on Win32 */
00022 #if defined (Q_OS_WIN32)
00023 #include <windows.h>
00024 #endif
00025   
00026 #include "torprocess.h"
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 

Generated on Wed Nov 26 21:04:20 2008 for Vidalia by  doxygen 1.5.7.1