return stop_service((nssm_service_t *) arg, 0, true, true);\r
}\r
\r
+/*\r
+ Wrapper to be called in a new thread so that we can acknowledge start\r
+ immediately.\r
+*/\r
+static unsigned long WINAPI launch_service(void *arg) {\r
+ return monitor_service((nssm_service_t *) arg);\r
+}\r
+\r
/* Connect to the service manager */\r
SC_HANDLE open_service_manager(unsigned long access) {\r
SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, access);\r
/* Remember our creation time. */\r
if (get_process_creation_time(GetCurrentProcess(), &service->nssm_creation_time)) ZeroMemory(&service->nssm_creation_time, sizeof(service->nssm_creation_time));\r
\r
- monitor_service(service);\r
+ service->allow_restart = true;\r
+ if (! CreateThread(NULL, 0, launch_service, (void *) service, 0, NULL)) {\r
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);\r
+ stop_service(service, 0, true, true);\r
+ }\r
}\r
\r
/* Make sure service recovery actions are taken where necessary */\r
service->last_control = control;\r
log_service_control(service->name, control, true);\r
\r
- /* Pre-stop hook. */\r
+ /* Immediately block further controls. */\r
+ service->allow_restart = false;\r
service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
+ service->status.dwControlsAccepted = 0;\r
SetServiceStatus(service->status_handle, &service->status);\r
+\r
+ /* Pre-stop hook. */\r
nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_STOP, NSSM_HOOK_ACTION_PRE, &control, NSSM_SERVICE_STATUS_DEADLINE, false);\r
\r
/*\r
/* Start the service */\r
int start_service(nssm_service_t *service) {\r
service->stopping = false;\r
- service->allow_restart = true;\r
\r
if (service->process_handle) return 0;\r
service->start_requested_count++;\r
if (service->env) duplicate_environment(service->env);\r
if (service->env_extra) set_environment_block(service->env_extra);\r
\r
- /* Pre-start hook. */\r
- unsigned long control = NSSM_SERVICE_CONTROL_START;\r
service->status.dwCurrentState = SERVICE_START_PENDING;\r
service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;\r
+ SetServiceStatus(service->status_handle, &service->status);\r
+\r
+ /* Pre-start hook. */\r
+ unsigned long control = NSSM_SERVICE_CONTROL_START;\r
if (nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, &control, NSSM_SERVICE_STATUS_DEADLINE, false) == NSSM_HOOK_STATUS_ABORT) {\r
TCHAR code[16];\r
_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), NSSM_HOOK_STATUS_ABORT);\r
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PRESTART_HOOK_ABORT, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, service->name, code, 0);\r
+ duplicate_environment_strings(service->initial_env);\r
return stop_service(service, 5, true, true);\r
}\r
\r
- /* Set up I/O redirection. */\r
- if (get_output_handles(service, &si)) {\r
- log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);\r
- if (! service->no_console) FreeConsole();\r
- close_output_handles(&si);\r
- return stop_service(service, 4, true, true);\r
- }\r
+ /* Did another thread receive a stop control? */\r
+ if (service->allow_restart) {\r
+ /* Set up I/O redirection. */\r
+ if (get_output_handles(service, &si)) {\r
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);\r
+ if (! service->no_console) FreeConsole();\r
+ close_output_handles(&si);\r
+ duplicate_environment_strings(service->initial_env);\r
+ return stop_service(service, 4, true, true);\r
+ }\r
\r
- bool inherit_handles = false;\r
- if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;\r
- unsigned long flags = service->priority & priority_mask();\r
- if (service->affinity) flags |= CREATE_SUSPENDED;\r
- if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {\r
- unsigned long exitcode = 3;\r
- unsigned long error = GetLastError();\r
- log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);\r
- close_output_handles(&si);\r
- duplicate_environment_strings(service->initial_env);\r
- return stop_service(service, exitcode, true, true);\r
- }\r
- service->start_count++;\r
- service->process_handle = pi.hProcess;\r
- service->pid = pi.dwProcessId;\r
+ bool inherit_handles = false;\r
+ if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;\r
+ unsigned long flags = service->priority & priority_mask();\r
+ if (service->affinity) flags |= CREATE_SUSPENDED;\r
+ if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {\r
+ unsigned long exitcode = 3;\r
+ unsigned long error = GetLastError();\r
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);\r
+ close_output_handles(&si);\r
+ duplicate_environment_strings(service->initial_env);\r
+ return stop_service(service, exitcode, true, true);\r
+ }\r
+ service->start_count++;\r
+ service->process_handle = pi.hProcess;\r
+ service->pid = pi.dwProcessId;\r
\r
- if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->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
+ close_output_handles(&si);\r
\r
- if (! service->no_console) FreeConsole();\r
+ if (! service->no_console) FreeConsole();\r
\r
- /* Restore our environment. */\r
- duplicate_environment_strings(service->initial_env);\r
+ if (service->affinity) {\r
+ /*\r
+ We are explicitly storing service->affinity as a 64-bit unsigned integer\r
+ so that we can parse it regardless of whether we're running in 32-bit\r
+ or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are\r
+ defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system\r
+ (or when running the 32-bit NSSM).\r
+\r
+ The result is a lot of seemingly-unnecessary casting throughout the code\r
+ and potentially confusion when we actually try to start the service.\r
+ Having said that, however, it's unlikely that we're actually going to\r
+ run in 32-bit mode on a system which has more than 32 CPUs so the\r
+ likelihood of seeing a confusing situation is somewhat diminished.\r
+ */\r
+ DWORD_PTR affinity, system_affinity;\r
\r
- if (service->affinity) {\r
- /*\r
- We are explicitly storing service->affinity as a 64-bit unsigned integer\r
- so that we can parse it regardless of whether we're running in 32-bit\r
- or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are\r
- defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system\r
- (or when running the 32-bit NSSM).\r
-\r
- The result is a lot of seemingly-unnecessary casting throughout the code\r
- and potentially confusion when we actually try to start the service.\r
- Having said that, however, it's unlikely that we're actually going to\r
- run in 32-bit mode on a system which has more than 32 CPUs so the\r
- likelihood of seeing a confusing situation is somewhat diminished.\r
- */\r
- DWORD_PTR affinity, system_affinity;\r
+ if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;\r
+ else {\r
+ affinity = (DWORD_PTR) service->affinity;\r
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
+ }\r
\r
- if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;\r
- else {\r
- affinity = (DWORD_PTR) service->affinity;\r
- log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
- }\r
+ if (! SetProcessAffinityMask(service->process_handle, affinity)) {\r
+ log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
+ }\r
\r
- if (! SetProcessAffinityMask(service->process_handle, affinity)) {\r
- log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);\r
+ ResumeThread(pi.hThread);\r
}\r
-\r
- ResumeThread(pi.hThread);\r
}\r
\r
+ /* Restore our environment. */\r
+ duplicate_environment_strings(service->initial_env);\r
+\r
/*\r
Wait for a clean startup before changing the service status to RUNNING\r
but be mindful of the fact that we are blocking the service control manager\r
so abandon the wait before too much time has elapsed.\r
*/\r
- service->status.dwCurrentState = SERVICE_START_PENDING;\r
if (await_single_handle(service->status_handle, &service->status, service->process_handle, service->name, _T("start_service"), service->throttle_delay) == 1) service->throttle = 0;\r
\r
+ /* Did another thread receive a stop control? */\r
+ if (! service->allow_restart) return 0;\r
+\r
/* Signal successful start */\r
service->status.dwCurrentState = SERVICE_RUNNING;\r
service->status.dwControlsAccepted &= ~SERVICE_ACCEPT_PAUSE_CONTINUE;\r
if (graceful) {\r
service->status.dwCurrentState = SERVICE_STOP_PENDING;\r
service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;\r
+ SetServiceStatus(service->status_handle, &service->status);\r
}\r
- service->status.dwControlsAccepted = 0;\r
- SetServiceStatus(service->status_handle, &service->status);\r
\r
/* Nothing to do if service isn't running */\r
if (service->pid) {\r