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 */
10 #define GENSRV_SERVICENAME TEXT("tor")
11 #define GENSRV_DISPLAYNAME TEXT("Tor Win32 Service")
12 #define GENSRV_DESCRIPTION \
13 TEXT("Provides an anonymous Internet communication system")
14 #define GENSRV_USERACCT TEXT("NT AUTHORITY\\LocalService")
16 // Cheating: using the pre-defined error codes, tricks Windows into displaying
17 // a semi-related human-readable error message if startup fails as
18 // opposed to simply scaring people with Error: 0xffffffff
19 #define NT_SERVICE_ERROR_TORINIT_FAILED ERROR_EXCEPTION_IN_SERVICE
21 static SERVICE_STATUS service_status
;
22 static SERVICE_STATUS_HANDLE hStatus
;
24 /* XXXX This 'backup argv' and 'backup argc' business is an ugly hack. This
25 * is a job for arguments, not globals. Alas, some of the functions that
26 * use them use them need to have fixed signatures, so they can be passed
27 * to the NT service functions. */
28 static char **backup_argv
;
29 static int backup_argc
;
30 static char* nt_strerror(uint32_t errnum
);
32 static void nt_service_control(DWORD request
);
33 static void nt_service_body(int argc
, char **argv
);
34 static void nt_service_main(void);
35 static SC_HANDLE
nt_service_open_scm(void);
36 static SC_HANDLE
nt_service_open(SC_HANDLE hSCManager
);
37 static int nt_service_start(SC_HANDLE hService
);
38 static int nt_service_stop(SC_HANDLE hService
);
39 static int nt_service_install(int argc
, char **argv
);
40 static int nt_service_remove(void);
41 static int nt_service_cmd_start(void);
42 static int nt_service_cmd_stop(void);
44 /** Struct to hold dynamically loaded NT-service related function pointers.
49 BOOL (WINAPI
*ChangeServiceConfig2A_fn
)(
54 BOOL (WINAPI
*CloseServiceHandle_fn
)(
57 BOOL (WINAPI
*ControlService_fn
)(
60 LPSERVICE_STATUS lpServiceStatus
);
62 SC_HANDLE (WINAPI
*CreateServiceA_fn
)(
64 LPCTSTR lpServiceName
,
65 LPCTSTR lpDisplayName
,
66 DWORD dwDesiredAccess
,
70 LPCTSTR lpBinaryPathName
,
71 LPCTSTR lpLoadOrderGroup
,
73 LPCTSTR lpDependencies
,
74 LPCTSTR lpServiceStartName
,
77 BOOL (WINAPI
*DeleteService_fn
)(
80 SC_HANDLE (WINAPI
*OpenSCManagerA_fn
)(
81 LPCTSTR lpMachineName
,
82 LPCTSTR lpDatabaseName
,
83 DWORD dwDesiredAccess
);
85 SC_HANDLE (WINAPI
*OpenServiceA_fn
)(
87 LPCTSTR lpServiceName
,
88 DWORD dwDesiredAccess
);
90 BOOL (WINAPI
*QueryServiceStatus_fn
)(
92 LPSERVICE_STATUS lpServiceStatus
);
94 SERVICE_STATUS_HANDLE (WINAPI
*RegisterServiceCtrlHandlerA_fn
)(
95 LPCTSTR lpServiceName
,
96 LPHANDLER_FUNCTION lpHandlerProc
);
98 BOOL (WINAPI
*SetServiceStatus_fn
)(SERVICE_STATUS_HANDLE
,
101 BOOL (WINAPI
*StartServiceCtrlDispatcherA_fn
)(
102 const SERVICE_TABLE_ENTRY
* lpServiceTable
);
104 BOOL (WINAPI
*StartServiceA_fn
)(
106 DWORD dwNumServiceArgs
,
107 LPCTSTR
* lpServiceArgVectors
);
109 BOOL (WINAPI
*LookupAccountNameA_fn
)(
110 LPCTSTR lpSystemName
,
111 LPCTSTR lpAccountName
,
114 LPTSTR ReferencedDomainName
,
115 LPDWORD cchReferencedDomainName
,
116 PSID_NAME_USE peUse
);
118 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
119 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
122 /** Loads functions used by NT services. Returns on success, or prints a
123 * complaint to stdout and exits on error. */
125 nt_service_loadlibrary(void)
130 if (service_fns
.loaded
)
133 /* XXXX Possibly, we should hardcode the location of this DLL. */
134 if (!(library
= LoadLibrary("advapi32.dll"))) {
135 log_err(LD_GENERAL
, "Couldn't open advapi32.dll. Are you trying to use "
136 "NT services on Windows 98? That doesn't work.");
140 #define LOAD(f) STMT_BEGIN \
141 if (!(fn = GetProcAddress(library, #f))) { \
143 "Couldn't find %s in advapi32.dll! We probably got the " \
144 "name wrong.", #f); \
147 service_fns.f ## _fn = fn; \
151 LOAD(ChangeServiceConfig2A
);
152 LOAD(CloseServiceHandle
);
153 LOAD(ControlService
);
154 LOAD(CreateServiceA
);
156 LOAD(OpenSCManagerA
);
158 LOAD(QueryServiceStatus
);
159 LOAD(RegisterServiceCtrlHandlerA
);
160 LOAD(SetServiceStatus
);
161 LOAD(StartServiceCtrlDispatcherA
);
163 LOAD(LookupAccountNameA
);
165 service_fns
.loaded
= 1;
169 printf("Unable to load library support for NT services: exiting.\n");
173 /** If we're compiled to run as an NT service, and the service wants to
174 * shut down, then change our current status and return 1. Else
178 nt_service_is_stopping(void)
179 /* XXXX this function would probably _love_ to be inline, in 0.2.0. */
181 /* If we haven't loaded the function pointers, we can't possibly be an NT
182 * service trying to shut down. */
183 if (!service_fns
.loaded
)
186 if (service_status
.dwCurrentState
== SERVICE_STOP_PENDING
) {
187 service_status
.dwWin32ExitCode
= 0;
188 service_status
.dwCurrentState
= SERVICE_STOPPED
;
189 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
191 } else if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
197 /** Set the dwCurrentState field for our service to <b>state</b>. */
199 nt_service_set_state(DWORD state
)
201 service_status
.dwCurrentState
= state
;
204 /** Handles service control requests, such as stopping or starting the
207 nt_service_control(DWORD request
)
209 static struct timeval exit_now
;
211 exit_now
.tv_usec
= 0;
213 nt_service_loadlibrary();
216 case SERVICE_CONTROL_STOP
:
217 case SERVICE_CONTROL_SHUTDOWN
:
218 log_notice(LD_GENERAL
,
219 "Got stop/shutdown request; shutting down cleanly.");
220 service_status
.dwCurrentState
= SERVICE_STOP_PENDING
;
221 event_loopexit(&exit_now
);
224 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
227 /** Called when the service is started via the system's service control
228 * manager. This calls tor_init() and starts the main event loop. If
229 * tor_init() fails, the service will be stopped and exit code set to
230 * NT_SERVICE_ERROR_TORINIT_FAILED. */
232 nt_service_body(int argc
, char **argv
)
235 (void) argc
; /* unused */
236 (void) argv
; /* unused */
237 nt_service_loadlibrary();
238 service_status
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
;
239 service_status
.dwCurrentState
= SERVICE_START_PENDING
;
240 service_status
.dwControlsAccepted
=
241 SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
;
242 service_status
.dwWin32ExitCode
= 0;
243 service_status
.dwServiceSpecificExitCode
= 0;
244 service_status
.dwCheckPoint
= 0;
245 service_status
.dwWaitHint
= 1000;
246 hStatus
= service_fns
.RegisterServiceCtrlHandlerA_fn(GENSRV_SERVICENAME
,
247 (LPHANDLER_FUNCTION
) nt_service_control
);
250 /* Failed to register the service control handler function */
254 r
= tor_init(backup_argc
, backup_argv
);
256 /* Failed to start the Tor service */
257 r
= NT_SERVICE_ERROR_TORINIT_FAILED
;
258 service_status
.dwCurrentState
= SERVICE_STOPPED
;
259 service_status
.dwWin32ExitCode
= r
;
260 service_status
.dwServiceSpecificExitCode
= r
;
261 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
265 /* Set the service's status to SERVICE_RUNNING and start the main
267 service_status
.dwCurrentState
= SERVICE_RUNNING
;
268 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
273 /** Main service entry point. Starts the service control dispatcher and waits
274 * until the service status is set to SERVICE_STOPPED. */
276 nt_service_main(void)
278 SERVICE_TABLE_ENTRY table
[2];
281 nt_service_loadlibrary();
282 table
[0].lpServiceName
= (char*)GENSRV_SERVICENAME
;
283 table
[0].lpServiceProc
= (LPSERVICE_MAIN_FUNCTION
)nt_service_body
;
284 table
[1].lpServiceName
= NULL
;
285 table
[1].lpServiceProc
= NULL
;
287 if (!service_fns
.StartServiceCtrlDispatcherA_fn(table
)) {
288 result
= GetLastError();
289 errmsg
= nt_strerror(result
);
290 printf("Service error %d : %s\n", (int) result
, errmsg
);
292 if (result
== ERROR_FAILED_SERVICE_CONTROLLER_CONNECT
) {
293 if (tor_init(backup_argc
, backup_argv
) < 0)
295 switch (get_options()->command
) {
299 case CMD_LIST_FINGERPRINT
:
300 case CMD_HASH_PASSWORD
:
301 case CMD_VERIFY_CONFIG
:
302 log_err(LD_CONFIG
, "Unsupported command (--list-fingerint, "
303 "--hash-password, or --verify-config) in NT service.");
305 case CMD_RUN_UNITTESTS
:
307 log_err(LD_CONFIG
, "Illegal command number %d: internal error.",
308 get_options()->command
);
315 /** Return a handle to the service control manager on success, or NULL on
318 nt_service_open_scm(void)
320 SC_HANDLE hSCManager
;
323 nt_service_loadlibrary();
324 if ((hSCManager
= service_fns
.OpenSCManagerA_fn(
325 NULL
, NULL
, SC_MANAGER_CREATE_SERVICE
)) == NULL
) {
326 errmsg
= nt_strerror(GetLastError());
327 printf("OpenSCManager() failed : %s\n", errmsg
);
333 /** Open a handle to the Tor service using <b>hSCManager</b>. Return NULL
336 nt_service_open(SC_HANDLE hSCManager
)
340 nt_service_loadlibrary();
341 if ((hService
= service_fns
.OpenServiceA_fn(hSCManager
, GENSRV_SERVICENAME
,
342 SERVICE_ALL_ACCESS
)) == NULL
) {
343 errmsg
= nt_strerror(GetLastError());
344 printf("OpenService() failed : %s\n", errmsg
);
350 /** Start the Tor service. Return 0 if the service is started or was
351 * previously running. Return -1 on error. */
353 nt_service_start(SC_HANDLE hService
)
357 nt_service_loadlibrary();
359 service_fns
.QueryServiceStatus_fn(hService
, &service_status
);
360 if (service_status
.dwCurrentState
== SERVICE_RUNNING
) {
361 printf("Service is already running\n");
365 if (service_fns
.StartServiceA_fn(hService
, 0, NULL
)) {
366 /* Loop until the service has finished attempting to start */
367 while (service_fns
.QueryServiceStatus_fn(hService
, &service_status
) &&
368 (service_status
.dwCurrentState
== SERVICE_START_PENDING
)) {
372 /* Check if it started successfully or not */
373 if (service_status
.dwCurrentState
== SERVICE_RUNNING
) {
374 printf("Service started successfully\n");
377 errmsg
= nt_strerror(service_status
.dwWin32ExitCode
);
378 printf("Service failed to start : %s\n", errmsg
);
382 errmsg
= nt_strerror(GetLastError());
383 printf("StartService() failed : %s\n", errmsg
);
389 /** Stop the Tor service. Return 0 if the service is stopped or was not
390 * previously running. Return -1 on error. */
392 nt_service_stop(SC_HANDLE hService
)
394 /** Wait at most 10 seconds for the service to stop. */
395 #define MAX_SERVICE_WAIT_TIME 10
398 nt_service_loadlibrary();
400 service_fns
.QueryServiceStatus_fn(hService
, &service_status
);
401 if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
402 printf("Service is already stopped\n");
406 if (service_fns
.ControlService_fn(hService
, SERVICE_CONTROL_STOP
,
409 while (service_fns
.QueryServiceStatus_fn(hService
, &service_status
) &&
410 (service_status
.dwCurrentState
!= SERVICE_STOPPED
) &&
411 (wait_time
< MAX_SERVICE_WAIT_TIME
)) {
415 if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
416 printf("Service stopped successfully\n");
418 } else if (wait_time
== MAX_SERVICE_WAIT_TIME
) {
419 printf("Service did not stop within %d seconds.\n", wait_time
);
421 errmsg
= nt_strerror(GetLastError());
422 printf("QueryServiceStatus() failed : %s\n",errmsg
);
426 errmsg
= nt_strerror(GetLastError());
427 printf("ControlService() failed : %s\n", errmsg
);
433 /** Build a formatted command line used for the NT service. Return a
434 * pointer to the formatted string on success, or NULL on failure. Set
435 * *<b>using_default_torrc</b> to true if we're going to use the default
436 * location to torrc, or 1 if an option was specified on the command line.
439 nt_service_command_line(int *using_default_torrc
)
441 TCHAR tor_exe
[MAX_PATH
+1];
442 char *command
, *options
=NULL
;
445 *using_default_torrc
= 1;
447 /* Get the location of tor.exe */
448 if (0 == GetModuleFileName(NULL
, tor_exe
, MAX_PATH
))
451 /* Get the service arguments */
452 sl
= smartlist_create();
453 for (i
= 1; i
< backup_argc
; ++i
) {
454 if (!strcmp(backup_argv
[i
], "--options") ||
455 !strcmp(backup_argv
[i
], "-options")) {
456 while (++i
< backup_argc
) {
457 if (!strcmp(backup_argv
[i
], "-f"))
458 *using_default_torrc
= 0;
459 smartlist_add(sl
, backup_argv
[i
]);
463 if (smartlist_len(sl
))
464 options
= smartlist_join_strings(sl
,"\" \"",0,NULL
);
467 /* Allocate a string for the NT service command line */
468 cmdlen
= strlen(tor_exe
) + (options
?strlen(options
):0) + 32;
469 command
= tor_malloc(cmdlen
);
471 /* Format the service command */
473 if (tor_snprintf(command
, cmdlen
, "\"%s\" --nt-service \"%s\"",
474 tor_exe
, options
)<0) {
475 tor_free(command
); /* sets command to NULL. */
477 } else { /* ! options */
478 if (tor_snprintf(command
, cmdlen
, "\"%s\" --nt-service", tor_exe
)<0) {
479 tor_free(command
); /* sets command to NULL. */
487 /** Creates a Tor NT service, set to start on boot. The service will be
488 * started if installation succeeds. Returns 0 on success, or -1 on
491 nt_service_install(int argc
, char **argv
)
493 /* Notes about developing NT services:
495 * 1. Don't count on your CWD. If an absolute path is not given, the
496 * fopen() function goes wrong.
497 * 2. The parameters given to the nt_service_body() function differ
498 * from those given to main() function.
501 SC_HANDLE hSCManager
= NULL
;
502 SC_HANDLE hService
= NULL
;
503 SERVICE_DESCRIPTION sdBuff
;
506 const char *user_acct
= GENSRV_USERACCT
;
507 const char *password
= "";
509 OSVERSIONINFOEX info
;
511 DWORD sidLen
= 0, domainLen
= 0;
512 int is_win2k_or_worse
= 0;
513 int using_default_torrc
= 0;
515 nt_service_loadlibrary();
517 /* Open the service control manager so we can create a new service */
518 if ((hSCManager
= nt_service_open_scm()) == NULL
)
520 /* Build the command line used for the service */
521 if ((command
= nt_service_command_line(&using_default_torrc
)) == NULL
) {
522 printf("Unable to build service command line.\n");
523 service_fns
.CloseServiceHandle_fn(hSCManager
);
527 for (i
=1; i
< argc
; ++i
) {
528 if (!strcmp(argv
[i
], "--user") && i
+1<argc
) {
529 user_acct
= argv
[i
+1];
532 if (!strcmp(argv
[i
], "--password") && i
+1<argc
) {
533 password
= argv
[i
+1];
538 /* Compute our version and see whether we're running win2k or earlier. */
539 memset(&info
, 0, sizeof(info
));
540 info
.dwOSVersionInfoSize
= sizeof(info
);
541 if (! GetVersionEx((LPOSVERSIONINFO
)&info
)) {
542 printf("Call to GetVersionEx failed.\n");
543 is_win2k_or_worse
= 1;
545 if (info
.dwMajorVersion
< 5 ||
546 (info
.dwMajorVersion
== 5 && info
.dwMinorVersion
== 0))
547 is_win2k_or_worse
= 1;
550 if (user_acct
== GENSRV_USERACCT
) {
551 if (is_win2k_or_worse
) {
552 /* On Win2k, there is no LocalService account, so we actually need to
553 * fall back on NULL (the system account). */
554 printf("Running on Win2K or earlier, so the LocalService account "
555 "doesn't exist. Falling back to SYSTEM account.\n");
558 /* Genericity is apparently _so_ last year in Redmond, where some
559 * accounts are accounts that you can look up, and some accounts
560 * are magic and undetectable via the security subsystem. See
561 * http://msdn2.microsoft.com/en-us/library/ms684188.aspx
563 printf("Running on a Post-Win2K OS, so we'll assume that the "
564 "LocalService account exists.\n");
566 } else if (0 && service_fns
.LookupAccountNameA_fn(NULL
, // On this system
568 NULL
, &sidLen
, // Don't care about the SID
569 NULL
, &domainLen
, // Don't care about the domain
571 /* XXXX For some reason, the above test segfaults. Fix that. */
572 printf("User \"%s\" doesn't seem to exist.\n", user_acct
);
575 printf("Will try to install service as user \"%s\".\n", user_acct
);
577 /* XXXX This warning could be better about explaining how to resolve the
579 if (using_default_torrc
)
580 printf("IMPORTANT NOTE:\n"
581 " The Tor service will run under the account \"%s\". This means\n"
582 " that Tor will look for its configuration file under that\n"
583 " account's Application Data directory, which is probably not\n"
584 " the same as yours.\n", user_acct
?user_acct
:"<local system>");
586 /* Create the Tor service, set to auto-start on boot */
587 if ((hService
= service_fns
.CreateServiceA_fn(hSCManager
, GENSRV_SERVICENAME
,
589 SERVICE_ALL_ACCESS
, SERVICE_WIN32_OWN_PROCESS
,
590 SERVICE_AUTO_START
, SERVICE_ERROR_IGNORE
,
591 command
, NULL
, NULL
, NULL
,
592 user_acct
, password
)) == NULL
) {
593 errmsg
= nt_strerror(GetLastError());
594 printf("CreateService() failed : %s\n", errmsg
);
595 service_fns
.CloseServiceHandle_fn(hSCManager
);
600 printf("Done with CreateService.\n");
602 /* Set the service's description */
603 sdBuff
.lpDescription
= (char*)GENSRV_DESCRIPTION
;
604 service_fns
.ChangeServiceConfig2A_fn(hService
, SERVICE_CONFIG_DESCRIPTION
,
606 printf("Service installed successfully\n");
608 /* Start the service initially */
609 nt_service_start(hService
);
611 service_fns
.CloseServiceHandle_fn(hService
);
612 service_fns
.CloseServiceHandle_fn(hSCManager
);
618 /** Removes the Tor NT service. Returns 0 if the service was successfully
619 * removed, or -1 on error. */
621 nt_service_remove(void)
623 SC_HANDLE hSCManager
= NULL
;
624 SC_HANDLE hService
= NULL
;
627 nt_service_loadlibrary();
628 if ((hSCManager
= nt_service_open_scm()) == NULL
)
630 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
631 service_fns
.CloseServiceHandle_fn(hSCManager
);
635 nt_service_stop(hService
);
636 if (service_fns
.DeleteService_fn(hService
) == FALSE
) {
637 errmsg
= nt_strerror(GetLastError());
638 printf("DeleteService() failed : %s\n", errmsg
);
640 service_fns
.CloseServiceHandle_fn(hService
);
641 service_fns
.CloseServiceHandle_fn(hSCManager
);
645 service_fns
.CloseServiceHandle_fn(hService
);
646 service_fns
.CloseServiceHandle_fn(hSCManager
);
647 printf("Service removed successfully\n");
652 /** Starts the Tor service. Returns 0 on success, or -1 on error. */
654 nt_service_cmd_start(void)
656 SC_HANDLE hSCManager
;
660 if ((hSCManager
= nt_service_open_scm()) == NULL
)
662 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
663 service_fns
.CloseServiceHandle_fn(hSCManager
);
667 start
= nt_service_start(hService
);
668 service_fns
.CloseServiceHandle_fn(hService
);
669 service_fns
.CloseServiceHandle_fn(hSCManager
);
674 /** Stops the Tor service. Returns 0 on success, or -1 on error. */
676 nt_service_cmd_stop(void)
678 SC_HANDLE hSCManager
;
682 if ((hSCManager
= nt_service_open_scm()) == NULL
)
684 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
685 service_fns
.CloseServiceHandle_fn(hSCManager
);
689 stop
= nt_service_stop(hService
);
690 service_fns
.CloseServiceHandle_fn(hService
);
691 service_fns
.CloseServiceHandle_fn(hSCManager
);
696 /** Given a Win32 error code, this attempts to make Windows
697 * return a human-readable error message. The char* returned
698 * is allocated by Windows, but should be freed with LocalFree()
699 * when finished with it. */
701 nt_strerror(uint32_t errnum
)
704 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
,
705 NULL
, errnum
, MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
706 (LPSTR
)&msgbuf
, 0, NULL
);
711 nt_service_parse_options(int argc
, char **argv
, int *should_exit
)
718 (!strcmp(argv
[1], "-service") || !strcmp(argv
[1], "--service"))) {
719 nt_service_loadlibrary();
720 if (!strcmp(argv
[2], "install"))
721 return nt_service_install(argc
, argv
);
722 if (!strcmp(argv
[2], "remove"))
723 return nt_service_remove();
724 if (!strcmp(argv
[2], "start"))
725 return nt_service_cmd_start();
726 if (!strcmp(argv
[2], "stop"))
727 return nt_service_cmd_stop();
728 printf("Unrecognized service command '%s'\n", argv
[2]);
733 if (!strcmp(argv
[1], "-nt-service") || !strcmp(argv
[1], "--nt-service")) {
734 nt_service_loadlibrary();
739 // These values have been deprecated since 0.1.1.2-alpha; we've warned
740 // about them since 0.1.2.7-alpha.
741 if (!strcmp(argv
[1], "-install") || !strcmp(argv
[1], "--install")) {
742 nt_service_loadlibrary();
744 "The %s option is deprecated; use \"--service install\" instead.",
747 return nt_service_install(argc
, argv
);
749 if (!strcmp(argv
[1], "-remove") || !strcmp(argv
[1], "--remove")) {
750 nt_service_loadlibrary();
752 "The %s option is deprecated; use \"--service remove\" instead.",
755 return nt_service_remove();