+Changes since 2.17
+-----------------
+ * Timeouts for each shutdown method can be configured in
+ the registry.
+
Changes since 2.16
-----------------
* NSSM can now redirect the service's I/O streams to any path
Since version 2.17, NSSM can redirect the managed application's I/O streams\r
to an arbitrary path.\r
\r
+Since version 2.18, NSSM can be configured to wait a user-specified amount\r
+of time for the application to exit when shutting down.\r
+\r
\r
Usage\r
-----\r
does not call TerminateProcess() it is possible that the application will not\r
exit when the service stops.\r
\r
+By default NSSM will allow processes 1500ms to respond to each of the methods\r
+described above before proceeding to the next one. The timeout can be\r
+configured on a per-method basis by creating REG_DWORD entries in the\r
+registry under HKLM\SYSTEM\CurrentControlSet\Services\<service>\Parameters.\r
+\r
+ AppStopMethodConsole\r
+ AppStopMethodWindow\r
+ AppStopMethodThreads\r
+\r
+Each value should be set to the number of milliseconds to wait. Please note\r
+that the timeout applies to each process in the application's process tree,\r
+so the actual time to shutdown may be longer than the sum of all configured\r
+timeouts if the application spawns multiple subprocesses.\r
+\r
\r
I/O redirection\r
---------------\r
Thanks to Eric Cheldelin for the inspiration to generate a Control-C event\r
on shutdown.\r
Thanks to Brian Baxter for suggesting how to escape quotes from the command prompt.\r
+Thanks to Russ Holmann for suggesting that the shutdown timeout be configurable.\r
\r
Licence\r
-------\r
Chiamata a GetProcAddress(%1) fallita:
%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.
+.
\r
/*\r
How many milliseconds to wait for the application to die after sending\r
- a Control-C event to its console.\r
+ a Control-C event to its console. Override in registry.\r
*/\r
#define NSSM_KILL_CONSOLE_GRACE_PERIOD 1500\r
/*\r
How many milliseconds to wait for the application to die after posting to\r
- its windows' message queues.\r
+ its windows' message queues. Override in registry.\r
*/\r
#define NSSM_KILL_WINDOW_GRACE_PERIOD 1500\r
/*\r
How many milliseconds to wait for the application to die after posting to\r
- its threads' message queues.\r
+ its threads' message queues. Override in registry.\r
*/\r
#define NSSM_KILL_THREADS_GRACE_PERIOD 1500\r
\r
#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;
if (stop_method & NSSM_STOP_METHOD_WINDOW) {
EnumWindows((WNDENUMPROC) kill_window, (LPARAM) &k);
if (k.signalled) {
- if (! WaitForSingleObject(process_handle, NSSM_KILL_WINDOW_GRACE_PERIOD)) return 1;
+ if (! WaitForSingleObject(process_handle, kill_window_delay)) return 1;
}
}
*/
if (stop_method & NSSM_STOP_METHOD_THREADS) {
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;
}
}
}
/* 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;
}
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, STARTUPINFO *si) {\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
unsigned long ret;\r
\r
/* Get registry */\r
*stop_method = ~0;\r
if (stop_ok) *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
+\r
/* Close registry */\r
RegCloseKey(key);\r
\r
#define NSSM_REG_EXIT "AppExit"\r
#define NSSM_REG_THROTTLE "AppThrottle"\r
#define NSSM_REG_STOP_METHOD_SKIP "AppStopMethodSkip"\r
+#define NSSM_REG_KILL_CONSOLE_GRACE_PERIOD "AppStopMethodConsole"\r
+#define NSSM_REG_KILL_WINDOW_GRACE_PERIOD "AppStopMethodWindow"\r
+#define NSSM_REG_KILL_THREADS_GRACE_PERIOD "AppStopMethodThreads"\r
#define NSSM_REG_STDIN "AppStdin"\r
#define NSSM_REG_STDOUT "AppStdout"\r
#define NSSM_REG_STDERR "AppStderr"\r
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 *, STARTUPINFO *);\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_exit_action(char *, unsigned long *, unsigned char *, bool *);\r
\r
#endif\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
\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, &si);\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
if (ret) {\r
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);\r
return stop_service(2, true, true);\r
if (graceful) {\r
service_status.dwCurrentState = SERVICE_STOP_PENDING;\r
service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
- if (stop_method & NSSM_STOP_METHOD_CONSOLE && imports.AttachConsole) service_status.dwWaitHint += NSSM_KILL_CONSOLE_GRACE_PERIOD;\r
- if (stop_method & NSSM_STOP_METHOD_WINDOW) service_status.dwWaitHint += NSSM_KILL_WINDOW_GRACE_PERIOD;\r
- if (stop_method & NSSM_STOP_METHOD_THREADS) service_status.dwWaitHint += NSSM_KILL_THREADS_GRACE_PERIOD;\r
+ if (stop_method & NSSM_STOP_METHOD_CONSOLE && imports.AttachConsole) service_status.dwWaitHint += kill_console_delay;\r
+ if (stop_method & NSSM_STOP_METHOD_WINDOW) service_status.dwWaitHint += kill_window_delay;\r
+ if (stop_method & NSSM_STOP_METHOD_THREADS) service_status.dwWaitHint += kill_threads_delay;\r
SetServiceStatus(service_handle, &service_status);\r
}\r
\r