1 /* Copyright (c) 2001-2004, Roger Dingledine.
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2009, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
9 #ifdef HAVE_EVENT2_EVENT_H
10 #include <event2/event.h>
16 #define GENSRV_SERVICENAME TEXT("tor")
17 #define GENSRV_DISPLAYNAME TEXT("Tor Win32 Service")
18 #define GENSRV_DESCRIPTION \
19 TEXT("Provides an anonymous Internet communication system")
20 #define GENSRV_USERACCT TEXT("NT AUTHORITY\\LocalService")
22 // Cheating: using the pre-defined error codes, tricks Windows into displaying
23 // a semi-related human-readable error message if startup fails as
24 // opposed to simply scaring people with Error: 0xffffffff
25 #define NT_SERVICE_ERROR_TORINIT_FAILED ERROR_EXCEPTION_IN_SERVICE
27 static SERVICE_STATUS service_status
;
28 static SERVICE_STATUS_HANDLE hStatus
;
30 /* XXXX This 'backup argv' and 'backup argc' business is an ugly hack. This
31 * is a job for arguments, not globals. Alas, some of the functions that
32 * use them use them need to have fixed signatures, so they can be passed
33 * to the NT service functions. */
34 static char **backup_argv
;
35 static int backup_argc
;
36 static char* nt_strerror(uint32_t errnum
);
38 static void nt_service_control(DWORD request
);
39 static void nt_service_body(int argc
, char **argv
);
40 static void nt_service_main(void);
41 static SC_HANDLE
nt_service_open_scm(void);
42 static SC_HANDLE
nt_service_open(SC_HANDLE hSCManager
);
43 static int nt_service_start(SC_HANDLE hService
);
44 static int nt_service_stop(SC_HANDLE hService
);
45 static int nt_service_install(int argc
, char **argv
);
46 static int nt_service_remove(void);
47 static int nt_service_cmd_start(void);
48 static int nt_service_cmd_stop(void);
50 /** Struct to hold dynamically loaded NT-service related function pointers.
55 BOOL (WINAPI
*ChangeServiceConfig2A_fn
)(
60 BOOL (WINAPI
*CloseServiceHandle_fn
)(
63 BOOL (WINAPI
*ControlService_fn
)(
66 LPSERVICE_STATUS lpServiceStatus
);
68 SC_HANDLE (WINAPI
*CreateServiceA_fn
)(
70 LPCTSTR lpServiceName
,
71 LPCTSTR lpDisplayName
,
72 DWORD dwDesiredAccess
,
76 LPCTSTR lpBinaryPathName
,
77 LPCTSTR lpLoadOrderGroup
,
79 LPCTSTR lpDependencies
,
80 LPCTSTR lpServiceStartName
,
83 BOOL (WINAPI
*DeleteService_fn
)(
86 SC_HANDLE (WINAPI
*OpenSCManagerA_fn
)(
87 LPCTSTR lpMachineName
,
88 LPCTSTR lpDatabaseName
,
89 DWORD dwDesiredAccess
);
91 SC_HANDLE (WINAPI
*OpenServiceA_fn
)(
93 LPCTSTR lpServiceName
,
94 DWORD dwDesiredAccess
);
96 BOOL (WINAPI
*QueryServiceStatus_fn
)(
98 LPSERVICE_STATUS lpServiceStatus
);
100 SERVICE_STATUS_HANDLE (WINAPI
*RegisterServiceCtrlHandlerA_fn
)(
101 LPCTSTR lpServiceName
,
102 LPHANDLER_FUNCTION lpHandlerProc
);
104 BOOL (WINAPI
*SetServiceStatus_fn
)(SERVICE_STATUS_HANDLE
,
107 BOOL (WINAPI
*StartServiceCtrlDispatcherA_fn
)(
108 const SERVICE_TABLE_ENTRY
* lpServiceTable
);
110 BOOL (WINAPI
*StartServiceA_fn
)(
112 DWORD dwNumServiceArgs
,
113 LPCTSTR
* lpServiceArgVectors
);
115 BOOL (WINAPI
*LookupAccountNameA_fn
)(
116 LPCTSTR lpSystemName
,
117 LPCTSTR lpAccountName
,
120 LPTSTR ReferencedDomainName
,
121 LPDWORD cchReferencedDomainName
,
122 PSID_NAME_USE peUse
);
124 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
125 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
128 /** Loads functions used by NT services. Returns on success, or prints a
129 * complaint to stdout and exits on error. */
131 nt_service_loadlibrary(void)
136 if (service_fns
.loaded
)
139 /* XXXX Possibly, we should hardcode the location of this DLL. */
140 if (!(library
= LoadLibrary("advapi32.dll"))) {
141 log_err(LD_GENERAL
, "Couldn't open advapi32.dll. Are you trying to use "
142 "NT services on Windows 98? That doesn't work.");
146 #define LOAD(f) STMT_BEGIN \
147 if (!(fn = GetProcAddress(library, #f))) { \
149 "Couldn't find %s in advapi32.dll! We probably got the " \
150 "name wrong.", #f); \
153 service_fns.f ## _fn = fn; \
157 LOAD(ChangeServiceConfig2A
);
158 LOAD(CloseServiceHandle
);
159 LOAD(ControlService
);
160 LOAD(CreateServiceA
);
162 LOAD(OpenSCManagerA
);
164 LOAD(QueryServiceStatus
);
165 LOAD(RegisterServiceCtrlHandlerA
);
166 LOAD(SetServiceStatus
);
167 LOAD(StartServiceCtrlDispatcherA
);
169 LOAD(LookupAccountNameA
);
171 service_fns
.loaded
= 1;
175 printf("Unable to load library support for NT services: exiting.\n");
179 /** If we're compiled to run as an NT service, and the service wants to
180 * shut down, then change our current status and return 1. Else
184 nt_service_is_stopping(void)
185 /* XXXX this function would probably _love_ to be inline, in 0.2.0. */
187 /* If we haven't loaded the function pointers, we can't possibly be an NT
188 * service trying to shut down. */
189 if (!service_fns
.loaded
)
192 if (service_status
.dwCurrentState
== SERVICE_STOP_PENDING
) {
193 service_status
.dwWin32ExitCode
= 0;
194 service_status
.dwCurrentState
= SERVICE_STOPPED
;
195 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
197 } else if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
203 /** Set the dwCurrentState field for our service to <b>state</b>. */
205 nt_service_set_state(DWORD state
)
207 service_status
.dwCurrentState
= state
;
210 /** Handles service control requests, such as stopping or starting the
213 nt_service_control(DWORD request
)
215 static struct timeval exit_now
;
217 exit_now
.tv_usec
= 0;
219 nt_service_loadlibrary();
222 case SERVICE_CONTROL_STOP
:
223 case SERVICE_CONTROL_SHUTDOWN
:
224 log_notice(LD_GENERAL
,
225 "Got stop/shutdown request; shutting down cleanly.");
226 service_status
.dwCurrentState
= SERVICE_STOP_PENDING
;
227 event_base_loopexit(tor_libevent_get_base(), &exit_now
);
230 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
233 /** Called when the service is started via the system's service control
234 * manager. This calls tor_init() and starts the main event loop. If
235 * tor_init() fails, the service will be stopped and exit code set to
236 * NT_SERVICE_ERROR_TORINIT_FAILED. */
238 nt_service_body(int argc
, char **argv
)
241 (void) argc
; /* unused */
242 (void) argv
; /* unused */
243 nt_service_loadlibrary();
244 service_status
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
;
245 service_status
.dwCurrentState
= SERVICE_START_PENDING
;
246 service_status
.dwControlsAccepted
=
247 SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
;
248 service_status
.dwWin32ExitCode
= 0;
249 service_status
.dwServiceSpecificExitCode
= 0;
250 service_status
.dwCheckPoint
= 0;
251 service_status
.dwWaitHint
= 1000;
252 hStatus
= service_fns
.RegisterServiceCtrlHandlerA_fn(GENSRV_SERVICENAME
,
253 (LPHANDLER_FUNCTION
) nt_service_control
);
256 /* Failed to register the service control handler function */
260 r
= tor_init(backup_argc
, backup_argv
);
262 /* Failed to start the Tor service */
263 r
= NT_SERVICE_ERROR_TORINIT_FAILED
;
264 service_status
.dwCurrentState
= SERVICE_STOPPED
;
265 service_status
.dwWin32ExitCode
= r
;
266 service_status
.dwServiceSpecificExitCode
= r
;
267 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
271 /* Set the service's status to SERVICE_RUNNING and start the main
273 service_status
.dwCurrentState
= SERVICE_RUNNING
;
274 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
279 /** Main service entry point. Starts the service control dispatcher and waits
280 * until the service status is set to SERVICE_STOPPED. */
282 nt_service_main(void)
284 SERVICE_TABLE_ENTRY table
[2];
287 nt_service_loadlibrary();
288 table
[0].lpServiceName
= (char*)GENSRV_SERVICENAME
;
289 table
[0].lpServiceProc
= (LPSERVICE_MAIN_FUNCTION
)nt_service_body
;
290 table
[1].lpServiceName
= NULL
;
291 table
[1].lpServiceProc
= NULL
;
293 if (!service_fns
.StartServiceCtrlDispatcherA_fn(table
)) {
294 result
= GetLastError();
295 errmsg
= nt_strerror(result
);
296 printf("Service error %d : %s\n", (int) result
, errmsg
);
298 if (result
== ERROR_FAILED_SERVICE_CONTROLLER_CONNECT
) {
299 if (tor_init(backup_argc
, backup_argv
) < 0)
301 switch (get_options()->command
) {
305 case CMD_LIST_FINGERPRINT
:
306 case CMD_HASH_PASSWORD
:
307 case CMD_VERIFY_CONFIG
:
308 log_err(LD_CONFIG
, "Unsupported command (--list-fingerprint, "
309 "--hash-password, or --verify-config) in NT service.");
311 case CMD_RUN_UNITTESTS
:
313 log_err(LD_CONFIG
, "Illegal command number %d: internal error.",
314 get_options()->command
);
321 /** Return a handle to the service control manager on success, or NULL on
324 nt_service_open_scm(void)
326 SC_HANDLE hSCManager
;
329 nt_service_loadlibrary();
330 if ((hSCManager
= service_fns
.OpenSCManagerA_fn(
331 NULL
, NULL
, SC_MANAGER_CREATE_SERVICE
)) == NULL
) {
332 errmsg
= nt_strerror(GetLastError());
333 printf("OpenSCManager() failed : %s\n", errmsg
);
339 /** Open a handle to the Tor service using <b>hSCManager</b>. Return NULL
342 nt_service_open(SC_HANDLE hSCManager
)
346 nt_service_loadlibrary();
347 if ((hService
= service_fns
.OpenServiceA_fn(hSCManager
, GENSRV_SERVICENAME
,
348 SERVICE_ALL_ACCESS
)) == NULL
) {
349 errmsg
= nt_strerror(GetLastError());
350 printf("OpenService() failed : %s\n", errmsg
);
356 /** Start the Tor service. Return 0 if the service is started or was
357 * previously running. Return -1 on error. */
359 nt_service_start(SC_HANDLE hService
)
363 nt_service_loadlibrary();
365 service_fns
.QueryServiceStatus_fn(hService
, &service_status
);
366 if (service_status
.dwCurrentState
== SERVICE_RUNNING
) {
367 printf("Service is already running\n");
371 if (service_fns
.StartServiceA_fn(hService
, 0, NULL
)) {
372 /* Loop until the service has finished attempting to start */
373 while (service_fns
.QueryServiceStatus_fn(hService
, &service_status
) &&
374 (service_status
.dwCurrentState
== SERVICE_START_PENDING
)) {
378 /* Check if it started successfully or not */
379 if (service_status
.dwCurrentState
== SERVICE_RUNNING
) {
380 printf("Service started successfully\n");
383 errmsg
= nt_strerror(service_status
.dwWin32ExitCode
);
384 printf("Service failed to start : %s\n", errmsg
);
388 errmsg
= nt_strerror(GetLastError());
389 printf("StartService() failed : %s\n", errmsg
);
395 /** Stop the Tor service. Return 0 if the service is stopped or was not
396 * previously running. Return -1 on error. */
398 nt_service_stop(SC_HANDLE hService
)
400 /** Wait at most 10 seconds for the service to stop. */
401 #define MAX_SERVICE_WAIT_TIME 10
404 nt_service_loadlibrary();
406 service_fns
.QueryServiceStatus_fn(hService
, &service_status
);
407 if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
408 printf("Service is already stopped\n");
412 if (service_fns
.ControlService_fn(hService
, SERVICE_CONTROL_STOP
,
415 while (service_fns
.QueryServiceStatus_fn(hService
, &service_status
) &&
416 (service_status
.dwCurrentState
!= SERVICE_STOPPED
) &&
417 (wait_time
< MAX_SERVICE_WAIT_TIME
)) {
421 if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
422 printf("Service stopped successfully\n");
424 } else if (wait_time
== MAX_SERVICE_WAIT_TIME
) {
425 printf("Service did not stop within %d seconds.\n", wait_time
);
427 errmsg
= nt_strerror(GetLastError());
428 printf("QueryServiceStatus() failed : %s\n",errmsg
);
432 errmsg
= nt_strerror(GetLastError());
433 printf("ControlService() failed : %s\n", errmsg
);
439 /** Build a formatted command line used for the NT service. Return a
440 * pointer to the formatted string on success, or NULL on failure. Set
441 * *<b>using_default_torrc</b> to true if we're going to use the default
442 * location to torrc, or 1 if an option was specified on the command line.
445 nt_service_command_line(int *using_default_torrc
)
447 TCHAR tor_exe
[MAX_PATH
+1];
448 char *command
, *options
=NULL
;
451 *using_default_torrc
= 1;
453 /* Get the location of tor.exe */
454 if (0 == GetModuleFileName(NULL
, tor_exe
, MAX_PATH
))
457 /* Get the service arguments */
458 sl
= smartlist_create();
459 for (i
= 1; i
< backup_argc
; ++i
) {
460 if (!strcmp(backup_argv
[i
], "--options") ||
461 !strcmp(backup_argv
[i
], "-options")) {
462 while (++i
< backup_argc
) {
463 if (!strcmp(backup_argv
[i
], "-f"))
464 *using_default_torrc
= 0;
465 smartlist_add(sl
, backup_argv
[i
]);
469 if (smartlist_len(sl
))
470 options
= smartlist_join_strings(sl
,"\" \"",0,NULL
);
473 /* Allocate a string for the NT service command line */
474 cmdlen
= strlen(tor_exe
) + (options
?strlen(options
):0) + 32;
475 command
= tor_malloc(cmdlen
);
477 /* Format the service command */
479 if (tor_snprintf(command
, cmdlen
, "\"%s\" --nt-service \"%s\"",
480 tor_exe
, options
)<0) {
481 tor_free(command
); /* sets command to NULL. */
483 } else { /* ! options */
484 if (tor_snprintf(command
, cmdlen
, "\"%s\" --nt-service", tor_exe
)<0) {
485 tor_free(command
); /* sets command to NULL. */
493 /** Creates a Tor NT service, set to start on boot. The service will be
494 * started if installation succeeds. Returns 0 on success, or -1 on
497 nt_service_install(int argc
, char **argv
)
499 /* Notes about developing NT services:
501 * 1. Don't count on your CWD. If an absolute path is not given, the
502 * fopen() function goes wrong.
503 * 2. The parameters given to the nt_service_body() function differ
504 * from those given to main() function.
507 SC_HANDLE hSCManager
= NULL
;
508 SC_HANDLE hService
= NULL
;
509 SERVICE_DESCRIPTION sdBuff
;
512 const char *user_acct
= GENSRV_USERACCT
;
513 const char *password
= "";
515 OSVERSIONINFOEX info
;
517 DWORD sidLen
= 0, domainLen
= 0;
518 int is_win2k_or_worse
= 0;
519 int using_default_torrc
= 0;
521 nt_service_loadlibrary();
523 /* Open the service control manager so we can create a new service */
524 if ((hSCManager
= nt_service_open_scm()) == NULL
)
526 /* Build the command line used for the service */
527 if ((command
= nt_service_command_line(&using_default_torrc
)) == NULL
) {
528 printf("Unable to build service command line.\n");
529 service_fns
.CloseServiceHandle_fn(hSCManager
);
533 for (i
=1; i
< argc
; ++i
) {
534 if (!strcmp(argv
[i
], "--user") && i
+1<argc
) {
535 user_acct
= argv
[i
+1];
538 if (!strcmp(argv
[i
], "--password") && i
+1<argc
) {
539 password
= argv
[i
+1];
544 /* Compute our version and see whether we're running win2k or earlier. */
545 memset(&info
, 0, sizeof(info
));
546 info
.dwOSVersionInfoSize
= sizeof(info
);
547 if (! GetVersionEx((LPOSVERSIONINFO
)&info
)) {
548 printf("Call to GetVersionEx failed.\n");
549 is_win2k_or_worse
= 1;
551 if (info
.dwMajorVersion
< 5 ||
552 (info
.dwMajorVersion
== 5 && info
.dwMinorVersion
== 0))
553 is_win2k_or_worse
= 1;
556 if (user_acct
== GENSRV_USERACCT
) {
557 if (is_win2k_or_worse
) {
558 /* On Win2k, there is no LocalService account, so we actually need to
559 * fall back on NULL (the system account). */
560 printf("Running on Win2K or earlier, so the LocalService account "
561 "doesn't exist. Falling back to SYSTEM account.\n");
564 /* Genericity is apparently _so_ last year in Redmond, where some
565 * accounts are accounts that you can look up, and some accounts
566 * are magic and undetectable via the security subsystem. See
567 * http://msdn2.microsoft.com/en-us/library/ms684188.aspx
569 printf("Running on a Post-Win2K OS, so we'll assume that the "
570 "LocalService account exists.\n");
572 } else if (0 && service_fns
.LookupAccountNameA_fn(NULL
, // On this system
574 NULL
, &sidLen
, // Don't care about the SID
575 NULL
, &domainLen
, // Don't care about the domain
577 /* XXXX For some reason, the above test segfaults. Fix that. */
578 printf("User \"%s\" doesn't seem to exist.\n", user_acct
);
581 printf("Will try to install service as user \"%s\".\n", user_acct
);
583 /* XXXX This warning could be better about explaining how to resolve the
585 if (using_default_torrc
)
586 printf("IMPORTANT NOTE:\n"
587 " The Tor service will run under the account \"%s\". This means\n"
588 " that Tor will look for its configuration file under that\n"
589 " account's Application Data directory, which is probably not\n"
590 " the same as yours.\n", user_acct
?user_acct
:"<local system>");
592 /* Create the Tor service, set to auto-start on boot */
593 if ((hService
= service_fns
.CreateServiceA_fn(hSCManager
, GENSRV_SERVICENAME
,
595 SERVICE_ALL_ACCESS
, SERVICE_WIN32_OWN_PROCESS
,
596 SERVICE_AUTO_START
, SERVICE_ERROR_IGNORE
,
597 command
, NULL
, NULL
, NULL
,
598 user_acct
, password
)) == NULL
) {
599 errmsg
= nt_strerror(GetLastError());
600 printf("CreateService() failed : %s\n", errmsg
);
601 service_fns
.CloseServiceHandle_fn(hSCManager
);
606 printf("Done with CreateService.\n");
608 /* Set the service's description */
609 sdBuff
.lpDescription
= (char*)GENSRV_DESCRIPTION
;
610 service_fns
.ChangeServiceConfig2A_fn(hService
, SERVICE_CONFIG_DESCRIPTION
,
612 printf("Service installed successfully\n");
614 /* Start the service initially */
615 nt_service_start(hService
);
617 service_fns
.CloseServiceHandle_fn(hService
);
618 service_fns
.CloseServiceHandle_fn(hSCManager
);
624 /** Removes the Tor NT service. Returns 0 if the service was successfully
625 * removed, or -1 on error. */
627 nt_service_remove(void)
629 SC_HANDLE hSCManager
= NULL
;
630 SC_HANDLE hService
= NULL
;
633 nt_service_loadlibrary();
634 if ((hSCManager
= nt_service_open_scm()) == NULL
)
636 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
637 service_fns
.CloseServiceHandle_fn(hSCManager
);
641 nt_service_stop(hService
);
642 if (service_fns
.DeleteService_fn(hService
) == FALSE
) {
643 errmsg
= nt_strerror(GetLastError());
644 printf("DeleteService() failed : %s\n", errmsg
);
646 service_fns
.CloseServiceHandle_fn(hService
);
647 service_fns
.CloseServiceHandle_fn(hSCManager
);
651 service_fns
.CloseServiceHandle_fn(hService
);
652 service_fns
.CloseServiceHandle_fn(hSCManager
);
653 printf("Service removed successfully\n");
658 /** Starts the Tor service. Returns 0 on success, or -1 on error. */
660 nt_service_cmd_start(void)
662 SC_HANDLE hSCManager
;
666 if ((hSCManager
= nt_service_open_scm()) == NULL
)
668 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
669 service_fns
.CloseServiceHandle_fn(hSCManager
);
673 start
= nt_service_start(hService
);
674 service_fns
.CloseServiceHandle_fn(hService
);
675 service_fns
.CloseServiceHandle_fn(hSCManager
);
680 /** Stops the Tor service. Returns 0 on success, or -1 on error. */
682 nt_service_cmd_stop(void)
684 SC_HANDLE hSCManager
;
688 if ((hSCManager
= nt_service_open_scm()) == NULL
)
690 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
691 service_fns
.CloseServiceHandle_fn(hSCManager
);
695 stop
= nt_service_stop(hService
);
696 service_fns
.CloseServiceHandle_fn(hService
);
697 service_fns
.CloseServiceHandle_fn(hSCManager
);
702 /** Given a Win32 error code, this attempts to make Windows
703 * return a human-readable error message. The char* returned
704 * is allocated by Windows, but should be freed with LocalFree()
705 * when finished with it. */
707 nt_strerror(uint32_t errnum
)
710 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
,
711 NULL
, errnum
, MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
712 (LPSTR
)&msgbuf
, 0, NULL
);
717 nt_service_parse_options(int argc
, char **argv
, int *should_exit
)
724 (!strcmp(argv
[1], "-service") || !strcmp(argv
[1], "--service"))) {
725 nt_service_loadlibrary();
726 if (!strcmp(argv
[2], "install"))
727 return nt_service_install(argc
, argv
);
728 if (!strcmp(argv
[2], "remove"))
729 return nt_service_remove();
730 if (!strcmp(argv
[2], "start"))
731 return nt_service_cmd_start();
732 if (!strcmp(argv
[2], "stop"))
733 return nt_service_cmd_stop();
734 printf("Unrecognized service command '%s'\n", argv
[2]);
739 if (!strcmp(argv
[1], "-nt-service") || !strcmp(argv
[1], "--nt-service")) {
740 nt_service_loadlibrary();
745 // These values have been deprecated since 0.1.1.2-alpha; we've warned
746 // about them since 0.1.2.7-alpha.
747 if (!strcmp(argv
[1], "-install") || !strcmp(argv
[1], "--install")) {
748 nt_service_loadlibrary();
750 "The %s option is deprecated; use \"--service install\" instead.",
753 return nt_service_install(argc
, argv
);
755 if (!strcmp(argv
[1], "-remove") || !strcmp(argv
[1], "--remove")) {
756 nt_service_loadlibrary();
758 "The %s option is deprecated; use \"--service remove\" instead.",
761 return nt_service_remove();