Single waiting function.
[nssm.git] / service.cpp
index 5b10233..406196b 100644 (file)
@@ -689,6 +689,7 @@ void set_nssm_service_defaults(nssm_service_t *service) {
   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
+  service->kill_process_tree = 1;\r
 }\r
 \r
 /* Allocate and zero memory for a service. */\r
@@ -709,9 +710,9 @@ void cleanup_nssm_service(nssm_service_t *service) {
   if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies);\r
   if (service->env) HeapFree(GetProcessHeap(), 0, service->env);\r
   if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);\r
-  if (service->handle) CloseHandle(service->handle);\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->wait_handle) UnregisterWait(service->wait_handle);\r
   if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);\r
   if (service->throttle_timer) CloseHandle(service->throttle_timer);\r
   if (service->initial_env) FreeEnvironmentStrings(service->initial_env);\r
@@ -856,7 +857,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
   /* Get system details. */\r
   QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);\r
   if (! qsc) {\r
-    CloseHandle(service->handle);\r
+    CloseServiceHandle(service->handle);\r
     CloseServiceHandle(services);\r
     return 4;\r
   }\r
@@ -865,7 +866,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
   if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {\r
     if (mode != MODE_GETTING) {\r
       HeapFree(GetProcessHeap(), 0, qsc);\r
-      CloseHandle(service->handle);\r
+      CloseServiceHandle(service->handle);\r
       CloseServiceHandle(services);\r
       print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);\r
       return 3;\r
@@ -875,7 +876,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
   if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {\r
     if (mode != MODE_GETTING) {\r
       HeapFree(GetProcessHeap(), 0, qsc);\r
-      CloseHandle(service->handle);\r
+      CloseServiceHandle(service->handle);\r
       CloseServiceHandle(services);\r
       return 4;\r
     }\r
@@ -884,7 +885,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
   if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {\r
     if (mode != MODE_GETTING) {\r
       HeapFree(GetProcessHeap(), 0, qsc);\r
-      CloseHandle(service->handle);\r
+      CloseServiceHandle(service->handle);\r
       CloseServiceHandle(services);\r
       return 5;\r
     }\r
@@ -903,7 +904,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
   /* Get extended system details. */\r
   if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {\r
     if (mode != MODE_GETTING) {\r
-      CloseHandle(service->handle);\r
+      CloseServiceHandle(service->handle);\r
       CloseServiceHandle(services);\r
       return 6;\r
     }\r
@@ -911,7 +912,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
 \r
   if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {\r
     if (mode != MODE_GETTING) {\r
-      CloseHandle(service->handle);\r
+      CloseServiceHandle(service->handle);\r
       CloseServiceHandle(services);\r
       return 7;\r
     }\r
@@ -935,7 +936,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
 \r
   /* Trying to manage App* parameters for a non-NSSM service. */\r
   if (! setting->native && service->native) {\r
-    CloseHandle(service->handle);\r
+    CloseServiceHandle(service->handle);\r
     print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);\r
     return 1;\r
   }\r
@@ -953,7 +954,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
     if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);\r
     else ret = get_setting(service->name, key, setting, &value, additional);\r
     if (ret < 0) {\r
-      CloseHandle(service->handle);\r
+      CloseServiceHandle(service->handle);\r
       return 5;\r
     }\r
 \r
@@ -971,7 +972,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
     }\r
 \r
     if (! service->native) RegCloseKey(key);\r
-    CloseHandle(service->handle);\r
+    CloseServiceHandle(service->handle);\r
     return 0;\r
   }\r
 \r
@@ -993,7 +994,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
     value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));\r
     if (! value.string) {\r
       print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));\r
-      CloseHandle(service->handle);\r
+      CloseServiceHandle(service->handle);\r
       return 2;\r
     }\r
 \r
@@ -1026,12 +1027,12 @@ int pre_edit_service(int argc, TCHAR **argv) {
   if (value.string) HeapFree(GetProcessHeap(), 0, value.string);\r
   if (ret < 0) {\r
     if (! service->native) RegCloseKey(key);\r
-    CloseHandle(service->handle);\r
+    CloseServiceHandle(service->handle);\r
     return 6;\r
   }\r
 \r
   if (! service->native) RegCloseKey(key);\r
-  CloseHandle(service->handle);\r
+  CloseServiceHandle(service->handle);\r
 \r
   return 0;\r
 }\r
@@ -1119,17 +1120,19 @@ int edit_service(nssm_service_t *service, bool editing) {
     Empty passwords are valid but we won't allow them in the GUI.\r
   */\r
   TCHAR *username = 0;\r
+  TCHAR *canon = 0;\r
   TCHAR *password = 0;\r
   if (service->usernamelen) {\r
     username = service->username;\r
+    if (canonicalise_username(username, &canon)) return 5;\r
     if (service->passwordlen) password = service->password;\r
-    else password = _T("");\r
   }\r
-  else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;\r
+  else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT;\r
 \r
-  if (well_known_username(username)) password = _T("");\r
+  if (well_known_username(canon)) password = _T("");\r
   else {\r
-    if (grant_logon_as_service(username)) {\r
+    if (grant_logon_as_service(canon)) {\r
+      if (canon != username) HeapFree(GetProcessHeap(), 0, canon);\r
       print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);\r
       return 5;\r
     }\r
@@ -1138,10 +1141,12 @@ int edit_service(nssm_service_t *service, bool editing) {
   TCHAR *dependencies = _T("");\r
   if (service->dependencieslen) dependencies = 0; /* Change later. */\r
 \r
-  if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, username, password, service->displayname)) {\r
+  if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) {\r
+    if (canon != username) HeapFree(GetProcessHeap(), 0, canon);\r
     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
     return 5;\r
   }\r
+  if (canon != username) HeapFree(GetProcessHeap(), 0, canon);\r
 \r
   if (service->dependencieslen) {\r
     if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;\r
@@ -1237,7 +1242,7 @@ int control_service(unsigned long control, int argc, TCHAR **argv) {
 \r
     if (ret) {\r
       int response = await_service_control_response(control, service_handle, &service_status, initial_status);\r
-      CloseHandle(service_handle);\r
+      CloseServiceHandle(service_handle);\r
 \r
       if (response) {\r
         print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));\r
@@ -1247,7 +1252,7 @@ int control_service(unsigned long control, int argc, TCHAR **argv) {
       return 0;\r
     }\r
     else {\r
-      CloseHandle(service_handle);\r
+      CloseServiceHandle(service_handle);\r
       _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
       return 1;\r
     }\r
@@ -1283,7 +1288,7 @@ int control_service(unsigned long control, int argc, TCHAR **argv) {
 \r
     if (ret) {\r
       int response = await_service_control_response(control, service_handle, &service_status, initial_status);\r
-      CloseHandle(service_handle);\r
+      CloseServiceHandle(service_handle);\r
 \r
       if (response) {\r
         print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));\r
@@ -1293,7 +1298,7 @@ int control_service(unsigned long control, int argc, TCHAR **argv) {
       return 0;\r
     }\r
     else {\r
-      CloseHandle(service_handle);\r
+      CloseServiceHandle(service_handle);\r
       _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));\r
       if (error == ERROR_SERVICE_NOT_ACTIVE) {\r
         if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;\r
@@ -1358,7 +1363,7 @@ void WINAPI service_main(unsigned long argc, TCHAR **argv) {
   /* 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.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | 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
@@ -1389,6 +1394,7 @@ void WINAPI service_main(unsigned long argc, TCHAR **argv) {
     if (services) {\r
       service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);\r
       set_service_recovery(service);\r
+\r
       CloseServiceHandle(services);\r
     }\r
   }\r
@@ -1456,6 +1462,7 @@ TCHAR *service_control_text(unsigned long control) {
     case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");\r
     case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");\r
     case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");\r
+    case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");\r
     default: return 0;\r
   }\r
 }\r
@@ -1563,6 +1570,15 @@ unsigned long WINAPI service_control_handler(unsigned long control, unsigned lon
       if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;\r
       if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;\r
       return NO_ERROR;\r
+\r
+    case SERVICE_CONTROL_POWEREVENT:\r
+      if (event != PBT_APMRESUMEAUTOMATIC) {\r
+        log_service_control(service->name, control, false);\r
+        return NO_ERROR;\r
+      }\r
+      log_service_control(service->name, control, true);\r
+      end_service((void *) service, false);\r
+      return NO_ERROR;\r
   }\r
 \r
   /* Unknown control */\r
@@ -1623,7 +1639,7 @@ int start_service(nssm_service_t *service) {
     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(service->initial_env);\r
+    duplicate_environment_strings(service->initial_env);\r
     return stop_service(service, exitcode, true, true);\r
   }\r
   service->process_handle = pi.hProcess;\r
@@ -1636,7 +1652,7 @@ int start_service(nssm_service_t *service) {
   if (! service->no_console) FreeConsole();\r
 \r
   /* Restore our environment. */\r
-  duplicate_environment(service->initial_env);\r
+  duplicate_environment_strings(service->initial_env);\r
 \r
   if (service->affinity) {\r
     /*\r
@@ -1672,16 +1688,7 @@ int start_service(nssm_service_t *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 = service->throttle_delay;\r
-  if (delay > NSSM_SERVICE_STATUS_DEADLINE) {\r
-    TCHAR delay_milliseconds[16];\r
-    _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);\r
-    TCHAR deadline_milliseconds[16];\r
-    _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%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
-    delay = NSSM_SERVICE_STATUS_DEADLINE;\r
-  }\r
-  unsigned long deadline = WaitForSingleObject(service->process_handle, delay);\r
+  if (await_service_status_change(service, SERVICE_START_PENDING, _T("start_service"), service->throttle_delay) == 1) service->throttle = 0;\r
 \r
   /* Signal successful start */\r
   service->status.dwCurrentState = SERVICE_RUNNING;\r
@@ -1786,7 +1793,7 @@ void CALLBACK end_service(void *arg, unsigned char why) {
 \r
   /* Clean up. */\r
   if (exitcode == STILL_ACTIVE) exitcode = 0;\r
-  if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);\r
+  if (service->pid && service->kill_process_tree) kill_process_tree(service, service->pid, exitcode, service->pid);\r
   service->pid = 0;\r
 \r
   /*\r
@@ -1912,7 +1919,7 @@ void throttle_restart(nssm_service_t *service) {
            0 if the wait completed.\r
           -1 on error.\r
 */\r
-int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {\r
+int await_service_status_change(nssm_service_t *service, unsigned long status, unsigned long desired, TCHAR *function_name, unsigned long timeout) {\r
   unsigned long interval;\r
   unsigned long waithint;\r
   unsigned long ret;\r
@@ -1937,7 +1944,7 @@ int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long
     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.dwCurrentState = control;\r
     service->status.dwWaitHint += interval;\r
     service->status.dwCheckPoint++;\r
     SetServiceStatus(service->status_handle, &service->status);\r
@@ -1945,7 +1952,7 @@ int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long
     if (waited) {\r
       _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);\r
       _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%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_SERVICE, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, service_status_text(status), 0);\r
     }\r
 \r
     switch (WaitForSingleObject(service->process_handle, interval)) {\r
@@ -1955,7 +1962,7 @@ int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long
 \r
       case WAIT_TIMEOUT:\r
         ret = 1;\r
-      break;\r
+        break;\r
 \r
       default:\r
         ret = -1;\r