Vidalia  0.2.21
TorProcess.cpp
Go to the documentation of this file.
1 /*
2 ** This file is part of Vidalia, and is subject to the license terms in the
3 ** LICENSE file, found in the top level directory of this distribution. If
4 ** you did not receive the LICENSE file with this file, you may obtain it
5 ** from the Vidalia source package distributed by the Vidalia Project at
6 ** http://www.torproject.org/projects/vidalia.html. No part of Vidalia,
7 ** including this file, may be copied, modified, propagated, or distributed
8 ** except according to the terms described in the LICENSE file.
9 */
10 
11 /*
12 ** \file TorProcess.cpp
13 ** \brief Starts and stops a Tor process
14 */
15 
16 #include "TorProcess.h"
17 #include "tcglobal.h"
18 
19 #include "stringutil.h"
20 
21 #include <QString>
22 
23 /* Needed for _PROCESS_INFORMATION so that pid() works on Win32 */
24 #if defined (Q_OS_WIN32)
25 #include <windows.h>
26 #endif
27 
28 
29 /** Default constructor */
30 TorProcess::TorProcess(QObject *parent)
31 : QProcess(parent)
32 {
33  openStdout();
34  connect(this, SIGNAL(readyReadStandardOutput()),
35  this, SLOT(onReadyRead()));
36  connect(this, SIGNAL(error(QProcess::ProcessError)),
37  this, SLOT(onError(QProcess::ProcessError)));
38 }
39 
40 /** Formats the Tor process arguments for logging. */
41 QString
42 TorProcess::formatArguments(const QStringList &args)
43 {
44  QStringList out;
45  foreach (QString arg, args) {
46  out << (arg.contains(" ") || arg.isEmpty() ? string_escape(arg) : arg);
47  }
48  return out.join(" ");
49 }
50 
51 /** Attempts to start the Tor process using the location, executable, and
52  * command-line arguments specified in Vidalia's settings. If Tor starts, the
53  * signal started() will be emitted. If Tor fails to start,
54  * startFailed(errmsg) will be emitted, with an appropriate error message. */
55 void
56 TorProcess::start(const QString &app, const QStringList &args)
57 {
58  QString exe = app;
59 #if defined(Q_OS_WIN32)
60  /* If we're on Windows, QProcess::start requires that paths with spaces are
61  * quoted before being passed to it. */
62  exe = "\"" + exe + "\"";
63 #endif
64 
65  /* Attempt to start Tor with the given command-line arguments */
66  QStringList env = QProcess::systemEnvironment();
67 #if !defined(Q_OS_WIN32)
68  /* Add "/usr/sbin" to an existing $PATH
69  * XXX What if they have no path? Would always just making one with
70  * "/usr/sbin" smart? Should we add anything else? */
71  for (int i = 0; i < env.size(); i++) {
72  QString envVar = env.at(i);
73  if (envVar.startsWith("PATH="))
74  env.replace(i, envVar += ":/usr/sbin");
75  }
76 #endif
77  setEnvironment(env);
78 
79  tc::debug("Starting Tor using '%1 %2'").arg(app).arg(formatArguments(args));
80  QProcess::start(exe, args, QIODevice::ReadOnly | QIODevice::Text);
81 }
82 
83 /** Stops the Tor process */
84 bool
85 TorProcess::stop(QString *errmsg)
86 {
87  /* First, check if the process is already stopped before closing it
88  * forcefully. */
89  if (state() == QProcess::NotRunning) {
90  return true;
91  }
92 
93  tc::debug("Stopping the Tor process.");
94  /* Tell the process to stop */
95 #if defined(Q_OS_WIN32)
96  /* Tor on Windows doesn't understand a WM_CLOSE message (which is what
97  * QProcess::terminate() sends it), so we have to kill it harshly. */
98  kill();
99 #else
100  terminate();
101 
102  /* Wait for it to complete */
103  if (!waitForFinished(5000)) {
104  tc::error("Tor failed to stop: %1").arg(errorString());
105  if (errmsg) {
106  *errmsg =
107  tr("Process %1 failed to stop. [%2]").arg(pid()).arg(errorString());
108  }
109  return false;
110  }
111 #endif
112  return true;
113 }
114 
115 /** Return the process ID for the current process. */
116 quint64
118 {
119 #if defined(Q_OS_WIN32)
120  return (quint64)((QProcess::pid())->dwProcessId);
121 #else
122  return QProcess::pid();
123 #endif
124 }
125 
126 /** Opens logging on stdout. When this is open, the log() signal will be
127  * emitted when Tor prints a message to stdout. */
128 void
130 {
131  setReadChannelMode(QProcess::MergedChannels);
132  setReadChannel(QProcess::StandardOutput);
133 }
134 
135 /** Closes logging on stdout. When this is closed, the log() signal will not
136  * be emitted when Tor prints a message to stdout. */
137 void
139 {
140  /* Close the stdout channel */
141  closeReadChannel(QProcess::StandardOutput);
142  /* Read anything left waiting on the buffer */
143  onReadyRead();
144 }
145 
146 /** Called when there is data to be read from stdout */
147 void
149 {
150  int i, j;
151  QString line;
152 
153  while (canReadLine()) {
154  line = readLine();
155  if (!line.isEmpty()) {
156  /* Parse the log message and emit log() */
157  i = line.indexOf("[");
158  j = line.indexOf("]");
159  if (i > 0 && j > i && line.length() >= j+2) {
160  emit log(line.mid(i+1, j-i-1), line.mid(j+2));
161  }
162  }
163  }
164 }
165 
166 /** Called when the process encounters an error. If the error tells us that
167  * the process failed to start, then we will emit the startFailed() signal and
168  * an error message indicating why. */
169 void
170 TorProcess::onError(QProcess::ProcessError error)
171 {
172  if (error == QProcess::FailedToStart) {
173  tc::error("The Tor process failed to start: %1").arg(errorString());
174  /* Tor didn't start, so let everyone know why. */
175  emit startFailed(errorString());
176  } else {
177  tc::error("Tor process error: %1").arg(errorString());
178  }
179 }
180 
181 /** Returns the version reported by the Tor executable specified in
182  * <b>exe</b>, or a default-constructed QString on failure. */
183 QString
184 TorProcess::version(const QString &exe)
185 {
186  QProcess tor;
187 
188  tor.start(exe, QStringList() << "--version");
189  if (!tor.waitForStarted() || !tor.waitForFinished())
190  return QString();
191 
192  while (tor.canReadLine()) {
193  QString line = tor.readLine();
194  if (line.startsWith("Tor version", Qt::CaseInsensitive)) {
195  QStringList parts = line.split(" ");
196  if (parts.size() >= 3)
197  return parts.at(2);
198  }
199  }
200  return QString();
201 }
202