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 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 

Generated on Mon Aug 30 19:14:02 2010 for Vidalia by  doxygen 1.5.9