Browse Source

Allow controlling services.

Send start/stop/query messages to services is easy so adding the ability
to do so is a quick win.
Iain Patterson 8 years ago
parent
commit
9cf66b8684
6 changed files with 103 additions and 3 deletions
  1. 1 1
      ChangeLog.txt
  2. 12 1
      README.txt
  3. BIN
      messages.mc
  4. 9 1
      nssm.cpp
  5. 80 0
      service.cpp
  6. 1 0
      service.h

+ 1 - 1
ChangeLog.txt

@@ -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

+ 12 - 1
README.txt

@@ -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
 type and log on details.
 
-Since version 2.22, NSSM can edit existing services.
+Since version 2.22, NSSM can manage existing services.
 
 
 Usage
@@ -423,6 +423,17 @@ is in two stages as follows.
     nssm set <servicename> Type SERVICE_INTERACTIVE_PROCESS
 
 
+Controlling services using the command line
+-------------------------------------------
+NSSM offers rudimentary service control features.
+
+    nssm start <servicename>
+
+    nssm stop <servicename>
+
+    nssm status <servicename>
+
+
 Removing services using the GUI
 -------------------------------
 NSSM can also remove services.  Run

BIN
messages.mc


+ 9 - 1
nssm.cpp

@@ -79,7 +79,15 @@ int _tmain(int argc, TCHAR **argv) {
 
   /* Elevate */
   if (argc > 1) {
-    /* Valid commands are install, edit, get, set, reset, unset or remove */
+    /*
+      Valid commands are:
+      start, stop, pause, continue, install, edit, get, set, reset, unset, remove
+    */
+    if (str_equiv(argv[1], _T("start"))) exit(control_service(0, argc - 2, argv + 2));
+    if (str_equiv(argv[1], _T("stop"))) exit(control_service(SERVICE_CONTROL_STOP, argc - 2, argv + 2));
+    if (str_equiv(argv[1], _T("pause"))) exit(control_service(SERVICE_CONTROL_PAUSE, argc - 2, argv + 2));
+    if (str_equiv(argv[1], _T("continue"))) exit(control_service(SERVICE_CONTROL_CONTINUE, argc - 2, argv + 2));
+    if (str_equiv(argv[1], _T("status"))) exit(control_service(SERVICE_CONTROL_INTERROGATE, argc - 2, argv + 2));
     if (str_equiv(argv[1], _T("install"))) {
       if (! is_admin) {
         print_message(stderr, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_INSTALL);

+ 80 - 0
service.cpp

@@ -802,6 +802,86 @@ int edit_service(nssm_service_t *service, bool editing) {
   return 0;
 }
 
+/* Control a service. */
+int control_service(unsigned long control, int argc, TCHAR **argv) {
+  if (argc < 1) return usage(1);
+  TCHAR *service_name = argv[0];
+
+  SC_HANDLE services = open_service_manager();
+  if (! services) {
+    print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
+    return 2;
+  }
+
+  SC_HANDLE service_handle = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);
+  if (! service_handle) {
+    print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);
+    CloseServiceHandle(services);
+    return 3;
+  }
+
+  int ret;
+  unsigned long error;
+  SERVICE_STATUS service_status;
+  if (control == 0) {
+    ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
+    error = GetLastError();
+    CloseHandle(service_handle);
+    CloseServiceHandle(services);
+
+    if (ret) {
+      _tprintf(_T("%s: %s"), canonical_name, error_string(error));
+      return 0;
+    }
+    else {
+      _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));
+      return 1;
+    }
+  }
+  else if (control == SERVICE_CONTROL_INTERROGATE) {
+    /*
+      We could actually send an INTERROGATE control but that won't return
+      any information if the service is stopped and we don't care about
+      the extra details it might give us in any case.  So we'll fake it.
+    */
+    ret = QueryServiceStatus(service_handle, &service_status);
+    error = GetLastError();
+
+    if (ret) {
+      switch (service_status.dwCurrentState) {
+        case SERVICE_STOPPED: _tprintf(_T("SERVICE_STOPPED\n")); break;
+        case SERVICE_START_PENDING: _tprintf(_T("SERVICE_START_PENDING\n")); break;
+        case SERVICE_STOP_PENDING: _tprintf(_T("SERVICE_STOP_PENDING\n")); break;
+        case SERVICE_RUNNING: _tprintf(_T("SERVICE_RUNNING\n")); break;
+        case SERVICE_CONTINUE_PENDING: _tprintf(_T("SERVICE_CONTINUE_PENDING\n")); break;
+        case SERVICE_PAUSE_PENDING: _tprintf(_T("SERVICE_PAUSE_PENDING\n")); break;
+        case SERVICE_PAUSED: _tprintf(_T("SERVICE_PAUSED\n")); break;
+        default: _tprintf(_T("?\n")); return 1;
+      }
+      return 0;
+    }
+    else {
+      _ftprintf(stderr, _T("%s: %s\n"), service_name, error_string(error));
+      return 1;
+    }
+  }
+  else {
+    ret = ControlService(service_handle, control, &service_status);
+    error = GetLastError();
+    CloseHandle(service_handle);
+    CloseServiceHandle(services);
+
+    if (ret) {
+      _tprintf(_T("%s: %s"), canonical_name, error_string(error));
+      return 0;
+    }
+    else {
+      _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));
+      return 1;
+    }
+  }
+}
+
 /* Remove the service */
 int remove_service(nssm_service_t *service) {
   if (! service) return 1;

+ 1 - 0
service.h

@@ -108,6 +108,7 @@ int pre_edit_service(int, TCHAR **);
 int install_service(nssm_service_t *);
 int remove_service(nssm_service_t *);
 int edit_service(nssm_service_t *, bool);
+int control_service(unsigned long, int, TCHAR **);
 void set_service_recovery(nssm_service_t *);
 int monitor_service(nssm_service_t *);
 int start_service(nssm_service_t *);