Vidalia 0.3.1
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 */
30TorProcess::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. */
41QString
42TorProcess::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. */
55void
56TorProcess::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 */
84bool
85TorProcess::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. */
116quint64
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. */
128void
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. */
137void
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 */
147void
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. */
169void
170TorProcess::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. */
183QString
184TorProcess::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
stop errmsg connect(const QHostAddress &address, quint16 port)
void onReadyRead()
Definition: TorProcess.cpp:148
void start(const QString &app, const QStringList &args)
Definition: TorProcess.cpp:56
void log(const QString &severity, const QString &message)
TorProcess(QObject *parent=0)
Definition: TorProcess.cpp:30
void openStdout()
Definition: TorProcess.cpp:129
void onError(QProcess::ProcessError error)
Definition: TorProcess.cpp:170
QString formatArguments(const QStringList &args)
Definition: TorProcess.cpp:42
quint64 pid()
Definition: TorProcess.cpp:117
bool stop(QString *errmsg=0)
Definition: TorProcess.cpp:85
static QString version(const QString &exe)
Definition: TorProcess.cpp:184
void closeStdout()
Definition: TorProcess.cpp:138
void startFailed(const QString &errorMessage)
DebugMessage arg(const QString &a)
Definition: tcglobal.h:48
QString i(QString str)
Definition: html.cpp:32
DebugMessage error(const QString &fmt)
Definition: tcglobal.cpp:40
DebugMessage debug(const QString &fmt)
Definition: tcglobal.cpp:24
QString string_escape(const QString &str)
Definition: stringutil.cpp:131