Allow controlling services.
authorIain Patterson <me@iain.cx>
Wed, 1 Jan 2014 15:14:51 +0000 (15:14 +0000)
committerIain Patterson <me@iain.cx>
Wed, 1 Jan 2014 21:27:59 +0000 (21:27 +0000)
Send start/stop/query messages to services is easy so adding the ability
to do so is a quick win.

ChangeLog.txt
README.txt
messages.mc
nssm.cpp
service.cpp
service.h

index bdc1aed..cfcbce6 100644 (file)
@@ -1,6 +1,6 @@
 Changes since 2.21
 ------------------
-  * Existing services can now be edited using the GUI
+  * Existing services can now be managed using the GUI
     or on the command line.
 
   * NSSM can now optionally rotate existing files when
index a7655d9..9a9a11e 100644 (file)
@@ -58,7 +58,7 @@ Since version 2.22, NSSM can rotate existing output files when redirecting I/O.
 Since version 2.22, NSSM can set service display name, description, startup\r
 type and log on details.\r
 \r
-Since version 2.22, NSSM can edit existing services.\r
+Since version 2.22, NSSM can manage existing services.\r
 \r
 \r
 Usage\r
@@ -423,6 +423,17 @@ is in two stages as follows.
     nssm set <servicename> Type SERVICE_INTERACTIVE_PROCESS\r
 \r
 \r
+Controlling services using the command line\r
+-------------------------------------------\r
+NSSM offers rudimentary service control features.\r
+\r
+    nssm start <servicename>\r
+\r
+    nssm stop <servicename>\r
+\r
+    nssm status <servicename>\r
+\r
+\r
 Removing services using the GUI\r
 -------------------------------\r
 NSSM can also remove services.  Run\r
index aaaeb8c..1be1d0a 100644 (file)
Binary files a/messages.mc and b/messages.mc differ
index 43faa41..792b62b 100644 (file)
--- a/nssm.cpp
+++ b/nssm.cpp
@@ -79,7 +79,15 @@ int _tmain(int argc, TCHAR **argv) {
 \r
   /* Elevate */\r
   if (argc > 1) {\r
-    /* Valid commands are install, edit, get, set, reset, unset or remove */\r
+    /*\r
+      Valid commands are:\r
+      start, stop, pause, continue, install, edit, get, set, reset, unset, remove\r
+    */\r
+    if (str_equiv(argv[1], _T("start"))) exit(control_service(0, argc - 2, argv + 2));\r
+    if (str_equiv(argv[1], _T("stop"))) exit(control_service(SERVICE_CONTROL_STOP, argc - 2, argv + 2));\r
+    if (str_equiv(argv[1], _T("pause"))) exit(control_service(SERVICE_CONTROL_PAUSE, argc - 2, argv + 2));\r
+    if (str_equiv(argv[1], _T("continue"))) exit(control_service(SERVICE_CONTROL_CONTINUE, argc - 2, argv + 2));\r
+    if (str_equiv(argv[1], _T("status"))) exit(control_service(SERVICE_CONTROL_INTERROGATE, argc - 2, argv + 2));\r
     if (str_equiv(argv[1], _T("install"))) {\r
       if (! is_admin) {\r
         print_message(stderr, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_INSTALL);\r
index 296d416..8440038 100644 (file)
@@ -802,6 +802,86 @@ int edit_service(nssm_service_t *service, bool editing) {
   return 0;\r
 }\r
 \r
+/* Control a service. */\r
+int control_service(unsigned long control, int argc, TCHAR **argv) {\r
+  if (argc < 1) return usage(1);\r
+  TCHAR *service_name = argv[0];\r
+\r
+  SC_HANDLE services = open_service_manager();\r
+  if (! services) {\r
+    print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
+    return 2;\r
+  }\r
+\r
+  SC_HANDLE 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
+  int ret;\r
+  unsigned long error;\r
+  SERVICE_STATUS service_status;\r
+  if (control == 0) {\r
+    ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);\r
+    error = GetLastError();\r
+    CloseHandle(service_handle);\r
+    CloseServiceHandle(services);\r
+\r
+    if (ret) {\r
+      _tprintf(_T("%s: %s"), canonical_name, error_string(error));\r
+      return 0;\r
+    }\r
+    else {\r
+      _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));\r
+      return 1;\r
+    }\r
+  }\r
+  else if (control == SERVICE_CONTROL_INTERROGATE) {\r
+    /*\r
+      We could actually send an INTERROGATE control but that won't return\r
+      any information if the service is stopped and we don't care about\r
+      the extra details it might give us in any case.  So we'll fake it.\r
+    */\r
+    ret = QueryServiceStatus(service_handle, &service_status);\r
+    error = GetLastError();\r
+\r
+    if (ret) {\r
+      switch (service_status.dwCurrentState) {\r
+        case SERVICE_STOPPED: _tprintf(_T("SERVICE_STOPPED\n")); break;\r
+        case SERVICE_START_PENDING: _tprintf(_T("SERVICE_START_PENDING\n")); break;\r
+        case SERVICE_STOP_PENDING: _tprintf(_T("SERVICE_STOP_PENDING\n")); break;\r
+        case SERVICE_RUNNING: _tprintf(_T("SERVICE_RUNNING\n")); break;\r
+        case SERVICE_CONTINUE_PENDING: _tprintf(_T("SERVICE_CONTINUE_PENDING\n")); break;\r
+        case SERVICE_PAUSE_PENDING: _tprintf(_T("SERVICE_PAUSE_PENDING\n")); break;\r
+        case SERVICE_PAUSED: _tprintf(_T("SERVICE_PAUSED\n")); break;\r
+        default: _tprintf(_T("?\n")); return 1;\r
+      }\r
+      return 0;\r
+    }\r
+    else {\r
+      _ftprintf(stderr, _T("%s: %s\n"), service_name, error_string(error));\r
+      return 1;\r
+    }\r
+  }\r
+  else {\r
+    ret = ControlService(service_handle, control, &service_status);\r
+    error = GetLastError();\r
+    CloseHandle(service_handle);\r
+    CloseServiceHandle(services);\r
+\r
+    if (ret) {\r
+      _tprintf(_T("%s: %s"), canonical_name, error_string(error));\r
+      return 0;\r
+    }\r
+    else {\r
+      _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));\r
+      return 1;\r
+    }\r
+  }\r
+}\r
+\r
 /* Remove the service */\r
 int remove_service(nssm_service_t *service) {\r
   if (! service) return 1;\r
index 515b108..29c237e 100644 (file)
--- a/service.h
+++ b/service.h
@@ -108,6 +108,7 @@ int pre_edit_service(int, TCHAR **);
 int install_service(nssm_service_t *);\r
 int remove_service(nssm_service_t *);\r
 int edit_service(nssm_service_t *, bool);\r
+int control_service(unsigned long, int, TCHAR **);\r
 void set_service_recovery(nssm_service_t *);\r
 int monitor_service(nssm_service_t *);\r
 int start_service(nssm_service_t *);\r