#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;
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;
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;
}
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;
}
}
/* 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;
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;
}
/*
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;
}
}
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) {
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;
}
}
/* 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;
}
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);
/* 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);
}
}
return 0;\r
}\r
\r
-int create_parameters(char *service_name, char *exe, char *flags, char *dir) {\r
+int create_parameters(nssm_service_t *service) {\r
/* Get registry */\r
char registry[KEY_LENGTH];\r
- if (_snprintf_s(registry, sizeof(registry), _TRUNCATE, NSSM_REGISTRY, service_name) < 0) {\r
+ if (_snprintf_s(registry, sizeof(registry), _TRUNCATE, NSSM_REGISTRY, service->name) < 0) {\r
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "NSSM_REGISTRY", "create_parameters()", 0);\r
return 1;\r
}\r
}\r
\r
/* Try to create the parameters */\r
- if (RegSetValueEx(key, NSSM_REG_EXE, 0, REG_EXPAND_SZ, (const unsigned char *) exe, (unsigned long) strlen(exe) + 1) != ERROR_SUCCESS) {\r
+ if (RegSetValueEx(key, NSSM_REG_EXE, 0, REG_EXPAND_SZ, (const unsigned char *) service->exe, (unsigned long) strlen(service->exe) + 1) != ERROR_SUCCESS) {\r
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXE, error_string(GetLastError()), 0);\r
RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);\r
RegCloseKey(key);\r
return 3;\r
}\r
- if (RegSetValueEx(key, NSSM_REG_FLAGS, 0, REG_EXPAND_SZ, (const unsigned char *) flags, (unsigned long) strlen(flags) + 1) != ERROR_SUCCESS) {\r
+ if (RegSetValueEx(key, NSSM_REG_FLAGS, 0, REG_EXPAND_SZ, (const unsigned char *) service->flags, (unsigned long) strlen(service->flags) + 1) != ERROR_SUCCESS) {\r
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_FLAGS, error_string(GetLastError()), 0);\r
RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);\r
RegCloseKey(key);\r
return 4;\r
}\r
- if (RegSetValueEx(key, NSSM_REG_DIR, 0, REG_EXPAND_SZ, (const unsigned char *) dir, (unsigned long) strlen(dir) + 1) != ERROR_SUCCESS) {\r
+ if (RegSetValueEx(key, NSSM_REG_DIR, 0, REG_EXPAND_SZ, (const unsigned char *) service->dir, (unsigned long) strlen(service->dir) + 1) != ERROR_SUCCESS) {\r
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_DIR, error_string(GetLastError()), 0);\r
RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);\r
RegCloseKey(key);\r
/* Probably not possible */\r
if (! envlen) return 0;\r
\r
+ /* Previously initialised? */\r
+ if (*env) HeapFree(GetProcessHeap(), 0, *env);\r
+\r
*env = (char *) HeapAlloc(GetProcessHeap(), 0, envlen);\r
if (! *env) {\r
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "environment registry", "set_environment()", 0);\r
if (! ok) *buffer = default_value;\r
}\r
\r
-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) {\r
+int get_parameters(nssm_service_t *service, STARTUPINFO *si) {\r
unsigned long ret;\r
\r
/* Get registry */\r
char registry[KEY_LENGTH];\r
- if (_snprintf_s(registry, sizeof(registry), _TRUNCATE, NSSM_REGISTRY, service_name) < 0) {\r
+ if (_snprintf_s(registry, sizeof(registry), _TRUNCATE, NSSM_REGISTRY, service->name) < 0) {\r
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "NSSM_REGISTRY", "get_parameters()", 0);\r
return 1;\r
}\r
}\r
\r
/* Try to get executable file - MUST succeed */\r
- if (expand_parameter(key, NSSM_REG_EXE, exe, exelen, false)) {\r
+ if (expand_parameter(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), false)) {\r
RegCloseKey(key);\r
return 3;\r
}\r
\r
/* Try to get flags - may fail and we don't care */\r
- if (expand_parameter(key, NSSM_REG_FLAGS, flags, flagslen, false)) {\r
- log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service_name, exe, 0);\r
- ZeroMemory(flags, flagslen);\r
+ if (expand_parameter(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), false)) {\r
+ log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);\r
+ ZeroMemory(service->flags, sizeof(service->flags));\r
}\r
\r
/* Try to get startup directory - may fail and we fall back to a default */\r
- if (expand_parameter(key, NSSM_REG_DIR, dir, dirlen, true) || ! dir[0]) {\r
+ if (expand_parameter(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), true) || ! service->dir[0]) {\r
/* Our buffers are defined to be long enough for this to be safe */\r
size_t i;\r
- for (i = strlen(exe); i && exe[i] != '\\' && exe[i] != '/'; i--);\r
+ for (i = strlen(service->exe); i && service->exe[i] != '\\' && service->exe[i] != '/'; i--);\r
if (i) {\r
- memmove(dir, exe, i);\r
- dir[i] = '\0';\r
+ memmove(service->dir, service->exe, i);\r
+ service->dir[i] = '\0';\r
}\r
else {\r
/* Help! */\r
- ret = GetWindowsDirectory(dir, dirlen);\r
- if (! ret || ret > dirlen) {\r
- log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service_name, 0);\r
+ ret = GetWindowsDirectory(service->dir, sizeof(service->dir));\r
+ if (! ret || ret > sizeof(service->dir)) {\r
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);\r
RegCloseKey(key);\r
return 4;\r
}\r
}\r
- log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service_name, dir, 0);\r
+ log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);\r
}\r
\r
/* Try to get environment variables - may fail */\r
- set_environment(service_name, key, env);\r
+ set_environment(service->name, key, &service->env);\r
\r
/* Try to get stdout and stderr */\r
if (get_output_handles(key, si)) {\r
- log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service_name, 0);\r
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);\r
RegCloseKey(key);\r
return 5;\r
}\r
\r
/* Try to get throttle restart delay */\r
- override_milliseconds(service_name, key, NSSM_REG_THROTTLE, throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);\r
+ override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);\r
\r
/* Try to get service stop flags. */\r
unsigned long type = REG_DWORD;\r
if (ret != ERROR_SUCCESS) {\r
if (ret != ERROR_FILE_NOT_FOUND) {\r
if (type != REG_DWORD) {\r
- log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service_name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);\r
+ log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);\r
}\r
else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);\r
}\r
else stop_ok = true;\r
\r
/* Try all methods except those requested to be skipped. */\r
- *stop_method = ~0;\r
- if (stop_ok) *stop_method &= ~stop_method_skip;\r
+ service->stop_method = ~0;\r
+ if (stop_ok) service->stop_method &= ~stop_method_skip;\r
\r
/* Try to get kill delays - may fail. */\r
- 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);\r
- 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);\r
- 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);\r
+ 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);\r
+ 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);\r
+ 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);\r
\r
/* Close registry */\r
RegCloseKey(key);\r
#include "nssm.h"\r
\r
bool is_admin;\r
-SERVICE_STATUS service_status;\r
-SERVICE_STATUS_HANDLE service_handle;\r
-HANDLE process_handle;\r
-HANDLE wait_handle;\r
-unsigned long pid;\r
-static char service_name[SERVICE_NAME_LENGTH];\r
-char exe[EXE_LENGTH];\r
-char flags[CMD_LENGTH];\r
-char dir[MAX_PATH];\r
-bool stopping;\r
-bool allow_restart;\r
-unsigned long throttle_delay;\r
-unsigned long stop_method;\r
-unsigned long kill_console_delay;\r
-unsigned long kill_window_delay;\r
-unsigned long kill_threads_delay;\r
-CRITICAL_SECTION throttle_section;\r
-CONDITION_VARIABLE throttle_condition;\r
-HANDLE throttle_timer;\r
-LARGE_INTEGER throttle_duetime;\r
bool use_critical_section;\r
-FILETIME creation_time;\r
\r
extern imports_t imports;\r
\r
static enum { NSSM_EXIT_RESTART, NSSM_EXIT_IGNORE, NSSM_EXIT_REALLY, NSSM_EXIT_UNCLEAN } exit_actions;\r
static const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", "Suicide", 0 };\r
\r
-static unsigned long throttle;\r
-\r
-static inline int throttle_milliseconds() {\r
+static inline int throttle_milliseconds(unsigned long throttle) {\r
/* pow() operates on doubles. */\r
int ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;\r
return ret * 1000;\r
control immediately.\r
*/\r
static unsigned long WINAPI shutdown_service(void *arg) {\r
- return stop_service(0, true, true);\r
+ return stop_service((nssm_service_t *) arg, 0, true, true);\r
}\r
\r
/* Connect to the service manager */\r
return ret;\r
}\r
\r
+/* Allocate and zero memory for a service. */\r
+nssm_service_t *alloc_nssm_service() {\r
+ nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));\r
+ if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service", "alloc_nssm_service()", 0);\r
+ return service;\r
+}\r
+\r
+/* Free memory for a service. */\r
+void cleanup_nssm_service(nssm_service_t *service) {\r
+ if (! service) return;\r
+ if (service->env) HeapFree(GetProcessHeap(), 0, service->env);\r
+ if (service->handle) CloseServiceHandle(service->handle);\r
+ if (service->process_handle) CloseHandle(service->process_handle);\r
+ if (service->wait_handle) UnregisterWait(service->process_handle);\r
+ if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);\r
+ if (service->throttle_timer) CloseHandle(service->throttle_timer);\r
+ HeapFree(GetProcessHeap(), 0, service);\r
+}\r
+\r
/* About to install the service */\r
int pre_install_service(int argc, char **argv) {\r
/* Show the dialogue box if we didn't give the service name and path */\r
if (argc < 2) return nssm_gui(IDD_INSTALL, argv[0]);\r
\r
+ nssm_service_t *service = alloc_nssm_service();\r
+ if (! service) {\r
+ print_message(stderr, NSSM_EVENT_OUT_OF_MEMORY, "service", "pre_install_service()");\r
+ return 1;\r
+ }\r
+\r
+ memmove(service->name, argv[0], strlen(argv[0]));\r
+ memmove(service->exe, argv[1], strlen(argv[1]));\r
+\r
/* Arguments are optional */\r
- char *flags;\r
size_t flagslen = 0;\r
size_t s = 0;\r
- int i;\r
+ size_t i;\r
for (i = 2; i < argc; i++) flagslen += strlen(argv[i]) + 1;\r
if (! flagslen) flagslen = 1;\r
\r
- flags = (char *) HeapAlloc(GetProcessHeap(), 0, flagslen);\r
- if (! flags) {\r
- log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "flags", "pre_install_service()", 0);\r
- return 2;\r
- }\r
- ZeroMemory(flags, flagslen);\r
-\r
/*\r
This probably isn't UTF8-safe and should use std::string or something\r
but it's been broken for the best part of a decade and due for a rewrite\r
*/\r
for (i = 2; i < argc; i++) {\r
size_t len = strlen(argv[i]);\r
- memmove(flags + s, argv[i], len);\r
+ memmove(service->flags + s, argv[i], len);\r
s += len;\r
- if (i < argc - 1) flags[s++] = ' ';\r
+ if (i < argc - 1) service->flags[s++] = ' ';\r
}\r
\r
- return install_service(argv[0], argv[1], flags);\r
+ /* Work out directory name */\r
+ size_t len = strlen(service->exe);\r
+ for (i = len; i && service->exe[i] != '\\' && service->exe[i] != '/'; i--);\r
+ memmove(service->dir, service->exe, i);\r
+ service->dir[i] = '\0';\r
+\r
+ int ret = install_service(service);\r
+ cleanup_nssm_service(service);\r
+ return ret;\r
}\r
\r
/* About to remove the service */\r
int pre_remove_service(int argc, char **argv) {\r
/* Show dialogue box if we didn't pass service name and "confirm" */\r
if (argc < 2) return nssm_gui(IDD_REMOVE, argv[0]);\r
- if (str_equiv(argv[1], "confirm")) return remove_service(argv[0]);\r
+ if (str_equiv(argv[1], "confirm")) {\r
+ nssm_service_t *service = alloc_nssm_service();\r
+ memmove(service->name, argv[0], strlen(argv[0]));\r
+ int ret = remove_service(service);\r
+ cleanup_nssm_service(service);\r
+ return ret;\r
+ }\r
print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);\r
return 100;\r
}\r
\r
/* Install the service */\r
-int install_service(char *name, char *exe, char *flags) {\r
+int install_service(nssm_service_t *service) {\r
+ if (! service) return 1;\r
+\r
/* Open service manager */\r
SC_HANDLE services = open_service_manager();\r
if (! services) {\r
print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
+ cleanup_nssm_service(service);\r
return 2;\r
}\r
\r
return 4;\r
}\r
\r
- /* Work out directory name */\r
- size_t len = strlen(exe);\r
- size_t i;\r
- for (i = len; i && exe[i] != '\\' && exe[i] != '/'; i--);\r
- char dir[MAX_PATH];\r
- memmove(dir, exe, i);\r
- dir[i] = '\0';\r
-\r
/* Create the service */\r
- 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);\r
- if (! service) {\r
+ 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);\r
+ if (! service->handle) {\r
print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED);\r
CloseServiceHandle(services);\r
return 5;\r
}\r
\r
/* Now we need to put the parameters into the registry */\r
- if (create_parameters(name, exe, flags, dir)) {\r
+ if (create_parameters(service)) {\r
print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);\r
- DeleteService(service);\r
+ DeleteService(service->handle);\r
CloseServiceHandle(services);\r
return 6;\r
}\r
\r
- set_service_recovery(service, name);\r
+ set_service_recovery(service);\r
+\r
+ print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);\r
\r
/* Cleanup */\r
- CloseServiceHandle(service);\r
CloseServiceHandle(services);\r
\r
- print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, name);\r
return 0;\r
}\r
\r
/* Remove the service */\r
-int remove_service(char *name) {\r
+int remove_service(nssm_service_t *service) {\r
+ if (! service) return 1;\r
+\r
/* Open service manager */\r
SC_HANDLE services = open_service_manager();\r
if (! services) {\r
}\r
\r
/* Try to open the service */\r
- SC_HANDLE service = OpenService(services, name, SC_MANAGER_ALL_ACCESS);\r
- if (! service) {\r
+ service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);\r
+ if (! service->handle) {\r
print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);\r
CloseServiceHandle(services);\r
return 3;\r
}\r
\r
/* Try to delete the service */\r
- if (! DeleteService(service)) {\r
+ if (! DeleteService(service->handle)) {\r
print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);\r
- CloseServiceHandle(service);\r
CloseServiceHandle(services);\r
return 4;\r
}\r
\r
/* Cleanup */\r
- CloseServiceHandle(service);\r
CloseServiceHandle(services);\r
\r
- print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, name);\r
+ print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);\r
return 0;\r
}\r
\r
/* Service initialisation */\r
void WINAPI service_main(unsigned long argc, char **argv) {\r
- if (_snprintf_s(service_name, sizeof(service_name), _TRUNCATE, "%s", argv[0]) < 0) {\r
- log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service_name", "service_main()", 0);\r
+ nssm_service_t *service = alloc_nssm_service();\r
+ if (! service) return;\r
+\r
+ if (_snprintf_s(service->name, sizeof(service->name), _TRUNCATE, "%s", argv[0]) < 0) {\r
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service->name", "service_main()", 0);\r
return;\r
}\r
\r
else use_critical_section = false;\r
\r
/* Initialise status */\r
- ZeroMemory(&service_status, sizeof(service_status));\r
- service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;\r
- service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;\r
- service_status.dwWin32ExitCode = NO_ERROR;\r
- service_status.dwServiceSpecificExitCode = 0;\r
- service_status.dwCheckPoint = 0;\r
- service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
+ ZeroMemory(&service->status, sizeof(service->status));\r
+ service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;\r
+ service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;\r
+ service->status.dwWin32ExitCode = NO_ERROR;\r
+ service->status.dwServiceSpecificExitCode = 0;\r
+ service->status.dwCheckPoint = 0;\r
+ service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
\r
/* Signal we AREN'T running the server */\r
- process_handle = 0;\r
- pid = 0;\r
+ service->process_handle = 0;\r
+ service->pid = 0;\r
\r
/* Register control handler */\r
- service_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, 0);\r
- if (! service_handle) {\r
+ service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);\r
+ if (! service->status_handle) {\r
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);\r
return;\r
}\r
\r
- log_service_control(service_name, 0, true);\r
+ log_service_control(service->name, 0, true);\r
\r
- service_status.dwCurrentState = SERVICE_START_PENDING;\r
- service_status.dwWaitHint = throttle_delay + NSSM_WAITHINT_MARGIN;\r
- SetServiceStatus(service_handle, &service_status);\r
+ service->status.dwCurrentState = SERVICE_START_PENDING;\r
+ service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;\r
+ SetServiceStatus(service->status_handle, &service->status);\r
\r
if (is_admin) {\r
/* Try to create the exit action parameters; we don't care if it fails */\r
- create_exit_action(argv[0], exit_action_strings[0]);\r
+ create_exit_action(service->name, exit_action_strings[0]);\r
\r
- set_service_recovery(0, service_name);\r
+ SC_HANDLE services = open_service_manager();\r
+ if (services) {\r
+ service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);\r
+ set_service_recovery(service);\r
+ CloseServiceHandle(services);\r
+ }\r
}\r
\r
/* Used for signalling a resume if the service pauses when throttled. */\r
- if (use_critical_section) InitializeCriticalSection(&throttle_section);\r
+ if (use_critical_section) {\r
+ InitializeCriticalSection(&service->throttle_section);\r
+ service->throttle_section_initialised = true;\r
+ }\r
else {\r
- throttle_timer = CreateWaitableTimer(0, 1, 0);\r
- if (! throttle_timer) {\r
- log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service_name, error_string(GetLastError()), 0);\r
+ service->throttle_timer = CreateWaitableTimer(0, 1, 0);\r
+ if (! service->throttle_timer) {\r
+ log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);\r
}\r
}\r
\r
- monitor_service();\r
+ monitor_service(service);\r
}\r
\r
/* Make sure service recovery actions are taken where necessary */\r
-void set_service_recovery(SC_HANDLE service, char *service_name) {\r
- SC_HANDLE services = 0;\r
-\r
- if (! service) {\r
- services = open_service_manager();\r
- if (! services) return;\r
-\r
- service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);\r
- if (! service) return;\r
- }\r
-\r
+void set_service_recovery(nssm_service_t *service) {\r
SERVICE_FAILURE_ACTIONS_FLAG flag;\r
ZeroMemory(&flag, sizeof(flag));\r
flag.fFailureActionsOnNonCrashFailures = true;\r
\r
/* This functionality was added in Vista so the call may fail */\r
- if (! ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {\r
+ if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {\r
unsigned long error = GetLastError();\r
/* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */\r
if (error != ERROR_INVALID_LEVEL) {\r
- log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CHANGESERVICECONFIG2_FAILED, service_name, error_string(error), 0);\r
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CHANGESERVICECONFIG2_FAILED, service->name, error_string(error), 0);\r
}\r
}\r
-\r
- if (services) {\r
- CloseServiceHandle(service);\r
- CloseServiceHandle(services);\r
- }\r
}\r
\r
-int monitor_service() {\r
+int monitor_service(nssm_service_t *service) {\r
/* Set service status to started */\r
- int ret = start_service();\r
+ int ret = start_service(service);\r
if (ret) {\r
char code[16];\r
_snprintf_s(code, sizeof(code), _TRUNCATE, "%d", ret);\r
- log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0);\r
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);\r
return ret;\r
}\r
- log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);\r
+ log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);\r
\r
/* Monitor service */\r
- if (! RegisterWaitForSingleObject(&wait_handle, process_handle, end_service, (void *) pid, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {\r
- log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, error_string(GetLastError()), 0);\r
+ if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {\r
+ log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);\r
}\r
\r
return 0;\r
\r
/* Service control handler */\r
unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {\r
+ nssm_service_t *service = (nssm_service_t *) context;\r
+\r
switch (control) {\r
case SERVICE_CONTROL_INTERROGATE:\r
/* We always keep the service status up-to-date so this is a no-op. */\r
\r
case SERVICE_CONTROL_SHUTDOWN:\r
case SERVICE_CONTROL_STOP:\r
- log_service_control(service_name, control, true);\r
+ log_service_control(service->name, control, true);\r
/*\r
We MUST acknowledge the stop request promptly but we're committed to\r
waiting for the application to exit. Spawn a new thread to wait\r
while we acknowledge the request.\r
*/\r
- if (! CreateThread(NULL, 0, shutdown_service, (void *) service_name, 0, NULL)) {\r
+ if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {\r
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);\r
\r
/*\r
We couldn't create a thread to tidy up so we'll have to force the tidyup\r
to complete in time in this thread.\r
*/\r
- kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;\r
- kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;\r
- kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;\r
+ service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;\r
+ service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;\r
+ service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;\r
\r
- stop_service(0, true, true);\r
+ stop_service(service, 0, true, true);\r
}\r
return NO_ERROR;\r
\r
case SERVICE_CONTROL_CONTINUE:\r
- log_service_control(service_name, control, true);\r
- throttle = 0;\r
- if (use_critical_section) imports.WakeConditionVariable(&throttle_condition);\r
+ log_service_control(service->name, control, true);\r
+ service->throttle = 0;\r
+ if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);\r
else {\r
- if (! throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;\r
- ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));\r
- SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);\r
+ if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;\r
+ ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));\r
+ SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);\r
}\r
- service_status.dwCurrentState = SERVICE_CONTINUE_PENDING;\r
- service_status.dwWaitHint = throttle_milliseconds() + NSSM_WAITHINT_MARGIN;\r
- log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service_name, 0);\r
- SetServiceStatus(service_handle, &service_status);\r
+ service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;\r
+ service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;\r
+ log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);\r
+ SetServiceStatus(service->status_handle, &service->status);\r
return NO_ERROR;\r
\r
case SERVICE_CONTROL_PAUSE:\r
We don't accept pause messages but it isn't possible to register\r
only for continue messages so we have to handle this case.\r
*/\r
- log_service_control(service_name, control, false);\r
+ log_service_control(service->name, control, false);\r
return ERROR_CALL_NOT_IMPLEMENTED;\r
}\r
\r
/* Unknown control */\r
- log_service_control(service_name, control, false);\r
+ log_service_control(service->name, control, false);\r
return ERROR_CALL_NOT_IMPLEMENTED;\r
}\r
\r
/* Start the service */\r
-int start_service() {\r
- stopping = false;\r
- allow_restart = true;\r
+int start_service(nssm_service_t *service) {\r
+ service->stopping = false;\r
+ service->allow_restart = true;\r
\r
- if (process_handle) return 0;\r
+ if (service->process_handle) return 0;\r
\r
/* Allocate a STARTUPINFO structure for a new process */\r
STARTUPINFO si;\r
ZeroMemory(&pi, sizeof(pi));\r
\r
/* Get startup parameters */\r
- char *env = 0;\r
- 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);\r
+ int ret = get_parameters(service, &si);\r
if (ret) {\r
- log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);\r
- return stop_service(2, true, true);\r
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);\r
+ return stop_service(service, 2, true, true);\r
}\r
\r
/* Launch executable with arguments */\r
char cmd[CMD_LENGTH];\r
- if (_snprintf_s(cmd, sizeof(cmd), _TRUNCATE, "\"%s\" %s", exe, flags) < 0) {\r
+ if (_snprintf_s(cmd, sizeof(cmd), _TRUNCATE, "\"%s\" %s", service->exe, service->flags) < 0) {\r
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);\r
close_output_handles(&si);\r
- return stop_service(2, true, true);\r
+ return stop_service(service, 2, true, true);\r
}\r
\r
- throttle_restart();\r
+ throttle_restart(service);\r
\r
bool inherit_handles = false;\r
if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;\r
- if (! CreateProcess(0, cmd, 0, 0, inherit_handles, 0, env, dir, &si, &pi)) {\r
+ if (! CreateProcess(0, cmd, 0, 0, inherit_handles, 0, service->env, service->dir, &si, &pi)) {\r
unsigned long error = GetLastError();\r
- if (error == ERROR_INVALID_PARAMETER && env) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service_name, exe, NSSM_REG_ENV, 0);\r
- else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(error), 0);\r
+ 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);\r
+ else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);\r
close_output_handles(&si);\r
- return stop_service(3, true, true);\r
+ return stop_service(service, 3, true, true);\r
}\r
- process_handle = pi.hProcess;\r
- pid = pi.dwProcessId;\r
+ service->process_handle = pi.hProcess;\r
+ service->pid = pi.dwProcessId;\r
\r
- if (get_process_creation_time(process_handle, &creation_time)) ZeroMemory(&creation_time, sizeof(creation_time));\r
+ if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));\r
\r
close_output_handles(&si);\r
\r
but be mindful of the fact that we are blocking the service control manager\r
so abandon the wait before too much time has elapsed.\r
*/\r
- unsigned long delay = throttle_delay;\r
+ unsigned long delay = service->throttle_delay;\r
if (delay > NSSM_SERVICE_STATUS_DEADLINE) {\r
char delay_milliseconds[16];\r
_snprintf_s(delay_milliseconds, sizeof(delay_milliseconds), _TRUNCATE, "%lu", delay);\r
char deadline_milliseconds[16];\r
_snprintf_s(deadline_milliseconds, sizeof(deadline_milliseconds), _TRUNCATE, "%lu", NSSM_SERVICE_STATUS_DEADLINE);\r
- log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service_name, delay_milliseconds, NSSM, deadline_milliseconds, 0);\r
+ log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);\r
delay = NSSM_SERVICE_STATUS_DEADLINE;\r
}\r
- unsigned long deadline = WaitForSingleObject(process_handle, delay);\r
+ unsigned long deadline = WaitForSingleObject(service->process_handle, delay);\r
\r
/* Signal successful start */\r
- service_status.dwCurrentState = SERVICE_RUNNING;\r
- SetServiceStatus(service_handle, &service_status);\r
+ service->status.dwCurrentState = SERVICE_RUNNING;\r
+ SetServiceStatus(service->status_handle, &service->status);\r
\r
/* Continue waiting for a clean startup. */\r
if (deadline == WAIT_TIMEOUT) {\r
- if (throttle_delay > delay) {\r
- if (WaitForSingleObject(process_handle, throttle_delay - delay) == WAIT_TIMEOUT) throttle = 0;\r
+ if (service->throttle_delay > delay) {\r
+ if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;\r
}\r
- else throttle = 0;\r
+ else service->throttle = 0;\r
}\r
\r
return 0;\r
}\r
\r
/* Stop the service */\r
-int stop_service(unsigned long exitcode, bool graceful, bool default_action) {\r
- allow_restart = false;\r
- if (wait_handle) UnregisterWait(wait_handle);\r
+int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {\r
+ service->allow_restart = false;\r
+ if (service->wait_handle) {\r
+ UnregisterWait(service->wait_handle);\r
+ service->wait_handle = 0;\r
+ }\r
\r
if (default_action && ! exitcode && ! graceful) {\r
- 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);\r
+ 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);\r
graceful = true;\r
}\r
\r
/* Signal we are stopping */\r
if (graceful) {\r
- service_status.dwCurrentState = SERVICE_STOP_PENDING;\r
- service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
- SetServiceStatus(service_handle, &service_status);\r
+ service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
+ service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
+ SetServiceStatus(service->status_handle, &service->status);\r
}\r
\r
/* Nothing to do if service isn't running */\r
- if (pid) {\r
+ if (service->pid) {\r
/* Shut down service */\r
- log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0);\r
- kill_process(service_name, service_handle, &service_status, stop_method, process_handle, pid, 0);\r
+ log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);\r
+ kill_process(service, service->process_handle, service->pid, 0);\r
}\r
- else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0);\r
+ else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);\r
\r
- end_service((void *) pid, true);\r
+ end_service((void *) service, true);\r
\r
/* Signal we stopped */\r
if (graceful) {\r
- service_status.dwCurrentState = SERVICE_STOPPED;\r
+ service->status.dwCurrentState = SERVICE_STOPPED;\r
if (exitcode) {\r
- service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;\r
- service_status.dwServiceSpecificExitCode = exitcode;\r
+ service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;\r
+ service->status.dwServiceSpecificExitCode = exitcode;\r
}\r
else {\r
- service_status.dwWin32ExitCode = NO_ERROR;\r
- service_status.dwServiceSpecificExitCode = 0;\r
+ service->status.dwWin32ExitCode = NO_ERROR;\r
+ service->status.dwServiceSpecificExitCode = 0;\r
}\r
- SetServiceStatus(service_handle, &service_status);\r
+ SetServiceStatus(service->status_handle, &service->status);\r
}\r
\r
return exitcode;\r
\r
/* Callback function triggered when the server exits */\r
void CALLBACK end_service(void *arg, unsigned char why) {\r
- if (stopping) return;\r
+ nssm_service_t *service = (nssm_service_t *) arg;\r
\r
- stopping = true;\r
+ if (service->stopping) return;\r
\r
- pid = (unsigned long) arg;\r
+ service->stopping = true;\r
\r
/* Check exit code */\r
unsigned long exitcode = 0;\r
char code[16];\r
- FILETIME exit_time;\r
- GetExitCodeProcess(process_handle, &exitcode);\r
- if (exitcode == STILL_ACTIVE || get_process_exit_time(process_handle, &exit_time)) GetSystemTimeAsFileTime(&exit_time);\r
- CloseHandle(process_handle);\r
+ GetExitCodeProcess(service->process_handle, &exitcode);\r
+ if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);\r
+ CloseHandle(service->process_handle);\r
+\r
+ service->process_handle = 0;\r
+ service->pid = 0;\r
\r
/*\r
Log that the service ended BEFORE logging about killing the process\r
*/\r
if (! why) {\r
_snprintf_s(code, sizeof(code), _TRUNCATE, "%lu", exitcode);\r
- log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);\r
+ log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);\r
}\r
\r
/* Clean up. */\r
if (exitcode == STILL_ACTIVE) exitcode = 0;\r
- kill_process_tree(service_name, service_handle, &service_status, stop_method, pid, exitcode, pid, &creation_time, &exit_time);\r
+ kill_process_tree(service, service->pid, exitcode, service->pid);\r
\r
/*\r
The why argument is true if our wait timed out or false otherwise.\r
this is a controlled shutdown, and don't take any restart action.\r
*/\r
if (why) return;\r
- if (! allow_restart) return;\r
+ if (! service->allow_restart) return;\r
\r
/* What action should we take? */\r
int action = NSSM_EXIT_RESTART;\r
unsigned char action_string[ACTION_LEN];\r
bool default_action;\r
- if (! get_exit_action(service_name, &exitcode, action_string, &default_action)) {\r
+ if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {\r
for (int i = 0; exit_action_strings[i]; i++) {\r
if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
action = i;\r
}\r
}\r
\r
- process_handle = 0;\r
- pid = 0;\r
switch (action) {\r
/* Try to restart the service or return failure code to service manager */\r
case NSSM_EXIT_RESTART:\r
- log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service_name, code, exit_action_strings[action], exe, 0);\r
- while (monitor_service()) {\r
- log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, exe, service_name, 0);\r
+ log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);\r
+ while (monitor_service(service)) {\r
+ log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);\r
Sleep(30000);\r
}\r
break;\r
\r
/* Do nothing, just like srvany would */\r
case NSSM_EXIT_IGNORE:\r
- log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service_name, code, exit_action_strings[action], exe, 0);\r
+ log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);\r
Sleep(INFINITE);\r
break;\r
\r
/* Tell the service manager we are finished */\r
case NSSM_EXIT_REALLY:\r
- log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);\r
- stop_service(exitcode, true, default_action);\r
+ log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);\r
+ stop_service(service, exitcode, true, default_action);\r
break;\r
\r
/* Fake a crash so pre-Vista service managers will run recovery actions. */\r
case NSSM_EXIT_UNCLEAN:\r
- log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0);\r
- stop_service(exitcode, false, default_action);\r
+ log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);\r
+ stop_service(service, exitcode, false, default_action);\r
free_imports();\r
exit(exitcode);\r
break;\r
}\r
}\r
\r
-void throttle_restart() {\r
+void throttle_restart(nssm_service_t *service) {\r
/* This can't be a restart if the service is already running. */\r
- if (! throttle++) return;\r
+ if (! service->throttle++) return;\r
\r
- int ms = throttle_milliseconds();\r
+ int ms = throttle_milliseconds(service->throttle);\r
\r
- if (throttle > 7) throttle = 8;\r
+ if (service->throttle > 7) service->throttle = 8;\r
\r
char threshold[8], milliseconds[8];\r
- _snprintf_s(threshold, sizeof(threshold), _TRUNCATE, "%lu", throttle_delay);\r
+ _snprintf_s(threshold, sizeof(threshold), _TRUNCATE, "%lu", service->throttle_delay);\r
_snprintf_s(milliseconds, sizeof(milliseconds), _TRUNCATE, "%lu", ms);\r
- log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service_name, threshold, milliseconds, 0);\r
+ log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);\r
\r
- if (use_critical_section) EnterCriticalSection(&throttle_section);\r
- else if (throttle_timer) {\r
- ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));\r
- throttle_duetime.QuadPart = 0 - (ms * 10000LL);\r
- SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);\r
+ if (use_critical_section) EnterCriticalSection(&service->throttle_section);\r
+ else if (service->throttle_timer) {\r
+ ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));\r
+ service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);\r
+ SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);\r
}\r
\r
- service_status.dwCurrentState = SERVICE_PAUSED;\r
- SetServiceStatus(service_handle, &service_status);\r
+ service->status.dwCurrentState = SERVICE_PAUSED;\r
+ SetServiceStatus(service->status_handle, &service->status);\r
\r
if (use_critical_section) {\r
- imports.SleepConditionVariableCS(&throttle_condition, &throttle_section, ms);\r
- LeaveCriticalSection(&throttle_section);\r
+ imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);\r
+ LeaveCriticalSection(&service->throttle_section);\r
}\r
else {\r
- if (throttle_timer) WaitForSingleObject(throttle_timer, INFINITE);\r
+ if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);\r
else Sleep(ms);\r
}\r
}\r
0 if the wait completed.\r
-1 on error.\r
*/\r
-int await_shutdown(char *function_name, char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVICE_STATUS *service_status, HANDLE process_handle, unsigned long timeout) {\r
+int await_shutdown(nssm_service_t *service, char *function_name, unsigned long timeout) {\r
unsigned long interval;\r
unsigned long waithint;\r
unsigned long ret;\r
\r
_snprintf_s(timeout_milliseconds, sizeof(timeout_milliseconds), _TRUNCATE, "%lu", timeout);\r
\r
- waithint = service_status->dwWaitHint;\r
+ waithint = service->status.dwWaitHint;\r
waited = 0;\r
while (waited < timeout) {\r
interval = timeout - waited;\r
if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;\r
\r
- service_status->dwCurrentState = SERVICE_STOP_PENDING;\r
- service_status->dwWaitHint += interval;\r
- service_status->dwCheckPoint++;\r
- SetServiceStatus(service_handle, service_status);\r
+ service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
+ service->status.dwWaitHint += interval;\r
+ service->status.dwCheckPoint++;\r
+ SetServiceStatus(service->status_handle, &service->status);\r
\r
if (waited) {\r
_snprintf_s(waited_milliseconds, sizeof(waited_milliseconds), _TRUNCATE, "%lu", waited);\r
_snprintf_s(interval_milliseconds, sizeof(interval_milliseconds), _TRUNCATE, "%lu", interval);\r
- log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service_name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);\r
+ log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);\r
}\r
\r
- switch (WaitForSingleObject(process_handle, interval)) {\r
+ switch (WaitForSingleObject(service->process_handle, interval)) {\r
case WAIT_OBJECT_0:\r
ret = 0;\r
goto awaited;\r