diff --git a/ChangeLog b/ChangeLog index 0c600e6873..f6f37f3dce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,10 @@ Changes in version 0.1.2.5-xxxx - 200?-??-?? clients to tell which servers support BEGIN_DIR and which don't. The implementation is forward-compatible with a proposed future protocol version scheme not tied to Tor versions. + - Support running the Tor service with a torrc not in the + same directory as tor.exe (Bug #356) and default to using the torrc + located in the %appdata%\Tor\ of the user who installed the service. + Patch from Matt Edman. o Minor features: - Start using the state file to store bandwidth accounting data: @@ -100,6 +104,10 @@ Changes in version 0.1.2.5-xxxx - 200?-??-?? - Stop accepting certain malformed ports in configured exit policies. - Don't re-write the fingerprint file every restart, unless it has changed. + - Cleaned-up code and documentation in NT services. Patch from Matt + Edman. + - Removed the supposedly misleading error message mentioned in Bug #294. + Patch from Matt Edman. o Controller features: - Have GETINFO dir/status/* work on hosts with DirPort disabled. diff --git a/src/or/main.c b/src/or/main.c index d134bb3928..c67dcd7538 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -78,7 +78,6 @@ int has_completed_circuit=0; // 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_NO_TORRC ERROR_FILE_NOT_FOUND #define NT_SERVICE_ERROR_TORINIT_FAILED ERROR_EXCEPTION_IN_SERVICE SERVICE_STATUS service_status; @@ -1723,6 +1722,8 @@ struct service_fns { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; +/** Loads functions used by NT services. Returns 0 on success, or -1 on + * error. */ static int nt_service_loadlibrary(void) { @@ -1767,49 +1768,6 @@ nt_service_loadlibrary(void) return 0; } -/** Checks if torrc is present in the same directory - * as the service executable. - * Return 1 if it is, 0 if it is not present. */ -static int -nt_torrc_is_present() -{ - HANDLE hFile; - TCHAR szPath[_MAX_PATH]; - TCHAR szDrive[_MAX_DRIVE]; - TCHAR szDir[_MAX_DIR]; - char torrc[] = "torrc"; - char *path_to_torrc; - int len = 0; - - /* Get the service executable path */ - if (0 == GetModuleFileName(NULL, szPath, MAX_PATH)) - return 0; - _tsplitpath(szPath, szDrive, szDir, NULL, NULL); - - /* Build the path to the torrc file */ - len = _MAX_PATH + _MAX_DRIVE + _MAX_DIR + strlen(torrc) + 1; - path_to_torrc = tor_malloc(len); - if (tor_snprintf(path_to_torrc, len, "%s%s%s", szDrive, szDir, torrc)<0) { - printf("Failed: tor_snprinf()\n"); - tor_free(path_to_torrc); - return 0; - } - - /* See if torrc is present */ - hFile = CreateFile(TEXT(path_to_torrc), - GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, - NULL); - - tor_free(path_to_torrc); - - if (hFile == INVALID_HANDLE_VALUE) { - return 0; - } - CloseHandle(hFile); - return 1; -} - /** If we're compiled to run as an NT service, and the service has been * shut down, then change our current status and return 1. Else * return 0. @@ -1831,7 +1789,8 @@ nt_service_is_stopped(void) return 0; } -/** DOCDOC */ +/** Handles service control requests, such as stopping or starting the + * Tor service. */ void nt_service_control(DWORD request) { @@ -1854,7 +1813,10 @@ nt_service_control(DWORD request) service_fns.SetServiceStatus_fn(hStatus, &service_status); } -/** DOCDOC */ +/** 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) { @@ -1873,40 +1835,31 @@ nt_service_body(int argc, char **argv) (LPHANDLER_FUNCTION) nt_service_control); if (hStatus == 0) { - // failed; + /* Failed to register the service control handler function */ return; } - // check for torrc - if (nt_torrc_is_present()) { - r = tor_init(backup_argc, backup_argv); - // refactor this part out of tor_main and do_main_loop - if (r) { - r = NT_SERVICE_ERROR_TORINIT_FAILED; - } - } - else { - log_err(LD_CONFIG, "torrc is not in the current working directory. " - "The Tor service will not start."); - r = NT_SERVICE_ERROR_NO_TORRC; - } - + r = tor_init(backup_argc, backup_argv); if (r) { - // failed. + /* 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(); - return; } -/** DOCDOC */ +/** 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) { @@ -1950,12 +1903,14 @@ nt_service_main(void) } } -/** DOCDOC */ +/** 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; + if (nt_service_loadlibrary()<0) return 0; if ((hSCManager = service_fns.OpenSCManagerA_fn( @@ -1967,7 +1922,8 @@ nt_service_open_scm(void) return hSCManager; } -/** DOCDOC */ +/** Open a handle to the Tor service using hSCManager. Return NULL + * on failure. */ SC_HANDLE nt_service_open(SC_HANDLE hSCManager) { @@ -1985,7 +1941,8 @@ nt_service_open(SC_HANDLE hSCManager) return hService; } -/** DOCDOC */ +/** 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) { @@ -1997,7 +1954,7 @@ nt_service_start(SC_HANDLE hService) service_fns.QueryServiceStatus_fn(hService, &service_status); if (service_status.dwCurrentState == SERVICE_RUNNING) { printf("Service is already running\n"); - return 1; + return 0; } if (service_fns.StartServiceA_fn(hService, 0, NULL)) { @@ -2010,7 +1967,7 @@ nt_service_start(SC_HANDLE hService) /* Check if it started successfully or not */ if (service_status.dwCurrentState == SERVICE_RUNNING) { printf("Service started successfully\n"); - return 1; + return 0; } else { errmsg = nt_strerror(service_status.dwWin32ExitCode); printf("Service failed to start : %s\n", errmsg); @@ -2021,10 +1978,11 @@ nt_service_start(SC_HANDLE hService) printf("StartService() failed : %s\n", errmsg); LocalFree(errmsg); } - return 0; + return -1; } -/** DOCDOC */ +/** 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) { @@ -2038,7 +1996,7 @@ nt_service_stop(SC_HANDLE hService) service_fns.QueryServiceStatus_fn(hService, &service_status); if (service_status.dwCurrentState == SERVICE_STOPPED) { printf("Service is already stopped\n"); - return 1; + return 0; } if (service_fns.ControlService_fn(hService, SERVICE_CONTROL_STOP, @@ -2052,7 +2010,7 @@ nt_service_stop(SC_HANDLE hService) } if (service_status.dwCurrentState == SERVICE_STOPPED) { printf("Service stopped successfully\n"); - return 1; + return 0; } else if (wait_time == MAX_SERVICE_WAIT_TIME) { printf("Service did not stop within %d seconds.\n", wait_time); } else { @@ -2060,32 +2018,71 @@ nt_service_stop(SC_HANDLE hService) printf("QueryServiceStatus() failed : %s\n",errmsg); LocalFree(errmsg); } - } - else { + } else { errmsg = nt_strerror(GetLastError()); printf("ControlService() failed : %s\n", errmsg); LocalFree(errmsg); } - return 0; + return -1; } -/** DOCDOC */ +/** Build a formatted command line used for the NT service. Return a + * pointer to the formatted string on success, or NULL on failure. */ +char * +nt_service_command_line(void) +{ + TCHAR tor_exe[MAX_PATH+1]; + char *command, *options; + const char *torrc; + smartlist_t *sl; + int i, cmdlen; + int use_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(); + torrc = get_torrc_fname(); + 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")) + use_default_torrc = 0; + smartlist_add(sl, backup_argv[i]); + } + } + if (use_default_torrc) { + smartlist_add(sl, "-f"); + smartlist_add(sl, torrc); + } + tor_assert(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)+ strlen(" --nt-service -f ") + + strlen(options) + 32; + command = tor_malloc(cmdlen); + + /* Format the service command */ + if (tor_snprintf(command, cmdlen, "\"%s\" --nt-service \"%s\"", + tor_exe, options)<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(void) { - /* XXXX Problems with NT services: - * 1. The configuration file needs to be in the same directory as the .exe - * - * 2. The exe and the configuration file can't be on any directory path - * that contains a space. - * mje - you can quote the string (i.e., "c:\program files") - * - * 3. Ideally, there should be one EXE that can either run as a - * separate process (as now) or that can install and run itself - * as an NT service. I have no idea how hard this is. - * mje - should be done. It can install and run itself as a service - * - * Notes about developing NT services: + /* Notes about developing NT services: * * 1. Don't count on your CWD. If an absolute path is not given, the * fopen() function goes wrong. @@ -2096,47 +2093,24 @@ nt_service_install(void) SC_HANDLE hSCManager = NULL; SC_HANDLE hService = NULL; SERVICE_DESCRIPTION sdBuff; - TCHAR szPath[_MAX_PATH]; - TCHAR szDrive[_MAX_DRIVE]; - TCHAR szDir[_MAX_DIR]; - char cmd1[] = " -f "; - char cmd2[] = "\\torrc"; char *command; char *errmsg; int len = 0; if (nt_service_loadlibrary()<0) return -1; - if (0 == GetModuleFileName(NULL, szPath, MAX_PATH)) - return 0; - _tsplitpath(szPath, szDrive, szDir, NULL, NULL); - - /* Account for the extra quotes */ - //len = _MAX_PATH + strlen(cmd1) + _MAX_DRIVE + _MAX_DIR + strlen(cmd2); - len = _MAX_PATH + strlen(cmd1) + _MAX_DRIVE + _MAX_DIR + strlen(cmd2) + 64; - command = tor_malloc(len); - - /* Create a quoted command line, like "c:\with spaces\tor.exe" -f - * "c:\with spaces\tor.exe" - */ - if (tor_snprintf(command, len, "\"%s\" --nt-service -f \"%s%storrc\"", - szPath, szDrive, szDir)<0) { - printf("Failed: tor_snprinf()\n"); - tor_free(command); - return 0; + /* 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()) == NULL) { + printf("Unable to build service command line.\n"); + service_fns.CloseServiceHandle_fn(hSCManager); + return -1; } - if ((hSCManager = nt_service_open_scm()) == NULL) { - tor_free(command); - return 0; - } - - /* 1/26/2005 mje - * - changed the service start type to auto - * - and changed the lpPassword param to "" instead of NULL as per an - * MSDN article. - */ + /* 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, @@ -2148,7 +2122,7 @@ nt_service_install(void) service_fns.CloseServiceHandle_fn(hSCManager); LocalFree(errmsg); tor_free(command); - return 0; + return -1; } /* Set the service's description */ @@ -2167,7 +2141,8 @@ nt_service_install(void) return 0; } -/** DOCDOC */ +/** Removes the Tor NT service. Returns 0 if the service was successfully + * removed, or -1 on error. */ int nt_service_remove(void) { @@ -2177,37 +2152,31 @@ nt_service_remove(void) if (nt_service_loadlibrary()<0) return -1; - - if ((hSCManager = nt_service_open_scm()) == NULL) { - return 0; - } - + if ((hSCManager = nt_service_open_scm()) == NULL) + return -1; if ((hService = nt_service_open(hSCManager)) == NULL) { service_fns.CloseServiceHandle_fn(hSCManager); - return 0; + return -1; } - if (nt_service_stop(hService)) { - if (service_fns.DeleteService_fn(hService)) { - printf("Removed service successfully\n"); - } - else { - errmsg = nt_strerror(GetLastError()); - printf("DeleteService() failed : %s\n", errmsg); - LocalFree(errmsg); - } - } - else { - printf("Service could not be removed\n"); + 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; } -/** DOCDOC */ +/** Starts the Tor service. Returns 0 on success, or -1 on error. */ int nt_service_cmd_start(void) { @@ -2218,18 +2187,18 @@ nt_service_cmd_start(void) if ((hSCManager = nt_service_open_scm()) == NULL) return -1; if ((hService = nt_service_open(hSCManager)) == NULL) { - CloseHandle(hSCManager); + service_fns.CloseServiceHandle_fn(hSCManager); return -1; } start = nt_service_start(hService); - CloseHandle(hService); - CloseHandle(hSCManager); + service_fns.CloseServiceHandle_fn(hService); + service_fns.CloseServiceHandle_fn(hSCManager); return start; } -/** DOCDOC */ +/** Stops the Tor service. Returns 0 on success, or -1 on error. */ int nt_service_cmd_stop(void) { @@ -2240,13 +2209,13 @@ nt_service_cmd_stop(void) if ((hSCManager = nt_service_open_scm()) == NULL) return -1; if ((hService = nt_service_open(hSCManager)) == NULL) { - CloseHandle(hSCManager); + service_fns.CloseServiceHandle_fn(hSCManager); return -1; } stop = nt_service_stop(hService); - CloseHandle(hService); - CloseHandle(hSCManager); + service_fns.CloseServiceHandle_fn(hService); + service_fns.CloseServiceHandle_fn(hSCManager); return stop; } diff --git a/src/or/or.h b/src/or/or.h index 15dee82437..82abdb3032 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1221,7 +1221,7 @@ typedef uint16_t circid_t; * OR connections multiplex many circuits at once, and stay standing even * when there are no circuits running over them. * - * A circuit_t structure cann fill one of two roles. First, a or_circuit_t + * A circuit_t structure can fill one of two roles. First, a or_circuit_t * links two connections together: either an edge connection and an OR * connection, or two OR connections. (When joined to an OR connection, a * circuit_t affects only cells sent to a particular circID on that