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
);
289 /** Main service entry point. Starts the service control dispatcher and waits
290 * until the service status is set to SERVICE_STOPPED. */
292 nt_service_main(void)
294 SERVICE_TABLE_ENTRYA table
[2];
297 nt_service_loadlibrary();
298 table
[0].lpServiceName
= (char*)GENSRV_SERVICENAME
;
299 table
[0].lpServiceProc
= (LPSERVICE_MAIN_FUNCTIONA
)nt_service_body
;
300 table
[1].lpServiceName
= NULL
;
301 table
[1].lpServiceProc
= NULL
;
303 if (!service_fns
.StartServiceCtrlDispatcherA_fn(table
)) {
304 result
= GetLastError();
305 errmsg
= format_win32_error(result
);
306 printf("Service error %d : %s\n", (int) result
, errmsg
);
308 if (result
== ERROR_FAILED_SERVICE_CONTROLLER_CONNECT
) {
309 if (tor_init(backup_argc
, backup_argv
) < 0)
311 switch (get_options()->command
) {
315 case CMD_LIST_FINGERPRINT
:
316 case CMD_HASH_PASSWORD
:
317 case CMD_VERIFY_CONFIG
:
318 case CMD_DUMP_CONFIG
:
319 log_err(LD_CONFIG
, "Unsupported command (--list-fingerprint, "
320 "--hash-password, or --verify-config) in NT service.");
322 case CMD_RUN_UNITTESTS
:
324 log_err(LD_CONFIG
, "Illegal command number %d: internal error.",
325 get_options()->command
);
332 /** Return a handle to the service control manager on success, or NULL on
335 nt_service_open_scm(void)
337 SC_HANDLE hSCManager
;
340 nt_service_loadlibrary();
341 if ((hSCManager
= service_fns
.OpenSCManagerA_fn(
342 NULL
, NULL
, SC_MANAGER_CREATE_SERVICE
)) == NULL
) {
343 errmsg
= format_win32_error(GetLastError());
344 printf("OpenSCManager() failed : %s\n", errmsg
);
350 /** Open a handle to the Tor service using <b>hSCManager</b>. Return NULL
353 nt_service_open(SC_HANDLE hSCManager
)
357 nt_service_loadlibrary();
358 if ((hService
= service_fns
.OpenServiceA_fn(hSCManager
, GENSRV_SERVICENAME
,
359 SERVICE_ALL_ACCESS
)) == NULL
) {
360 errmsg
= format_win32_error(GetLastError());
361 printf("OpenService() failed : %s\n", errmsg
);
367 /** Start the Tor service. Return 0 if the service is started or was
368 * previously running. Return -1 on error. */
370 nt_service_start(SC_HANDLE hService
)
374 nt_service_loadlibrary();
376 service_fns
.QueryServiceStatus_fn(hService
, &service_status
);
377 if (service_status
.dwCurrentState
== SERVICE_RUNNING
) {
378 printf("Service is already running\n");
382 if (service_fns
.StartServiceA_fn(hService
, 0, NULL
)) {
383 /* Loop until the service has finished attempting to start */
384 while (service_fns
.QueryServiceStatus_fn(hService
, &service_status
) &&
385 (service_status
.dwCurrentState
== SERVICE_START_PENDING
)) {
389 /* Check if it started successfully or not */
390 if (service_status
.dwCurrentState
== SERVICE_RUNNING
) {
391 printf("Service started successfully\n");
394 errmsg
= format_win32_error(service_status
.dwWin32ExitCode
);
395 printf("Service failed to start : %s\n", errmsg
);
399 errmsg
= format_win32_error(GetLastError());
400 printf("StartService() failed : %s\n", errmsg
);
406 /** Stop the Tor service. Return 0 if the service is stopped or was not
407 * previously running. Return -1 on error. */
409 nt_service_stop(SC_HANDLE hService
)
411 /** Wait at most 10 seconds for the service to stop. */
412 #define MAX_SERVICE_WAIT_TIME 10
415 nt_service_loadlibrary();
417 service_fns
.QueryServiceStatus_fn(hService
, &service_status
);
418 if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
419 printf("Service is already stopped\n");
423 if (service_fns
.ControlService_fn(hService
, SERVICE_CONTROL_STOP
,
426 while (service_fns
.QueryServiceStatus_fn(hService
, &service_status
) &&
427 (service_status
.dwCurrentState
!= SERVICE_STOPPED
) &&
428 (wait_time
< MAX_SERVICE_WAIT_TIME
)) {
432 if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
433 printf("Service stopped successfully\n");
435 } else if (wait_time
== MAX_SERVICE_WAIT_TIME
) {
436 printf("Service did not stop within %d seconds.\n", wait_time
);
438 errmsg
= format_win32_error(GetLastError());
439 printf("QueryServiceStatus() failed : %s\n",errmsg
);
443 errmsg
= format_win32_error(GetLastError());
444 printf("ControlService() failed : %s\n", errmsg
);
450 /** Build a formatted command line used for the NT service. Return a
451 * pointer to the formatted string on success, or NULL on failure. Set
452 * *<b>using_default_torrc</b> to true if we're going to use the default
453 * location to torrc, or 1 if an option was specified on the command line.
456 nt_service_command_line(int *using_default_torrc
)
458 TCHAR tor_exe
[MAX_PATH
+1];
459 char tor_exe_ascii
[MAX_PATH
*2+1];
460 char *command
=NULL
, *options
=NULL
;
463 *using_default_torrc
= 1;
465 /* Get the location of tor.exe */
466 if (0 == GetModuleFileName(NULL
, tor_exe
, MAX_PATH
))
469 /* Get the service arguments */
470 sl
= smartlist_new();
471 for (i
= 1; i
< backup_argc
; ++i
) {
472 if (!strcmp(backup_argv
[i
], "--options") ||
473 !strcmp(backup_argv
[i
], "-options")) {
474 while (++i
< backup_argc
) {
475 if (!strcmp(backup_argv
[i
], "-f"))
476 *using_default_torrc
= 0;
477 smartlist_add(sl
, backup_argv
[i
]);
481 if (smartlist_len(sl
))
482 options
= smartlist_join_strings(sl
,"\" \"",0,NULL
);
486 wcstombs(tor_exe_ascii
, tor_exe
, sizeof(tor_exe_ascii
));
487 tor_exe_ascii
[sizeof(tor_exe_ascii
)-1] = '\0';
489 strlcpy(tor_exe_ascii
, tor_exe
, sizeof(tor_exe_ascii
));
492 /* Allocate a string for the NT service command line and */
493 /* Format the service command */
495 tor_asprintf(&command
, "\"%s\" --nt-service \"%s\"",
496 tor_exe_ascii
, options
);
497 } else { /* ! options */
498 tor_asprintf(&command
, "\"%s\" --nt-service", tor_exe_ascii
);
505 /** Creates a Tor NT service, set to start on boot. The service will be
506 * started if installation succeeds. Returns 0 on success, or -1 on
509 nt_service_install(int argc
, char **argv
)
511 /* Notes about developing NT services:
513 * 1. Don't count on your CWD. If an absolute path is not given, the
514 * fopen() function goes wrong.
515 * 2. The parameters given to the nt_service_body() function differ
516 * from those given to main() function.
519 SC_HANDLE hSCManager
= NULL
;
520 SC_HANDLE hService
= NULL
;
521 SERVICE_DESCRIPTIONA sdBuff
;
524 const char *user_acct
= NULL
;
525 const char *password
= "";
527 OSVERSIONINFOEX info
;
529 DWORD sidLen
= 0, domainLen
= 0;
530 int is_win2k_or_worse
= 0;
531 int using_default_torrc
= 0;
533 nt_service_loadlibrary();
535 /* Open the service control manager so we can create a new service */
536 if ((hSCManager
= nt_service_open_scm()) == NULL
)
538 /* Build the command line used for the service */
539 if ((command
= nt_service_command_line(&using_default_torrc
)) == NULL
) {
540 printf("Unable to build service command line.\n");
541 service_fns
.CloseServiceHandle_fn(hSCManager
);
545 for (i
=1; i
< argc
; ++i
) {
546 if (!strcmp(argv
[i
], "--user") && i
+1<argc
) {
547 user_acct
= argv
[i
+1];
550 if (!strcmp(argv
[i
], "--password") && i
+1<argc
) {
551 password
= argv
[i
+1];
556 /* Compute our version and see whether we're running win2k or earlier. */
557 memset(&info
, 0, sizeof(info
));
558 info
.dwOSVersionInfoSize
= sizeof(info
);
559 if (! GetVersionEx((LPOSVERSIONINFO
)&info
)) {
560 printf("Call to GetVersionEx failed.\n");
561 is_win2k_or_worse
= 1;
563 if (info
.dwMajorVersion
< 5 ||
564 (info
.dwMajorVersion
== 5 && info
.dwMinorVersion
== 0))
565 is_win2k_or_worse
= 1;
569 if (is_win2k_or_worse
) {
570 /* On Win2k, there is no LocalService account, so we actually need to
571 * fall back on NULL (the system account). */
572 printf("Running on Win2K or earlier, so the LocalService account "
573 "doesn't exist. Falling back to SYSTEM account.\n");
575 /* Genericity is apparently _so_ last year in Redmond, where some
576 * accounts are accounts that you can look up, and some accounts
577 * are magic and undetectable via the security subsystem. See
578 * http://msdn2.microsoft.com/en-us/library/ms684188.aspx
580 printf("Running on a Post-Win2K OS, so we'll assume that the "
581 "LocalService account exists.\n");
582 user_acct
= GENSRV_USERACCT
;
584 } else if (0 && service_fns
.LookupAccountNameA_fn(NULL
, // On this system
586 NULL
, &sidLen
, // Don't care about the SID
587 NULL
, &domainLen
, // Don't care about the domain
589 /* XXXX For some reason, the above test segfaults. Fix that. */
590 printf("User \"%s\" doesn't seem to exist.\n", user_acct
);
593 printf("Will try to install service as user \"%s\".\n", user_acct
);
595 /* XXXX This warning could be better about explaining how to resolve the
597 if (using_default_torrc
)
598 printf("IMPORTANT NOTE:\n"
599 " The Tor service will run under the account \"%s\". This means\n"
600 " that Tor will look for its configuration file under that\n"
601 " account's Application Data directory, which is probably not\n"
602 " the same as yours.\n", user_acct
?user_acct
:"<local system>");
604 /* Create the Tor service, set to auto-start on boot */
605 if ((hService
= service_fns
.CreateServiceA_fn(hSCManager
, GENSRV_SERVICENAME
,
607 SERVICE_ALL_ACCESS
, SERVICE_WIN32_OWN_PROCESS
,
608 SERVICE_AUTO_START
, SERVICE_ERROR_IGNORE
,
609 command
, NULL
, NULL
, NULL
,
610 user_acct
, password
)) == NULL
) {
611 errmsg
= format_win32_error(GetLastError());
612 printf("CreateService() failed : %s\n", errmsg
);
613 service_fns
.CloseServiceHandle_fn(hSCManager
);
618 printf("Done with CreateService.\n");
620 /* Set the service's description */
621 sdBuff
.lpDescription
= (char*)GENSRV_DESCRIPTION
;
622 service_fns
.ChangeServiceConfig2A_fn(hService
, SERVICE_CONFIG_DESCRIPTION
,
624 printf("Service installed successfully\n");
626 /* Start the service initially */
627 nt_service_start(hService
);
629 service_fns
.CloseServiceHandle_fn(hService
);
630 service_fns
.CloseServiceHandle_fn(hSCManager
);
636 /** Removes the Tor NT service. Returns 0 if the service was successfully
637 * removed, or -1 on error. */
639 nt_service_remove(void)
641 SC_HANDLE hSCManager
= NULL
;
642 SC_HANDLE hService
= NULL
;
645 nt_service_loadlibrary();
646 if ((hSCManager
= nt_service_open_scm()) == NULL
)
648 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
649 service_fns
.CloseServiceHandle_fn(hSCManager
);
653 nt_service_stop(hService
);
654 if (service_fns
.DeleteService_fn(hService
) == FALSE
) {
655 errmsg
= format_win32_error(GetLastError());
656 printf("DeleteService() failed : %s\n", errmsg
);
658 service_fns
.CloseServiceHandle_fn(hService
);
659 service_fns
.CloseServiceHandle_fn(hSCManager
);
663 service_fns
.CloseServiceHandle_fn(hService
);
664 service_fns
.CloseServiceHandle_fn(hSCManager
);
665 printf("Service removed successfully\n");
670 /** Starts the Tor service. Returns 0 on success, or -1 on error. */
672 nt_service_cmd_start(void)
674 SC_HANDLE hSCManager
;
678 if ((hSCManager
= nt_service_open_scm()) == NULL
)
680 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
681 service_fns
.CloseServiceHandle_fn(hSCManager
);
685 start
= nt_service_start(hService
);
686 service_fns
.CloseServiceHandle_fn(hService
);
687 service_fns
.CloseServiceHandle_fn(hSCManager
);
692 /** Stops the Tor service. Returns 0 on success, or -1 on error. */
694 nt_service_cmd_stop(void)
696 SC_HANDLE hSCManager
;
700 if ((hSCManager
= nt_service_open_scm()) == NULL
)
702 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
703 service_fns
.CloseServiceHandle_fn(hSCManager
);
707 stop
= nt_service_stop(hService
);
708 service_fns
.CloseServiceHandle_fn(hService
);
709 service_fns
.CloseServiceHandle_fn(hSCManager
);
715 nt_service_parse_options(int argc
, char **argv
, int *should_exit
)
722 (!strcmp(argv
[1], "-service") || !strcmp(argv
[1], "--service"))) {
723 nt_service_loadlibrary();
725 if (!strcmp(argv
[2], "install"))
726 return nt_service_install(argc
, argv
);
727 if (!strcmp(argv
[2], "remove"))
728 return nt_service_remove();
729 if (!strcmp(argv
[2], "start"))
730 return nt_service_cmd_start();
731 if (!strcmp(argv
[2], "stop"))
732 return nt_service_cmd_stop();
733 printf("Unrecognized service command '%s'\n", argv
[2]);
737 if (!strcmp(argv
[1], "-nt-service") || !strcmp(argv
[1], "--nt-service")) {
738 nt_service_loadlibrary();
743 // These values have been deprecated since 0.1.1.2-alpha; we've warned
744 // about them since 0.1.2.7-alpha.
745 if (!strcmp(argv
[1], "-install") || !strcmp(argv
[1], "--install")) {
746 nt_service_loadlibrary();
748 "The %s option is deprecated; use \"--service install\" instead.",
751 return nt_service_install(argc
, argv
);
753 if (!strcmp(argv
[1], "-remove") || !strcmp(argv
[1], "--remove")) {
754 nt_service_loadlibrary();
756 "The %s option is deprecated; use \"--service remove\" instead.",
759 return nt_service_remove();