torprocess.cpp

Go to the documentation of this file.
00001 /****************************************************************
00002  *  Vidalia is distributed under the following license:
00003  *
00004  *  Copyright (C) 2006,  Matt Edman, Justin Hipple
00005  *
00006  *  This program is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU General Public License
00008  *  as published by the Free Software Foundation; either version 2
00009  *  of the License, or (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, 
00019  *  Boston, MA  02110-1301, USA.
00020  ****************************************************************/
00021 
00022 /** 
00023  * \file torprocess.cpp
00024  * \version $Id: torprocess.cpp 1864 2007-08-23 23:31:35Z edmanm $
00025  * \brief Starts and stops a Tor process
00026  */
00027 
00028 #include <QString>
00029 #include <vidalia.h>
00030 #include <util/string.h>
00031 
00032 /* Needed for _PROCESS_INFORMATION so that pid() works on Win32 */
00033 #if defined (Q_OS_WIN32)
00034 #include <windows.h>
00035 #endif
00036   
00037 #include "torprocess.h"
00038 
00039 
00040 /** Default constructor */
00041 TorProcess::TorProcess()
00042 {
00043   openStdout();
00044   connect(this, SIGNAL(readyReadStandardOutput()), 
00045           this,   SLOT(onReadyRead()));
00046   connect(this, SIGNAL(error(QProcess::ProcessError)), 
00047           this,   SLOT(onError(QProcess::ProcessError)));
00048 }
00049 
00050 /** Formats the Tor process arguments for logging. */
00051 QString
00052 TorProcess::formatArguments(const QStringList args)
00053 {
00054   QStringList out;
00055   foreach (QString arg, args) {
00056     out << (arg.contains(" ") || arg.isEmpty() ? string_escape(arg) : arg);
00057   }
00058   return out.join(" ");
00059 }
00060 
00061 /** Attempts to start the Tor process using the location, executable, and
00062  * command-line arguments specified in Vidalia's settings. If Tor starts, the
00063  * signal started() will be emitted. If Tor fails to start,
00064  * startFailed(errmsg) will be emitted, with an appropriate error message. */
00065 void
00066 TorProcess::start(QString app, QStringList args) 
00067 {
00068 #if defined(Q_OS_WIN32)
00069   /* If we're on Windows, QProcess::start requires that paths with spaces are
00070    * quoted before being passed to it. */
00071   app = "\"" + app + "\"";
00072 #endif
00073   
00074   /* Attempt to start Tor with the given command-line arguments */
00075   QStringList env = QProcess::systemEnvironment();
00076 #if !defined(Q_OS_WIN32)
00077   /* Add "/usr/sbin" to an existing $PATH
00078    * XXX What if they have no path? Would always just making one with 
00079    *     "/usr/sbin" smart? Should we add anything else? */
00080   for (int i = 0; i < env.size(); i++) {
00081     QString envVar = env.at(i);
00082     if (envVar.startsWith("PATH="))
00083       env.replace(i, envVar += ":/usr/sbin");
00084   }
00085 #endif
00086   setEnvironment(env);
00087 
00088   vNotice("Starting Tor using '%1 %2'").arg(app).arg(formatArguments(args));
00089   QProcess::start(app, args, QIODevice::ReadOnly | QIODevice::Text);
00090 }
00091 
00092 /** Stops the Tor process */
00093 bool
00094 TorProcess::stop(QString *errmsg)
00095 {
00096   /* First, check if the process is already stopped before closing it
00097    * forcefully. */
00098   if (state() == QProcess::NotRunning) {
00099     return true;
00100   }
00101 
00102   vNotice("Stopping the Tor process.");
00103   /* Tell the process to stop */
00104 #if defined(Q_OS_WIN32)
00105   /* Tor on Windows doesn't understand a WM_CLOSE message (which is what 
00106    * QProcess::terminate() sends it), so we have to kill it harshly. */
00107   kill();
00108 #else
00109   terminate();
00110 
00111   /* Wait for it to complete */
00112   if (!waitForFinished(5000)) {
00113     vWarn("Tor failed to stop: %1").arg(errorString());
00114     if (errmsg) {
00115       *errmsg = 
00116         tr("Process %1 failed to stop. [%2]").arg(pid()).arg(errorString());
00117     }
00118     return false;
00119   }
00120 #endif
00121   return true;
00122 }
00123 
00124 /** Return the process ID for the current process. */
00125 quint64
00126 TorProcess::pid()
00127 {
00128 #if defined(Q_OS_WIN32)
00129   return (quint64)((QProcess::pid())->dwProcessId);
00130 #else
00131   return QProcess::pid();
00132 #endif
00133 }
00134 
00135 /** Opens logging on stdout. When this is open, the log() signal will be
00136  * emitted when Tor prints a message to stdout. */
00137 void
00138 TorProcess::openStdout()
00139 {
00140   setReadChannelMode(QProcess::MergedChannels);
00141   setReadChannel(QProcess::StandardOutput);
00142 }
00143 
00144 /** Closes logging on stdout. When this is closed, the log() signal will not
00145  * be emitted when Tor prints a message to stdout. */
00146 void
00147 TorProcess::closeStdout()
00148 {
00149   /* Close the stdout channel */
00150   closeReadChannel(QProcess::StandardOutput);
00151   /* Read anything left waiting on the buffer */
00152   onReadyRead();
00153 }
00154 
00155 /** Called when there is data to be read from stdout */
00156 void
00157 TorProcess::onReadyRead()
00158 {
00159   int i, j;
00160   QString line;
00161   
00162   while (canReadLine()) {
00163     line = readLine();
00164     if (!line.isEmpty()) {
00165       /* Parse the log message and emit log() */
00166       i = line.indexOf("[");
00167       j = line.indexOf("]");
00168       if (i > 0 && j > i && line.length() >= j+2) {
00169         emit log(line.mid(i+1, j-i-1), line.mid(j+2));
00170       }
00171     }
00172   }
00173 }
00174 
00175 /** Called when the process encounters an error. If the error tells us that
00176  * the process failed to start, then we will emit the startFailed() signal and
00177  * an error message indicating why. */
00178 void
00179 TorProcess::onError(QProcess::ProcessError error)
00180 {
00181   if (error == QProcess::FailedToStart) {
00182     vWarn("The Tor process failed to start: %1").arg(errorString());
00183     /* Tor didn't start, so let everyone know why. */
00184     emit startFailed(errorString());
00185   } else {
00186     vWarn("Tor process error: %1").arg(errorString());
00187   }
00188 }
00189 

Generated on Wed Sep 5 15:49:28 2007 for Vidalia by  doxygen 1.5.3