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 1238 2006-09-25 17:50:57Z edmanm $
00025  * \brief Starts and stops a Tor process
00026  */
00027 
00028 #include <QFileInfo>
00029 #include <QCoreApplication>
00030 
00031 /* Needed for _PROCESS_INFORMATION so that pid() works on Win32 */
00032 #if defined (Q_OS_WIN32)
00033 #include <windows.h>
00034 #endif
00035   
00036 #include "torprocess.h"
00037 
00038 /** Format of log message timestamps Tor prints to stdout */
00039 #define FMT_TIMESTAMP "MMM dd hh:mm:ss.zzz yyyy"
00040 
00041 /** Default constructor */
00042 TorProcess::TorProcess()
00043 {
00044   openStdout();
00045   connect(this, SIGNAL(readyReadStandardOutput()), 
00046           this,   SLOT(onReadyRead()));
00047   connect(this, SIGNAL(error(QProcess::ProcessError)), 
00048           this,   SLOT(onError(QProcess::ProcessError)));
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(QString app, QString args) 
00057 {
00058 #if defined(Q_OS_WIN32)
00059   /* If we're on Windows, QProcess::start requires that paths with spaces are
00060    * quoted before being passed to it. */
00061   app = "\"" + app + "\"";
00062 #endif
00063 
00064   /* Attempt to start Tor with the given command-line arguments */
00065   setEnvironment(QProcess::systemEnvironment());
00066   QProcess::start(app + " " + args, QIODevice::ReadOnly | QIODevice::Text);
00067 }
00068 
00069 /** Stops the Tor process */
00070 bool
00071 TorProcess::stop(QString *errmsg)
00072 {
00073   /* First, check if the process is already stopped before closing it
00074    * forcefully. */
00075   if (state() == QProcess::NotRunning) {
00076     return true;
00077   }
00078 
00079   /* Tell the process to stop */
00080 #if defined(Q_OS_WIN32)
00081   /* Tor on Windows doesn't understand a WM_CLOSE message (which is what 
00082    * QProcess::terminate() sends it), so we have to kill it harshly. */
00083   kill();
00084 #else
00085   terminate();
00086 
00087   /* Wait for it to complete */
00088   if (!waitForFinished(-1)) {
00089     if (errmsg) {
00090       *errmsg = 
00091         tr("Process %1 failed to stop. [%2]").arg(pid()).arg(errorString());
00092     }
00093     return false;
00094   }
00095 #endif
00096   return true;
00097 }
00098 
00099 /** Return the process ID for the current process. */
00100 quint64
00101 TorProcess::pid()
00102 {
00103 #if defined(Q_OS_WIN32)
00104   return (quint64)((QProcess::pid())->dwProcessId);
00105 #else
00106   return QProcess::pid();
00107 #endif
00108 }
00109 
00110 /** Opens logging on stdout. When this is open, the log() signal will be
00111  * emitted when Tor prints a message to stdout. */
00112 void
00113 TorProcess::openStdout()
00114 {
00115   _logState = Open;
00116   setReadChannelMode(QProcess::MergedChannels);
00117   setReadChannel(QProcess::StandardOutput);
00118 }
00119 
00120 /** Closes logging on stdout. When this is closed, the log() signal will not
00121  * be emitted when Tor prints a message to stdout. */
00122 void
00123 TorProcess::closeStdout()
00124 {
00125   _logState = Closing;
00126   _logCloseTime = QDateTime::currentDateTime();
00127 }
00128 
00129 /** Called when there is data to be read from stdout */
00130 void
00131 TorProcess::onReadyRead()
00132 {
00133   int i, j;
00134   while (canReadLine()) {
00135     /* Pull the waiting data from the buffer. Note that we do this even if the
00136      * log event won't be emitted, so that the size of the waiting buffer doesn't 
00137      * keep growing. */
00138     QString line = readLine();
00139     if (_logState == Closed) {
00140       continue;
00141     }
00142     
00143     /* If _logState != Closed, then we will parse the log message and emit log() */
00144     i = line.indexOf("[");
00145     j = line.indexOf("]");
00146     if (i > 0 && j > 0) {
00147       if (_logState == Closing) {
00148         /* Parse the timestamp from this log message */
00149         QString msgTime = QString("%1 %2").arg(line.mid(0, i-1))
00150                                           .arg(_logCloseTime.date().year());
00151           
00152         if (_logCloseTime < QDateTime::fromString(msgTime, FMT_TIMESTAMP)) {
00153           /* We've read all log messages from stdout since closeStdout() was
00154            * called, so go ahead and close stdout for real. */
00155           _logState = Closed;
00156           closeReadChannel(QProcess::StandardOutput);
00157         }
00158       }
00159       if (_logState != Closed) {
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     /* Tor didn't start, so let everyone know why. */
00174     emit startFailed(errorString());
00175   }
00176 }
00177 

Generated on Mon Oct 23 20:08:16 2006 for Vidalia by  doxygen 1.5.0