1 /* Copyright (c) 2001-2004, Roger Dingledine.
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2008, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
10 const char ntmain_c_id
[] =
14 #define GENSRV_SERVICENAME TEXT("tor")
15 #define GENSRV_DISPLAYNAME TEXT("Tor Win32 Service")
16 #define GENSRV_DESCRIPTION \
17 TEXT("Provides an anonymous Internet communication system")
18 #define GENSRV_USERACCT TEXT("NT AUTHORITY\\LocalService")
20 // Cheating: using the pre-defined error codes, tricks Windows into displaying
21 // a semi-related human-readable error message if startup fails as
22 // opposed to simply scaring people with Error: 0xffffffff
23 #define NT_SERVICE_ERROR_TORINIT_FAILED ERROR_EXCEPTION_IN_SERVICE
25 static SERVICE_STATUS service_status
;
26 static SERVICE_STATUS_HANDLE hStatus
;
28 /* XXXX This 'backup argv' and 'backup argc' business is an ugly hack. This
29 * is a job for arguments, not globals. Alas, some of the functions that
30 * use them use them need to have fixed signatures, so they can be passed
31 * to the NT service functions. */
32 static char **backup_argv
;
33 static int backup_argc
;
34 static char* nt_strerror(uint32_t errnum
);
36 static void nt_service_control(DWORD request
);
37 static void nt_service_body(int argc
, char **argv
);
38 static void nt_service_main(void);
39 static SC_HANDLE
nt_service_open_scm(void);
40 static SC_HANDLE
nt_service_open(SC_HANDLE hSCManager
);
41 static int nt_service_start(SC_HANDLE hService
);
42 static int nt_service_stop(SC_HANDLE hService
);
43 static int nt_service_install(int argc
, char **argv
);
44 static int nt_service_remove(void);
45 static int nt_service_cmd_start(void);
46 static int nt_service_cmd_stop(void);
48 /** Struct to hold dynamically loaded NT-service related function pointers.
53 BOOL (WINAPI
*ChangeServiceConfig2A_fn
)(
58 BOOL (WINAPI
*CloseServiceHandle_fn
)(
61 BOOL (WINAPI
*ControlService_fn
)(
64 LPSERVICE_STATUS lpServiceStatus
);
66 SC_HANDLE (WINAPI
*CreateServiceA_fn
)(
68 LPCTSTR lpServiceName
,
69 LPCTSTR lpDisplayName
,
70 DWORD dwDesiredAccess
,
74 LPCTSTR lpBinaryPathName
,
75 LPCTSTR lpLoadOrderGroup
,
77 LPCTSTR lpDependencies
,
78 LPCTSTR lpServiceStartName
,
81 BOOL (WINAPI
*DeleteService_fn
)(
84 SC_HANDLE (WINAPI
*OpenSCManagerA_fn
)(
85 LPCTSTR lpMachineName
,
86 LPCTSTR lpDatabaseName
,
87 DWORD dwDesiredAccess
);
89 SC_HANDLE (WINAPI
*OpenServiceA_fn
)(
91 LPCTSTR lpServiceName
,
92 DWORD dwDesiredAccess
);
94 BOOL (WINAPI
*QueryServiceStatus_fn
)(
96 LPSERVICE_STATUS lpServiceStatus
);
98 SERVICE_STATUS_HANDLE (WINAPI
*RegisterServiceCtrlHandlerA_fn
)(
99 LPCTSTR lpServiceName
,
100 LPHANDLER_FUNCTION lpHandlerProc
);
102 BOOL (WINAPI
*SetServiceStatus_fn
)(SERVICE_STATUS_HANDLE
,
105 BOOL (WINAPI
*StartServiceCtrlDispatcherA_fn
)(
106 const SERVICE_TABLE_ENTRY
* lpServiceTable
);
108 BOOL (WINAPI
*StartServiceA_fn
)(
110 DWORD dwNumServiceArgs
,
111 LPCTSTR
* lpServiceArgVectors
);
113 BOOL (WINAPI
*LookupAccountNameA_fn
)(
114 LPCTSTR lpSystemName
,
115 LPCTSTR lpAccountName
,
118 LPTSTR ReferencedDomainName
,
119 LPDWORD cchReferencedDomainName
,
120 PSID_NAME_USE peUse
);
122 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
123 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
126 /** Loads functions used by NT services. Returns on success, or prints a
127 * complaint to stdout and exits on error. */
129 nt_service_loadlibrary(void)
134 if (service_fns
.loaded
)
137 /* XXXX Possibly, we should hardcode the location of this DLL. */
138 if (!(library
= LoadLibrary("advapi32.dll"))) {
139 log_err(LD_GENERAL
, "Couldn't open advapi32.dll. Are you trying to use "
140 "NT services on Windows 98? That doesn't work.");
144 #define LOAD(f) STMT_BEGIN \
145 if (!(fn = GetProcAddress(library, #f))) { \
147 "Couldn't find %s in advapi32.dll! We probably got the " \
148 "name wrong.", #f); \
151 service_fns.f ## _fn = fn; \
155 LOAD(ChangeServiceConfig2A
);
156 LOAD(CloseServiceHandle
);
157 LOAD(ControlService
);
158 LOAD(CreateServiceA
);
160 LOAD(OpenSCManagerA
);
162 LOAD(QueryServiceStatus
);
163 LOAD(RegisterServiceCtrlHandlerA
);
164 LOAD(SetServiceStatus
);
165 LOAD(StartServiceCtrlDispatcherA
);
167 LOAD(LookupAccountNameA
);
169 service_fns
.loaded
= 1;
173 printf("Unable to load library support for NT services: exiting.\n");
177 /** If we're compiled to run as an NT service, and the service wants to
178 * shut down, then change our current status and return 1. Else
182 nt_service_is_stopping(void)
183 /* XXXX this function would probably _love_ to be inline, in 0.2.0. */
185 /* If we haven't loaded the function pointers, we can't possibly be an NT
186 * service trying to shut down. */
187 if (!service_fns
.loaded
)
190 if (service_status
.dwCurrentState
== SERVICE_STOP_PENDING
) {
191 service_status
.dwWin32ExitCode
= 0;
192 service_status
.dwCurrentState
= SERVICE_STOPPED
;
193 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
195 } else if (service_status
.dwCurrentState
== SERVICE_STOPPED
) {
201 /** Set the dwCurrentState field for our service to <b>state</b>. */
203 nt_service_set_state(DWORD state
)
205 service_status
.dwCurrentState
= state
;
208 /** Handles service control requests, such as stopping or starting the
211 nt_service_control(DWORD request
)
213 static struct timeval exit_now
;
215 exit_now
.tv_usec
= 0;
217 nt_service_loadlibrary();
220 case SERVICE_CONTROL_STOP
:
221 case SERVICE_CONTROL_SHUTDOWN
:
222 log_notice(LD_GENERAL
,
223 "Got stop/shutdown request; shutting down cleanly.");
224 service_status
.dwCurrentState
= SERVICE_STOP_PENDING
;
225 event_loopexit(&exit_now
);
228 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
231 /** Called when the service is started via the system's service control
232 * manager. This calls tor_init() and starts the main event loop. If
233 * tor_init() fails, the service will be stopped and exit code set to
234 * NT_SERVICE_ERROR_TORINIT_FAILED. */
236 nt_service_body(int argc
, char **argv
)
239 (void) argc
; /* unused */
240 (void) argv
; /* unused */
241 nt_service_loadlibrary();
242 service_status
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
;
243 service_status
.dwCurrentState
= SERVICE_START_PENDING
;
244 service_status
.dwControlsAccepted
=
245 SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
;
246 service_status
.dwWin32ExitCode
= 0;
247 service_status
.dwServiceSpecificExitCode
= 0;
248 service_status
.dwCheckPoint
= 0;
249 service_status
.dwWaitHint
= 1000;
250 hStatus
= service_fns
.RegisterServiceCtrlHandlerA_fn(GENSRV_SERVICENAME
,
251 (LPHANDLER_FUNCTION
) nt_service_control
);
254 /* Failed to register the service control handler function */
258 r
= tor_init(backup_argc
, backup_argv
);
260 /* Failed to start the Tor service */
261 r
= NT_SERVICE_ERROR_TORINIT_FAILED
;
262 service_status
.dwCurrentState
= SERVICE_STOPPED
;
263 service_status
.dwWin32ExitCode
= r
;
264 service_status
.dwServiceSpecificExitCode
= r
;
265 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
269 /* Set the service's status to SERVICE_RUNNING and start the main
271 service_status
.dwCurrentState
= SERVICE_RUNNING
;
272 service_fns
.SetServiceStatus_fn(hStatus
, &service_status
);
277 /** Main service entry point. Starts the service control dispatcher and waits
278 * until the service status is set to SERVICE_STOPPED. */
280 nt_service_main(void)
282 SERVICE_TABLE_ENTRY table
[2];
285 nt_service_loadlibrary();
286 table
[0].lpServiceName
= (char*)GENSRV_SERVICENAME
;
287 table
[0].lpServiceProc
= (LPSERVICE_MAIN_FUNCTION
)nt_service_body
;
288 table
[1].lpServiceName
= NULL
;
289 table
[1].lpServiceProc
= NULL
;
291 if (!service_fns
.StartServiceCtrlDispatcherA_fn(table
)) {
292 result
= GetLastError();
293 errmsg
= nt_strerror(result
);
294 printf("Service error %d : %s\n", (int) result
, errmsg
);
296 if (result
== ERROR_FAILED_SERVICE_CONTROLLER_CONNECT
) {
297 if (tor_init(backup_argc
, backup_argv
) < 0)
299 switch (get_options()->command
) {
303 case CMD_LIST_FINGERPRINT
:
304 do_list_fingerprint();
306 case CMD_HASH_PASSWORD
:
309 case CMD_VERIFY_CONFIG
:
310 printf("Configuration was valid\n");
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
= nt_strerror(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
= nt_strerror(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
= nt_strerror(service_status
.dwWin32ExitCode
);
385 printf("Service failed to start : %s\n", errmsg
);
389 errmsg
= nt_strerror(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
= nt_strerror(GetLastError());
429 printf("QueryServiceStatus() failed : %s\n",errmsg
);
433 errmsg
= nt_strerror(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 *command
, *options
=NULL
;
452 *using_default_torrc
= 1;
454 /* Get the location of tor.exe */
455 if (0 == GetModuleFileName(NULL
, tor_exe
, MAX_PATH
))
458 /* Get the service arguments */
459 sl
= smartlist_create();
460 for (i
= 1; i
< backup_argc
; ++i
) {
461 if (!strcmp(backup_argv
[i
], "--options") ||
462 !strcmp(backup_argv
[i
], "-options")) {
463 while (++i
< backup_argc
) {
464 if (!strcmp(backup_argv
[i
], "-f"))
465 *using_default_torrc
= 0;
466 smartlist_add(sl
, backup_argv
[i
]);
470 if (smartlist_len(sl
))
471 options
= smartlist_join_strings(sl
,"\" \"",0,NULL
);
474 /* Allocate a string for the NT service command line */
475 cmdlen
= strlen(tor_exe
) + (options
?strlen(options
):0) + 32;
476 command
= tor_malloc(cmdlen
);
478 /* Format the service command */
480 if (tor_snprintf(command
, cmdlen
, "\"%s\" --nt-service \"%s\"",
481 tor_exe
, options
)<0) {
482 tor_free(command
); /* sets command to NULL. */
484 } else { /* ! options */
485 if (tor_snprintf(command
, cmdlen
, "\"%s\" --nt-service", tor_exe
)<0) {
486 tor_free(command
); /* sets command to NULL. */
494 /** Creates a Tor NT service, set to start on boot. The service will be
495 * started if installation succeeds. Returns 0 on success, or -1 on
498 nt_service_install(int argc
, char **argv
)
500 /* Notes about developing NT services:
502 * 1. Don't count on your CWD. If an absolute path is not given, the
503 * fopen() function goes wrong.
504 * 2. The parameters given to the nt_service_body() function differ
505 * from those given to main() function.
508 SC_HANDLE hSCManager
= NULL
;
509 SC_HANDLE hService
= NULL
;
510 SERVICE_DESCRIPTION sdBuff
;
513 const char *user_acct
= GENSRV_USERACCT
;
514 const char *password
= "";
516 OSVERSIONINFOEX info
;
518 DWORD sidLen
= 0, domainLen
= 0;
519 int is_win2k_or_worse
= 0;
520 int using_default_torrc
= 0;
522 nt_service_loadlibrary();
524 /* Open the service control manager so we can create a new service */
525 if ((hSCManager
= nt_service_open_scm()) == NULL
)
527 /* Build the command line used for the service */
528 if ((command
= nt_service_command_line(&using_default_torrc
)) == NULL
) {
529 printf("Unable to build service command line.\n");
530 service_fns
.CloseServiceHandle_fn(hSCManager
);
534 for (i
=1; i
< argc
; ++i
) {
535 if (!strcmp(argv
[i
], "--user") && i
+1<argc
) {
536 user_acct
= argv
[i
+1];
539 if (!strcmp(argv
[i
], "--password") && i
+1<argc
) {
540 password
= argv
[i
+1];
545 /* Compute our version and see whether we're running win2k or earlier. */
546 memset(&info
, 0, sizeof(info
));
547 info
.dwOSVersionInfoSize
= sizeof(info
);
548 if (! GetVersionEx((LPOSVERSIONINFO
)&info
)) {
549 printf("Call to GetVersionEx failed.\n");
550 is_win2k_or_worse
= 1;
552 if (info
.dwMajorVersion
< 5 ||
553 (info
.dwMajorVersion
== 5 && info
.dwMinorVersion
== 0))
554 is_win2k_or_worse
= 1;
557 if (user_acct
== GENSRV_USERACCT
) {
558 if (is_win2k_or_worse
) {
559 /* On Win2k, there is no LocalService account, so we actually need to
560 * fall back on NULL (the system account). */
561 printf("Running on Win2K or earlier, so the LocalService account "
562 "doesn't exist. Falling back to SYSTEM account.\n");
565 /* Genericity is apparently _so_ last year in Redmond, where some
566 * accounts are accounts that you can look up, and some accounts
567 * are magic and undetectable via the security subsystem. See
568 * http://msdn2.microsoft.com/en-us/library/ms684188.aspx
570 printf("Running on a Post-Win2K OS, so we'll assume that the "
571 "LocalService account exists.\n");
573 } else if (0 && service_fns
.LookupAccountNameA_fn(NULL
, // On this system
575 NULL
, &sidLen
, // Don't care about the SID
576 NULL
, &domainLen
, // Don't care about the domain
578 /* XXXX For some reason, the above test segfaults. Fix that. */
579 printf("User \"%s\" doesn't seem to exist.\n", user_acct
);
582 printf("Will try to install service as user \"%s\".\n", user_acct
);
584 /* XXXX This warning could be better about explaining how to resolve the
586 if (using_default_torrc
)
587 printf("IMPORTANT NOTE:\n"
588 " The Tor service will run under the account \"%s\". This means\n"
589 " that Tor will look for its configuration file under that\n"
590 " account's Application Data directory, which is probably not\n"
591 " the same as yours.\n", user_acct
?user_acct
:"<local system>");
593 /* Create the Tor service, set to auto-start on boot */
594 if ((hService
= service_fns
.CreateServiceA_fn(hSCManager
, GENSRV_SERVICENAME
,
596 SERVICE_ALL_ACCESS
, SERVICE_WIN32_OWN_PROCESS
,
597 SERVICE_AUTO_START
, SERVICE_ERROR_IGNORE
,
598 command
, NULL
, NULL
, NULL
,
599 user_acct
, password
)) == NULL
) {
600 errmsg
= nt_strerror(GetLastError());
601 printf("CreateService() failed : %s\n", errmsg
);
602 service_fns
.CloseServiceHandle_fn(hSCManager
);
607 printf("Done with CreateService.\n");
609 /* Set the service's description */
610 sdBuff
.lpDescription
= (char*)GENSRV_DESCRIPTION
;
611 service_fns
.ChangeServiceConfig2A_fn(hService
, SERVICE_CONFIG_DESCRIPTION
,
613 printf("Service installed successfully\n");
615 /* Start the service initially */
616 nt_service_start(hService
);
618 service_fns
.CloseServiceHandle_fn(hService
);
619 service_fns
.CloseServiceHandle_fn(hSCManager
);
625 /** Removes the Tor NT service. Returns 0 if the service was successfully
626 * removed, or -1 on error. */
628 nt_service_remove(void)
630 SC_HANDLE hSCManager
= NULL
;
631 SC_HANDLE hService
= NULL
;
634 nt_service_loadlibrary();
635 if ((hSCManager
= nt_service_open_scm()) == NULL
)
637 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
638 service_fns
.CloseServiceHandle_fn(hSCManager
);
642 nt_service_stop(hService
);
643 if (service_fns
.DeleteService_fn(hService
) == FALSE
) {
644 errmsg
= nt_strerror(GetLastError());
645 printf("DeleteService() failed : %s\n", errmsg
);
647 service_fns
.CloseServiceHandle_fn(hService
);
648 service_fns
.CloseServiceHandle_fn(hSCManager
);
652 service_fns
.CloseServiceHandle_fn(hService
);
653 service_fns
.CloseServiceHandle_fn(hSCManager
);
654 printf("Service removed successfully\n");
659 /** Starts the Tor service. Returns 0 on success, or -1 on error. */
661 nt_service_cmd_start(void)
663 SC_HANDLE hSCManager
;
667 if ((hSCManager
= nt_service_open_scm()) == NULL
)
669 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
670 service_fns
.CloseServiceHandle_fn(hSCManager
);
674 start
= nt_service_start(hService
);
675 service_fns
.CloseServiceHandle_fn(hService
);
676 service_fns
.CloseServiceHandle_fn(hSCManager
);
681 /** Stops the Tor service. Returns 0 on success, or -1 on error. */
683 nt_service_cmd_stop(void)
685 SC_HANDLE hSCManager
;
689 if ((hSCManager
= nt_service_open_scm()) == NULL
)
691 if ((hService
= nt_service_open(hSCManager
)) == NULL
) {
692 service_fns
.CloseServiceHandle_fn(hSCManager
);
696 stop
= nt_service_stop(hService
);
697 service_fns
.CloseServiceHandle_fn(hService
);
698 service_fns
.CloseServiceHandle_fn(hSCManager
);
703 /** Given a Win32 error code, this attempts to make Windows
704 * return a human-readable error message. The char* returned
705 * is allocated by Windows, but should be freed with LocalFree()
706 * when finished with it. */
708 nt_strerror(uint32_t errnum
)
711 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
,
712 NULL
, errnum
, MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
713 (LPSTR
)&msgbuf
, 0, NULL
);
718 nt_service_parse_options(int argc
, char **argv
, int *should_exit
)
725 (!strcmp(argv
[1], "-service") || !strcmp(argv
[1], "--service"))) {
726 nt_service_loadlibrary();
727 if (!strcmp(argv
[2], "install"))
728 return nt_service_install(argc
, argv
);
729 if (!strcmp(argv
[2], "remove"))
730 return nt_service_remove();
731 if (!strcmp(argv
[2], "start"))
732 return nt_service_cmd_start();
733 if (!strcmp(argv
[2], "stop"))
734 return nt_service_cmd_stop();
735 printf("Unrecognized service command '%s'\n", argv
[2]);
740 if (!strcmp(argv
[1], "-nt-service") || !strcmp(argv
[1], "--nt-service")) {
741 nt_service_loadlibrary();
746 // These values have been deprecated since 0.1.1.2-alpha; we've warned
747 // about them since 0.1.2.7-alpha.
748 if (!strcmp(argv
[1], "-install") || !strcmp(argv
[1], "--install")) {
749 nt_service_loadlibrary();
751 "The %s option is deprecated; use \"--service install\" instead.",
754 return nt_service_install(argc
, argv
);
756 if (!strcmp(argv
[1], "-remove") || !strcmp(argv
[1], "--remove")) {
757 nt_service_loadlibrary();
759 "The %s option is deprecated; use \"--service remove\" instead.",
762 return nt_service_remove();