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
 ------------------
 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
     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 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
 \r
 \r
 Usage\r
@@ -423,6 +423,17 @@ is in two stages as follows.
     nssm set <servicename> Type SERVICE_INTERACTIVE_PROCESS\r
 \r
 \r
     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
 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
 \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
     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
   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
 /* 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 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
 void set_service_recovery(nssm_service_t *);\r
 int monitor_service(nssm_service_t *);\r
 int start_service(nssm_service_t *);\r