1 /* Copyright (c) 2001-2004, Roger Dingledine.
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2010, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
12 #ifdef HAVE_EVENT2_EVENT_H
13 #include <event2/event.h>
19 #define GENSRV_SERVICENAME "tor"
20 #define GENSRV_DISPLAYNAME "Tor Win32 Service"
21 #define GENSRV_DESCRIPTION \
22 "Provides an anonymous Internet communication system"
23 #define GENSRV_USERACCT "NT AUTHORITY\\LocalService"
25 // Cheating: using the pre-defined error codes, tricks Windows into displaying
26 // a semi-related human-readable error message if startup fails as
27 // opposed to simply scaring people with Error: 0xffffffff
28 #define NT_SERVICE_ERROR_TORINIT_FAILED ERROR_EXCEPTION_IN_SERVICE
30 static SERVICE_STATUS service_status
;
31 static SERVICE_STATUS_HANDLE hStatus
;
33 /* XXXX This 'backup argv' and 'backup argc' business is an ugly hack. This
34 * is a job for arguments, not globals. Alas, some of the functions that
35 * use them use them need to have fixed signatures, so they can be passed
36 * to the NT service functions. */
37 static char **backup_argv
;
38 static int backup_argc
;
40 static void nt_service_control(DWORD request
);
41 static void nt_service_body(int argc
, char **argv
);
42 static void nt_service_main(void);
43 static SC_HANDLE
nt_service_open_scm(void);
44 static SC_HANDLE
nt_service_open(SC_HANDLE hSCManager
);
45 static int nt_service_start(SC_HANDLE hService
);
46 static int nt_service_stop(SC_HANDLE hService
);
47 static int nt_service_install(int argc
, char **argv
);
48 static int nt_service_remove(void);
49 static int nt_service_cmd_start(void);
50 static int nt_service_cmd_stop(void);
52 /** Struct to hold dynamically loaded NT-service related function pointers.
57 BOOL (WINAPI
*ChangeServiceConfig2A_fn
)(
62 BOOL (WINAPI
*CloseServiceHandle_fn
)(
65 BOOL (WINAPI
*ControlService_fn
)(
68 LPSERVICE_STATUS lpServiceStatus
);
70 SC_HANDLE (WINAPI
*CreateServiceA_fn
)(
74 DWORD dwDesiredAccess
,
78 LPCSTR lpBinaryPathName
,
79 LPCSTR lpLoadOrderGroup
,
81 LPCSTR lpDependencies
,
82 LPCSTR lpServiceStartName
,
85 BOOL (WINAPI
*DeleteService_fn
)(
88 SC_HANDLE (WINAPI
*OpenSCManagerA_fn
)(
90 LPCSTR lpDatabaseName
,
91 DWORD dwDesiredAccess
);
93 SC_HANDLE (WINAPI
*OpenServiceA_fn
)(
96 DWORD dwDesiredAccess
);
98 BOOL (WINAPI
*QueryServiceStatus_fn
)(
100 LPSERVICE_STATUS lpServiceStatus
);
102 SERVICE_STATUS_HANDLE (WINAPI
*RegisterServiceCtrlHandlerA_fn
)(
103 LPCSTR lpServiceName
,
104 LPHANDLER_FUNCTION lpHandlerProc
);
106 BOOL (WINAPI
*SetServiceStatus_fn
)(SERVICE_STATUS_HANDLE
,
109 BOOL (WINAPI
*StartServiceCtrlDispatcherA_fn
)(
110 const SERVICE_TABLE_ENTRYA
* lpServiceTable
);
112 BOOL (WINAPI
*StartServiceA_fn
)(
114 DWORD dwNumServiceArgs
,
115 LPCSTR
* lpServiceArgVectors
);
117 BOOL (WINAPI
*LookupAccountNameA_fn
)(
119 LPCSTR lpAccountName
,
122 LPTSTR ReferencedDomainName
,
123 LPDWORD cchReferencedDomainName
,
124 PSID_NAME_USE peUse
);
126 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
127 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
130 /** Loads functions used by NT services. Returns on success, or prints a
131 * complaint to stdout and exits on error. */
133 nt_service_loadlibrary(void)
138 if (service_fns
.loaded
)
141 if (!(library
= load_windows_system_library(TEXT("advapi32.dll")))) {
142 log_err(LD_GENERAL
, "Couldn't open advapi32.dll. Are you trying to use "
143 "NT services on Windows 98? That doesn't work.");
147 #define LOAD(f) STMT_BEGIN \
148 if (!(fn = GetProcAddress(library, #f))) { \
150 "Couldn't find %s in advapi32.dll! We probably got the " \
151 "name wrong.", #f); \
154 service_fns.f ## _fn = fn; \
158 LOAD(ChangeServiceConfig2A
);
159 LOAD(CloseServiceHandle
);
160 LOAD(ControlService
);
161 LOAD(CreateServiceA
);
163 LOAD(OpenSCManagerA
);
165 LOAD(QueryServiceStatus
);
166 LOAD(RegisterServiceCtrlHandlerA
);
167 LOAD(SetServiceStatus
);
168 LOAD(StartServiceCtrlDispatcherA
);
170 LOAD(LookupAccountNameA
);
172 service_fns
.loaded
= 1;
176 printf("Unable to load library support for NT services: exiting.\n");
180 /** If we're compiled to run as an NT service, and the service wants to
181 * shut down, then change our current status and return 1. Else
185 nt_service_is_stopping(void)
186 /* XXXX this function would probably _love_ to be inline, in 0.2.0. */
188 /* If we haven't loaded the function pointers, we can't possibly be an NT
189 * service trying to shut down. */
190 if (!service_fns
.loaded
)
193 if (service_status
.dwCurrentState
== SERVICE_STOP_PENDING
) {
194 service_status
.dwWin32ExitCode
= 0;
195 service_status
.dwCurrentState
= SERVICE_STOPPED
;
196 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
198 } else if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
204 /** Set the dwCurrentState field for our service to <b>state</b>. */
206 nt_service_set_state(DWORD state
)
208 service_status
.dwCurrentState
= state
;
211 /** Handles service control requests, such as stopping or starting the
214 nt_service_control(DWORD request
)
216 static struct timeval exit_now
;
218 exit_now
.tv_usec
= 0;
220 nt_service_loadlibrary();
223 case SERVICE_CONTROL_STOP
:
224 case SERVICE_CONTROL_SHUTDOWN
:
225 log_notice(LD_GENERAL
,
226 "Got stop/shutdown request; shutting down cleanly.");
227 service_status
.dwCurrentState
= SERVICE_STOP_PENDING
;
228 event_base_loopexit(tor_libevent_get_base(), &exit_now
);
231 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
234 /** Called when the service is started via the system's service control
235 * manager. This calls tor_init() and starts the main event loop. If
236 * tor_init() fails, the service will be stopped and exit code set to
237 * NT_SERVICE_ERROR_TORINIT_FAILED. */
239 nt_service_body(int argc
, char **argv
)
242 (void) argc
; /* unused */
243 (void) argv
; /* unused */
244 nt_service_loadlibrary();
245 service_status
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
;
246 service_status
.dwCurrentState
= SERVICE_START_PENDING
;
247 service_status
.dwControlsAccepted
=
248 SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
;
249 service_status
.dwWin32ExitCode
= 0;
250 service_status
.dwServiceSpecificExitCode
= 0;
251 service_status
.dwCheckPoint
= 0;
252 service_status
.dwWaitHint
= 1000;
253 hStatus
= service_fns
.RegisterServiceCtrlHandlerA_fn(GENSRV_SERVICENAME
,
254 (LPHANDLER_FUNCTION
) nt_service_control
);
257 /* Failed to register the service control handler function */
261 r
= tor_init(backup_argc
, backup_argv
);
263 /* Failed to start the Tor service */
264 r
= NT_SERVICE_ERROR_TORINIT_FAILED
;
265 service_status
.dwCurrentState
= SERVICE_STOPPED
;
266 service_status
.dwWin32ExitCode
= r
;
267 service_status
.dwServiceSpecificExitCode
= r
;
268 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
272 /* Set the service's status to SERVICE_RUNNING and start the main
274 service_status
.dwCurrentState
= SERVICE_RUNNING
;
275 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
280 /** Main service entry point. Starts the service control dispatcher and waits
281 * until the service status is set to SERVICE_STOPPED. */
283 nt_service_main(void)
285 SERVICE_TABLE_ENTRYA table
[2];
288 nt_service_loadlibrary();
289 table
[0].lpServiceName
= (char*)GENSRV_SERVICENAME
;
290 table
[0].lpServiceProc
= (LPSERVICE_MAIN_FUNCTIONA
)nt_service_body
;
291 table
[1].lpServiceName
= NULL
;
292 table
[1].lpServiceProc
= NULL
;
294 if (!service_fns
.StartServiceCtrlDispatcherA_fn(table
)) {
295 result
= GetLastError();
296 errmsg
= format_win32_error(result
);
297 printf("Service error %d : %s\n", (int) result
, errmsg
);
299 if (result
== ERROR_FAILED_SERVICE_CONTROLLER_CONNECT
) {
300 if (tor_init(backup_argc
, backup_argv
) < 0)
302 switch (get_options()->command
) {
306 case CMD_LIST_FINGERPRINT
:
307 case CMD_HASH_PASSWORD
:
308 case CMD_VERIFY_CONFIG
:
309 log_err(LD_CONFIG
, "Unsupported command (--list-fingerprint, "
310 "--hash-password, or --verify-config) in NT service.");
312 case CMD_RUN_UNITTESTS
:
314 log_err(LD_CONFIG
, "Illegal command number %d: internal error.",
315 get_options()->command
);
322 /** Return a handle to the service control manager on success, or NULL on
325 nt_service_open_scm(void)
327 SC_HANDLE hSCManager
;
330 nt_service_loadlibrary();
331 if ((hSCManager
= service_fns
.OpenSCManagerA_fn(
332 NULL
, NULL
, SC_MANAGER_CREATE_SERVICE
)) == NULL
) {
333 errmsg
= format_win32_error(GetLastError());
334 printf("OpenSCManager() failed : %s\n", errmsg
);
340 /** Open a handle to the Tor service using <b>hSCManager</b>. Return NULL
343 nt_service_open(SC_HANDLE hSCManager
)
347 nt_service_loadlibrary();
348 if ((hService
= service_fns
.OpenServiceA_fn(hSCManager
, GENSRV_SERVICENAME
,
349 SERVICE_ALL_ACCESS
)) == NULL
) {
350 errmsg
= format_win32_error(GetLastError());
351 printf("OpenService() failed : %s\n", errmsg
);
357 /** Start the Tor service. Return 0 if the service is started or was
358 * previously running. Return -1 on error. */
360 nt_service_start(SC_HANDLE hService
)
364 nt_service_loadlibrary();
366 service_fns
.QueryServiceStatus_fn(hService
, &service_status
);
367 if (service_status
.dwCurrentState
== SERVICE_RUNNING
) {
368 printf("Service is already running\n");
372 if (service_fns
.StartServiceA_fn(hService
, 0, NULL
)) {
373 /* Loop until the service has finished attempting to start */
374 while (service_fns
.QueryServiceStatus_fn(hService
, &service_status
) &&
375 (service_status
.dwCurrentState
== SERVICE_START_PENDING
)) {
379 /* Check if it started successfully or not */
380 if (service_status
.dwCurrentState
== SERVICE_RUNNING
) {
381 printf("Service started successfully\n");
384 errmsg
= format_win32_error(service_status
.dwWin32ExitCode
);
385 printf("Service failed to start : %s\n", errmsg
);
389 errmsg
= format_win32_error(GetLastError());
390 printf("StartService() failed : %s\n", errmsg
);
396 /** Stop the Tor service. Return 0 if the service is stopped or was not
397 * previously running. Return -1 on error. */
399 nt_service_stop(SC_HANDLE hService
)
401 /** Wait at most 10 seconds for the service to stop. */
402 #define MAX_SERVICE_WAIT_TIME 10
405 nt_service_loadlibrary();
407 service_fns
.QueryServiceStatus_fn(hService
, &service_status
);
408 if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
409 printf("Service is already stopped\n");
413 if (service_fns
.ControlService_fn(hService
, SERVICE_CONTROL_STOP
,
416 while (service_fns
.QueryServiceStatus_fn(hService
, &service_status
) &&
417 (service_status
.dwCurrentState
!= SERVICE_STOPPED
) &&
418 (wait_time
< MAX_SERVICE_WAIT_TIME
)) {
422 if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
423 printf("Service stopped successfully\n");
425 } else if (wait_time
== MAX_SERVICE_WAIT_TIME
) {
426 printf("Service did not stop within %d seconds.\n", wait_time
);
428 errmsg
= format_win32_error(GetLastError());
429 printf("QueryServiceStatus() failed : %s\n",errmsg
);
433 errmsg
= format_win32_error(GetLastError());
434 printf("ControlService() failed : %s\n", errmsg
);
440 /** Build a formatted command line used for the NT service. Return a
441 * pointer to the formatted string on success, or NULL on failure. Set
442 * *<b>using_default_torrc</b> to true if we're going to use the default
443 * location to torrc, or 1 if an option was specified on the command line.
446 nt_service_command_line(int *using_default_torrc
)
448 TCHAR tor_exe
[MAX_PATH
+1];
449 char tor_exe_ascii
[MAX_PATH
+1];
450 char *command
, *options
=NULL
;
453 *using_default_torrc
= 1;
455 /* Get the location of tor.exe */
456 if (0 == GetModuleFileName(NULL
, tor_exe
, MAX_PATH
))
459 /* Get the service arguments */
460 sl
= smartlist_create();
461 for (i
= 1; i
< backup_argc
; ++i
) {
462 if (!strcmp(backup_argv
[i
], "--options") ||
463 !strcmp(backup_argv
[i
], "-options")) {
464 while (++i
< backup_argc
) {
465 if (!strcmp(backup_argv
[i
], "-f"))
466 *using_default_torrc
= 0;
467 smartlist_add(sl
, backup_argv
[i
]);
471 if (smartlist_len(sl
))
472 options
= smartlist_join_strings(sl
,"\" \"",0,NULL
);
476 wcstombs(tor_exe_ascii
, tor_exe
, sizeof(tor_exe_ascii
));
478 strlcpy(tor_exe_ascii
, tor_exe
, sizeof(tor_exe_ascii
));
481 /* Allocate a string for the NT service command line */
482 cmdlen
= strlen(tor_exe_ascii
) + (options
?strlen(options
):0) + 32;
483 command
= tor_malloc(cmdlen
);
485 /* Format the service command */
487 if (tor_snprintf(command
, cmdlen
, "\"%s\" --nt-service \"%s\"",
488 tor_exe_ascii
, options
)<0) {
489 tor_free(command
); /* sets command to NULL. */
491 } else { /* ! options */
492 if (tor_snprintf(command
, cmdlen
, "\"%s\" --nt-service",
494 tor_free(command
); /* sets command to NULL. */
502 /** Creates a Tor NT service, set to start on boot. The service will be
503 * started if installation succeeds. Returns 0 on success, or -1 on
506 nt_service_install(int argc
, char **argv
)
508 /* Notes about developing NT services:
510 * 1. Don't count on your CWD. If an absolute path is not given, the
511 * fopen() function goes wrong.
512 * 2. The parameters given to the nt_service_body() function differ
513 * from those given to main() function.
516 SC_HANDLE hSCManager
= NULL
;
517 SC_HANDLE hService
= NULL
;
518 SERVICE_DESCRIPTIONA sdBuff
;
521 const char *user_acct
= GENSRV_USERACCT
;
522 const char *password
= "";
524 OSVERSIONINFOEX info
;
526 DWORD sidLen
= 0, domainLen
= 0;
527 int is_win2k_or_worse
= 0;
528 int using_default_torrc
= 0;
530 nt_service_loadlibrary();
532 /* Open the service control manager so we can create a new service */
533 if ((hSCManager
= nt_service_open_scm()) == NULL
)
535 /* Build the command line used for the service */
536 if ((command
= nt_service_command_line(&using_default_torrc
)) == NULL
) {
537 printf("Unable to build service command line.\n");
538 service_fns
.CloseServiceHandle_fn(hSCManager
);
542 for (i
=1; i
< argc
; ++i
) {
543 if (!strcmp(argv
[i
], "--user") && i
+1<argc
) {
544 user_acct
= argv
[i
+1];
547 if (!strcmp(argv
[i
], "--password") && i
+1<argc
) {
548 password
= argv
[i
+1];
553 /* Compute our version and see whether we're running win2k or earlier. */
554 memset(&info
, 0, sizeof(info
));
555 info
.dwOSVersionInfoSize
= sizeof(info
);
556 if (! GetVersionEx((LPOSVERSIONINFO
)&info
)) {
557 printf("Call to GetVersionEx failed.\n");
558 is_win2k_or_worse
= 1;
560 if (info
.dwMajorVersion
< 5 ||
561 (info
.dwMajorVersion
== 5 && info
.dwMinorVersion
== 0))
562 is_win2k_or_worse
= 1;
565 if (user_acct
== GENSRV_USERACCT
) {
566 if (is_win2k_or_worse
) {
567 /* On Win2k, there is no LocalService account, so we actually need to
568 * fall back on NULL (the system account). */
569 printf("Running on Win2K or earlier, so the LocalService account "
570 "doesn't exist. Falling back to SYSTEM account.\n");
573 /* Genericity is apparently _so_ last year in Redmond, where some
574 * accounts are accounts that you can look up, and some accounts
575 * are magic and undetectable via the security subsystem. See
576 * http://msdn2.microsoft.com/en-us/library/ms684188.aspx
578 printf("Running on a Post-Win2K OS, so we'll assume that the "
579 "LocalService account exists.\n");
581 } else if (0 && service_fns
.LookupAccountNameA_fn(NULL
, // On this system
583 NULL
, &sidLen
, // Don't care about the SID
584 NULL
, &domainLen
, // Don't care about the domain
586 /* XXXX For some reason, the above test segfaults. Fix that. */
587 printf("User \"%s\" doesn't seem to exist.\n", user_acct
);
590 printf("Will try to install service as user \"%s\".\n", user_acct
);
592 /* XXXX This warning could be better about explaining how to resolve the
594 if (using_default_torrc
)
595 printf("IMPORTANT NOTE:\n"
596 " The Tor service will run under the account \"%s\". This means\n"
597 " that Tor will look for its configuration file under that\n"
598 " account's Application Data directory, which is probably not\n"
599 " the same as yours.\n", user_acct
?user_acct
:"<local system>");
601 /* Create the Tor service, set to auto-start on boot */
602 if ((hService
= service_fns
.CreateServiceA_fn(hSCManager
, GENSRV_SERVICENAME
,
604 SERVICE_ALL_ACCESS
, SERVICE_WIN32_OWN_PROCESS
,
605 SERVICE_AUTO_START
, SERVICE_ERROR_IGNORE
,
606 command
, NULL
, NULL
, NULL
,
607 user_acct
, password
)) == NULL
) {
608 errmsg
= format_win32_error(GetLastError());
609 printf("CreateService() failed : %s\n", errmsg
);
610 service_fns
.CloseServiceHandle_fn(hSCManager
);
615 printf("Done with CreateService.\n");
617 /* Set the service's description */
618 sdBuff
.lpDescription
= (char*)GENSRV_DESCRIPTION
;
619 service_fns
.ChangeServiceConfig2A_fn(hService
, SERVICE_CONFIG_DESCRIPTION
,
621 printf("Service installed successfully\n");
623 /* Start the service initially */
624 nt_service_start(hService
);
626 service_fns
.CloseServiceHandle_fn(hService
);
627 service_fns
.CloseServiceHandle_fn(hSCManager
);
633 /** Removes the Tor NT service. Returns 0 if the service was successfully
634 * removed, or -1 on error. */
636 nt_service_remove(void)
638 SC_HANDLE hSCManager
= NULL
;
639 SC_HANDLE hService
= NULL
;
642 nt_service_loadlibrary();
643 if ((hSCManager
= nt_service_open_scm()) == NULL
)
645 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
646 service_fns
.CloseServiceHandle_fn(hSCManager
);
650 nt_service_stop(hService
);
651 if (service_fns
.DeleteService_fn(hService
) == FALSE
) {
652 errmsg
= format_win32_error(GetLastError());
653 printf("DeleteService() failed : %s\n", errmsg
);
655 service_fns
.CloseServiceHandle_fn(hService
);
656 service_fns
.CloseServiceHandle_fn(hSCManager
);
660 service_fns
.CloseServiceHandle_fn(hService
);
661 service_fns
.CloseServiceHandle_fn(hSCManager
);
662 printf("Service removed successfully\n");
667 /** Starts the Tor service. Returns 0 on success, or -1 on error. */
669 nt_service_cmd_start(void)
671 SC_HANDLE hSCManager
;
675 if ((hSCManager
= nt_service_open_scm()) == NULL
)
677 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
678 service_fns
.CloseServiceHandle_fn(hSCManager
);
682 start
= nt_service_start(hService
);
683 service_fns
.CloseServiceHandle_fn(hService
);
684 service_fns
.CloseServiceHandle_fn(hSCManager
);
689 /** Stops the Tor service. Returns 0 on success, or -1 on error. */
691 nt_service_cmd_stop(void)
693 SC_HANDLE hSCManager
;
697 if ((hSCManager
= nt_service_open_scm()) == NULL
)
699 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
700 service_fns
.CloseServiceHandle_fn(hSCManager
);
704 stop
= nt_service_stop(hService
);
705 service_fns
.CloseServiceHandle_fn(hService
);
706 service_fns
.CloseServiceHandle_fn(hSCManager
);
712 nt_service_parse_options(int argc
, char **argv
, int *should_exit
)
719 (!strcmp(argv
[1], "-service") || !strcmp(argv
[1], "--service"))) {
720 nt_service_loadlibrary();
721 if (!strcmp(argv
[2], "install"))
722 return nt_service_install(argc
, argv
);
723 if (!strcmp(argv
[2], "remove"))
724 return nt_service_remove();
725 if (!strcmp(argv
[2], "start"))
726 return nt_service_cmd_start();
727 if (!strcmp(argv
[2], "stop"))
728 return nt_service_cmd_stop();
729 printf("Unrecognized service command '%s'\n", argv
[2]);
734 if (!strcmp(argv
[1], "-nt-service") || !strcmp(argv
[1], "--nt-service")) {
735 nt_service_loadlibrary();
740 // These values have been deprecated since 0.1.1.2-alpha; we've warned
741 // about them since 0.1.2.7-alpha.
742 if (!strcmp(argv
[1], "-install") || !strcmp(argv
[1], "--install")) {
743 nt_service_loadlibrary();
745 "The %s option is deprecated; use \"--service install\" instead.",
748 return nt_service_install(argc
, argv
);
750 if (!strcmp(argv
[1], "-remove") || !strcmp(argv
[1], "--remove")) {
751 nt_service_loadlibrary();
753 "The %s option is deprecated; use \"--service remove\" instead.",
756 return nt_service_remove();