From f17a38ab04636235e672ccbf7e96742a8e3f79f5 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 6 Sep 2007 17:42:05 +0000 Subject: [PATCH] r14940@catbus: nickm | 2007-09-06 13:22:26 -0400 Move NT services code into its own C file. Probably will not build happily on win32 yet; more hacking needed. svn:r11388 --- ChangeLog | 1 + configure.in | 1 + src/or/Makefile.am | 12 +- src/or/config.c | 6 + src/or/main.c | 755 +------------------------------------------- src/or/ntmain.c | 757 +++++++++++++++++++++++++++++++++++++++++++++ src/or/or.h | 13 + 7 files changed, 793 insertions(+), 752 deletions(-) create mode 100644 src/or/ntmain.c diff --git a/ChangeLog b/ChangeLog index fe3f08cd80..4e7af1f584 100644 --- a/ChangeLog +++ b/ChangeLog @@ -32,6 +32,7 @@ Changes in version 0.2.0.7-alpha - 2007-??-?? - Revamp file-writing logic so we don't need to have the entire contents of a file in memory at once before we write to disk. Tor, meet stdio. - Turn "descriptor store" into a full-fledged type. + - Move all NT services code into a separate source file. Changes in version 0.1.2.17 - 2007-08-30 diff --git a/configure.in b/configure.in index 1f62c11162..bf2458f891 100644 --- a/configure.in +++ b/configure.in @@ -126,6 +126,7 @@ fi if test $bwin32 = true; then AC_DEFINE(MS_WINDOWS, 1, [Define to 1 if we are building for Windows.]) fi +AM_CONDITIONAL(BUILD_NT_SERVICES, test x$bwin32 = xtrue) dnl Enable C99 when compiling with MIPSpro AC_MSG_CHECKING([for MIPSpro compiler]) diff --git a/src/or/Makefile.am b/src/or/Makefile.am index fd86aa21eb..dd6c85b899 100644 --- a/src/or/Makefile.am +++ b/src/or/Makefile.am @@ -4,11 +4,19 @@ noinst_PROGRAMS = test bin_PROGRAMS = tor +if BUILD_NT_SERVICES +PLATFORM_SOURCES=ntmain.c +else +PLATFORM_SOURCES= +endif + +EXTRA_DIST=ntmain.c + tor_SOURCES = buffers.c circuitbuild.c circuitlist.c \ circuituse.c command.c config.c \ connection.c connection_edge.c connection_or.c control.c \ cpuworker.c directory.c dirserv.c dirvote.c \ - dns.c dnsserv.c hibernate.c main.c \ + dns.c dnsserv.c hibernate.c main.c $(PLATFORM_SOURCES) \ onion.c policies.c relay.c rendcommon.c rendclient.c rendmid.c \ rendservice.c rephist.c router.c routerlist.c routerparse.c \ eventdns.c \ @@ -25,7 +33,7 @@ test_SOURCES = buffers.c circuitbuild.c circuitlist.c \ circuituse.c command.c config.c \ connection.c connection_edge.c connection_or.c control.c \ cpuworker.c directory.c dirserv.c dirvote.c \ - dns.c dnsserv.c hibernate.c main.c \ + dns.c dnsserv.c hibernate.c main.c $(PLATFORM_SOURCES) \ onion.c policies.c relay.c rendcommon.c rendclient.c rendmid.c \ rendservice.c rephist.c router.c routerlist.c routerparse.c \ eventdns.c \ diff --git a/src/or/config.c b/src/or/config.c index b6523ba834..570c5363d0 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -4610,6 +4610,9 @@ extern const char dirserv_c_id[]; extern const char dns_c_id[]; extern const char hibernate_c_id[]; extern const char main_c_id[]; +#ifdef NT_SERVICE +extern const char ntmain_c_id[]; +#endif extern const char onion_c_id[]; extern const char policies_c_id[]; extern const char relay_c_id[]; @@ -4663,6 +4666,9 @@ print_svn_version(void) puts(dns_c_id); puts(hibernate_c_id); puts(main_c_id); +#ifdef NT_SERVICE + puts(ntmain_c_id); +#endif puts(onion_c_id); puts(policies_c_id); puts(relay_c_id); diff --git a/src/or/main.c b/src/or/main.c index 4096c40712..651b46f077 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -86,35 +86,6 @@ static int called_loop_once = 0; * entry to inform the user that Tor is working. */ int has_completed_circuit=0; -#ifdef MS_WINDOWS -#define MS_WINDOWS_SERVICE -#endif - -#ifdef MS_WINDOWS_SERVICE -#include -#define GENSRV_SERVICENAME TEXT("tor") -#define GENSRV_DISPLAYNAME TEXT("Tor Win32 Service") -#define GENSRV_DESCRIPTION \ - TEXT("Provides an anonymous Internet communication system") -#define GENSRV_USERACCT TEXT("NT AUTHORITY\\LocalService") - -// Cheating: using the pre-defined error codes, tricks Windows into displaying -// a semi-related human-readable error message if startup fails as -// opposed to simply scaring people with Error: 0xffffffff -#define NT_SERVICE_ERROR_TORINIT_FAILED ERROR_EXCEPTION_IN_SERVICE - -SERVICE_STATUS service_status; -SERVICE_STATUS_HANDLE hStatus; -/* XXXX020 This 'backup argv' and 'backup argc' business is an ugly hack. This - * is a job for arguments, not globals. */ -static char **backup_argv; -static int backup_argc; -static int nt_service_is_stopping(void); -static char* nt_strerror(uint32_t errnum); -#else -#define nt_service_is_stopping() (0) -#endif - /** If our router descriptor ever goes this long without being regenerated * because something changed, we force an immediate regenerate-and-upload. */ #define FORCE_REGENERATE_DESCRIPTOR_INTERVAL (18*60*60) @@ -1909,685 +1880,6 @@ do_hash_password(void) printf("16:%s\n",output); } -#ifdef MS_WINDOWS_SERVICE - -/* XXXX can some/all these functions become static? without breaking NT - * services? -NM */ -/* XXXX I'd also like to move much of the NT service stuff into its own - * file. -RD */ -void nt_service_control(DWORD request); -void nt_service_body(int argc, char **argv); -void nt_service_main(void); -SC_HANDLE nt_service_open_scm(void); -SC_HANDLE nt_service_open(SC_HANDLE hSCManager); -int nt_service_start(SC_HANDLE hService); -int nt_service_stop(SC_HANDLE hService); -int nt_service_install(int argc, char **argv); -int nt_service_remove(void); -int nt_service_cmd_start(void); -int nt_service_cmd_stop(void); - -struct service_fns { - int loaded; - - BOOL (WINAPI *ChangeServiceConfig2A_fn)( - SC_HANDLE hService, - DWORD dwInfoLevel, - LPVOID lpInfo); - - BOOL (WINAPI *CloseServiceHandle_fn)( - SC_HANDLE hSCObject); - - BOOL (WINAPI *ControlService_fn)( - SC_HANDLE hService, - DWORD dwControl, - LPSERVICE_STATUS lpServiceStatus); - - SC_HANDLE (WINAPI *CreateServiceA_fn)( - SC_HANDLE hSCManager, - LPCTSTR lpServiceName, - LPCTSTR lpDisplayName, - DWORD dwDesiredAccess, - DWORD dwServiceType, - DWORD dwStartType, - DWORD dwErrorControl, - LPCTSTR lpBinaryPathName, - LPCTSTR lpLoadOrderGroup, - LPDWORD lpdwTagId, - LPCTSTR lpDependencies, - LPCTSTR lpServiceStartName, - LPCTSTR lpPassword); - - BOOL (WINAPI *DeleteService_fn)( - SC_HANDLE hService); - - SC_HANDLE (WINAPI *OpenSCManagerA_fn)( - LPCTSTR lpMachineName, - LPCTSTR lpDatabaseName, - DWORD dwDesiredAccess); - - SC_HANDLE (WINAPI *OpenServiceA_fn)( - SC_HANDLE hSCManager, - LPCTSTR lpServiceName, - DWORD dwDesiredAccess); - - BOOL (WINAPI *QueryServiceStatus_fn)( - SC_HANDLE hService, - LPSERVICE_STATUS lpServiceStatus); - - SERVICE_STATUS_HANDLE (WINAPI *RegisterServiceCtrlHandlerA_fn)( - LPCTSTR lpServiceName, - LPHANDLER_FUNCTION lpHandlerProc); - - BOOL (WINAPI *SetServiceStatus_fn)(SERVICE_STATUS_HANDLE, - LPSERVICE_STATUS); - - BOOL (WINAPI *StartServiceCtrlDispatcherA_fn)( - const SERVICE_TABLE_ENTRY* lpServiceTable); - - BOOL (WINAPI *StartServiceA_fn)( - SC_HANDLE hService, - DWORD dwNumServiceArgs, - LPCTSTR* lpServiceArgVectors); - - BOOL (WINAPI *LookupAccountNameA_fn)( - LPCTSTR lpSystemName, - LPCTSTR lpAccountName, - PSID Sid, - LPDWORD cbSid, - LPTSTR ReferencedDomainName, - LPDWORD cchReferencedDomainName, - PSID_NAME_USE peUse); -} service_fns = { 0, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL}; - -/** Loads functions used by NT services. Returns on success, or prints a - * complaint to stdout and exits on error. */ -static void -nt_service_loadlibrary(void) -{ - HMODULE library = 0; - void *fn; - - if (service_fns.loaded) - return; - - /* XXXX Possibly, we should hardcode the location of this DLL. */ - if (!(library = LoadLibrary("advapi32.dll"))) { - log_err(LD_GENERAL, "Couldn't open advapi32.dll. Are you trying to use " - "NT services on Windows 98? That doesn't work."); - goto err; - } - -#define LOAD(f) STMT_BEGIN \ - if (!(fn = GetProcAddress(library, #f))) { \ - log_err(LD_BUG, \ - "Couldn't find %s in advapi32.dll! We probably got the " \ - "name wrong.", #f); \ - goto err; \ - } else { \ - service_fns.f ## _fn = fn; \ - } \ - STMT_END - - LOAD(ChangeServiceConfig2A); - LOAD(CloseServiceHandle); - LOAD(ControlService); - LOAD(CreateServiceA); - LOAD(DeleteService); - LOAD(OpenSCManagerA); - LOAD(OpenServiceA); - LOAD(QueryServiceStatus); - LOAD(RegisterServiceCtrlHandlerA); - LOAD(SetServiceStatus); - LOAD(StartServiceCtrlDispatcherA); - LOAD(StartServiceA); - LOAD(LookupAccountNameA); - - service_fns.loaded = 1; - - return; - err: - printf("Unable to load library support for NT services: exiting.\n"); - exit(1); -} - -/** If we're compiled to run as an NT service, and the service wants to - * shut down, then change our current status and return 1. Else - * return 0. - */ -static int -nt_service_is_stopping(void) -/* XXXX this function would probably _love_ to be inline, in 0.2.0. */ -{ - /* If we haven't loaded the function pointers, we can't possibly be an NT - * service trying to shut down. */ - if (!service_fns.loaded) - return 0; - - if (service_status.dwCurrentState == SERVICE_STOP_PENDING) { - service_status.dwWin32ExitCode = 0; - service_status.dwCurrentState = SERVICE_STOPPED; - service_fns.SetServiceStatus_fn(hStatus, &service_status); - return 1; - } else if (service_status.dwCurrentState == SERVICE_STOPPED) { - return 1; - } - return 0; -} - -/** Handles service control requests, such as stopping or starting the - * Tor service. */ -void -nt_service_control(DWORD request) -{ - static struct timeval exit_now; - exit_now.tv_sec = 0; - exit_now.tv_usec = 0; - - nt_service_loadlibrary(); - - switch (request) { - case SERVICE_CONTROL_STOP: - case SERVICE_CONTROL_SHUTDOWN: - log_notice(LD_GENERAL, - "Got stop/shutdown request; shutting down cleanly."); - service_status.dwCurrentState = SERVICE_STOP_PENDING; - event_loopexit(&exit_now); - return; - } - service_fns.SetServiceStatus_fn(hStatus, &service_status); -} - -/** Called when the service is started via the system's service control - * manager. This calls tor_init() and starts the main event loop. If - * tor_init() fails, the service will be stopped and exit code set to - * NT_SERVICE_ERROR_TORINIT_FAILED. */ -void -nt_service_body(int argc, char **argv) -{ - int r; - (void) argc; /* unused */ - (void) argv; /* unused */ - nt_service_loadlibrary(); - service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; - service_status.dwCurrentState = SERVICE_START_PENDING; - service_status.dwControlsAccepted = - SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; - service_status.dwWin32ExitCode = 0; - service_status.dwServiceSpecificExitCode = 0; - service_status.dwCheckPoint = 0; - service_status.dwWaitHint = 1000; - hStatus = service_fns.RegisterServiceCtrlHandlerA_fn(GENSRV_SERVICENAME, - (LPHANDLER_FUNCTION) nt_service_control); - - if (hStatus == 0) { - /* Failed to register the service control handler function */ - return; - } - - r = tor_init(backup_argc, backup_argv); - if (r) { - /* Failed to start the Tor service */ - r = NT_SERVICE_ERROR_TORINIT_FAILED; - service_status.dwCurrentState = SERVICE_STOPPED; - service_status.dwWin32ExitCode = r; - service_status.dwServiceSpecificExitCode = r; - service_fns.SetServiceStatus_fn(hStatus, &service_status); - return; - } - - /* Set the service's status to SERVICE_RUNNING and start the main - * event loop */ - service_status.dwCurrentState = SERVICE_RUNNING; - service_fns.SetServiceStatus_fn(hStatus, &service_status); - do_main_loop(); - tor_cleanup(); -} - -/** Main service entry point. Starts the service control dispatcher and waits - * until the service status is set to SERVICE_STOPPED. */ -void -nt_service_main(void) -{ - SERVICE_TABLE_ENTRY table[2]; - DWORD result = 0; - char *errmsg; - nt_service_loadlibrary(); - table[0].lpServiceName = (char*)GENSRV_SERVICENAME; - table[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)nt_service_body; - table[1].lpServiceName = NULL; - table[1].lpServiceProc = NULL; - - if (!service_fns.StartServiceCtrlDispatcherA_fn(table)) { - result = GetLastError(); - errmsg = nt_strerror(result); - printf("Service error %d : %s\n", (int) result, errmsg); - LocalFree(errmsg); - if (result == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { - if (tor_init(backup_argc, backup_argv) < 0) - return; - switch (get_options()->command) { - case CMD_RUN_TOR: - do_main_loop(); - break; - case CMD_LIST_FINGERPRINT: - do_list_fingerprint(); - break; - case CMD_HASH_PASSWORD: - do_hash_password(); - break; - case CMD_VERIFY_CONFIG: - printf("Configuration was valid\n"); - break; - case CMD_RUN_UNITTESTS: - default: - log_err(LD_CONFIG, "Illegal command number %d: internal error.", - get_options()->command); - } - tor_cleanup(); - } - } -} - -/** Return a handle to the service control manager on success, or NULL on - * failure. */ -SC_HANDLE -nt_service_open_scm(void) -{ - SC_HANDLE hSCManager; - char *errmsg = NULL; - - nt_service_loadlibrary(); - if ((hSCManager = service_fns.OpenSCManagerA_fn( - NULL, NULL, SC_MANAGER_CREATE_SERVICE)) == NULL) { - errmsg = nt_strerror(GetLastError()); - printf("OpenSCManager() failed : %s\n", errmsg); - LocalFree(errmsg); - } - return hSCManager; -} - -/** Open a handle to the Tor service using hSCManager. Return NULL - * on failure. */ -SC_HANDLE -nt_service_open(SC_HANDLE hSCManager) -{ - SC_HANDLE hService; - char *errmsg = NULL; - nt_service_loadlibrary(); - if ((hService = service_fns.OpenServiceA_fn(hSCManager, GENSRV_SERVICENAME, - SERVICE_ALL_ACCESS)) == NULL) { - errmsg = nt_strerror(GetLastError()); - printf("OpenService() failed : %s\n", errmsg); - LocalFree(errmsg); - } - return hService; -} - -/** Start the Tor service. Return 0 if the service is started or was - * previously running. Return -1 on error. */ -int -nt_service_start(SC_HANDLE hService) -{ - char *errmsg = NULL; - - nt_service_loadlibrary(); - - service_fns.QueryServiceStatus_fn(hService, &service_status); - if (service_status.dwCurrentState == SERVICE_RUNNING) { - printf("Service is already running\n"); - return 0; - } - - if (service_fns.StartServiceA_fn(hService, 0, NULL)) { - /* Loop until the service has finished attempting to start */ - while (service_fns.QueryServiceStatus_fn(hService, &service_status) && - (service_status.dwCurrentState == SERVICE_START_PENDING)) { - Sleep(500); - } - - /* Check if it started successfully or not */ - if (service_status.dwCurrentState == SERVICE_RUNNING) { - printf("Service started successfully\n"); - return 0; - } else { - errmsg = nt_strerror(service_status.dwWin32ExitCode); - printf("Service failed to start : %s\n", errmsg); - LocalFree(errmsg); - } - } else { - errmsg = nt_strerror(GetLastError()); - printf("StartService() failed : %s\n", errmsg); - LocalFree(errmsg); - } - return -1; -} - -/** Stop the Tor service. Return 0 if the service is stopped or was not - * previously running. Return -1 on error. */ -int -nt_service_stop(SC_HANDLE hService) -{ -/** Wait at most 10 seconds for the service to stop. */ -#define MAX_SERVICE_WAIT_TIME 10 - int wait_time; - char *errmsg = NULL; - nt_service_loadlibrary(); - - service_fns.QueryServiceStatus_fn(hService, &service_status); - if (service_status.dwCurrentState == SERVICE_STOPPED) { - printf("Service is already stopped\n"); - return 0; - } - - if (service_fns.ControlService_fn(hService, SERVICE_CONTROL_STOP, - &service_status)) { - wait_time = 0; - while (service_fns.QueryServiceStatus_fn(hService, &service_status) && - (service_status.dwCurrentState != SERVICE_STOPPED) && - (wait_time < MAX_SERVICE_WAIT_TIME)) { - Sleep(1000); - wait_time++; - } - if (service_status.dwCurrentState == SERVICE_STOPPED) { - printf("Service stopped successfully\n"); - return 0; - } else if (wait_time == MAX_SERVICE_WAIT_TIME) { - printf("Service did not stop within %d seconds.\n", wait_time); - } else { - errmsg = nt_strerror(GetLastError()); - printf("QueryServiceStatus() failed : %s\n",errmsg); - LocalFree(errmsg); - } - } else { - errmsg = nt_strerror(GetLastError()); - printf("ControlService() failed : %s\n", errmsg); - LocalFree(errmsg); - } - return -1; -} - -/** Build a formatted command line used for the NT service. Return a - * pointer to the formatted string on success, or NULL on failure. Set - * *using_default_torrc to true if we're going to use the default - * location to torrc, or 1 if an option was specified on the command line. - */ -static char * -nt_service_command_line(int *using_default_torrc) -{ - TCHAR tor_exe[MAX_PATH+1]; - char *command, *options=NULL; - smartlist_t *sl; - int i, cmdlen; - *using_default_torrc = 1; - - /* Get the location of tor.exe */ - if (0 == GetModuleFileName(NULL, tor_exe, MAX_PATH)) - return NULL; - - /* Get the service arguments */ - sl = smartlist_create(); - for (i = 1; i < backup_argc; ++i) { - if (!strcmp(backup_argv[i], "--options") || - !strcmp(backup_argv[i], "-options")) { - while (++i < backup_argc) { - if (!strcmp(backup_argv[i], "-f")) - *using_default_torrc = 0; - smartlist_add(sl, backup_argv[i]); - } - } - } - if (smartlist_len(sl)) - options = smartlist_join_strings(sl,"\" \"",0,NULL); - smartlist_free(sl); - - /* Allocate a string for the NT service command line */ - cmdlen = strlen(tor_exe) + (options?strlen(options):0) + 32; - command = tor_malloc(cmdlen); - - /* Format the service command */ - if (options) { - if (tor_snprintf(command, cmdlen, "\"%s\" --nt-service \"%s\"", - tor_exe, options)<0) { - tor_free(command); /* sets command to NULL. */ - } - } else { /* ! options */ - if (tor_snprintf(command, cmdlen, "\"%s\" --nt-service", tor_exe)<0) { - tor_free(command); /* sets command to NULL. */ - } - } - - tor_free(options); - return command; -} - -/** Creates a Tor NT service, set to start on boot. The service will be - * started if installation succeeds. Returns 0 on success, or -1 on - * failure. */ -int -nt_service_install(int argc, char **argv) -{ - /* Notes about developing NT services: - * - * 1. Don't count on your CWD. If an absolute path is not given, the - * fopen() function goes wrong. - * 2. The parameters given to the nt_service_body() function differ - * from those given to main() function. - */ - - SC_HANDLE hSCManager = NULL; - SC_HANDLE hService = NULL; - SERVICE_DESCRIPTION sdBuff; - char *command; - char *errmsg; - const char *user_acct = GENSRV_USERACCT; - const char *password = ""; - int i; - OSVERSIONINFOEX info; - SID_NAME_USE sidUse; - DWORD sidLen = 0, domainLen = 0; - int is_win2k_or_worse = 0; - int using_default_torrc = 0; - - nt_service_loadlibrary(); - - /* Open the service control manager so we can create a new service */ - if ((hSCManager = nt_service_open_scm()) == NULL) - return -1; - /* Build the command line used for the service */ - if ((command = nt_service_command_line(&using_default_torrc)) == NULL) { - printf("Unable to build service command line.\n"); - service_fns.CloseServiceHandle_fn(hSCManager); - return -1; - } - - for (i=1; i < argc; ++i) { - if (!strcmp(argv[i], "--user") && i+1"); - - /* Create the Tor service, set to auto-start on boot */ - if ((hService = service_fns.CreateServiceA_fn(hSCManager, GENSRV_SERVICENAME, - GENSRV_DISPLAYNAME, - SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, - SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, - command, NULL, NULL, NULL, - user_acct, password)) == NULL) { - errmsg = nt_strerror(GetLastError()); - printf("CreateService() failed : %s\n", errmsg); - service_fns.CloseServiceHandle_fn(hSCManager); - LocalFree(errmsg); - tor_free(command); - return -1; - } - printf("Done with CreateService.\n"); - - /* Set the service's description */ - sdBuff.lpDescription = (char*)GENSRV_DESCRIPTION; - service_fns.ChangeServiceConfig2A_fn(hService, SERVICE_CONFIG_DESCRIPTION, - &sdBuff); - printf("Service installed successfully\n"); - - /* Start the service initially */ - nt_service_start(hService); - - service_fns.CloseServiceHandle_fn(hService); - service_fns.CloseServiceHandle_fn(hSCManager); - tor_free(command); - - return 0; -} - -/** Removes the Tor NT service. Returns 0 if the service was successfully - * removed, or -1 on error. */ -int -nt_service_remove(void) -{ - SC_HANDLE hSCManager = NULL; - SC_HANDLE hService = NULL; - char *errmsg; - - nt_service_loadlibrary(); - if ((hSCManager = nt_service_open_scm()) == NULL) - return -1; - if ((hService = nt_service_open(hSCManager)) == NULL) { - service_fns.CloseServiceHandle_fn(hSCManager); - return -1; - } - - nt_service_stop(hService); - if (service_fns.DeleteService_fn(hService) == FALSE) { - errmsg = nt_strerror(GetLastError()); - printf("DeleteService() failed : %s\n", errmsg); - LocalFree(errmsg); - service_fns.CloseServiceHandle_fn(hService); - service_fns.CloseServiceHandle_fn(hSCManager); - return -1; - } - - service_fns.CloseServiceHandle_fn(hService); - service_fns.CloseServiceHandle_fn(hSCManager); - printf("Service removed successfully\n"); - - return 0; -} - -/** Starts the Tor service. Returns 0 on success, or -1 on error. */ -int -nt_service_cmd_start(void) -{ - SC_HANDLE hSCManager; - SC_HANDLE hService; - int start; - - if ((hSCManager = nt_service_open_scm()) == NULL) - return -1; - if ((hService = nt_service_open(hSCManager)) == NULL) { - service_fns.CloseServiceHandle_fn(hSCManager); - return -1; - } - - start = nt_service_start(hService); - service_fns.CloseServiceHandle_fn(hService); - service_fns.CloseServiceHandle_fn(hSCManager); - - return start; -} - -/** Stops the Tor service. Returns 0 on success, or -1 on error. */ -int -nt_service_cmd_stop(void) -{ - SC_HANDLE hSCManager; - SC_HANDLE hService; - int stop; - - if ((hSCManager = nt_service_open_scm()) == NULL) - return -1; - if ((hService = nt_service_open(hSCManager)) == NULL) { - service_fns.CloseServiceHandle_fn(hSCManager); - return -1; - } - - stop = nt_service_stop(hService); - service_fns.CloseServiceHandle_fn(hService); - service_fns.CloseServiceHandle_fn(hSCManager); - - return stop; -} - -/** Given a Win32 error code, this attempts to make Windows - * return a human-readable error message. The char* returned - * is allocated by Windows, but should be freed with LocalFree() - * when finished with it. */ -static char* -nt_strerror(uint32_t errnum) -{ - char *msgbuf; - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, errnum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&msgbuf, 0, NULL); - return msgbuf; -} -#endif - #ifdef USE_DMALLOC #include static void @@ -2609,53 +1901,16 @@ tor_main(int argc, char *argv[]) _tor_dmalloc_free); log_notice(LD_CONFIG, "Set up dmalloc; returned %d", r); #endif -#ifdef MS_WINDOWS_SERVICE - backup_argv = argv; - backup_argc = argc; - if ((argc >= 3) && - (!strcmp(argv[1], "-service") || !strcmp(argv[1], "--service"))) { - nt_service_loadlibrary(); - if (!strcmp(argv[2], "install")) - return nt_service_install(argc, argv); - if (!strcmp(argv[2], "remove")) - return nt_service_remove(); - if (!strcmp(argv[2], "start")) - return nt_service_cmd_start(); - if (!strcmp(argv[2], "stop")) - return nt_service_cmd_stop(); - printf("Unrecognized service command '%s'\n", argv[2]); - return -1; - } - if (argc >= 2) { - if (!strcmp(argv[1], "-nt-service") || !strcmp(argv[1], "--nt-service")) { - nt_service_loadlibrary(); - nt_service_main(); - return 0; - } - // These values have been deprecated since 0.1.1.2-alpha; we've warned - // about them since 0.1.2.7-alpha. - if (!strcmp(argv[1], "-install") || !strcmp(argv[1], "--install")) { - nt_service_loadlibrary(); - fprintf(stderr, - "The %s option is deprecated; use \"--service install\" instead.", - argv[1]); - return nt_service_install(argc, argv); - } - if (!strcmp(argv[1], "-remove") || !strcmp(argv[1], "--remove")) { - nt_service_loadlibrary(); - fprintf(stderr, - "The %s option is deprecated; use \"--service remove\" instead.", - argv[1]); - return nt_service_remove(); - } - } +#ifdef NT_SERVICE + if ((result = nt_service_parse_options(argv, argc))) + return result; #endif if (tor_init(argc, argv)<0) return -1; switch (get_options()->command) { case CMD_RUN_TOR: -#ifdef MS_WINDOWS_SERVICE - service_status.dwCurrentState = SERVICE_RUNNING; +#ifdef NT_SERVICE + nt_service_set_state(SERVICE_RUNNING); #endif result = do_main_loop(); break; diff --git a/src/or/ntmain.c b/src/or/ntmain.c new file mode 100644 index 0000000000..da149298ba --- /dev/null +++ b/src/or/ntmain.c @@ -0,0 +1,757 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2007, Roger Dingledine, Nick Mathewson. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "or.h" + +const char ntmain_c_id[] = + "$Id$"; + +#include +#define GENSRV_SERVICENAME TEXT("tor") +#define GENSRV_DISPLAYNAME TEXT("Tor Win32 Service") +#define GENSRV_DESCRIPTION \ + TEXT("Provides an anonymous Internet communication system") +#define GENSRV_USERACCT TEXT("NT AUTHORITY\\LocalService") + +// Cheating: using the pre-defined error codes, tricks Windows into displaying +// a semi-related human-readable error message if startup fails as +// opposed to simply scaring people with Error: 0xffffffff +#define NT_SERVICE_ERROR_TORINIT_FAILED ERROR_EXCEPTION_IN_SERVICE + +static SERVICE_STATUS service_status; +static SERVICE_STATUS_HANDLE hStatus; + +/* XXXX020 This 'backup argv' and 'backup argc' business is an ugly hack. This + * is a job for arguments, not globals. */ +static char **backup_argv; +static int backup_argc; +static char* nt_strerror(uint32_t errnum); + +static void nt_service_control(DWORD request); +static void nt_service_body(int argc, char **argv); +static void nt_service_main(void); +static SC_HANDLE nt_service_open_scm(void); +static SC_HANDLE nt_service_open(SC_HANDLE hSCManager); +static int nt_service_start(SC_HANDLE hService); +static int nt_service_stop(SC_HANDLE hService); +static int nt_service_install(int argc, char **argv); +static int nt_service_remove(void); +static int nt_service_cmd_start(void); +static int nt_service_cmd_stop(void); + +struct service_fns { + int loaded; + + BOOL (WINAPI *ChangeServiceConfig2A_fn)( + SC_HANDLE hService, + DWORD dwInfoLevel, + LPVOID lpInfo); + + BOOL (WINAPI *CloseServiceHandle_fn)( + SC_HANDLE hSCObject); + + BOOL (WINAPI *ControlService_fn)( + SC_HANDLE hService, + DWORD dwControl, + LPSERVICE_STATUS lpServiceStatus); + + SC_HANDLE (WINAPI *CreateServiceA_fn)( + SC_HANDLE hSCManager, + LPCTSTR lpServiceName, + LPCTSTR lpDisplayName, + DWORD dwDesiredAccess, + DWORD dwServiceType, + DWORD dwStartType, + DWORD dwErrorControl, + LPCTSTR lpBinaryPathName, + LPCTSTR lpLoadOrderGroup, + LPDWORD lpdwTagId, + LPCTSTR lpDependencies, + LPCTSTR lpServiceStartName, + LPCTSTR lpPassword); + + BOOL (WINAPI *DeleteService_fn)( + SC_HANDLE hService); + + SC_HANDLE (WINAPI *OpenSCManagerA_fn)( + LPCTSTR lpMachineName, + LPCTSTR lpDatabaseName, + DWORD dwDesiredAccess); + + SC_HANDLE (WINAPI *OpenServiceA_fn)( + SC_HANDLE hSCManager, + LPCTSTR lpServiceName, + DWORD dwDesiredAccess); + + BOOL (WINAPI *QueryServiceStatus_fn)( + SC_HANDLE hService, + LPSERVICE_STATUS lpServiceStatus); + + SERVICE_STATUS_HANDLE (WINAPI *RegisterServiceCtrlHandlerA_fn)( + LPCTSTR lpServiceName, + LPHANDLER_FUNCTION lpHandlerProc); + + BOOL (WINAPI *SetServiceStatus_fn)(SERVICE_STATUS_HANDLE, + LPSERVICE_STATUS); + + BOOL (WINAPI *StartServiceCtrlDispatcherA_fn)( + const SERVICE_TABLE_ENTRY* lpServiceTable); + + BOOL (WINAPI *StartServiceA_fn)( + SC_HANDLE hService, + DWORD dwNumServiceArgs, + LPCTSTR* lpServiceArgVectors); + + BOOL (WINAPI *LookupAccountNameA_fn)( + LPCTSTR lpSystemName, + LPCTSTR lpAccountName, + PSID Sid, + LPDWORD cbSid, + LPTSTR ReferencedDomainName, + LPDWORD cchReferencedDomainName, + PSID_NAME_USE peUse); +} service_fns = { 0, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL}; + +/** Loads functions used by NT services. Returns on success, or prints a + * complaint to stdout and exits on error. */ +static void +nt_service_loadlibrary(void) +{ + HMODULE library = 0; + void *fn; + + if (service_fns.loaded) + return; + + /* XXXX Possibly, we should hardcode the location of this DLL. */ + if (!(library = LoadLibrary("advapi32.dll"))) { + log_err(LD_GENERAL, "Couldn't open advapi32.dll. Are you trying to use " + "NT services on Windows 98? That doesn't work."); + goto err; + } + +#define LOAD(f) STMT_BEGIN \ + if (!(fn = GetProcAddress(library, #f))) { \ + log_err(LD_BUG, \ + "Couldn't find %s in advapi32.dll! We probably got the " \ + "name wrong.", #f); \ + goto err; \ + } else { \ + service_fns.f ## _fn = fn; \ + } \ + STMT_END + + LOAD(ChangeServiceConfig2A); + LOAD(CloseServiceHandle); + LOAD(ControlService); + LOAD(CreateServiceA); + LOAD(DeleteService); + LOAD(OpenSCManagerA); + LOAD(OpenServiceA); + LOAD(QueryServiceStatus); + LOAD(RegisterServiceCtrlHandlerA); + LOAD(SetServiceStatus); + LOAD(StartServiceCtrlDispatcherA); + LOAD(StartServiceA); + LOAD(LookupAccountNameA); + + service_fns.loaded = 1; + + return; + err: + printf("Unable to load library support for NT services: exiting.\n"); + exit(1); +} + +/** If we're compiled to run as an NT service, and the service wants to + * shut down, then change our current status and return 1. Else + * return 0. + */ +int +nt_service_is_stopping(void) +/* XXXX this function would probably _love_ to be inline, in 0.2.0. */ +{ + /* If we haven't loaded the function pointers, we can't possibly be an NT + * service trying to shut down. */ + if (!service_fns.loaded) + return 0; + + if (service_status.dwCurrentState == SERVICE_STOP_PENDING) { + service_status.dwWin32ExitCode = 0; + service_status.dwCurrentState = SERVICE_STOPPED; + service_fns.SetServiceStatus_fn(hStatus, &service_status); + return 1; + } else if (service_status.dwCurrentState == SERVICE_STOPPED) { + return 1; + } + return 0; +} + +/** DOCDOC */ +int +nt_service_set_state(DWORD state) +{ + service_status.dwCurrentState = state; +} + +/** Handles service control requests, such as stopping or starting the + * Tor service. */ +static void +nt_service_control(DWORD request) +{ + static struct timeval exit_now; + exit_now.tv_sec = 0; + exit_now.tv_usec = 0; + + nt_service_loadlibrary(); + + switch (request) { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + log_notice(LD_GENERAL, + "Got stop/shutdown request; shutting down cleanly."); + service_status.dwCurrentState = SERVICE_STOP_PENDING; + event_loopexit(&exit_now); + return; + } + service_fns.SetServiceStatus_fn(hStatus, &service_status); +} + +/** Called when the service is started via the system's service control + * manager. This calls tor_init() and starts the main event loop. If + * tor_init() fails, the service will be stopped and exit code set to + * NT_SERVICE_ERROR_TORINIT_FAILED. */ +static void +nt_service_body(int argc, char **argv) +{ + int r; + (void) argc; /* unused */ + (void) argv; /* unused */ + nt_service_loadlibrary(); + service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + service_status.dwCurrentState = SERVICE_START_PENDING; + service_status.dwControlsAccepted = + SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + service_status.dwWin32ExitCode = 0; + service_status.dwServiceSpecificExitCode = 0; + service_status.dwCheckPoint = 0; + service_status.dwWaitHint = 1000; + hStatus = service_fns.RegisterServiceCtrlHandlerA_fn(GENSRV_SERVICENAME, + (LPHANDLER_FUNCTION) nt_service_control); + + if (hStatus == 0) { + /* Failed to register the service control handler function */ + return; + } + + r = tor_init(backup_argc, backup_argv); + if (r) { + /* Failed to start the Tor service */ + r = NT_SERVICE_ERROR_TORINIT_FAILED; + service_status.dwCurrentState = SERVICE_STOPPED; + service_status.dwWin32ExitCode = r; + service_status.dwServiceSpecificExitCode = r; + service_fns.SetServiceStatus_fn(hStatus, &service_status); + return; + } + + /* Set the service's status to SERVICE_RUNNING and start the main + * event loop */ + service_status.dwCurrentState = SERVICE_RUNNING; + service_fns.SetServiceStatus_fn(hStatus, &service_status); + do_main_loop(); + tor_cleanup(); +} + +/** Main service entry point. Starts the service control dispatcher and waits + * until the service status is set to SERVICE_STOPPED. */ +static void +nt_service_main(void) +{ + SERVICE_TABLE_ENTRY table[2]; + DWORD result = 0; + char *errmsg; + nt_service_loadlibrary(); + table[0].lpServiceName = (char*)GENSRV_SERVICENAME; + table[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)nt_service_body; + table[1].lpServiceName = NULL; + table[1].lpServiceProc = NULL; + + if (!service_fns.StartServiceCtrlDispatcherA_fn(table)) { + result = GetLastError(); + errmsg = nt_strerror(result); + printf("Service error %d : %s\n", (int) result, errmsg); + LocalFree(errmsg); + if (result == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { + if (tor_init(backup_argc, backup_argv) < 0) + return; + switch (get_options()->command) { + case CMD_RUN_TOR: + do_main_loop(); + break; + case CMD_LIST_FINGERPRINT: + do_list_fingerprint(); + break; + case CMD_HASH_PASSWORD: + do_hash_password(); + break; + case CMD_VERIFY_CONFIG: + printf("Configuration was valid\n"); + break; + case CMD_RUN_UNITTESTS: + default: + log_err(LD_CONFIG, "Illegal command number %d: internal error.", + get_options()->command); + } + tor_cleanup(); + } + } +} + +/** Return a handle to the service control manager on success, or NULL on + * failure. */ +static SC_HANDLE +nt_service_open_scm(void) +{ + SC_HANDLE hSCManager; + char *errmsg = NULL; + + nt_service_loadlibrary(); + if ((hSCManager = service_fns.OpenSCManagerA_fn( + NULL, NULL, SC_MANAGER_CREATE_SERVICE)) == NULL) { + errmsg = nt_strerror(GetLastError()); + printf("OpenSCManager() failed : %s\n", errmsg); + LocalFree(errmsg); + } + return hSCManager; +} + +/** Open a handle to the Tor service using hSCManager. Return NULL + * on failure. */ +static SC_HANDLE +nt_service_open(SC_HANDLE hSCManager) +{ + SC_HANDLE hService; + char *errmsg = NULL; + nt_service_loadlibrary(); + if ((hService = service_fns.OpenServiceA_fn(hSCManager, GENSRV_SERVICENAME, + SERVICE_ALL_ACCESS)) == NULL) { + errmsg = nt_strerror(GetLastError()); + printf("OpenService() failed : %s\n", errmsg); + LocalFree(errmsg); + } + return hService; +} + +/** Start the Tor service. Return 0 if the service is started or was + * previously running. Return -1 on error. */ +static int +nt_service_start(SC_HANDLE hService) +{ + char *errmsg = NULL; + + nt_service_loadlibrary(); + + service_fns.QueryServiceStatus_fn(hService, &service_status); + if (service_status.dwCurrentState == SERVICE_RUNNING) { + printf("Service is already running\n"); + return 0; + } + + if (service_fns.StartServiceA_fn(hService, 0, NULL)) { + /* Loop until the service has finished attempting to start */ + while (service_fns.QueryServiceStatus_fn(hService, &service_status) && + (service_status.dwCurrentState == SERVICE_START_PENDING)) { + Sleep(500); + } + + /* Check if it started successfully or not */ + if (service_status.dwCurrentState == SERVICE_RUNNING) { + printf("Service started successfully\n"); + return 0; + } else { + errmsg = nt_strerror(service_status.dwWin32ExitCode); + printf("Service failed to start : %s\n", errmsg); + LocalFree(errmsg); + } + } else { + errmsg = nt_strerror(GetLastError()); + printf("StartService() failed : %s\n", errmsg); + LocalFree(errmsg); + } + return -1; +} + +/** Stop the Tor service. Return 0 if the service is stopped or was not + * previously running. Return -1 on error. */ +static int +nt_service_stop(SC_HANDLE hService) +{ +/** Wait at most 10 seconds for the service to stop. */ +#define MAX_SERVICE_WAIT_TIME 10 + int wait_time; + char *errmsg = NULL; + nt_service_loadlibrary(); + + service_fns.QueryServiceStatus_fn(hService, &service_status); + if (service_status.dwCurrentState == SERVICE_STOPPED) { + printf("Service is already stopped\n"); + return 0; + } + + if (service_fns.ControlService_fn(hService, SERVICE_CONTROL_STOP, + &service_status)) { + wait_time = 0; + while (service_fns.QueryServiceStatus_fn(hService, &service_status) && + (service_status.dwCurrentState != SERVICE_STOPPED) && + (wait_time < MAX_SERVICE_WAIT_TIME)) { + Sleep(1000); + wait_time++; + } + if (service_status.dwCurrentState == SERVICE_STOPPED) { + printf("Service stopped successfully\n"); + return 0; + } else if (wait_time == MAX_SERVICE_WAIT_TIME) { + printf("Service did not stop within %d seconds.\n", wait_time); + } else { + errmsg = nt_strerror(GetLastError()); + printf("QueryServiceStatus() failed : %s\n",errmsg); + LocalFree(errmsg); + } + } else { + errmsg = nt_strerror(GetLastError()); + printf("ControlService() failed : %s\n", errmsg); + LocalFree(errmsg); + } + return -1; +} + +/** Build a formatted command line used for the NT service. Return a + * pointer to the formatted string on success, or NULL on failure. Set + * *using_default_torrc to true if we're going to use the default + * location to torrc, or 1 if an option was specified on the command line. + */ +static char * +nt_service_command_line(int *using_default_torrc) +{ + TCHAR tor_exe[MAX_PATH+1]; + char *command, *options=NULL; + smartlist_t *sl; + int i, cmdlen; + *using_default_torrc = 1; + + /* Get the location of tor.exe */ + if (0 == GetModuleFileName(NULL, tor_exe, MAX_PATH)) + return NULL; + + /* Get the service arguments */ + sl = smartlist_create(); + for (i = 1; i < backup_argc; ++i) { + if (!strcmp(backup_argv[i], "--options") || + !strcmp(backup_argv[i], "-options")) { + while (++i < backup_argc) { + if (!strcmp(backup_argv[i], "-f")) + *using_default_torrc = 0; + smartlist_add(sl, backup_argv[i]); + } + } + } + if (smartlist_len(sl)) + options = smartlist_join_strings(sl,"\" \"",0,NULL); + smartlist_free(sl); + + /* Allocate a string for the NT service command line */ + cmdlen = strlen(tor_exe) + (options?strlen(options):0) + 32; + command = tor_malloc(cmdlen); + + /* Format the service command */ + if (options) { + if (tor_snprintf(command, cmdlen, "\"%s\" --nt-service \"%s\"", + tor_exe, options)<0) { + tor_free(command); /* sets command to NULL. */ + } + } else { /* ! options */ + if (tor_snprintf(command, cmdlen, "\"%s\" --nt-service", tor_exe)<0) { + tor_free(command); /* sets command to NULL. */ + } + } + + tor_free(options); + return command; +} + +/** Creates a Tor NT service, set to start on boot. The service will be + * started if installation succeeds. Returns 0 on success, or -1 on + * failure. */ +static int +nt_service_install(int argc, char **argv) +{ + /* Notes about developing NT services: + * + * 1. Don't count on your CWD. If an absolute path is not given, the + * fopen() function goes wrong. + * 2. The parameters given to the nt_service_body() function differ + * from those given to main() function. + */ + + SC_HANDLE hSCManager = NULL; + SC_HANDLE hService = NULL; + SERVICE_DESCRIPTION sdBuff; + char *command; + char *errmsg; + const char *user_acct = GENSRV_USERACCT; + const char *password = ""; + int i; + OSVERSIONINFOEX info; + SID_NAME_USE sidUse; + DWORD sidLen = 0, domainLen = 0; + int is_win2k_or_worse = 0; + int using_default_torrc = 0; + + nt_service_loadlibrary(); + + /* Open the service control manager so we can create a new service */ + if ((hSCManager = nt_service_open_scm()) == NULL) + return -1; + /* Build the command line used for the service */ + if ((command = nt_service_command_line(&using_default_torrc)) == NULL) { + printf("Unable to build service command line.\n"); + service_fns.CloseServiceHandle_fn(hSCManager); + return -1; + } + + for (i=1; i < argc; ++i) { + if (!strcmp(argv[i], "--user") && i+1"); + + /* Create the Tor service, set to auto-start on boot */ + if ((hService = service_fns.CreateServiceA_fn(hSCManager, GENSRV_SERVICENAME, + GENSRV_DISPLAYNAME, + SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, + command, NULL, NULL, NULL, + user_acct, password)) == NULL) { + errmsg = nt_strerror(GetLastError()); + printf("CreateService() failed : %s\n", errmsg); + service_fns.CloseServiceHandle_fn(hSCManager); + LocalFree(errmsg); + tor_free(command); + return -1; + } + printf("Done with CreateService.\n"); + + /* Set the service's description */ + sdBuff.lpDescription = (char*)GENSRV_DESCRIPTION; + service_fns.ChangeServiceConfig2A_fn(hService, SERVICE_CONFIG_DESCRIPTION, + &sdBuff); + printf("Service installed successfully\n"); + + /* Start the service initially */ + nt_service_start(hService); + + service_fns.CloseServiceHandle_fn(hService); + service_fns.CloseServiceHandle_fn(hSCManager); + tor_free(command); + + return 0; +} + +/** Removes the Tor NT service. Returns 0 if the service was successfully + * removed, or -1 on error. */ +static int +nt_service_remove(void) +{ + SC_HANDLE hSCManager = NULL; + SC_HANDLE hService = NULL; + char *errmsg; + + nt_service_loadlibrary(); + if ((hSCManager = nt_service_open_scm()) == NULL) + return -1; + if ((hService = nt_service_open(hSCManager)) == NULL) { + service_fns.CloseServiceHandle_fn(hSCManager); + return -1; + } + + nt_service_stop(hService); + if (service_fns.DeleteService_fn(hService) == FALSE) { + errmsg = nt_strerror(GetLastError()); + printf("DeleteService() failed : %s\n", errmsg); + LocalFree(errmsg); + service_fns.CloseServiceHandle_fn(hService); + service_fns.CloseServiceHandle_fn(hSCManager); + return -1; + } + + service_fns.CloseServiceHandle_fn(hService); + service_fns.CloseServiceHandle_fn(hSCManager); + printf("Service removed successfully\n"); + + return 0; +} + +/** Starts the Tor service. Returns 0 on success, or -1 on error. */ +static int +nt_service_cmd_start(void) +{ + SC_HANDLE hSCManager; + SC_HANDLE hService; + int start; + + if ((hSCManager = nt_service_open_scm()) == NULL) + return -1; + if ((hService = nt_service_open(hSCManager)) == NULL) { + service_fns.CloseServiceHandle_fn(hSCManager); + return -1; + } + + start = nt_service_start(hService); + service_fns.CloseServiceHandle_fn(hService); + service_fns.CloseServiceHandle_fn(hSCManager); + + return start; +} + +/** Stops the Tor service. Returns 0 on success, or -1 on error. */ +static int +nt_service_cmd_stop(void) +{ + SC_HANDLE hSCManager; + SC_HANDLE hService; + int stop; + + if ((hSCManager = nt_service_open_scm()) == NULL) + return -1; + if ((hService = nt_service_open(hSCManager)) == NULL) { + service_fns.CloseServiceHandle_fn(hSCManager); + return -1; + } + + stop = nt_service_stop(hService); + service_fns.CloseServiceHandle_fn(hService); + service_fns.CloseServiceHandle_fn(hSCManager); + + return stop; +} + +/** Given a Win32 error code, this attempts to make Windows + * return a human-readable error message. The char* returned + * is allocated by Windows, but should be freed with LocalFree() + * when finished with it. */ +static char* +nt_strerror(uint32_t errnum) +{ + char *msgbuf; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, errnum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&msgbuf, 0, NULL); + return msgbuf; +} +#endif + +int +nt_service_parse_options(int argc, char **argv) +{ + backup_argv = argv; + backup_argc = argc; + + if ((argc >= 3) && + (!strcmp(argv[1], "-service") || !strcmp(argv[1], "--service"))) { + nt_service_loadlibrary(); + if (!strcmp(argv[2], "install")) + return nt_service_install(argc, argv); + if (!strcmp(argv[2], "remove")) + return nt_service_remove(); + if (!strcmp(argv[2], "start")) + return nt_service_cmd_start(); + if (!strcmp(argv[2], "stop")) + return nt_service_cmd_stop(); + printf("Unrecognized service command '%s'\n", argv[2]); + return -1; + } + if (argc >= 2) { + if (!strcmp(argv[1], "-nt-service") || !strcmp(argv[1], "--nt-service")) { + nt_service_loadlibrary(); + nt_service_main(); + return ; + } + // These values have been deprecated since 0.1.1.2-alpha; we've warned + // about them since 0.1.2.7-alpha. + if (!strcmp(argv[1], "-install") || !strcmp(argv[1], "--install")) { + nt_service_loadlibrary(); + fprintf(stderr, + "The %s option is deprecated; use \"--service install\" instead.", + argv[1]); + return nt_service_install(argc, argv); + } + if (!strcmp(argv[1], "-remove") || !strcmp(argv[1], "--remove")) { + nt_service_loadlibrary(); + fprintf(stderr, + "The %s option is deprecated; use \"--service remove\" instead.", + argv[1]); + return nt_service_remove(); + } + } +} + diff --git a/src/or/or.h b/src/or/or.h index 72fd36133e..7ef606bbf4 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -3018,6 +3018,19 @@ void tor_free_all(int postfork); int tor_main(int argc, char *argv[]); +/********************************* ntmain.c ***************************/ +#ifdef MS_WINDOWS +#define NT_SERVICE +#endif + +#ifdef NT_SERVICE +int nt_service_parse_options(int argc, char **argv); +int nt_service_is_stopping(void); +void nt_service_set_state(DWORD state); +#else +#define nt_service_is_stopping() (0) +#endif + /********************************* onion.c ***************************/ int onion_pending_add(or_circuit_t *circ);