diff options
author | Nick Mathewson <nickm@torproject.org> | 2005-07-09 16:37:12 +0000 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2005-07-09 16:37:12 +0000 |
commit | e1db33971e56949c5021185c3c156fde37a016d8 (patch) | |
tree | 1800c2d2664f52df24ecd696c97e53d3aea36236 | |
parent | 2f1f0c8d2155ef5f63c0598c0175131995e0e9a7 (diff) | |
download | tor-e1db33971e56949c5021185c3c156fde37a016d8.tar.gz tor-e1db33971e56949c5021185c3c156fde37a016d8.zip |
NT service patch from Matt Edman to improve error messages, lightly cleaned; needs more testing.
svn:r4527
-rw-r--r-- | src/or/main.c | 314 |
1 files changed, 268 insertions, 46 deletions
diff --git a/src/or/main.c b/src/or/main.c index e0427b8cf5..bf6198a862 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -77,11 +77,19 @@ int has_completed_circuit=0; #define GENSRV_SERVICENAME TEXT("tor") #define GENSRV_DISPLAYNAME TEXT("Tor Win32 Service") #define GENSRV_DESCRIPTION TEXT("Provides an anonymous Internet communication system") + +// 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; SERVICE_STATUS_HANDLE hStatus; static char **backup_argv; static int backup_argc; static int nt_service_is_stopped(void); +static char* nt_strerror(uint32_t errnum); #else #define nt_service_is_stopped() (0) #endif @@ -1409,6 +1417,48 @@ do_hash_password(void) } #ifdef MS_WINDOWS_SERVICE +/** 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"); + 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 compile to run as an NT service, and the service has been * shut down, then change our current status and return 1. Else * return 0. @@ -1459,11 +1509,23 @@ nt_service_body(int argc, char **argv) return; } - err = tor_init(backup_argc, backup_argv); // refactor this part out of tor_main and do_main_loop + // check for torrc + if (nt_torrc_is_present()) { + err = tor_init(backup_argc, backup_argv); // refactor this part out of tor_main and do_main_loop + if (err) { + err = NT_SERVICE_ERROR_TORINIT_FAILED; + } + } + else { + log_fn(LOG_ERR, "torrc is not in the current working directory. The Tor service will not start."); + err = NT_SERVICE_ERROR_NO_TORRC; + } + if (err) { // failed. service_status.dwCurrentState = SERVICE_STOPPED; - service_status.dwWin32ExitCode = -1; + service_status.dwWin32ExitCode = err; + service_status.dwServiceSpecificExitCode = err; SetServiceStatus(hStatus, &service_status); return; } @@ -1480,6 +1542,7 @@ nt_service_main(void) { SERVICE_TABLE_ENTRY table[2]; DWORD result = 0; + char *errmsg; table[0].lpServiceName = GENSRV_SERVICENAME; table[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)nt_service_body; table[1].lpServiceName = NULL; @@ -1487,7 +1550,9 @@ nt_service_main(void) if (!StartServiceCtrlDispatcher(table)) { result = GetLastError(); - printf("Error was %d\n",result); + errmsg = nt_strerror(result); + printf("Service error %d : %s\n", result, errmsg); + LocalFree(errmsg); if (result == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { if (tor_init(backup_argc, backup_argv) < 0) return; @@ -1513,6 +1578,113 @@ nt_service_main(void) } /** DOCDOC */ +SC_HANDLE +nt_service_open_scm(void) +{ + SC_HANDLE hSCManager; + char *errmsg = NULL; + + if ((hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE)) == NULL) { + errmsg = nt_strerror(GetLastError()); + printf("OpenSCManager() failed : %s\n", errmsg); + LocalFree(errmsg); + } + return hSCManager; +} + +/** DOCDOC */ +SC_HANDLE +nt_service_open(SC_HANDLE hSCManager) +{ + SC_HANDLE hService; + char *errmsg = NULL; + + if ((hService = OpenService(hSCManager, GENSRV_SERVICENAME, SERVICE_ALL_ACCESS)) == NULL) { + errmsg = nt_strerror(GetLastError()); + printf("OpenService() failed : %s\n", errmsg); + LocalFree(errmsg); + } + return hService; +} + +/** DOCDOC */ +int +nt_service_start(SC_HANDLE hService) +{ + char *errmsg = NULL; + + QueryServiceStatus(hService, &service_status); + if (service_status.dwCurrentState == SERVICE_RUNNING) { + printf("Service is already running\n"); + return 1; + } + + if (StartService(hService, 0, NULL)) { + /* Loop until the service has finished attempting to start */ + while (QueryServiceStatus(hService, &service_status)) { + if (service_status.dwCurrentState == SERVICE_START_PENDING) + Sleep(500); + else + break; + } + + /* Check if it started successfully or not */ + if (service_status.dwCurrentState == SERVICE_RUNNING) { + printf("Service started successfully\n"); + return 1; + } + 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 0; +} + +/** DOCDOC */ +int +nt_service_stop(SC_HANDLE hService) +{ + char *errmsg = NULL; + + QueryServiceStatus(hService, &service_status); + if (service_status.dwCurrentState == SERVICE_STOPPED) { + printf("Service is already stopped\n"); + return 1; + } + + if (ControlService(hService, SERVICE_CONTROL_STOP, &service_status)) { + while (QueryServiceStatus(hService, &service_status)) { + if (service_status.dwCurrentState == SERVICE_STOP_PENDING) + Sleep(500); + else + break; + } + if (service_status.dwCurrentState == SERVICE_STOPPED) { + printf("Service stopped successfully\n"); + return 1; + } + else { + errmsg = nt_strerror(GetLastError()); + printf("Service failed to stop : %s\n"); + LocalFree(errmsg); + } + } + else { + errmsg = nt_strerror(GetLastError()); + printf("ControlService() failed : %s\n", errmsg); + LocalFree(errmsg); + } + return 0; +} + +/** DOCDOC */ int nt_service_install(void) { @@ -1545,6 +1717,7 @@ nt_service_install(void) char cmd1[] = " -f "; char cmd2[] = "\\torrc"; char *command; + char *errmsg; int len = 0; if (0 == GetModuleFileName(NULL, szPath, MAX_PATH)) @@ -1567,8 +1740,7 @@ nt_service_install(void) return 0; } - if ((hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE)) == NULL) { - printf("Failed: OpenSCManager()\n"); + if ((hSCManager = nt_service_open_scm()) == NULL) { free(command); return 0; } @@ -1582,33 +1754,21 @@ nt_service_install(void) SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, command, NULL, NULL, NULL, NULL, "")) == NULL) { - printf("Failed: CreateService()\n"); + errmsg = nt_strerror(GetLastError()); + printf("CreateService() failed : %s\n", errmsg); CloseServiceHandle(hSCManager); + LocalFree(errmsg); free(command); return 0; } - /* Start the service initially, so you don't have to muck with it in the SCM - */ /* Set the service's description */ sdBuff.lpDescription = GENSRV_DESCRIPTION; ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &sdBuff); + printf("Service installed successfully\n"); - /* Start the service, so you don't have to muck with it in the SCM */ - if (StartService(hService, 0, NULL)) { - /* Loop until the service has finished attempting to start */ - while (QueryServiceStatus(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 installed and started successfully.\n"); - else - printf("Service installed, but failed to start.\n"); - } else { - printf("Service installed, but failed to start.\n"); - } + /* Start the service initially */ + nt_service_start(hService); CloseServiceHandle(hService); CloseServiceHandle(hSCManager); @@ -1623,39 +1783,30 @@ nt_service_remove(void) { SC_HANDLE hSCManager = NULL; SC_HANDLE hService = NULL; - SERVICE_STATUS service_status; BOOL result = FALSE; + char *errmsg; - if ((hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE)) == NULL) { - printf("Failed: OpenSCManager()\n"); + if ((hSCManager = nt_service_open_scm()) == NULL) { return 0; } - if ((hService = OpenService(hSCManager, GENSRV_SERVICENAME, SERVICE_ALL_ACCESS)) == NULL) { - printf("Failed: OpenService()\n"); + if ((hService = nt_service_open(hSCManager)) == NULL) { CloseServiceHandle(hSCManager); return 0; } - result = ControlService(hService, SERVICE_CONTROL_STOP, &service_status); - if (result) { - while (QueryServiceStatus(hService, &service_status)) - { - if (service_status.dwCurrentState == SERVICE_STOP_PENDING) - Sleep(500); - else - break; - } - if (DeleteService(hService)) + if (nt_service_stop(hService)) { + if (DeleteService(hService)) { printf("Removed service successfully\n"); - else - printf("Failed: DeleteService()\n"); - } else { - result = DeleteService(hService); - if (result) - printf("Removed service successfully\n"); - else - printf("Failed: DeleteService()\n"); + } + else { + errmsg = nt_strerror(GetLastError()); + printf("DeleteService() failed : %s\n", errmsg); + LocalFree(errmsg); + } + } + else { + printf("Service could not be removed\n"); } CloseServiceHandle(hService); @@ -1663,6 +1814,64 @@ nt_service_remove(void) return 0; } + +/** DOCDOC */ +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) { + CloseHandle(hSCManager); + return -1; + } + + start = nt_service_start(hService); + CloseHandle(hService); + CloseHandle(hSCManager); + + return start; +} + +/** DOCDOC */ +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) { + CloseHandle(hSCManager); + return -1; + } + + stop = nt_service_stop(hService); + CloseHandle(hService); + CloseHandle(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 /** DOCDOC */ @@ -1672,6 +1881,19 @@ tor_main(int argc, char *argv[]) #ifdef MS_WINDOWS_SERVICE backup_argv = argv; backup_argc = argc; + if ((argc >= 3) && (!strcmp(argv[1], "-service") || !strcmp(argv[1], "--service"))) { + if (!strcmp(argv[2], "install")) + return nt_service_install(); + 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; + } + // These are left so as not to confuse people who are used to these options if (argc >= 2) { if (!strcmp(argv[1], "-install") || !strcmp(argv[1], "--install")) return nt_service_install(); |