1 /* Copyright (c) 2001-2004, Roger Dingledine.
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2013, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
11 #ifdef HAVE_EVENT2_EVENT_H
12 #include <event2/event.h>
18 #define GENSRV_SERVICENAME "tor"
19 #define GENSRV_DISPLAYNAME "Tor Win32 Service"
20 #define GENSRV_DESCRIPTION \
21 "Provides an anonymous Internet communication system"
22 #define GENSRV_USERACCT "NT AUTHORITY\\LocalService"
24 // Cheating: using the pre-defined error codes, tricks Windows into displaying
25 // a semi-related human-readable error message if startup fails as
26 // opposed to simply scaring people with Error: 0xffffffff
27 #define NT_SERVICE_ERROR_TORINIT_FAILED ERROR_EXCEPTION_IN_SERVICE
29 static SERVICE_STATUS service_status
;
30 static SERVICE_STATUS_HANDLE hStatus
;
32 /* XXXX This 'backup argv' and 'backup argc' business is an ugly hack. This
33 * is a job for arguments, not globals. Alas, some of the functions that
34 * use them use them need to have fixed signatures, so they can be passed
35 * to the NT service functions. */
36 static char **backup_argv
;
37 static int backup_argc
;
39 static void nt_service_control(DWORD request
);
40 static void nt_service_body(int argc
, char **argv
);
41 static void nt_service_main(void);
42 static SC_HANDLE
nt_service_open_scm(void);
43 static SC_HANDLE
nt_service_open(SC_HANDLE hSCManager
);
44 static int nt_service_start(SC_HANDLE hService
);
45 static int nt_service_stop(SC_HANDLE hService
);
46 static int nt_service_install(int argc
, char **argv
);
47 static int nt_service_remove(void);
48 static int nt_service_cmd_start(void);
49 static int nt_service_cmd_stop(void);
51 /** Struct to hold dynamically loaded NT-service related function pointers.
57 /** Function pointers for Windows API functions related to service
58 * management. These are NULL, or they point to the . They're set by
59 * calling the LOAD macro below. */
61 BOOL (WINAPI
*ChangeServiceConfig2A_fn
)(
66 BOOL (WINAPI
*CloseServiceHandle_fn
)(
69 BOOL (WINAPI
*ControlService_fn
)(
72 LPSERVICE_STATUS lpServiceStatus
);
74 SC_HANDLE (WINAPI
*CreateServiceA_fn
)(
78 DWORD dwDesiredAccess
,
82 LPCSTR lpBinaryPathName
,
83 LPCSTR lpLoadOrderGroup
,
85 LPCSTR lpDependencies
,
86 LPCSTR lpServiceStartName
,
89 BOOL (WINAPI
*DeleteService_fn
)(
92 SC_HANDLE (WINAPI
*OpenSCManagerA_fn
)(
94 LPCSTR lpDatabaseName
,
95 DWORD dwDesiredAccess
);
97 SC_HANDLE (WINAPI
*OpenServiceA_fn
)(
100 DWORD dwDesiredAccess
);
102 BOOL (WINAPI
*QueryServiceStatus_fn
)(
104 LPSERVICE_STATUS lpServiceStatus
);
106 SERVICE_STATUS_HANDLE (WINAPI
*RegisterServiceCtrlHandlerA_fn
)(
107 LPCSTR lpServiceName
,
108 LPHANDLER_FUNCTION lpHandlerProc
);
110 BOOL (WINAPI
*SetServiceStatus_fn
)(SERVICE_STATUS_HANDLE
,
113 BOOL (WINAPI
*StartServiceCtrlDispatcherA_fn
)(
114 const SERVICE_TABLE_ENTRYA
* lpServiceTable
);
116 BOOL (WINAPI
*StartServiceA_fn
)(
118 DWORD dwNumServiceArgs
,
119 LPCSTR
* lpServiceArgVectors
);
121 BOOL (WINAPI
*LookupAccountNameA_fn
)(
123 LPCSTR lpAccountName
,
126 LPTSTR ReferencedDomainName
,
127 LPDWORD cchReferencedDomainName
,
128 PSID_NAME_USE peUse
);
131 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
132 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
135 /** Loads functions used by NT services. Returns on success, or prints a
136 * complaint to stdout and exits on error. */
138 nt_service_loadlibrary(void)
143 if (service_fns
.loaded
)
146 if (!(library
= load_windows_system_library(TEXT("advapi32.dll")))) {
147 log_err(LD_GENERAL
, "Couldn't open advapi32.dll. Are you trying to use "
148 "NT services on Windows 98? That doesn't work.");
152 /* Helper macro: try to load a function named <b>f</b> from "library" into
153 * service_functions.<b>f</b>_fn. On failure, log an error message, and goto
156 #define LOAD(f) STMT_BEGIN \
157 if (!(fn = GetProcAddress(library, #f))) { \
159 "Couldn't find %s in advapi32.dll! We probably got the " \
160 "name wrong.", #f); \
163 service_fns.f ## _fn = fn; \
167 LOAD(ChangeServiceConfig2A
);
168 LOAD(CloseServiceHandle
);
169 LOAD(ControlService
);
170 LOAD(CreateServiceA
);
172 LOAD(OpenSCManagerA
);
174 LOAD(QueryServiceStatus
);
175 LOAD(RegisterServiceCtrlHandlerA
);
176 LOAD(SetServiceStatus
);
177 LOAD(StartServiceCtrlDispatcherA
);
179 LOAD(LookupAccountNameA
);
181 service_fns
.loaded
= 1;
185 printf("Unable to load library support for NT services: exiting.\n");
189 /** If we're compiled to run as an NT service, and the service wants to
190 * shut down, then change our current status and return 1. Else
194 nt_service_is_stopping(void)
196 /* If we haven't loaded the function pointers, we can't possibly be an NT
197 * service trying to shut down. */
198 if (!service_fns
.loaded
)
201 if (service_status
.dwCurrentState
== SERVICE_STOP_PENDING
) {
202 service_status
.dwWin32ExitCode
= 0;
203 service_status
.dwCurrentState
= SERVICE_STOPPED
;
204 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
206 } else if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
212 /** Set the dwCurrentState field for our service to <b>state</b>. */
214 nt_service_set_state(DWORD state
)
216 service_status
.dwCurrentState
= state
;
219 /** Handles service control requests, such as stopping or starting the
222 nt_service_control(DWORD request
)
224 static struct timeval exit_now
;
226 exit_now
.tv_usec
= 0;
228 nt_service_loadlibrary();
231 case SERVICE_CONTROL_STOP
:
232 case SERVICE_CONTROL_SHUTDOWN
:
233 log_notice(LD_GENERAL
,
234 "Got stop/shutdown request; shutting down cleanly.");
235 service_status
.dwCurrentState
= SERVICE_STOP_PENDING
;
236 event_base_loopexit(tor_libevent_get_base(), &exit_now
);
239 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
242 /** Called when the service is started via the system's service control
243 * manager. This calls tor_init() and starts the main event loop. If
244 * tor_init() fails, the service will be stopped and exit code set to
245 * NT_SERVICE_ERROR_TORINIT_FAILED. */
247 nt_service_body(int argc
, char **argv
)
250 (void) argc
; /* unused */
251 (void) argv
; /* unused */
252 nt_service_loadlibrary();
253 service_status
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
;
254 service_status
.dwCurrentState
= SERVICE_START_PENDING
;
255 service_status
.dwControlsAccepted
=
256 SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
;
257 service_status
.dwWin32ExitCode
= 0;
258 service_status
.dwServiceSpecificExitCode
= 0;
259 service_status
.dwCheckPoint
= 0;
260 service_status
.dwWaitHint
= 1000;
261 hStatus
= service_fns
.RegisterServiceCtrlHandlerA_fn(GENSRV_SERVICENAME
,
262 (LPHANDLER_FUNCTION
) nt_service_control
);
265 /* Failed to register the service control handler function */
269 r
= tor_init(backup_argc
, backup_argv
);
271 /* Failed to start the Tor service */
272 r
= NT_SERVICE_ERROR_TORINIT_FAILED
;
273 service_status
.dwCurrentState
= SERVICE_STOPPED
;
274 service_status
.dwWin32ExitCode
= r
;
275 service_status
.dwServiceSpecificExitCode
= r
;
276 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
280 /* Set the service's status to SERVICE_RUNNING and start the main
282 service_status
.dwCurrentState
= SERVICE_RUNNING
;
283 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
288 /** Main service entry point. Starts the service control dispatcher and waits
289 * until the service status is set to SERVICE_STOPPED. */
291 nt_service_main(void)
293 SERVICE_TABLE_ENTRYA table
[2];
296 nt_service_loadlibrary();
297 table
[0].lpServiceName
= (char*)GENSRV_SERVICENAME
;
298 table
[0].lpServiceProc
= (LPSERVICE_MAIN_FUNCTIONA
)nt_service_body
;
299 table
[1].lpServiceName
= NULL
;
300 table
[1].lpServiceProc
= NULL
;
302 if (!service_fns
.StartServiceCtrlDispatcherA_fn(table
)) {
303 result
= GetLastError();
304 errmsg
= format_win32_error(result
);
305 printf("Service error %d : %s\n", (int) result
, errmsg
);
307 if (result
== ERROR_FAILED_SERVICE_CONTROLLER_CONNECT
) {
308 if (tor_init(backup_argc
, backup_argv
) < 0)
310 switch (get_options()->command
) {
314 case CMD_LIST_FINGERPRINT
:
315 case CMD_HASH_PASSWORD
:
316 case CMD_VERIFY_CONFIG
:
317 case CMD_DUMP_CONFIG
:
318 log_err(LD_CONFIG
, "Unsupported command (--list-fingerprint, "
319 "--hash-password, or --verify-config) in NT service.");
321 case CMD_RUN_UNITTESTS
:
323 log_err(LD_CONFIG
, "Illegal command number %d: internal error.",
324 get_options()->command
);
331 /** Return a handle to the service control manager on success, or NULL on
334 nt_service_open_scm(void)
336 SC_HANDLE hSCManager
;
339 nt_service_loadlibrary();
340 if ((hSCManager
= service_fns
.OpenSCManagerA_fn(
341 NULL
, NULL
, SC_MANAGER_CREATE_SERVICE
)) == NULL
) {
342 errmsg
= format_win32_error(GetLastError());
343 printf("OpenSCManager() failed : %s\n", errmsg
);
349 /** Open a handle to the Tor service using <b>hSCManager</b>. Return NULL
352 nt_service_open(SC_HANDLE hSCManager
)
356 nt_service_loadlibrary();
357 if ((hService
= service_fns
.OpenServiceA_fn(hSCManager
, GENSRV_SERVICENAME
,
358 SERVICE_ALL_ACCESS
)) == NULL
) {
359 errmsg
= format_win32_error(GetLastError());
360 printf("OpenService() failed : %s\n", errmsg
);
366 /** Start the Tor service. Return 0 if the service is started or was
367 * previously running. Return -1 on error. */
369 nt_service_start(SC_HANDLE hService
)
373 nt_service_loadlibrary();
375 service_fns
.QueryServiceStatus_fn(hService
, &service_status
);
376 if (service_status
.dwCurrentState
== SERVICE_RUNNING
) {
377 printf("Service is already running\n");
381 if (service_fns
.StartServiceA_fn(hService
, 0, NULL
)) {
382 /* Loop until the service has finished attempting to start */
383 while (service_fns
.QueryServiceStatus_fn(hService
, &service_status
) &&
384 (service_status
.dwCurrentState
== SERVICE_START_PENDING
)) {
388 /* Check if it started successfully or not */
389 if (service_status
.dwCurrentState
== SERVICE_RUNNING
) {
390 printf("Service started successfully\n");
393 errmsg
= format_win32_error(service_status
.dwWin32ExitCode
);
394 printf("Service failed to start : %s\n", errmsg
);
398 errmsg
= format_win32_error(GetLastError());
399 printf("StartService() failed : %s\n", errmsg
);
405 /** Stop the Tor service. Return 0 if the service is stopped or was not
406 * previously running. Return -1 on error. */
408 nt_service_stop(SC_HANDLE hService
)
410 /** Wait at most 10 seconds for the service to stop. */
411 #define MAX_SERVICE_WAIT_TIME 10
414 nt_service_loadlibrary();
416 service_fns
.QueryServiceStatus_fn(hService
, &service_status
);
417 if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
418 printf("Service is already stopped\n");
422 if (service_fns
.ControlService_fn(hService
, SERVICE_CONTROL_STOP
,
425 while (service_fns
.QueryServiceStatus_fn(hService
, &service_status
) &&
426 (service_status
.dwCurrentState
!= SERVICE_STOPPED
) &&
427 (wait_time
< MAX_SERVICE_WAIT_TIME
)) {
431 if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
432 printf("Service stopped successfully\n");
434 } else if (wait_time
== MAX_SERVICE_WAIT_TIME
) {
435 printf("Service did not stop within %d seconds.\n", wait_time
);
437 errmsg
= format_win32_error(GetLastError());
438 printf("QueryServiceStatus() failed : %s\n",errmsg
);
442 errmsg
= format_win32_error(GetLastError());
443 printf("ControlService() failed : %s\n", errmsg
);
449 /** Build a formatted command line used for the NT service. Return a
450 * pointer to the formatted string on success, or NULL on failure. Set
451 * *<b>using_default_torrc</b> to true if we're going to use the default
452 * location to torrc, or 1 if an option was specified on the command line.
455 nt_service_command_line(int *using_default_torrc
)
457 TCHAR tor_exe
[MAX_PATH
+1];
458 char tor_exe_ascii
[MAX_PATH
*2+1];
459 char *command
=NULL
, *options
=NULL
;
462 *using_default_torrc
= 1;
464 /* Get the location of tor.exe */
465 if (0 == GetModuleFileName(NULL
, tor_exe
, MAX_PATH
))
468 /* Get the service arguments */
469 sl
= smartlist_new();
470 for (i
= 1; i
< backup_argc
; ++i
) {
471 if (!strcmp(backup_argv
[i
], "--options") ||
472 !strcmp(backup_argv
[i
], "-options")) {
473 while (++i
< backup_argc
) {
474 if (!strcmp(backup_argv
[i
], "-f"))
475 *using_default_torrc
= 0;
476 smartlist_add(sl
, backup_argv
[i
]);
480 if (smartlist_len(sl
))
481 options
= smartlist_join_strings(sl
,"\" \"",0,NULL
);
485 wcstombs(tor_exe_ascii
, tor_exe
, sizeof(tor_exe_ascii
));
486 tor_exe_ascii
[sizeof(tor_exe_ascii
)-1] = '\0';
488 strlcpy(tor_exe_ascii
, tor_exe
, sizeof(tor_exe_ascii
));
491 /* Allocate a string for the NT service command line and */
492 /* Format the service command */
494 tor_asprintf(&command
, "\"%s\" --nt-service \"%s\"",
495 tor_exe_ascii
, options
);
496 } else { /* ! options */
497 tor_asprintf(&command
, "\"%s\" --nt-service", tor_exe_ascii
);
504 /** Creates a Tor NT service, set to start on boot. The service will be
505 * started if installation succeeds. Returns 0 on success, or -1 on
508 nt_service_install(int argc
, char **argv
)
510 /* Notes about developing NT services:
512 * 1. Don't count on your CWD. If an absolute path is not given, the
513 * fopen() function goes wrong.
514 * 2. The parameters given to the nt_service_body() function differ
515 * from those given to main() function.
518 SC_HANDLE hSCManager
= NULL
;
519 SC_HANDLE hService
= NULL
;
520 SERVICE_DESCRIPTIONA sdBuff
;
523 const char *user_acct
= NULL
;
524 const char *password
= "";
526 OSVERSIONINFOEX info
;
528 DWORD sidLen
= 0, domainLen
= 0;
529 int is_win2k_or_worse
= 0;
530 int using_default_torrc
= 0;
532 nt_service_loadlibrary();
534 /* Open the service control manager so we can create a new service */
535 if ((hSCManager
= nt_service_open_scm()) == NULL
)
537 /* Build the command line used for the service */
538 if ((command
= nt_service_command_line(&using_default_torrc
)) == NULL
) {
539 printf("Unable to build service command line.\n");
540 service_fns
.CloseServiceHandle_fn(hSCManager
);
544 for (i
=1; i
< argc
; ++i
) {
545 if (!strcmp(argv
[i
], "--user") && i
+1<argc
) {
546 user_acct
= argv
[i
+1];
549 if (!strcmp(argv
[i
], "--password") && i
+1<argc
) {
550 password
= argv
[i
+1];
555 /* Compute our version and see whether we're running win2k or earlier. */
556 memset(&info
, 0, sizeof(info
));
557 info
.dwOSVersionInfoSize
= sizeof(info
);
558 if (! GetVersionEx((LPOSVERSIONINFO
)&info
)) {
559 printf("Call to GetVersionEx failed.\n");
560 is_win2k_or_worse
= 1;
562 if (info
.dwMajorVersion
< 5 ||
563 (info
.dwMajorVersion
== 5 && info
.dwMinorVersion
== 0))
564 is_win2k_or_worse
= 1;
568 if (is_win2k_or_worse
) {
569 /* On Win2k, there is no LocalService account, so we actually need to
570 * fall back on NULL (the system account). */
571 printf("Running on Win2K or earlier, so the LocalService account "
572 "doesn't exist. Falling back to SYSTEM account.\n");
574 /* Genericity is apparently _so_ last year in Redmond, where some
575 * accounts are accounts that you can look up, and some accounts
576 * are magic and undetectable via the security subsystem. See
577 * http://msdn2.microsoft.com/en-us/library/ms684188.aspx
579 printf("Running on a Post-Win2K OS, so we'll assume that the "
580 "LocalService account exists.\n");
581 user_acct
= GENSRV_USERACCT
;
583 } else if (0 && service_fns
.LookupAccountNameA_fn(NULL
, // On this system
585 NULL
, &sidLen
, // Don't care about the SID
586 NULL
, &domainLen
, // Don't care about the domain
588 /* XXXX For some reason, the above test segfaults. Fix that. */
589 printf("User \"%s\" doesn't seem to exist.\n", user_acct
);
592 printf("Will try to install service as user \"%s\".\n", user_acct
);
594 /* XXXX This warning could be better about explaining how to resolve the
596 if (using_default_torrc
)
597 printf("IMPORTANT NOTE:\n"
598 " The Tor service will run under the account \"%s\". This means\n"
599 " that Tor will look for its configuration file under that\n"
600 " account's Application Data directory, which is probably not\n"
601 " the same as yours.\n", user_acct
?user_acct
:"<local system>");
603 /* Create the Tor service, set to auto-start on boot */
604 if ((hService
= service_fns
.CreateServiceA_fn(hSCManager
, GENSRV_SERVICENAME
,
606 SERVICE_ALL_ACCESS
, SERVICE_WIN32_OWN_PROCESS
,
607 SERVICE_AUTO_START
, SERVICE_ERROR_IGNORE
,
608 command
, NULL
, NULL
, NULL
,
609 user_acct
, password
)) == NULL
) {
610 errmsg
= format_win32_error(GetLastError());
611 printf("CreateService() failed : %s\n", errmsg
);
612 service_fns
.CloseServiceHandle_fn(hSCManager
);
617 printf("Done with CreateService.\n");
619 /* Set the service's description */
620 sdBuff
.lpDescription
= (char*)GENSRV_DESCRIPTION
;
621 service_fns
.ChangeServiceConfig2A_fn(hService
, SERVICE_CONFIG_DESCRIPTION
,
623 printf("Service installed successfully\n");
625 /* Start the service initially */
626 nt_service_start(hService
);
628 service_fns
.CloseServiceHandle_fn(hService
);
629 service_fns
.CloseServiceHandle_fn(hSCManager
);
635 /** Removes the Tor NT service. Returns 0 if the service was successfully
636 * removed, or -1 on error. */
638 nt_service_remove(void)
640 SC_HANDLE hSCManager
= NULL
;
641 SC_HANDLE hService
= NULL
;
644 nt_service_loadlibrary();
645 if ((hSCManager
= nt_service_open_scm()) == NULL
)
647 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
648 service_fns
.CloseServiceHandle_fn(hSCManager
);
652 nt_service_stop(hService
);
653 if (service_fns
.DeleteService_fn(hService
) == FALSE
) {
654 errmsg
= format_win32_error(GetLastError());
655 printf("DeleteService() failed : %s\n", errmsg
);
657 service_fns
.CloseServiceHandle_fn(hService
);
658 service_fns
.CloseServiceHandle_fn(hSCManager
);
662 service_fns
.CloseServiceHandle_fn(hService
);
663 service_fns
.CloseServiceHandle_fn(hSCManager
);
664 printf("Service removed successfully\n");
669 /** Starts the Tor service. Returns 0 on success, or -1 on error. */
671 nt_service_cmd_start(void)
673 SC_HANDLE hSCManager
;
677 if ((hSCManager
= nt_service_open_scm()) == NULL
)
679 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
680 service_fns
.CloseServiceHandle_fn(hSCManager
);
684 start
= nt_service_start(hService
);
685 service_fns
.CloseServiceHandle_fn(hService
);
686 service_fns
.CloseServiceHandle_fn(hSCManager
);
691 /** Stops the Tor service. Returns 0 on success, or -1 on error. */
693 nt_service_cmd_stop(void)
695 SC_HANDLE hSCManager
;
699 if ((hSCManager
= nt_service_open_scm()) == NULL
)
701 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
702 service_fns
.CloseServiceHandle_fn(hSCManager
);
706 stop
= nt_service_stop(hService
);
707 service_fns
.CloseServiceHandle_fn(hService
);
708 service_fns
.CloseServiceHandle_fn(hSCManager
);
714 nt_service_parse_options(int argc
, char **argv
, int *should_exit
)
721 (!strcmp(argv
[1], "-service") || !strcmp(argv
[1], "--service"))) {
722 nt_service_loadlibrary();
724 if (!strcmp(argv
[2], "install"))
725 return nt_service_install(argc
, argv
);
726 if (!strcmp(argv
[2], "remove"))
727 return nt_service_remove();
728 if (!strcmp(argv
[2], "start"))
729 return nt_service_cmd_start();
730 if (!strcmp(argv
[2], "stop"))
731 return nt_service_cmd_stop();
732 printf("Unrecognized service command '%s'\n", argv
[2]);
736 if (!strcmp(argv
[1], "-nt-service") || !strcmp(argv
[1], "--nt-service")) {
737 nt_service_loadlibrary();
742 // These values have been deprecated since 0.1.1.2-alpha; we've warned
743 // about them since 0.1.2.7-alpha.
744 if (!strcmp(argv
[1], "-install") || !strcmp(argv
[1], "--install")) {
745 nt_service_loadlibrary();
747 "The %s option is deprecated; use \"--service install\" instead.",
750 return nt_service_install(argc
, argv
);
752 if (!strcmp(argv
[1], "-remove") || !strcmp(argv
[1], "--remove")) {
753 nt_service_loadlibrary();
755 "The %s option is deprecated; use \"--service remove\" instead.",
758 return nt_service_remove();