From: Iain Patterson Date: Wed, 20 Nov 2013 12:14:15 +0000 (+0000) Subject: Pass parameters around in a data structure. X-Git-Tag: v2.19~9 X-Git-Url: http://git.iain.cx/?p=nssm.git;a=commitdiff_plain;h=fce252d07bdf443e3b283c26a940d3b0f26fd440 Pass parameters around in a data structure. Instead of static variables and functions taking a dozen arguments we now use a single data structure, nssm_service_t, for the service. The code is easier to read and understand, especially when adding more variables through to functions. --- diff --git a/gui.cpp b/gui.cpp index 5ceb33f..7773a18 100644 --- a/gui.cpp +++ b/gui.cpp @@ -72,56 +72,68 @@ void centre_window(HWND window) { int install(HWND window) { if (! window) return 1; - /* Check parameters in the window */ - char name[VALUE_LENGTH]; - char exe[EXE_LENGTH]; - char flags[VALUE_LENGTH]; - - /* Get service name */ - if (! GetDlgItemText(window, IDC_NAME, name, sizeof(name))) { - popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME); - return 2; - } - - /* Get executable name */ - if (! GetDlgItemText(window, IDC_PATH, exe, sizeof(exe))) { - popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PATH); - return 3; - } + nssm_service_t *service = alloc_nssm_service(); + if (service) { + /* Get service name. */ + if (! GetDlgItemText(window, IDC_NAME, service->name, sizeof(service->name))) { + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME); + cleanup_nssm_service(service); + return 2; + } - /* Get flags */ - if (SendMessage(GetDlgItem(window, IDC_FLAGS), WM_GETTEXTLENGTH, 0, 0)) { - if (! GetDlgItemText(window, IDC_FLAGS, flags, sizeof(flags))) { - popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_OPTIONS); - return 4; + /* Get executable name */ + if (! GetDlgItemText(window, IDC_PATH, service->exe, sizeof(service->exe))) { + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PATH); + return 3; } + + /* Get flags */ + if (SendMessage(GetDlgItem(window, IDC_FLAGS), WM_GETTEXTLENGTH, 0, 0)) { + if (! GetDlgItemText(window, IDC_FLAGS, service->flags, sizeof(service->flags))) { + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_OPTIONS); + return 4; + } + } + + memmove(service->dir, service->exe, strlen(service->exe)); + strip_basename(service->dir); } - else ZeroMemory(&flags, sizeof(flags)); - /* See if it works */ - switch (install_service(name, exe, flags)) { + /* See if it works. */ + switch (install_service(service)) { + case 1: + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, "service", "install()"); + cleanup_nssm_service(service); + return 1; + case 2: popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED); + cleanup_nssm_service(service); return 2; case 3: popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM); + cleanup_nssm_service(service); return 3; case 4: popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH); + cleanup_nssm_service(service); return 4; case 5: popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INSTALL_SERVICE_FAILED); + cleanup_nssm_service(service); return 5; case 6: popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_CREATE_PARAMETERS_FAILED); + cleanup_nssm_service(service); return 6; } - popup_message(MB_OK, NSSM_MESSAGE_SERVICE_INSTALLED, name); + popup_message(MB_OK, NSSM_MESSAGE_SERVICE_INSTALLED, service->name); + cleanup_nssm_service(service); return 0; } @@ -129,34 +141,47 @@ int install(HWND window) { int remove(HWND window) { if (! window) return 1; - /* Check parameters in the window */ - char name[VALUE_LENGTH]; + /* See if it works */ + nssm_service_t *service = alloc_nssm_service(); + if (service) { + /* Get service name */ + if (! GetDlgItemText(window, IDC_NAME, service->name, sizeof(service->name))) { + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME); + cleanup_nssm_service(service); + return 2; + } - /* Get service name */ - if (! GetDlgItemText(window, IDC_NAME, name, sizeof(name))) { - popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME); - return 2; + /* Confirm */ + if (popup_message(MB_YESNO, NSSM_GUI_ASK_REMOVE_SERVICE, service->name) != IDYES) { + cleanup_nssm_service(service); + return 0; + } } - /* Confirm */ - if (popup_message(MB_YESNO, NSSM_GUI_ASK_REMOVE_SERVICE, name) != IDYES) return 0; + switch (remove_service(service)) { + case 1: + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, "service", "remove()"); + cleanup_nssm_service(service); + return 1; - /* See if it works */ - switch (remove_service(name)) { case 2: popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED); + cleanup_nssm_service(service); return 2; case 3: popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_SERVICE_NOT_INSTALLED); return 3; + cleanup_nssm_service(service); case 4: popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_REMOVE_SERVICE_FAILED); + cleanup_nssm_service(service); return 4; } - popup_message(MB_OK, NSSM_MESSAGE_SERVICE_REMOVED, name); + popup_message(MB_OK, NSSM_MESSAGE_SERVICE_REMOVED, service->name); + cleanup_nssm_service(service); return 0; } diff --git a/nssm.h b/nssm.h index 1479f10..6edfedf 100644 --- a/nssm.h +++ b/nssm.h @@ -6,13 +6,13 @@ #include #include #include +#include "service.h" #include "event.h" #include "imports.h" #include "messages.h" #include "process.h" #include "registry.h" #include "io.h" -#include "service.h" #include "gui.h" int str_equiv(const char *, const char *); @@ -22,19 +22,6 @@ int str_equiv(const char *, const char *); #define NSSM_VERSIONINFO 2,18,0,0 #define NSSM_DATE "2013-11-15" -/* - MSDN says the commandline in CreateProcess() is limited to 32768 characters - and the application name to MAX_PATH. - A registry key is limited to 255 characters. - A registry value is limited to 16383 characters. - Therefore we limit the service name to accommodate the path under HKLM. -*/ -#define EXE_LENGTH MAX_PATH -#define CMD_LENGTH 32768 -#define KEY_LENGTH 255 -#define VALUE_LENGTH 16383 -#define SERVICE_NAME_LENGTH KEY_LENGTH - 55 - /* Throttle the restart of the service if it stops before this many milliseconds have elapsed since startup. Override in registry. diff --git a/process.cpp b/process.cpp index 7bb6dac..34b07d3 100644 --- a/process.cpp +++ b/process.cpp @@ -1,9 +1,6 @@ #include "nssm.h" extern imports_t imports; -extern unsigned long kill_console_delay; -extern unsigned long kill_window_delay; -extern unsigned long kill_threads_delay; int get_process_creation_time(HANDLE process_handle, FILETIME *ft) { FILETIME creation_time, exit_time, kernel_time, user_time; @@ -31,7 +28,7 @@ int get_process_exit_time(HANDLE process_handle, FILETIME *ft) { return 0; } -int check_parent(char *service_name, PROCESSENTRY32 *pe, unsigned long ppid, FILETIME *pft, FILETIME *exit_time) { +int check_parent(nssm_service_t *service, PROCESSENTRY32 *pe, unsigned long ppid) { /* Check parent process ID matches. */ if (pe->th32ParentProcessID != ppid) return 1; @@ -45,7 +42,7 @@ int check_parent(char *service_name, PROCESSENTRY32 *pe, unsigned long ppid, FIL if (! process_handle) { char pid_string[16]; _snprintf_s(pid_string, sizeof(pid_string), _TRUNCATE, "%lu", pe->th32ProcessID); - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, service_name, error_string(GetLastError()), 0); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, service->name, error_string(GetLastError()), 0); return 2; } @@ -58,10 +55,10 @@ int check_parent(char *service_name, PROCESSENTRY32 *pe, unsigned long ppid, FIL CloseHandle(process_handle); /* Verify that the parent's creation time is not later. */ - if (CompareFileTime(pft, &ft) > 0) return 4; + if (CompareFileTime(&service->creation_time, &ft) > 0) return 4; /* Verify that the parent's exit time is not earlier. */ - if (CompareFileTime(exit_time, &ft) < 0) return 5; + if (CompareFileTime(&service->exit_time, &ft) < 0) return 5; return 0; } @@ -139,8 +136,9 @@ int kill_threads(char *service_name, kill_t *k) { } /* Give the process a chance to die gracefully. */ -int kill_process(char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long stop_method, HANDLE process_handle, unsigned long pid, unsigned long exitcode) { +int kill_process(nssm_service_t *service, HANDLE process_handle, unsigned long pid, unsigned long exitcode) { /* Shouldn't happen. */ + if (! service) return 1; if (! pid) return 1; if (! process_handle) return 1; @@ -152,8 +150,8 @@ int kill_process(char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVI kill_t k = { pid, exitcode, 0 }; /* Try to send a Control-C event to the console. */ - if (stop_method & NSSM_STOP_METHOD_CONSOLE) { - if (! kill_console(service_name, service_handle, service_status, process_handle, pid)) return 1; + if (service->stop_method & NSSM_STOP_METHOD_CONSOLE) { + if (! kill_console(service)) return 1; } /* @@ -161,10 +159,10 @@ int kill_process(char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVI If the process is a console application it won't have any windows so there's no guarantee of success. */ - if (stop_method & NSSM_STOP_METHOD_WINDOW) { + if (service->stop_method & NSSM_STOP_METHOD_WINDOW) { EnumWindows((WNDENUMPROC) kill_window, (LPARAM) &k); if (k.signalled) { - if (! await_shutdown(__FUNCTION__, service_name, service_handle, service_status, process_handle, kill_window_delay)) return 1; + if (! await_shutdown(service, __FUNCTION__, service->kill_window_delay)) return 1; } } @@ -173,29 +171,31 @@ int kill_process(char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVI process. Console applications might have them (but probably won't) so there's still no guarantee of success. */ - if (stop_method & NSSM_STOP_METHOD_THREADS) { - if (kill_threads(service_name, &k)) { - if (! await_shutdown(__FUNCTION__, service_name, service_handle, service_status, process_handle, kill_threads_delay)) return 1; + if (service->stop_method & NSSM_STOP_METHOD_THREADS) { + if (kill_threads(service->name, &k)) { + if (! await_shutdown(service, __FUNCTION__, service->kill_threads_delay)) return 1; } } /* We tried being nice. Time for extreme prejudice. */ - if (stop_method & NSSM_STOP_METHOD_TERMINATE) { - return TerminateProcess(process_handle, exitcode); + if (service->stop_method & NSSM_STOP_METHOD_TERMINATE) { + return TerminateProcess(service->process_handle, exitcode); } return 0; } /* Simulate a Control-C event to our console (shared with the app). */ -int kill_console(char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVICE_STATUS *service_status, HANDLE process_handle, unsigned long pid) { +int kill_console(nssm_service_t *service) { unsigned long ret; + if (! service) return 1; + /* Check we loaded AttachConsole(). */ if (! imports.AttachConsole) return 4; /* Try to attach to the process's console. */ - if (! imports.AttachConsole(pid)) { + if (! imports.AttachConsole(service->pid)) { ret = GetLastError(); switch (ret) { @@ -210,7 +210,7 @@ int kill_console(char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVI case ERROR_ACCESS_DENIED: default: /* We already have a console. */ - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ATTACHCONSOLE_FAILED, service_name, error_string(ret), 0); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ATTACHCONSOLE_FAILED, service->name, error_string(ret), 0); return 3; } } @@ -218,42 +218,42 @@ int kill_console(char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVI /* Ignore the event ourselves. */ ret = 0; if (! SetConsoleCtrlHandler(0, TRUE)) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETCONSOLECTRLHANDLER_FAILED, service_name, error_string(GetLastError()), 0); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETCONSOLECTRLHANDLER_FAILED, service->name, error_string(GetLastError()), 0); ret = 4; } /* Send the event. */ if (! ret) { if (! GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GENERATECONSOLECTRLEVENT_FAILED, service_name, error_string(GetLastError()), 0); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GENERATECONSOLECTRLEVENT_FAILED, service->name, error_string(GetLastError()), 0); ret = 5; } } /* Detach from the console. */ if (! FreeConsole()) { - log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_FREECONSOLE_FAILED, service_name, error_string(GetLastError()), 0); + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_FREECONSOLE_FAILED, service->name, error_string(GetLastError()), 0); } /* Wait for process to exit. */ - if (await_shutdown(__FUNCTION__, service_name, service_handle, service_status, process_handle, kill_console_delay)) ret = 6; + if (await_shutdown(service, __FUNCTION__, service->kill_console_delay)) ret = 6; return ret; } -void kill_process_tree(char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long stop_method, unsigned long pid, unsigned long exitcode, unsigned long ppid, FILETIME *parent_creation_time, FILETIME *parent_exit_time) { +void kill_process_tree(nssm_service_t *service, unsigned long pid, unsigned long exitcode, unsigned long ppid) { /* Shouldn't happen unless the service failed to start. */ if (! pid) return; char pid_string[16], code[16]; _snprintf_s(pid_string, sizeof(pid_string), _TRUNCATE, "%lu", pid); _snprintf_s(code, sizeof(code), _TRUNCATE, "%lu", exitcode); - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILLING, service_name, pid_string, code, 0); + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILLING, service->name, pid_string, code, 0); /* Get a snapshot of all processes in the system. */ HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (! snapshot) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETOOLHELP32SNAPSHOT_PROCESS_FAILED, service_name, error_string(GetLastError()), 0); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETOOLHELP32SNAPSHOT_PROCESS_FAILED, service->name, error_string(GetLastError()), 0); return; } @@ -262,25 +262,25 @@ void kill_process_tree(char *service_name, SERVICE_STATUS_HANDLE service_handle, pe.dwSize = sizeof(pe); if (! Process32First(snapshot, &pe)) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, service_name, error_string(GetLastError()), 0); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, service->name, error_string(GetLastError()), 0); CloseHandle(snapshot); return; } /* This is a child of the doomed process so kill it. */ - if (! check_parent(service_name, &pe, pid, parent_creation_time, parent_exit_time)) kill_process_tree(service_name, service_handle, service_status, stop_method, pe.th32ProcessID, exitcode, ppid, parent_creation_time, parent_exit_time); + if (! check_parent(service, &pe, pid)) kill_process_tree(service, pe.th32ProcessID, exitcode, ppid); while (true) { /* Try to get the next process. */ if (! Process32Next(snapshot, &pe)) { unsigned long ret = GetLastError(); if (ret == ERROR_NO_MORE_FILES) break; - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, service_name, error_string(GetLastError()), 0); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, service->name, error_string(GetLastError()), 0); CloseHandle(snapshot); return; } - if (! check_parent(service_name, &pe, pid, parent_creation_time, parent_exit_time)) kill_process_tree(service_name, service_handle, service_status, stop_method, pe.th32ProcessID, exitcode, ppid, parent_creation_time, parent_exit_time); + if (! check_parent(service, &pe, pid)) kill_process_tree(service, pe.th32ProcessID, exitcode, ppid); } CloseHandle(snapshot); @@ -288,19 +288,19 @@ void kill_process_tree(char *service_name, SERVICE_STATUS_HANDLE service_handle, /* We will need a process handle in order to call TerminateProcess() later. */ HANDLE process_handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, false, pid); if (! process_handle) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, service_name, error_string(GetLastError()), 0); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, service->name, error_string(GetLastError()), 0); return; } char ppid_string[16]; _snprintf_s(ppid_string, sizeof(ppid_string), _TRUNCATE, "%lu", ppid); - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILL_PROCESS_TREE, pid_string, ppid_string, service_name, 0); - if (! kill_process(service_name, service_handle, service_status, stop_method, process_handle, pid, exitcode)) { + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILL_PROCESS_TREE, pid_string, ppid_string, service->name, 0); + if (! kill_process(service, process_handle, pid, exitcode)) { /* Maybe it already died. */ unsigned long ret; if (! GetExitCodeProcess(process_handle, &ret) || ret == STILL_ACTIVE) { - if (stop_method & NSSM_STOP_METHOD_TERMINATE) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_TERMINATEPROCESS_FAILED, pid_string, service_name, error_string(GetLastError()), 0); - else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_PROCESS_STILL_ACTIVE, service_name, pid_string, NSSM, NSSM_REG_STOP_METHOD_SKIP, 0); + if (service->stop_method & NSSM_STOP_METHOD_TERMINATE) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_TERMINATEPROCESS_FAILED, pid_string, service->name, error_string(GetLastError()), 0); + else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_PROCESS_STILL_ACTIVE, service->name, pid_string, NSSM, NSSM_REG_STOP_METHOD_SKIP, 0); } } diff --git a/process.h b/process.h index 21d86da..165bb22 100644 --- a/process.h +++ b/process.h @@ -13,9 +13,9 @@ int get_process_creation_time(HANDLE, FILETIME *); int get_process_exit_time(HANDLE, FILETIME *); int check_parent(char *, PROCESSENTRY32 *, unsigned long, FILETIME *, FILETIME *); int CALLBACK kill_window(HWND, LPARAM); -int kill_threads(char *, SERVICE_STATUS_HANDLE, SERVICE_STATUS *, kill_t *); -int kill_console(char *, SERVICE_STATUS_HANDLE, SERVICE_STATUS *, HANDLE, unsigned long); -int kill_process(char *, SERVICE_STATUS_HANDLE, SERVICE_STATUS *, unsigned long, HANDLE, unsigned long, unsigned long); -void kill_process_tree(char *, SERVICE_STATUS_HANDLE, SERVICE_STATUS *, unsigned long, unsigned long, unsigned long, unsigned long, FILETIME *, FILETIME *); +int kill_threads(nssm_service_t *, kill_t *); +int kill_console(nssm_service_t *); +int kill_process(nssm_service_t *, HANDLE, unsigned long, unsigned long); +void kill_process_tree(nssm_service_t *, unsigned long, unsigned long, unsigned long); #endif diff --git a/registry.cpp b/registry.cpp index f54ed03..59d17d4 100644 --- a/registry.cpp +++ b/registry.cpp @@ -26,10 +26,10 @@ int create_messages() { return 0; } -int create_parameters(char *service_name, char *exe, char *flags, char *dir) { +int create_parameters(nssm_service_t *service) { /* Get registry */ char registry[KEY_LENGTH]; - if (_snprintf_s(registry, sizeof(registry), _TRUNCATE, NSSM_REGISTRY, service_name) < 0) { + if (_snprintf_s(registry, sizeof(registry), _TRUNCATE, NSSM_REGISTRY, service->name) < 0) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "NSSM_REGISTRY", "create_parameters()", 0); return 1; } @@ -42,19 +42,19 @@ int create_parameters(char *service_name, char *exe, char *flags, char *dir) { } /* Try to create the parameters */ - if (RegSetValueEx(key, NSSM_REG_EXE, 0, REG_EXPAND_SZ, (const unsigned char *) exe, (unsigned long) strlen(exe) + 1) != ERROR_SUCCESS) { + if (RegSetValueEx(key, NSSM_REG_EXE, 0, REG_EXPAND_SZ, (const unsigned char *) service->exe, (unsigned long) strlen(service->exe) + 1) != ERROR_SUCCESS) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXE, error_string(GetLastError()), 0); RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY); RegCloseKey(key); return 3; } - if (RegSetValueEx(key, NSSM_REG_FLAGS, 0, REG_EXPAND_SZ, (const unsigned char *) flags, (unsigned long) strlen(flags) + 1) != ERROR_SUCCESS) { + if (RegSetValueEx(key, NSSM_REG_FLAGS, 0, REG_EXPAND_SZ, (const unsigned char *) service->flags, (unsigned long) strlen(service->flags) + 1) != ERROR_SUCCESS) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_FLAGS, error_string(GetLastError()), 0); RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY); RegCloseKey(key); return 4; } - if (RegSetValueEx(key, NSSM_REG_DIR, 0, REG_EXPAND_SZ, (const unsigned char *) dir, (unsigned long) strlen(dir) + 1) != ERROR_SUCCESS) { + if (RegSetValueEx(key, NSSM_REG_DIR, 0, REG_EXPAND_SZ, (const unsigned char *) service->dir, (unsigned long) strlen(service->dir) + 1) != ERROR_SUCCESS) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_DIR, error_string(GetLastError()), 0); RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY); RegCloseKey(key); @@ -123,6 +123,9 @@ int set_environment(char *service_name, HKEY key, char **env) { /* Probably not possible */ if (! envlen) return 0; + /* Previously initialised? */ + if (*env) HeapFree(GetProcessHeap(), 0, *env); + *env = (char *) HeapAlloc(GetProcessHeap(), 0, envlen); if (! *env) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "environment registry", "set_environment()", 0); @@ -261,12 +264,12 @@ void override_milliseconds(char *service_name, HKEY key, char *value, unsigned l if (! ok) *buffer = default_value; } -int get_parameters(char *service_name, char *exe, unsigned long exelen, char *flags, unsigned long flagslen, char *dir, unsigned long dirlen, char **env, unsigned long *throttle_delay, unsigned long *stop_method, unsigned long *kill_console_delay, unsigned long *kill_window_delay, unsigned long *kill_threads_delay, STARTUPINFO *si) { +int get_parameters(nssm_service_t *service, STARTUPINFO *si) { unsigned long ret; /* Get registry */ char registry[KEY_LENGTH]; - if (_snprintf_s(registry, sizeof(registry), _TRUNCATE, NSSM_REGISTRY, service_name) < 0) { + if (_snprintf_s(registry, sizeof(registry), _TRUNCATE, NSSM_REGISTRY, service->name) < 0) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "NSSM_REGISTRY", "get_parameters()", 0); return 1; } @@ -279,50 +282,50 @@ int get_parameters(char *service_name, char *exe, unsigned long exelen, char *fl } /* Try to get executable file - MUST succeed */ - if (expand_parameter(key, NSSM_REG_EXE, exe, exelen, false)) { + if (expand_parameter(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), false)) { RegCloseKey(key); return 3; } /* Try to get flags - may fail and we don't care */ - if (expand_parameter(key, NSSM_REG_FLAGS, flags, flagslen, false)) { - log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service_name, exe, 0); - ZeroMemory(flags, flagslen); + if (expand_parameter(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), false)) { + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0); + ZeroMemory(service->flags, sizeof(service->flags)); } /* Try to get startup directory - may fail and we fall back to a default */ - if (expand_parameter(key, NSSM_REG_DIR, dir, dirlen, true) || ! dir[0]) { + if (expand_parameter(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), true) || ! service->dir[0]) { /* Our buffers are defined to be long enough for this to be safe */ size_t i; - for (i = strlen(exe); i && exe[i] != '\\' && exe[i] != '/'; i--); + for (i = strlen(service->exe); i && service->exe[i] != '\\' && service->exe[i] != '/'; i--); if (i) { - memmove(dir, exe, i); - dir[i] = '\0'; + memmove(service->dir, service->exe, i); + service->dir[i] = '\0'; } else { /* Help! */ - ret = GetWindowsDirectory(dir, dirlen); - if (! ret || ret > dirlen) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service_name, 0); + ret = GetWindowsDirectory(service->dir, sizeof(service->dir)); + if (! ret || ret > sizeof(service->dir)) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0); RegCloseKey(key); return 4; } } - log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service_name, dir, 0); + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0); } /* Try to get environment variables - may fail */ - set_environment(service_name, key, env); + set_environment(service->name, key, &service->env); /* Try to get stdout and stderr */ if (get_output_handles(key, si)) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service_name, 0); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0); RegCloseKey(key); return 5; } /* Try to get throttle restart delay */ - override_milliseconds(service_name, key, NSSM_REG_THROTTLE, throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE); + override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE); /* Try to get service stop flags. */ unsigned long type = REG_DWORD; @@ -333,7 +336,7 @@ int get_parameters(char *service_name, char *exe, unsigned long exelen, char *fl if (ret != ERROR_SUCCESS) { if (ret != ERROR_FILE_NOT_FOUND) { if (type != REG_DWORD) { - log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service_name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0); + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0); } else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0); } @@ -341,13 +344,13 @@ int get_parameters(char *service_name, char *exe, unsigned long exelen, char *fl else stop_ok = true; /* Try all methods except those requested to be skipped. */ - *stop_method = ~0; - if (stop_ok) *stop_method &= ~stop_method_skip; + service->stop_method = ~0; + if (stop_ok) service->stop_method &= ~stop_method_skip; /* Try to get kill delays - may fail. */ - override_milliseconds(service_name, key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, kill_console_delay, NSSM_KILL_CONSOLE_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_CONSOLE_GRACE_PERIOD); - override_milliseconds(service_name, key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, kill_window_delay, NSSM_KILL_WINDOW_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_WINDOW_GRACE_PERIOD); - override_milliseconds(service_name, key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, kill_threads_delay, NSSM_KILL_THREADS_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_THREADS_GRACE_PERIOD); + override_milliseconds(service->name, key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, &service->kill_console_delay, NSSM_KILL_CONSOLE_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_CONSOLE_GRACE_PERIOD); + override_milliseconds(service->name, key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, &service->kill_window_delay, NSSM_KILL_WINDOW_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_WINDOW_GRACE_PERIOD); + override_milliseconds(service->name, key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, &service->kill_threads_delay, NSSM_KILL_THREADS_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_THREADS_GRACE_PERIOD); /* Close registry */ RegCloseKey(key); diff --git a/registry.h b/registry.h index 5d42792..0661b73 100644 --- a/registry.h +++ b/registry.h @@ -21,7 +21,7 @@ #define NSSM_STDIO_LENGTH 29 int create_messages(); -int create_parameters(char *, char *, char *, char *); +int create_parameters(nssm_service_t *); int create_exit_action(char *, const char *); int set_environment(char *, HKEY, char **); int expand_parameter(HKEY, char *, char *, unsigned long, bool, bool); @@ -31,7 +31,7 @@ int set_number(HKEY, char *, unsigned long); int get_number(HKEY, char *, unsigned long *, bool); int get_number(HKEY, char *, unsigned long *); void override_milliseconds(char *, HKEY, char *, unsigned long *, unsigned long, unsigned long); -int get_parameters(char *, char *, unsigned long, char *, unsigned long, char *, unsigned long, char **, unsigned long *, unsigned long *, unsigned long *, unsigned long *, unsigned long *, STARTUPINFO *); +int get_parameters(nssm_service_t *, STARTUPINFO *); int get_exit_action(char *, unsigned long *, unsigned char *, bool *); #endif diff --git a/service.cpp b/service.cpp index be70501..dffd7aa 100644 --- a/service.cpp +++ b/service.cpp @@ -1,37 +1,14 @@ #include "nssm.h" bool is_admin; -SERVICE_STATUS service_status; -SERVICE_STATUS_HANDLE service_handle; -HANDLE process_handle; -HANDLE wait_handle; -unsigned long pid; -static char service_name[SERVICE_NAME_LENGTH]; -char exe[EXE_LENGTH]; -char flags[CMD_LENGTH]; -char dir[MAX_PATH]; -bool stopping; -bool allow_restart; -unsigned long throttle_delay; -unsigned long stop_method; -unsigned long kill_console_delay; -unsigned long kill_window_delay; -unsigned long kill_threads_delay; -CRITICAL_SECTION throttle_section; -CONDITION_VARIABLE throttle_condition; -HANDLE throttle_timer; -LARGE_INTEGER throttle_duetime; bool use_critical_section; -FILETIME creation_time; extern imports_t imports; static enum { NSSM_EXIT_RESTART, NSSM_EXIT_IGNORE, NSSM_EXIT_REALLY, NSSM_EXIT_UNCLEAN } exit_actions; static const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", "Suicide", 0 }; -static unsigned long throttle; - -static inline int throttle_milliseconds() { +static inline int throttle_milliseconds(unsigned long throttle) { /* pow() operates on doubles. */ int ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2; return ret * 1000; @@ -42,7 +19,7 @@ static inline int throttle_milliseconds() { control immediately. */ static unsigned long WINAPI shutdown_service(void *arg) { - return stop_service(0, true, true); + return stop_service((nssm_service_t *) arg, 0, true, true); } /* Connect to the service manager */ @@ -56,26 +33,46 @@ SC_HANDLE open_service_manager() { return ret; } +/* Allocate and zero memory for a service. */ +nssm_service_t *alloc_nssm_service() { + nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t)); + if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service", "alloc_nssm_service()", 0); + return service; +} + +/* Free memory for a service. */ +void cleanup_nssm_service(nssm_service_t *service) { + if (! service) return; + if (service->env) HeapFree(GetProcessHeap(), 0, service->env); + if (service->handle) CloseServiceHandle(service->handle); + if (service->process_handle) CloseHandle(service->process_handle); + if (service->wait_handle) UnregisterWait(service->process_handle); + if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section); + if (service->throttle_timer) CloseHandle(service->throttle_timer); + HeapFree(GetProcessHeap(), 0, service); +} + /* About to install the service */ int pre_install_service(int argc, char **argv) { /* Show the dialogue box if we didn't give the service name and path */ if (argc < 2) return nssm_gui(IDD_INSTALL, argv[0]); + nssm_service_t *service = alloc_nssm_service(); + if (! service) { + print_message(stderr, NSSM_EVENT_OUT_OF_MEMORY, "service", "pre_install_service()"); + return 1; + } + + memmove(service->name, argv[0], strlen(argv[0])); + memmove(service->exe, argv[1], strlen(argv[1])); + /* Arguments are optional */ - char *flags; size_t flagslen = 0; size_t s = 0; - int i; + size_t i; for (i = 2; i < argc; i++) flagslen += strlen(argv[i]) + 1; if (! flagslen) flagslen = 1; - flags = (char *) HeapAlloc(GetProcessHeap(), 0, flagslen); - if (! flags) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "flags", "pre_install_service()", 0); - return 2; - } - ZeroMemory(flags, flagslen); - /* This probably isn't UTF8-safe and should use std::string or something but it's been broken for the best part of a decade and due for a rewrite @@ -84,29 +81,46 @@ int pre_install_service(int argc, char **argv) { */ for (i = 2; i < argc; i++) { size_t len = strlen(argv[i]); - memmove(flags + s, argv[i], len); + memmove(service->flags + s, argv[i], len); s += len; - if (i < argc - 1) flags[s++] = ' '; + if (i < argc - 1) service->flags[s++] = ' '; } - return install_service(argv[0], argv[1], flags); + /* Work out directory name */ + size_t len = strlen(service->exe); + for (i = len; i && service->exe[i] != '\\' && service->exe[i] != '/'; i--); + memmove(service->dir, service->exe, i); + service->dir[i] = '\0'; + + int ret = install_service(service); + cleanup_nssm_service(service); + return ret; } /* About to remove the service */ int pre_remove_service(int argc, char **argv) { /* Show dialogue box if we didn't pass service name and "confirm" */ if (argc < 2) return nssm_gui(IDD_REMOVE, argv[0]); - if (str_equiv(argv[1], "confirm")) return remove_service(argv[0]); + if (str_equiv(argv[1], "confirm")) { + nssm_service_t *service = alloc_nssm_service(); + memmove(service->name, argv[0], strlen(argv[0])); + int ret = remove_service(service); + cleanup_nssm_service(service); + return ret; + } print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE); return 100; } /* Install the service */ -int install_service(char *name, char *exe, char *flags) { +int install_service(nssm_service_t *service) { + if (! service) return 1; + /* Open service manager */ SC_HANDLE services = open_service_manager(); if (! services) { print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED); + cleanup_nssm_service(service); return 2; } @@ -126,42 +140,36 @@ int install_service(char *name, char *exe, char *flags) { return 4; } - /* Work out directory name */ - size_t len = strlen(exe); - size_t i; - for (i = len; i && exe[i] != '\\' && exe[i] != '/'; i--); - char dir[MAX_PATH]; - memmove(dir, exe, i); - dir[i] = '\0'; - /* Create the service */ - SC_HANDLE service = CreateService(services, name, name, SC_MANAGER_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, command, 0, 0, 0, 0, 0); - if (! service) { + service->handle = CreateService(services, service->name, service->name, SC_MANAGER_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, command, 0, 0, 0, 0, 0); + if (! service->handle) { print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED); CloseServiceHandle(services); return 5; } /* Now we need to put the parameters into the registry */ - if (create_parameters(name, exe, flags, dir)) { + if (create_parameters(service)) { print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED); - DeleteService(service); + DeleteService(service->handle); CloseServiceHandle(services); return 6; } - set_service_recovery(service, name); + set_service_recovery(service); + + print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name); /* Cleanup */ - CloseServiceHandle(service); CloseServiceHandle(services); - print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, name); return 0; } /* Remove the service */ -int remove_service(char *name) { +int remove_service(nssm_service_t *service) { + if (! service) return 1; + /* Open service manager */ SC_HANDLE services = open_service_manager(); if (! services) { @@ -170,33 +178,34 @@ int remove_service(char *name) { } /* Try to open the service */ - SC_HANDLE service = OpenService(services, name, SC_MANAGER_ALL_ACCESS); - if (! service) { + service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS); + if (! service->handle) { print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED); CloseServiceHandle(services); return 3; } /* Try to delete the service */ - if (! DeleteService(service)) { + if (! DeleteService(service->handle)) { print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED); - CloseServiceHandle(service); CloseServiceHandle(services); return 4; } /* Cleanup */ - CloseServiceHandle(service); CloseServiceHandle(services); - print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, name); + print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name); return 0; } /* Service initialisation */ void WINAPI service_main(unsigned long argc, char **argv) { - if (_snprintf_s(service_name, sizeof(service_name), _TRUNCATE, "%s", argv[0]) < 0) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service_name", "service_main()", 0); + nssm_service_t *service = alloc_nssm_service(); + if (! service) return; + + if (_snprintf_s(service->name, sizeof(service->name), _TRUNCATE, "%s", argv[0]) < 0) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service->name", "service_main()", 0); return; } @@ -205,95 +214,88 @@ void WINAPI service_main(unsigned long argc, char **argv) { else use_critical_section = false; /* Initialise status */ - ZeroMemory(&service_status, sizeof(service_status)); - service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS; - service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE; - service_status.dwWin32ExitCode = NO_ERROR; - service_status.dwServiceSpecificExitCode = 0; - service_status.dwCheckPoint = 0; - service_status.dwWaitHint = NSSM_WAITHINT_MARGIN; + ZeroMemory(&service->status, sizeof(service->status)); + service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS; + service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE; + service->status.dwWin32ExitCode = NO_ERROR; + service->status.dwServiceSpecificExitCode = 0; + service->status.dwCheckPoint = 0; + service->status.dwWaitHint = NSSM_WAITHINT_MARGIN; /* Signal we AREN'T running the server */ - process_handle = 0; - pid = 0; + service->process_handle = 0; + service->pid = 0; /* Register control handler */ - service_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, 0); - if (! service_handle) { + service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service); + if (! service->status_handle) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0); return; } - log_service_control(service_name, 0, true); + log_service_control(service->name, 0, true); - service_status.dwCurrentState = SERVICE_START_PENDING; - service_status.dwWaitHint = throttle_delay + NSSM_WAITHINT_MARGIN; - SetServiceStatus(service_handle, &service_status); + service->status.dwCurrentState = SERVICE_START_PENDING; + service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN; + SetServiceStatus(service->status_handle, &service->status); if (is_admin) { /* Try to create the exit action parameters; we don't care if it fails */ - create_exit_action(argv[0], exit_action_strings[0]); + create_exit_action(service->name, exit_action_strings[0]); - set_service_recovery(0, service_name); + SC_HANDLE services = open_service_manager(); + if (services) { + service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS); + set_service_recovery(service); + CloseServiceHandle(services); + } } /* Used for signalling a resume if the service pauses when throttled. */ - if (use_critical_section) InitializeCriticalSection(&throttle_section); + if (use_critical_section) { + InitializeCriticalSection(&service->throttle_section); + service->throttle_section_initialised = true; + } else { - throttle_timer = CreateWaitableTimer(0, 1, 0); - if (! throttle_timer) { - log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service_name, error_string(GetLastError()), 0); + service->throttle_timer = CreateWaitableTimer(0, 1, 0); + if (! service->throttle_timer) { + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0); } } - monitor_service(); + monitor_service(service); } /* Make sure service recovery actions are taken where necessary */ -void set_service_recovery(SC_HANDLE service, char *service_name) { - SC_HANDLE services = 0; - - if (! service) { - services = open_service_manager(); - if (! services) return; - - service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS); - if (! service) return; - } - +void set_service_recovery(nssm_service_t *service) { SERVICE_FAILURE_ACTIONS_FLAG flag; ZeroMemory(&flag, sizeof(flag)); flag.fFailureActionsOnNonCrashFailures = true; /* This functionality was added in Vista so the call may fail */ - if (! ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) { + if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) { unsigned long error = GetLastError(); /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */ if (error != ERROR_INVALID_LEVEL) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CHANGESERVICECONFIG2_FAILED, service_name, error_string(error), 0); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CHANGESERVICECONFIG2_FAILED, service->name, error_string(error), 0); } } - - if (services) { - CloseServiceHandle(service); - CloseServiceHandle(services); - } } -int monitor_service() { +int monitor_service(nssm_service_t *service) { /* Set service status to started */ - int ret = start_service(); + int ret = start_service(service); if (ret) { char code[16]; _snprintf_s(code, sizeof(code), _TRUNCATE, "%d", ret); - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0); return ret; } - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0); + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0); /* Monitor service */ - if (! RegisterWaitForSingleObject(&wait_handle, process_handle, end_service, (void *) pid, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) { - log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, error_string(GetLastError()), 0); + if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) { + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0); } return 0; @@ -343,6 +345,8 @@ void log_service_control(char *service_name, unsigned long control, bool handled /* Service control handler */ unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) { + nssm_service_t *service = (nssm_service_t *) context; + switch (control) { case SERVICE_CONTROL_INTERROGATE: /* We always keep the service status up-to-date so this is a no-op. */ @@ -350,40 +354,40 @@ unsigned long WINAPI service_control_handler(unsigned long control, unsigned lon case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: - log_service_control(service_name, control, true); + log_service_control(service->name, control, true); /* We MUST acknowledge the stop request promptly but we're committed to waiting for the application to exit. Spawn a new thread to wait while we acknowledge the request. */ - if (! CreateThread(NULL, 0, shutdown_service, (void *) service_name, 0, NULL)) { + if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0); /* We couldn't create a thread to tidy up so we'll have to force the tidyup to complete in time in this thread. */ - kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD; - kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD; - kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD; + service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD; + service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD; + service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD; - stop_service(0, true, true); + stop_service(service, 0, true, true); } return NO_ERROR; case SERVICE_CONTROL_CONTINUE: - log_service_control(service_name, control, true); - throttle = 0; - if (use_critical_section) imports.WakeConditionVariable(&throttle_condition); + log_service_control(service->name, control, true); + service->throttle = 0; + if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition); else { - if (! throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED; - ZeroMemory(&throttle_duetime, sizeof(throttle_duetime)); - SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0); + if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED; + ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime)); + SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0); } - service_status.dwCurrentState = SERVICE_CONTINUE_PENDING; - service_status.dwWaitHint = throttle_milliseconds() + NSSM_WAITHINT_MARGIN; - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service_name, 0); - SetServiceStatus(service_handle, &service_status); + service->status.dwCurrentState = SERVICE_CONTINUE_PENDING; + service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN; + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0); + SetServiceStatus(service->status_handle, &service->status); return NO_ERROR; case SERVICE_CONTROL_PAUSE: @@ -391,21 +395,21 @@ unsigned long WINAPI service_control_handler(unsigned long control, unsigned lon We don't accept pause messages but it isn't possible to register only for continue messages so we have to handle this case. */ - log_service_control(service_name, control, false); + log_service_control(service->name, control, false); return ERROR_CALL_NOT_IMPLEMENTED; } /* Unknown control */ - log_service_control(service_name, control, false); + log_service_control(service->name, control, false); return ERROR_CALL_NOT_IMPLEMENTED; } /* Start the service */ -int start_service() { - stopping = false; - allow_restart = true; +int start_service(nssm_service_t *service) { + service->stopping = false; + service->allow_restart = true; - if (process_handle) return 0; + if (service->process_handle) return 0; /* Allocate a STARTUPINFO structure for a new process */ STARTUPINFO si; @@ -417,36 +421,35 @@ int start_service() { ZeroMemory(&pi, sizeof(pi)); /* Get startup parameters */ - char *env = 0; - int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay, &stop_method, &kill_console_delay, &kill_window_delay, &kill_threads_delay, &si); + int ret = get_parameters(service, &si); if (ret) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0); - return stop_service(2, true, true); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0); + return stop_service(service, 2, true, true); } /* Launch executable with arguments */ char cmd[CMD_LENGTH]; - if (_snprintf_s(cmd, sizeof(cmd), _TRUNCATE, "\"%s\" %s", exe, flags) < 0) { + if (_snprintf_s(cmd, sizeof(cmd), _TRUNCATE, "\"%s\" %s", service->exe, service->flags) < 0) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0); close_output_handles(&si); - return stop_service(2, true, true); + return stop_service(service, 2, true, true); } - throttle_restart(); + throttle_restart(service); bool inherit_handles = false; if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true; - if (! CreateProcess(0, cmd, 0, 0, inherit_handles, 0, env, dir, &si, &pi)) { + if (! CreateProcess(0, cmd, 0, 0, inherit_handles, 0, service->env, service->dir, &si, &pi)) { unsigned long error = GetLastError(); - if (error == ERROR_INVALID_PARAMETER && env) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service_name, exe, NSSM_REG_ENV, 0); - else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(error), 0); + if (error == ERROR_INVALID_PARAMETER && service->env) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service->name, service->exe, NSSM_REG_ENV, 0); + else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0); close_output_handles(&si); - return stop_service(3, true, true); + return stop_service(service, 3, true, true); } - process_handle = pi.hProcess; - pid = pi.dwProcessId; + service->process_handle = pi.hProcess; + service->pid = pi.dwProcessId; - if (get_process_creation_time(process_handle, &creation_time)) ZeroMemory(&creation_time, sizeof(creation_time)); + if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time)); close_output_handles(&si); @@ -455,71 +458,74 @@ int start_service() { but be mindful of the fact that we are blocking the service control manager so abandon the wait before too much time has elapsed. */ - unsigned long delay = throttle_delay; + unsigned long delay = service->throttle_delay; if (delay > NSSM_SERVICE_STATUS_DEADLINE) { char delay_milliseconds[16]; _snprintf_s(delay_milliseconds, sizeof(delay_milliseconds), _TRUNCATE, "%lu", delay); char deadline_milliseconds[16]; _snprintf_s(deadline_milliseconds, sizeof(deadline_milliseconds), _TRUNCATE, "%lu", NSSM_SERVICE_STATUS_DEADLINE); - log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service_name, delay_milliseconds, NSSM, deadline_milliseconds, 0); + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0); delay = NSSM_SERVICE_STATUS_DEADLINE; } - unsigned long deadline = WaitForSingleObject(process_handle, delay); + unsigned long deadline = WaitForSingleObject(service->process_handle, delay); /* Signal successful start */ - service_status.dwCurrentState = SERVICE_RUNNING; - SetServiceStatus(service_handle, &service_status); + service->status.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus(service->status_handle, &service->status); /* Continue waiting for a clean startup. */ if (deadline == WAIT_TIMEOUT) { - if (throttle_delay > delay) { - if (WaitForSingleObject(process_handle, throttle_delay - delay) == WAIT_TIMEOUT) throttle = 0; + if (service->throttle_delay > delay) { + if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0; } - else throttle = 0; + else service->throttle = 0; } return 0; } /* Stop the service */ -int stop_service(unsigned long exitcode, bool graceful, bool default_action) { - allow_restart = false; - if (wait_handle) UnregisterWait(wait_handle); +int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) { + service->allow_restart = false; + if (service->wait_handle) { + UnregisterWait(service->wait_handle); + service->wait_handle = 0; + } if (default_action && ! exitcode && ! graceful) { - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_GRACEFUL_SUICIDE, service_name, exe, exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_REALLY] ,0); + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_GRACEFUL_SUICIDE, service->name, service->exe, exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_REALLY] ,0); graceful = true; } /* Signal we are stopping */ if (graceful) { - service_status.dwCurrentState = SERVICE_STOP_PENDING; - service_status.dwWaitHint = NSSM_WAITHINT_MARGIN; - SetServiceStatus(service_handle, &service_status); + service->status.dwCurrentState = SERVICE_STOP_PENDING; + service->status.dwWaitHint = NSSM_WAITHINT_MARGIN; + SetServiceStatus(service->status_handle, &service->status); } /* Nothing to do if service isn't running */ - if (pid) { + if (service->pid) { /* Shut down service */ - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0); - kill_process(service_name, service_handle, &service_status, stop_method, process_handle, pid, 0); + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0); + kill_process(service, service->process_handle, service->pid, 0); } - else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0); + else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0); - end_service((void *) pid, true); + end_service((void *) service, true); /* Signal we stopped */ if (graceful) { - service_status.dwCurrentState = SERVICE_STOPPED; + service->status.dwCurrentState = SERVICE_STOPPED; if (exitcode) { - service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; - service_status.dwServiceSpecificExitCode = exitcode; + service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + service->status.dwServiceSpecificExitCode = exitcode; } else { - service_status.dwWin32ExitCode = NO_ERROR; - service_status.dwServiceSpecificExitCode = 0; + service->status.dwWin32ExitCode = NO_ERROR; + service->status.dwServiceSpecificExitCode = 0; } - SetServiceStatus(service_handle, &service_status); + SetServiceStatus(service->status_handle, &service->status); } return exitcode; @@ -527,19 +533,21 @@ int stop_service(unsigned long exitcode, bool graceful, bool default_action) { /* Callback function triggered when the server exits */ void CALLBACK end_service(void *arg, unsigned char why) { - if (stopping) return; + nssm_service_t *service = (nssm_service_t *) arg; - stopping = true; + if (service->stopping) return; - pid = (unsigned long) arg; + service->stopping = true; /* Check exit code */ unsigned long exitcode = 0; char code[16]; - FILETIME exit_time; - GetExitCodeProcess(process_handle, &exitcode); - if (exitcode == STILL_ACTIVE || get_process_exit_time(process_handle, &exit_time)) GetSystemTimeAsFileTime(&exit_time); - CloseHandle(process_handle); + GetExitCodeProcess(service->process_handle, &exitcode); + if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time); + CloseHandle(service->process_handle); + + service->process_handle = 0; + service->pid = 0; /* Log that the service ended BEFORE logging about killing the process @@ -547,12 +555,12 @@ void CALLBACK end_service(void *arg, unsigned char why) { */ if (! why) { _snprintf_s(code, sizeof(code), _TRUNCATE, "%lu", exitcode); - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0); + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0); } /* Clean up. */ if (exitcode == STILL_ACTIVE) exitcode = 0; - kill_process_tree(service_name, service_handle, &service_status, stop_method, pid, exitcode, pid, &creation_time, &exit_time); + kill_process_tree(service, service->pid, exitcode, service->pid); /* The why argument is true if our wait timed out or false otherwise. @@ -561,13 +569,13 @@ void CALLBACK end_service(void *arg, unsigned char why) { this is a controlled shutdown, and don't take any restart action. */ if (why) return; - if (! allow_restart) return; + if (! service->allow_restart) return; /* What action should we take? */ int action = NSSM_EXIT_RESTART; unsigned char action_string[ACTION_LEN]; bool default_action; - if (! get_exit_action(service_name, &exitcode, action_string, &default_action)) { + if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) { for (int i = 0; exit_action_strings[i]; i++) { if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) { action = i; @@ -576,69 +584,67 @@ void CALLBACK end_service(void *arg, unsigned char why) { } } - process_handle = 0; - pid = 0; switch (action) { /* Try to restart the service or return failure code to service manager */ case NSSM_EXIT_RESTART: - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service_name, code, exit_action_strings[action], exe, 0); - while (monitor_service()) { - log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, exe, service_name, 0); + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0); + while (monitor_service(service)) { + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0); Sleep(30000); } break; /* Do nothing, just like srvany would */ case NSSM_EXIT_IGNORE: - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service_name, code, exit_action_strings[action], exe, 0); + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0); Sleep(INFINITE); break; /* Tell the service manager we are finished */ case NSSM_EXIT_REALLY: - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0); - stop_service(exitcode, true, default_action); + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0); + stop_service(service, exitcode, true, default_action); break; /* Fake a crash so pre-Vista service managers will run recovery actions. */ case NSSM_EXIT_UNCLEAN: - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0); - stop_service(exitcode, false, default_action); + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0); + stop_service(service, exitcode, false, default_action); free_imports(); exit(exitcode); break; } } -void throttle_restart() { +void throttle_restart(nssm_service_t *service) { /* This can't be a restart if the service is already running. */ - if (! throttle++) return; + if (! service->throttle++) return; - int ms = throttle_milliseconds(); + int ms = throttle_milliseconds(service->throttle); - if (throttle > 7) throttle = 8; + if (service->throttle > 7) service->throttle = 8; char threshold[8], milliseconds[8]; - _snprintf_s(threshold, sizeof(threshold), _TRUNCATE, "%lu", throttle_delay); + _snprintf_s(threshold, sizeof(threshold), _TRUNCATE, "%lu", service->throttle_delay); _snprintf_s(milliseconds, sizeof(milliseconds), _TRUNCATE, "%lu", ms); - log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service_name, threshold, milliseconds, 0); + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0); - if (use_critical_section) EnterCriticalSection(&throttle_section); - else if (throttle_timer) { - ZeroMemory(&throttle_duetime, sizeof(throttle_duetime)); - throttle_duetime.QuadPart = 0 - (ms * 10000LL); - SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0); + if (use_critical_section) EnterCriticalSection(&service->throttle_section); + else if (service->throttle_timer) { + ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime)); + service->throttle_duetime.QuadPart = 0 - (ms * 10000LL); + SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0); } - service_status.dwCurrentState = SERVICE_PAUSED; - SetServiceStatus(service_handle, &service_status); + service->status.dwCurrentState = SERVICE_PAUSED; + SetServiceStatus(service->status_handle, &service->status); if (use_critical_section) { - imports.SleepConditionVariableCS(&throttle_condition, &throttle_section, ms); - LeaveCriticalSection(&throttle_section); + imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms); + LeaveCriticalSection(&service->throttle_section); } else { - if (throttle_timer) WaitForSingleObject(throttle_timer, INFINITE); + if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE); else Sleep(ms); } } @@ -671,7 +677,7 @@ void throttle_restart() { 0 if the wait completed. -1 on error. */ -int await_shutdown(char *function_name, char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVICE_STATUS *service_status, HANDLE process_handle, unsigned long timeout) { +int await_shutdown(nssm_service_t *service, char *function_name, unsigned long timeout) { unsigned long interval; unsigned long waithint; unsigned long ret; @@ -690,24 +696,24 @@ int await_shutdown(char *function_name, char *service_name, SERVICE_STATUS_HANDL _snprintf_s(timeout_milliseconds, sizeof(timeout_milliseconds), _TRUNCATE, "%lu", timeout); - waithint = service_status->dwWaitHint; + waithint = service->status.dwWaitHint; waited = 0; while (waited < timeout) { interval = timeout - waited; if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE; - service_status->dwCurrentState = SERVICE_STOP_PENDING; - service_status->dwWaitHint += interval; - service_status->dwCheckPoint++; - SetServiceStatus(service_handle, service_status); + service->status.dwCurrentState = SERVICE_STOP_PENDING; + service->status.dwWaitHint += interval; + service->status.dwCheckPoint++; + SetServiceStatus(service->status_handle, &service->status); if (waited) { _snprintf_s(waited_milliseconds, sizeof(waited_milliseconds), _TRUNCATE, "%lu", waited); _snprintf_s(interval_milliseconds, sizeof(interval_milliseconds), _TRUNCATE, "%lu", interval); - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service_name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0); + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0); } - switch (WaitForSingleObject(process_handle, interval)) { + switch (WaitForSingleObject(service->process_handle, interval)) { case WAIT_OBJECT_0: ret = 0; goto awaited; diff --git a/service.h b/service.h index 49760ba..994a7ce 100644 --- a/service.h +++ b/service.h @@ -1,24 +1,68 @@ #ifndef SERVICE_H #define SERVICE_H +/* + MSDN says the commandline in CreateProcess() is limited to 32768 characters + and the application name to MAX_PATH. + A registry key is limited to 255 characters. + A registry value is limited to 16383 characters. + Therefore we limit the service name to accommodate the path under HKLM. +*/ +#define EXE_LENGTH MAX_PATH +#define CMD_LENGTH 32768 +#define KEY_LENGTH 255 +#define VALUE_LENGTH 16383 +#define SERVICE_NAME_LENGTH KEY_LENGTH - 55 + #define ACTION_LEN 16 +typedef struct { + char name[SERVICE_NAME_LENGTH]; + char exe[EXE_LENGTH]; + char flags[VALUE_LENGTH]; + char dir[MAX_PATH]; + char *env; + unsigned long throttle_delay; + unsigned long stop_method; + unsigned long kill_console_delay; + unsigned long kill_window_delay; + unsigned long kill_threads_delay; + SC_HANDLE handle; + SERVICE_STATUS status; + SERVICE_STATUS_HANDLE status_handle; + HANDLE process_handle; + unsigned long pid; + HANDLE wait_handle; + bool stopping; + bool allow_restart; + unsigned long throttle; + CRITICAL_SECTION throttle_section; + bool throttle_section_initialised; + CONDITION_VARIABLE throttle_condition; + HANDLE throttle_timer; + LARGE_INTEGER throttle_duetime; + FILETIME creation_time; + FILETIME exit_time; +} nssm_service_t; + void WINAPI service_main(unsigned long, char **); char *service_control_text(unsigned long); void log_service_control(char *, unsigned long, bool); unsigned long WINAPI service_control_handler(unsigned long, unsigned long, void *, void *); +nssm_service_t *alloc_nssm_service(); +void cleanup_nssm_service(nssm_service_t *); SC_HANDLE open_service_manager(); int pre_install_service(int, char **); int pre_remove_service(int, char **); -int install_service(char *, char *, char *); -int remove_service(char *); -void set_service_recovery(SC_HANDLE, char *); -int monitor_service(); -int start_service(); -int stop_service(unsigned long, bool, bool); +int install_service(nssm_service_t *); +int remove_service(nssm_service_t *); +void set_service_recovery(nssm_service_t *); +int monitor_service(nssm_service_t *); +int start_service(nssm_service_t *); +int stop_service(nssm_service_t *, unsigned long, bool, bool); void CALLBACK end_service(void *, unsigned char); -void throttle_restart(); -int await_shutdown(char *, char *, SERVICE_STATUS_HANDLE, SERVICE_STATUS *, HANDLE, unsigned long); +void throttle_restart(nssm_service_t *); +int await_shutdown(nssm_service_t *, char *, unsigned long); #endif