瀏覽代碼

Allow overriding time to wait when trying to kill the application.

Three new registry entries can be used to specify a wait time in
milliseconds after attempting a stop method.

  AppStopMethodConsole
  AppStopMethodWindow
  AppStopMethodThreads

The default for each remains the same, 1500ms.

Thanks Russ Holmann.
Iain Patterson 10 年之前
父節點
當前提交
cb571db509
共有 8 個文件被更改,包括 88 次插入12 次删除
  1. 5 0
      ChangeLog.txt
  2. 18 0
      README.txt
  3. 39 0
      messages.mc
  4. 3 3
      nssm.h
  5. 6 3
      process.cpp
  6. 6 1
      registry.cpp
  7. 4 1
      registry.h
  8. 7 4
      service.cpp

+ 5 - 0
ChangeLog.txt

@@ -1,3 +1,8 @@
+Changes since 2.17
+-----------------
+  * Timeouts for each shutdown method can be configured in
+    the registry.
+
 Changes since 2.16
 Changes since 2.16
 -----------------
 -----------------
   * NSSM can now redirect the service's I/O streams to any path
   * NSSM can now redirect the service's I/O streams to any path

+ 18 - 0
README.txt

@@ -43,6 +43,9 @@ they can clean up and shut down gracefully on receipt of the event.
 Since version 2.17, NSSM can redirect the managed application's I/O streams
 Since version 2.17, NSSM can redirect the managed application's I/O streams
 to an arbitrary path.
 to an arbitrary path.
 
 
+Since version 2.18, NSSM can be configured to wait a user-specified amount
+of time for the application to exit when shutting down.
+
 
 
 Usage
 Usage
 -----
 -----
@@ -182,6 +185,20 @@ Take great care when including 8 in the value of AppStopMethodSkip.  If NSSM
 does not call TerminateProcess() it is possible that the application will not
 does not call TerminateProcess() it is possible that the application will not
 exit when the service stops.
 exit when the service stops.
 
 
+By default NSSM will allow processes 1500ms to respond to each of the methods
+described above before proceeding to the next one.  The timeout can be
+configured on a per-method basis by creating REG_DWORD entries in the
+registry under HKLM\SYSTEM\CurrentControlSet\Services\<service>\Parameters.
+
+  AppStopMethodConsole
+  AppStopMethodWindow
+  AppStopMethodThreads
+
+Each value should be set to the number of milliseconds to wait.  Please note
+that the timeout applies to each process in the application's process tree,
+so the actual time to shutdown may be longer than the sum of all configured
+timeouts if the application spawns multiple subprocesses.
+
 
 
 I/O redirection
 I/O redirection
 ---------------
 ---------------
@@ -282,6 +299,7 @@ Thanks to Riccardo Gusmeroli for Italian translation.
 Thanks to Eric Cheldelin for the inspiration to generate a Control-C event
 Thanks to Eric Cheldelin for the inspiration to generate a Control-C event
 on shutdown.
 on shutdown.
 Thanks to Brian Baxter for suggesting how to escape quotes from the command prompt.
 Thanks to Brian Baxter for suggesting how to escape quotes from the command prompt.
+Thanks to Russ Holmann for suggesting that the shutdown timeout be configurable.
 
 
 Licence
 Licence
 -------
 -------

+ 39 - 0
messages.mc

@@ -1305,3 +1305,42 @@ Language = Italian
 Chiamata a GetProcAddress(%1) fallita:
 Chiamata a GetProcAddress(%1) fallita:
 %2
 %2
 .
 .
+
+MessageId = +1
+SymbolicName = NSSM_EVENT_BOGUS_KILL_CONSOLE_GRACE_PERIOD
+Severity = Warning
+Language = English
+The registry value %2, used to specify the maximum number of milliseconds to wait for service %1 to stop after sending a Control-C event, was not of type REG_DWORD.  The default time of %3 milliseconds will be used.
+.
+Language = French
+The registry value %2, used to specify the maximum number of milliseconds to wait for service %1 to stop after sending a Control-C event, was not of type REG_DWORD.  The default time of %3 milliseconds will be used.
+.
+Language = Italian
+The registry value %2, used to specify the maximum number of milliseconds to wait for service %1 to stop after sending a Control-C event, was not of type REG_DWORD.  The default time of %3 milliseconds will be used.
+.
+
+MessageId = +1
+SymbolicName = NSSM_EVENT_BOGUS_KILL_WINDOW_GRACE_PERIOD
+Severity = Warning
+Language = English
+The registry value %2, used to specify the maximum number of milliseconds to wait for service %1 to stop after posting a WM_CLOSE message to windows managed by the application, was not of type REG_DWORD.  The default time of %3 milliseconds will be used.
+.
+Language = French
+The registry value %2, used to specify the maximum number of milliseconds to wait for service %1 to stop after posting a WM_CLOSE message to windows managed by the application, was not of type REG_DWORD.  The default time of %3 milliseconds will be used.
+.
+Language = Italian
+The registry value %2, used to specify the maximum number of milliseconds to wait for service %1 to stop after posting a WM_CLOSE message to windows managed by the application, was not of type REG_DWORD.  The default time of %3 milliseconds will be used.
+.
+
+MessageId = +1
+SymbolicName = NSSM_EVENT_BOGUS_KILL_THREADS_GRACE_PERIOD
+Severity = Warning
+Language = English
+The registry value %2, used to specify the maximum number of milliseconds to wait for service %1 to stop after posting a WM_QUIT message to the message queues of threads managed by the application, was not of type REG_DWORD.  The default time of %3 milliseconds will be used.
+.
+Language = French
+The registry value %2, used to specify the maximum number of milliseconds to wait for service %1 to stop after posting a WM_QUIT message to the message queues of threads managed by the application, was not of type REG_DWORD.  The default time of %3 milliseconds will be used.
+.
+Language = Italian
+The registry value %2, used to specify the maximum number of milliseconds to wait for service %1 to stop after posting a WM_QUIT message to the message queues of threads managed by the application, was not of type REG_DWORD.  The default time of %3 milliseconds will be used.
+.

+ 3 - 3
nssm.h

@@ -43,17 +43,17 @@ int str_equiv(const char *, const char *);
 
 
 /*
 /*
   How many milliseconds to wait for the application to die after sending
   How many milliseconds to wait for the application to die after sending
-  a Control-C event to its console.
+  a Control-C event to its console.  Override in registry.
 */
 */
 #define NSSM_KILL_CONSOLE_GRACE_PERIOD 1500
 #define NSSM_KILL_CONSOLE_GRACE_PERIOD 1500
 /*
 /*
   How many milliseconds to wait for the application to die after posting to
   How many milliseconds to wait for the application to die after posting to
-  its windows' message queues.
+  its windows' message queues.  Override in registry.
 */
 */
 #define NSSM_KILL_WINDOW_GRACE_PERIOD 1500
 #define NSSM_KILL_WINDOW_GRACE_PERIOD 1500
 /*
 /*
   How many milliseconds to wait for the application to die after posting to
   How many milliseconds to wait for the application to die after posting to
-  its threads' message queues.
+  its threads' message queues.  Override in registry.
 */
 */
 #define NSSM_KILL_THREADS_GRACE_PERIOD 1500
 #define NSSM_KILL_THREADS_GRACE_PERIOD 1500
 
 

+ 6 - 3
process.cpp

@@ -1,6 +1,9 @@
 #include "nssm.h"
 #include "nssm.h"
 
 
 extern imports_t imports;
 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) {
 int get_process_creation_time(HANDLE process_handle, FILETIME *ft) {
   FILETIME creation_time, exit_time, kernel_time, user_time;
   FILETIME creation_time, exit_time, kernel_time, user_time;
@@ -161,7 +164,7 @@ int kill_process(char *service_name, unsigned long stop_method, HANDLE process_h
   if (stop_method & NSSM_STOP_METHOD_WINDOW) {
   if (stop_method & NSSM_STOP_METHOD_WINDOW) {
     EnumWindows((WNDENUMPROC) kill_window, (LPARAM) &k);
     EnumWindows((WNDENUMPROC) kill_window, (LPARAM) &k);
     if (k.signalled) {
     if (k.signalled) {
-      if (! WaitForSingleObject(process_handle, NSSM_KILL_WINDOW_GRACE_PERIOD)) return 1;
+      if (! WaitForSingleObject(process_handle, kill_window_delay)) return 1;
     }
     }
   }
   }
 
 
@@ -172,7 +175,7 @@ int kill_process(char *service_name, unsigned long stop_method, HANDLE process_h
   */
   */
   if (stop_method & NSSM_STOP_METHOD_THREADS) {
   if (stop_method & NSSM_STOP_METHOD_THREADS) {
     if (kill_threads(service_name, &k)) {
     if (kill_threads(service_name, &k)) {
-      if (! WaitForSingleObject(process_handle, NSSM_KILL_THREADS_GRACE_PERIOD)) return 1;
+      if (! WaitForSingleObject(process_handle, kill_threads_delay)) return 1;
     }
     }
   }
   }
 
 
@@ -233,7 +236,7 @@ int kill_console(char *service_name, HANDLE process_handle, unsigned long pid) {
   }
   }
 
 
   /* Wait for process to exit. */
   /* Wait for process to exit. */
-  if (WaitForSingleObject(process_handle, NSSM_KILL_CONSOLE_GRACE_PERIOD)) return 6;
+  if (WaitForSingleObject(process_handle, kill_console_delay)) return 6;
 
 
   return ret;
   return ret;
 }
 }

+ 6 - 1
registry.cpp

@@ -239,7 +239,7 @@ void override_milliseconds(char *service_name, HKEY key, char *value, unsigned l
   if (! ok) *buffer = default_value;
   if (! ok) *buffer = default_value;
 }
 }
 
 
-int get_parameters(char *service_name, char *exe, unsigned long exelen, char *flags, unsigned long flagslen, char *dir, unsigned long dirlen, char **env, unsigned long *throttle_delay, unsigned long *stop_method, STARTUPINFO *si) {
+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) {
   unsigned long ret;
   unsigned long ret;
 
 
   /* Get registry */
   /* Get registry */
@@ -322,6 +322,11 @@ int get_parameters(char *service_name, char *exe, unsigned long exelen, char *fl
   *stop_method = ~0;
   *stop_method = ~0;
   if (stop_ok) *stop_method &= ~stop_method_skip;
   if (stop_ok) *stop_method &= ~stop_method_skip;
 
 
+  /* Try to get kill delays - may fail. */
+  override_milliseconds(service_name, key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, kill_console_delay, NSSM_KILL_CONSOLE_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_CONSOLE_GRACE_PERIOD);
+  override_milliseconds(service_name, key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, kill_window_delay, NSSM_KILL_WINDOW_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_WINDOW_GRACE_PERIOD);
+  override_milliseconds(service_name, key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, kill_threads_delay, NSSM_KILL_THREADS_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_THREADS_GRACE_PERIOD);
+
   /* Close registry */
   /* Close registry */
   RegCloseKey(key);
   RegCloseKey(key);
 
 

+ 4 - 1
registry.h

@@ -9,6 +9,9 @@
 #define NSSM_REG_EXIT "AppExit"
 #define NSSM_REG_EXIT "AppExit"
 #define NSSM_REG_THROTTLE "AppThrottle"
 #define NSSM_REG_THROTTLE "AppThrottle"
 #define NSSM_REG_STOP_METHOD_SKIP "AppStopMethodSkip"
 #define NSSM_REG_STOP_METHOD_SKIP "AppStopMethodSkip"
+#define NSSM_REG_KILL_CONSOLE_GRACE_PERIOD "AppStopMethodConsole"
+#define NSSM_REG_KILL_WINDOW_GRACE_PERIOD "AppStopMethodWindow"
+#define NSSM_REG_KILL_THREADS_GRACE_PERIOD "AppStopMethodThreads"
 #define NSSM_REG_STDIN "AppStdin"
 #define NSSM_REG_STDIN "AppStdin"
 #define NSSM_REG_STDOUT "AppStdout"
 #define NSSM_REG_STDOUT "AppStdout"
 #define NSSM_REG_STDERR "AppStderr"
 #define NSSM_REG_STDERR "AppStderr"
@@ -26,7 +29,7 @@ int expand_parameter(HKEY, char *, char *, unsigned long, bool);
 int get_number(HKEY, char *, unsigned long *, bool);
 int get_number(HKEY, char *, unsigned long *, bool);
 int get_number(HKEY, char *, unsigned long *);
 int get_number(HKEY, char *, unsigned long *);
 void override_milliseconds(char *, HKEY, char *, unsigned long *, unsigned long, unsigned long);
 void override_milliseconds(char *, HKEY, char *, unsigned long *, unsigned long, unsigned long);
-int get_parameters(char *, char *, unsigned long, char *, unsigned long, char *, unsigned long, char **, unsigned long *, unsigned long *, STARTUPINFO *);
+int get_parameters(char *, char *, unsigned long, char *, unsigned long, char *, unsigned long, char **, unsigned long *, unsigned long *, unsigned long *, unsigned long *, unsigned long *, STARTUPINFO *);
 int get_exit_action(char *, unsigned long *, unsigned char *, bool *);
 int get_exit_action(char *, unsigned long *, unsigned char *, bool *);
 
 
 #endif
 #endif

+ 7 - 4
service.cpp

@@ -14,6 +14,9 @@ bool stopping;
 bool allow_restart;
 bool allow_restart;
 unsigned long throttle_delay;
 unsigned long throttle_delay;
 unsigned long stop_method;
 unsigned long stop_method;
+unsigned long kill_console_delay;
+unsigned long kill_window_delay;
+unsigned long kill_threads_delay;
 CRITICAL_SECTION throttle_section;
 CRITICAL_SECTION throttle_section;
 CONDITION_VARIABLE throttle_condition;
 CONDITION_VARIABLE throttle_condition;
 HANDLE throttle_timer;
 HANDLE throttle_timer;
@@ -390,7 +393,7 @@ int start_service() {
 
 
   /* Get startup parameters */
   /* Get startup parameters */
   char *env = 0;
   char *env = 0;
-  int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay, &stop_method, &si);
+  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);
   if (ret) {
   if (ret) {
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);
     return stop_service(2, true, true);
     return stop_service(2, true, true);
@@ -446,9 +449,9 @@ int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
   if (graceful) {
   if (graceful) {
     service_status.dwCurrentState = SERVICE_STOP_PENDING;
     service_status.dwCurrentState = SERVICE_STOP_PENDING;
     service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;
     service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;
-    if (stop_method & NSSM_STOP_METHOD_CONSOLE && imports.AttachConsole) service_status.dwWaitHint += NSSM_KILL_CONSOLE_GRACE_PERIOD;
-    if (stop_method & NSSM_STOP_METHOD_WINDOW) service_status.dwWaitHint += NSSM_KILL_WINDOW_GRACE_PERIOD;
-    if (stop_method & NSSM_STOP_METHOD_THREADS) service_status.dwWaitHint += NSSM_KILL_THREADS_GRACE_PERIOD;
+    if (stop_method & NSSM_STOP_METHOD_CONSOLE && imports.AttachConsole) service_status.dwWaitHint += kill_console_delay;
+    if (stop_method & NSSM_STOP_METHOD_WINDOW) service_status.dwWaitHint += kill_window_delay;
+    if (stop_method & NSSM_STOP_METHOD_THREADS) service_status.dwWaitHint += kill_threads_delay;
     SetServiceStatus(service_handle, &service_status);
     SetServiceStatus(service_handle, &service_status);
   }
   }