Pass parameters around in a data structure.
authorIain Patterson <me@iain.cx>
Wed, 20 Nov 2013 12:14:15 +0000 (12:14 +0000)
committerIain Patterson <me@iain.cx>
Wed, 20 Nov 2013 15:49:32 +0000 (15:49 +0000)
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.

gui.cpp
nssm.h
process.cpp
process.h
registry.cpp
registry.h
service.cpp
service.h

diff --git a/gui.cpp b/gui.cpp
index 5ceb33f..7773a18 100644 (file)
--- a/gui.cpp
+++ b/gui.cpp
@@ -72,56 +72,68 @@ void centre_window(HWND window) {
 int install(HWND window) {\r
   if (! window) return 1;\r
 \r
-  /* Check parameters in the window */\r
-  char name[VALUE_LENGTH];\r
-  char exe[EXE_LENGTH];\r
-  char flags[VALUE_LENGTH];\r
-\r
-  /* Get service name */\r
-  if (! GetDlgItemText(window, IDC_NAME, name, sizeof(name))) {\r
-    popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
-    return 2;\r
-  }\r
-\r
-  /* Get executable name */\r
-  if (! GetDlgItemText(window, IDC_PATH, exe, sizeof(exe))) {\r
-    popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PATH);\r
-    return 3;\r
-  }\r
+  nssm_service_t *service = alloc_nssm_service();\r
+  if (service) {\r
+    /* Get service name. */\r
+    if (! GetDlgItemText(window, IDC_NAME, service->name, sizeof(service->name))) {\r
+      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
+      cleanup_nssm_service(service);\r
+      return 2;\r
+    }\r
 \r
-  /* Get flags */\r
-  if (SendMessage(GetDlgItem(window, IDC_FLAGS), WM_GETTEXTLENGTH, 0, 0)) {\r
-    if (! GetDlgItemText(window, IDC_FLAGS, flags, sizeof(flags))) {\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_OPTIONS);\r
-      return 4;\r
+    /* Get executable name */\r
+    if (! GetDlgItemText(window, IDC_PATH, service->exe, sizeof(service->exe))) {\r
+      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PATH);\r
+      return 3;\r
     }\r
+  \r
+    /* Get flags */\r
+    if (SendMessage(GetDlgItem(window, IDC_FLAGS), WM_GETTEXTLENGTH, 0, 0)) {\r
+      if (! GetDlgItemText(window, IDC_FLAGS, service->flags, sizeof(service->flags))) {\r
+        popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_OPTIONS);\r
+        return 4;\r
+      }
+    }\r
+\r
+    memmove(service->dir, service->exe, strlen(service->exe));\r
+    strip_basename(service->dir);\r
   }\r
-  else ZeroMemory(&flags, sizeof(flags));\r
 \r
-  /* See if it works */\r
-  switch (install_service(name, exe, flags)) {\r
+  /* See if it works. */\r
+  switch (install_service(service)) {\r
+    case 1:\r
+      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, "service", "install()");\r
+      cleanup_nssm_service(service);\r
+      return 1;\r
+\r
     case 2:\r
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
+      cleanup_nssm_service(service);\r
       return 2;\r
 \r
     case 3:\r
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);\r
+      cleanup_nssm_service(service);\r
       return 3;\r
 \r
     case 4:\r
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);\r
+      cleanup_nssm_service(service);\r
       return 4;\r
 \r
     case 5:\r
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INSTALL_SERVICE_FAILED);\r
+      cleanup_nssm_service(service);\r
       return 5;\r
 \r
     case 6:\r
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_CREATE_PARAMETERS_FAILED);\r
+      cleanup_nssm_service(service);\r
       return 6;\r
   }\r
 \r
-  popup_message(MB_OK, NSSM_MESSAGE_SERVICE_INSTALLED, name);\r
+  popup_message(MB_OK, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);\r
+  cleanup_nssm_service(service);\r
   return 0;\r
 }\r
 \r
@@ -129,34 +141,47 @@ int install(HWND window) {
 int remove(HWND window) {\r
   if (! window) return 1;\r
 \r
-  /* Check parameters in the window */\r
-  char name[VALUE_LENGTH];\r
+  /* See if it works */\r
+  nssm_service_t *service = alloc_nssm_service();\r
+  if (service) {\r
+    /* Get service name */\r
+    if (! GetDlgItemText(window, IDC_NAME, service->name, sizeof(service->name))) {\r
+      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
+      cleanup_nssm_service(service);\r
+      return 2;\r
+    }\r
 \r
-  /* Get service name */\r
-  if (! GetDlgItemText(window, IDC_NAME, name, sizeof(name))) {\r
-    popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
-    return 2;\r
+    /* Confirm */\r
+    if (popup_message(MB_YESNO, NSSM_GUI_ASK_REMOVE_SERVICE, service->name) != IDYES) {\r
+      cleanup_nssm_service(service);\r
+      return 0;\r
+    }\r
   }\r
 \r
-  /* Confirm */\r
-  if (popup_message(MB_YESNO, NSSM_GUI_ASK_REMOVE_SERVICE, name) != IDYES) return 0;\r
+  switch (remove_service(service)) {\r
+    case 1:\r
+      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, "service", "remove()");\r
+      cleanup_nssm_service(service);\r
+      return 1;\r
 \r
-  /* See if it works */\r
-  switch (remove_service(name)) {\r
     case 2:\r
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
+      cleanup_nssm_service(service);\r
       return 2;\r
 \r
     case 3:\r
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_SERVICE_NOT_INSTALLED);\r
       return 3;\r
+      cleanup_nssm_service(service);\r
 \r
     case 4:\r
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_REMOVE_SERVICE_FAILED);\r
+      cleanup_nssm_service(service);\r
       return 4;\r
   }\r
 \r
-  popup_message(MB_OK, NSSM_MESSAGE_SERVICE_REMOVED, name);\r
+  popup_message(MB_OK, NSSM_MESSAGE_SERVICE_REMOVED, service->name);\r
+  cleanup_nssm_service(service);\r
   return 0;\r
 }\r
 \r
diff --git a/nssm.h b/nssm.h
index 1479f10..6edfedf 100644 (file)
--- a/nssm.h
+++ b/nssm.h
@@ -6,13 +6,13 @@
 #include <stdarg.h>\r
 #include <stdio.h>\r
 #include <windows.h>\r
+#include "service.h"\r
 #include "event.h"\r
 #include "imports.h"\r
 #include "messages.h"\r
 #include "process.h"\r
 #include "registry.h"\r
 #include "io.h"\r
-#include "service.h"\r
 #include "gui.h"\r
 \r
 int str_equiv(const char *, const char *);\r
@@ -23,19 +23,6 @@ int str_equiv(const char *, const char *);
 #define NSSM_DATE "2013-11-15"\r
 \r
 /*\r
-  MSDN says the commandline in CreateProcess() is limited to 32768 characters\r
-  and the application name to MAX_PATH.\r
-  A registry key is limited to 255 characters.\r
-  A registry value is limited to 16383 characters.\r
-  Therefore we limit the service name to accommodate the path under HKLM.\r
-*/\r
-#define EXE_LENGTH MAX_PATH\r
-#define CMD_LENGTH 32768\r
-#define KEY_LENGTH 255\r
-#define VALUE_LENGTH 16383\r
-#define SERVICE_NAME_LENGTH KEY_LENGTH - 55\r
-\r
-/*\r
   Throttle the restart of the service if it stops before this many\r
   milliseconds have elapsed since startup.  Override in registry.\r
 */\r
index 7bb6dac..34b07d3 100644 (file)
@@ -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);
     }
   }
 
index 21d86da..165bb22 100644 (file)
--- 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
index f54ed03..59d17d4 100644 (file)
@@ -26,10 +26,10 @@ int create_messages() {
   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
@@ -42,19 +42,19 @@ int create_parameters(char *service_name, char *exe, char *flags, char *dir) {
   }\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
@@ -123,6 +123,9 @@ int set_environment(char *service_name, HKEY key, char **env) {
   /* 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
@@ -261,12 +264,12 @@ void override_milliseconds(char *service_name, HKEY key, char *value, unsigned l
   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
@@ -279,50 +282,50 @@ int get_parameters(char *service_name, char *exe, unsigned long exelen, char *fl
   }\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
@@ -333,7 +336,7 @@ int get_parameters(char *service_name, char *exe, unsigned long exelen, char *fl
   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
@@ -341,13 +344,13 @@ int get_parameters(char *service_name, char *exe, unsigned long exelen, char *fl
   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
index 5d42792..0661b73 100644 (file)
@@ -21,7 +21,7 @@
 #define NSSM_STDIO_LENGTH 29\r
 \r
 int create_messages();\r
-int create_parameters(char *, char *, char *, char *);\r
+int create_parameters(nssm_service_t *);\r
 int create_exit_action(char *, const char *);\r
 int set_environment(char *, HKEY, char **);\r
 int expand_parameter(HKEY, char *, char *, unsigned long, bool, bool);\r
@@ -31,7 +31,7 @@ int set_number(HKEY, char *, unsigned long);
 int get_number(HKEY, char *, unsigned long *, bool);\r
 int get_number(HKEY, char *, unsigned long *);\r
 void override_milliseconds(char *, HKEY, char *, unsigned long *, unsigned long, unsigned long);\r
-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 *);\r
+int get_parameters(nssm_service_t *, STARTUPINFO *);\r
 int get_exit_action(char *, unsigned long *, unsigned char *, bool *);\r
 \r
 #endif\r
index be70501..dffd7aa 100644 (file)
@@ -1,37 +1,14 @@
 #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
@@ -42,7 +19,7 @@ static inline int throttle_milliseconds() {
   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
@@ -56,26 +33,46 @@ SC_HANDLE open_service_manager() {
   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
@@ -84,29 +81,46 @@ int pre_install_service(int argc, char **argv) {
   */\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
@@ -126,42 +140,36 @@ int install_service(char *name, char *exe, char *flags) {
     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
@@ -170,33 +178,34 @@ int remove_service(char *name) {
   }\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
@@ -205,95 +214,88 @@ void WINAPI service_main(unsigned long argc, char **argv) {
   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
@@ -343,6 +345,8 @@ void log_service_control(char *service_name, unsigned long control, bool handled
 \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
@@ -350,40 +354,40 @@ unsigned long WINAPI service_control_handler(unsigned long control, unsigned lon
 \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
@@ -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\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
@@ -417,36 +421,35 @@ int start_service() {
   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
@@ -455,71 +458,74 @@ int start_service() {
     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
@@ -527,19 +533,21 @@ int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
 \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
@@ -547,12 +555,12 @@ void CALLBACK end_service(void *arg, unsigned char why) {
   */\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
@@ -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.\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
@@ -576,69 +584,67 @@ void CALLBACK end_service(void *arg, unsigned char why) {
     }\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
@@ -671,7 +677,7 @@ void throttle_restart() {
            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
@@ -690,24 +696,24 @@ int await_shutdown(char *function_name, char *service_name, SERVICE_STATUS_HANDL
 \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
index 49760ba..994a7ce 100644 (file)
--- a/service.h
+++ b/service.h
@@ -1,24 +1,68 @@
 #ifndef SERVICE_H\r
 #define SERVICE_H\r
 \r
+/*\r
+  MSDN says the commandline in CreateProcess() is limited to 32768 characters\r
+  and the application name to MAX_PATH.\r
+  A registry key is limited to 255 characters.\r
+  A registry value is limited to 16383 characters.\r
+  Therefore we limit the service name to accommodate the path under HKLM.\r
+*/\r
+#define EXE_LENGTH MAX_PATH\r
+#define CMD_LENGTH 32768\r
+#define KEY_LENGTH 255\r
+#define VALUE_LENGTH 16383\r
+#define SERVICE_NAME_LENGTH KEY_LENGTH - 55\r
+\r
 #define ACTION_LEN 16\r
 \r
+typedef struct {\r
+  char name[SERVICE_NAME_LENGTH];\r
+  char exe[EXE_LENGTH];\r
+  char flags[VALUE_LENGTH];\r
+  char dir[MAX_PATH];\r
+  char *env;\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
+  SC_HANDLE handle;\r
+  SERVICE_STATUS status;\r
+  SERVICE_STATUS_HANDLE status_handle;\r
+  HANDLE process_handle;\r
+  unsigned long pid;\r
+  HANDLE wait_handle;\r
+  bool stopping;\r
+  bool allow_restart;\r
+  unsigned long throttle;\r
+  CRITICAL_SECTION throttle_section;\r
+  bool throttle_section_initialised;\r
+  CONDITION_VARIABLE throttle_condition;\r
+  HANDLE throttle_timer;\r
+  LARGE_INTEGER throttle_duetime;\r
+  FILETIME creation_time;\r
+  FILETIME exit_time;\r
+} nssm_service_t;\r
+\r
 void WINAPI service_main(unsigned long, char **);\r
 char *service_control_text(unsigned long);\r
 void log_service_control(char *, unsigned long, bool);\r
 unsigned long WINAPI service_control_handler(unsigned long, unsigned long, void *, void *);\r
 \r
+nssm_service_t *alloc_nssm_service();\r
+void cleanup_nssm_service(nssm_service_t *);\r
 SC_HANDLE open_service_manager();\r
 int pre_install_service(int, char **);\r
 int pre_remove_service(int, char **);\r
-int install_service(char *, char *, char *);\r
-int remove_service(char *);\r
-void set_service_recovery(SC_HANDLE, char *);\r
-int monitor_service();\r
-int start_service();\r
-int stop_service(unsigned long, bool, bool);\r
+int install_service(nssm_service_t *);\r
+int remove_service(nssm_service_t *);\r
+void set_service_recovery(nssm_service_t *);\r
+int monitor_service(nssm_service_t *);\r
+int start_service(nssm_service_t *);\r
+int stop_service(nssm_service_t *, unsigned long, bool, bool);\r
 void CALLBACK end_service(void *, unsigned char);\r
-void throttle_restart();\r
-int await_shutdown(char *, char *, SERVICE_STATUS_HANDLE, SERVICE_STATUS *, HANDLE, unsigned long);\r
+void throttle_restart(nssm_service_t *);\r
+int await_shutdown(nssm_service_t *, char *, unsigned long);\r
 \r
 #endif\r