Vidalia 0.3.1
TorService.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 TorService.cpp
13** \brief Starts, stops, installs, and uninstalls a Tor service (Win32).
14*/
15
16#include "TorService.h"
17#include "tcglobal.h"
18
19#include <QLibrary>
20
21/** Returned by TorService::exitCode() when we are unable to determine the
22 * actual exit code of the service (unless, of course, Tor returns -999999). */
23#define UNKNOWN_EXIT_CODE -999999
24
25/** List of dynamically loaded NT service functions. */
27 { false,
28 NULL, NULL, NULL, NULL, NULL,
29 NULL, NULL, NULL, NULL, NULL
30 };
31
32
33/** Default ctor. */
34TorService::TorService(QObject *parent)
35 : QObject(parent)
36{
37 _scm = openSCM();
38}
39
40/** Default dtor. */
42{
44}
45
46/** Returns true if services are supported. */
47bool
49{
50 return (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based);
51}
52
53/** Dyanmically loads NT service related functions from advapi32.dll. This
54 * function is adapted from Tor's nt_service_load_library() function. See
55 * LICENSE for details on Tor's license. */
56bool
58{
59#define LOAD_SERVICE_FN(f) do { \
60 void *fn; \
61 if (!((fn = QLibrary::resolve("advapi32", #f)))) { \
62 return false; \
63 } else { \
64 _service_fns.f = (f ## _fn) fn; \
65 } \
66 } while (0)
67
68 if (!isSupported()) {
69 _service_fns.loaded = false;
70 } else if (!_service_fns.loaded) {
71 LOAD_SERVICE_FN(ChangeServiceConfig2A);
72 LOAD_SERVICE_FN(CloseServiceHandle);
73 LOAD_SERVICE_FN(ControlService);
74 LOAD_SERVICE_FN(CreateServiceA);
75 LOAD_SERVICE_FN(DeleteService);
76 LOAD_SERVICE_FN(OpenSCManagerA);
77 LOAD_SERVICE_FN(OpenServiceA);
78 LOAD_SERVICE_FN(QueryServiceStatus);
79 LOAD_SERVICE_FN(SetServiceStatus);
80 LOAD_SERVICE_FN(StartServiceA);
81 _service_fns.loaded = true;
82 }
83 return _service_fns.loaded;
84}
85
86/** Opens a handle to the Tor service. Returns NULL on error. */
87SC_HANDLE
89{
91 return NULL;
92 if (!_scm)
93 return NULL;
95 (LPCTSTR)TOR_SERVICE_NAME,
97}
98
99/** Opens a handle to the service control manager. Returns NULL on error. */
100SC_HANDLE
102{
104 return NULL;
105 return _service_fns.OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
106}
107
108/** Closes the service <b>handle</b>. */
109void
110TorService::closeHandle(SC_HANDLE handle)
111{
113 return;
115}
116
117/** Returns true if the Tor service is installed. */
118bool
120{
121 bool installed;
122 SC_HANDLE service = openService();
123 installed = (service != NULL);
124 closeHandle(service);
125 return installed;
126}
127
128/** Returns true if the Tor service is running. */
129bool
131{
132 return (status() == SERVICE_RUNNING);
133}
134
135/** Starts Tor service. */
136void
138{
139 SC_HANDLE service = openService();
140
141 if (!service) {
142 tc::error("Bug: We tried to start the Tor service, but it is not installed.");
143 emit startFailed(tr("The Tor service is not installed."));
144 return;
145 }
146
147 /* Starting a service can take up to 30 seconds! */
148 if (status() != SERVICE_RUNNING) {
149 int tries = 0;
150 tc::debug("Starting the Tor service.");
151 _service_fns.StartServiceA(service, 0, NULL);
152
153 while ((status() != SERVICE_RUNNING) && ++tries <= 5)
154 Sleep(1000);
155 }
156
157 if (status() == SERVICE_RUNNING) {
158 emit started();
159 } else {
160 tc::error("Unable to start the Tor service.");
161 emit startFailed(tr("Unable to start the Tor service."));
162 }
163 closeHandle(service);
164}
165
166/** Stops Tor service. */
167bool
169{
170 SC_HANDLE service = openService();
171
172 if (!service)
173 return false;
174
175 if (status() != SERVICE_STOPPED) {
176 SERVICE_STATUS stat;
177 stat.dwCurrentState = SERVICE_RUNNING;
178 tc::debug("Stopping the Tor service.");
179 if (_service_fns.ControlService(service, SERVICE_CONTROL_STOP, &stat)) {
180 /* XXX Five seconds isn't long enough to wait when we're stopping a Tor
181 * that is running as a server, but we don't want to block for 30
182 * seconds. It would be nice if we could get an async notification when
183 * the service stops or fails to stop. */
184 int tries = 0;
185 while ((status() != SERVICE_STOPPED) && (++tries <= 5))
186 Sleep(1000);
187 }
188 }
189 closeHandle(service);
190
191 /* Find out if the service really stopped and return the result */
192 if (status() == SERVICE_STOPPED) {
193 emit finished(exitCode(), exitStatus());
194 return true;
195 }
196 /* XXX This needs an actual reason message. */
197 tc::error("Unable to stop the Tor service.");
198 return false;
199}
200
201/** Returns the exit code of the last Tor service that finished. */
202int
204{
205 SC_HANDLE service;
207
208 service = openService();
209 if (service) {
210 SERVICE_STATUS s;
211 if (_service_fns.QueryServiceStatus(service, &s)) {
212 /* Services return one exit code, but it could be in one of two
213 * variables. Fun. */
214 exitCode = (int)(s.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR
215 ? s.dwServiceSpecificExitCode
216 : s.dwWin32ExitCode);
217 }
218 closeHandle(service);
219 }
220 return exitCode;
221}
222
223/** Returns the exit status of the last Tor service that finished. */
224QProcess::ExitStatus
226{
227 /* NT services don't really have an equivalent to QProcess::CrashExit, so
228 * this just returns QProcess::NormalExit. Tor _could_ set
229 * dwServiceSpecificExitCode to some magic value when it starts and then
230 * set it to the real exit code when Tor exits. Then we would know if the
231 * service crashed when dwServiceSpecificExitCode is still the magic value.
232 * However, I don't care and it doesn't really matter anyway. */
233 return QProcess::NormalExit;
234}
235
236/** Installs the Tor service. Returns true if the service was successfully
237 * installed or already exists. */
238bool
239TorService::install(const QString &torPath, const QString &torrc,
240 quint16 controlPort)
241{
242 SC_HANDLE service;
243
244 if (!_scm)
245 return false;
246
247 service = openService();
248 if (!service) {
249 QString command = QString("\"%1\" --nt-service -f \"%2\" ControlPort %3")
250 .arg(torPath)
251 .arg(torrc)
252 .arg(controlPort);
253
254 tc::debug("Installing the Tor service using the command line '%1'")
255 .arg(command);
257 (LPCTSTR)TOR_SERVICE_NAME, (LPCTSTR)TOR_SERVICE_DISP,
258 TOR_SERVICE_ACCESS, SERVICE_WIN32_OWN_PROCESS,
259 SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
260 (LPCTSTR)command.toAscii().data(), NULL, NULL, NULL,
261 NULL, NULL);
262 if (!service) {
263 /* XXX This needs an actual reason message. */
264 tc::error("Failed to install the Tor service.");
265 return false;
266 }
267
268 SERVICE_DESCRIPTION desc;
269 desc.lpDescription = TOR_SERVICE_DESC;
271 SERVICE_CONFIG_DESCRIPTION, &desc);
272 closeHandle(service);
273 }
274 return true;
275}
276
277/** Removes the Tor service. Returns true if the service was removed
278 * successfully or does not exist. */
279bool
281{
282 bool removed = true;
283 SC_HANDLE service = openService();
284
285 if (service) {
286 stop();
287 tc::debug("Removing the Tor service.");
288 removed = _service_fns.DeleteService(service);
289 closeHandle(service);
290 }
291 if (!removed) {
292 /* XXX This needs an actual reason message. */
293 tc::error("Failed to remove the Tor service.");
294 }
295 return removed;
296}
297
298/** Gets the status of the Tor service. */
299DWORD
301{
302 SC_HANDLE service;
303 SERVICE_STATUS s;
304 DWORD stat = SERVICE_ERROR;
305
306 service = openService();
307 if (service && _service_fns.QueryServiceStatus(service, &s))
308 stat = s.dwCurrentState;
309 closeHandle(service);
310 return stat;
311}
312
#define UNKNOWN_EXIT_CODE
Definition: TorService.cpp:23
#define LOAD_SERVICE_FN(f)
#define TOR_SERVICE_DISP
Definition: TorService.h:24
#define TOR_SERVICE_DESC
Definition: TorService.h:25
#define SERVICE_ERROR
Definition: TorService.h:28
#define TOR_SERVICE_NAME
Definition: TorService.h:23
#define TOR_SERVICE_ACCESS
Definition: TorService.h:27
SC_HANDLE openService()
Definition: TorService.cpp:88
void finished(int exitCode, QProcess::ExitStatus)
bool stop()
Definition: TorService.cpp:168
bool isRunning()
Definition: TorService.cpp:130
bool remove()
Definition: TorService.cpp:280
int exitCode()
Definition: TorService.cpp:203
SC_HANDLE _scm
Definition: TorService.h:145
static ServiceFunctions _service_fns
Definition: TorService.h:147
static void closeHandle(SC_HANDLE handle)
Definition: TorService.cpp:110
TorService(QObject *parent=0)
Definition: TorService.cpp:34
DWORD status()
Definition: TorService.cpp:300
bool isInstalled()
Definition: TorService.cpp:119
QProcess::ExitStatus exitStatus()
Definition: TorService.cpp:225
void started()
static SC_HANDLE openSCM()
Definition: TorService.cpp:101
void startFailed(QString error)
void start()
Definition: TorService.cpp:137
bool install(const QString &torPath, const QString &torrc, quint16 controlPort)
Definition: TorService.cpp:239
static bool isSupported()
Definition: TorService.cpp:48
static bool loadServiceFunctions()
Definition: TorService.cpp:57
DebugMessage arg(const QString &a)
Definition: tcglobal.h:48
DebugMessage error(const QString &fmt)
Definition: tcglobal.cpp:40
DebugMessage debug(const QString &fmt)
Definition: tcglobal.cpp:24
OpenSCManagerA_fn OpenSCManagerA
Definition: TorService.h:85
StartServiceA_fn StartServiceA
Definition: TorService.h:89
QueryServiceStatus_fn QueryServiceStatus
Definition: TorService.h:87
CloseServiceHandle_fn CloseServiceHandle
Definition: TorService.h:81
OpenServiceA_fn OpenServiceA
Definition: TorService.h:86
ControlService_fn ControlService
Definition: TorService.h:82
ChangeServiceConfig2A_fn ChangeServiceConfig2A
Definition: TorService.h:80
CreateServiceA_fn CreateServiceA
Definition: TorService.h:83
DeleteService_fn DeleteService
Definition: TorService.h:84