Kaynağa Gözat

Allow specifying output streams.

Allow starting the monitor application with one or more of stdin,
stdout and stderr redirected to a file or anything which can be
opened with CreateFile().

New registry values corresponding to CreateFile() arguments for
stdin (and analogously for stdout and stderr):

AppStdin: Path to open.
AppStdinShareMode: Sharing mode.
AppStdinCreationDisposition: Creation disposition.
AppStdinFlagsAndAtrributes: Flags and attributes.

All are optional.  If no path is given for a particular stream it
will not be redirected.  If a path is given but any of the other
values are omitted they will receive sensible defaults.
Iain Patterson 11 yıl önce
ebeveyn
işleme
40792fac2e
8 değiştirilmiş dosya ile 112 ekleme ve 4 silme
  1. 3 0
      ChangeLog.txt
  2. 31 0
      README.txt
  3. 46 0
      messages.mc
  4. 1 0
      nssm.h
  5. 8 0
      nssm.vcproj
  6. 8 1
      registry.cpp
  7. 8 1
      registry.h
  8. 7 2
      service.cpp

+ 3 - 0
ChangeLog.txt

@@ -1,5 +1,8 @@
 Changes since 2.16
 Changes since 2.16
 -----------------
 -----------------
+  * NSSM can now redirect the service's I/O streams to any path
+	  capable of being opened by CreateFile().
+
   * Allow building on Visual Studio Express.
   * Allow building on Visual Studio Express.
 
 
   * Silently ignore INTERROGATE control.
   * Silently ignore INTERROGATE control.

+ 31 - 0
README.txt

@@ -40,6 +40,10 @@ Since version 2.17, NSSM can try to shut down console applications by
 simulating a Control-C keypress.  If they have installed a handler routine
 simulating a Control-C keypress.  If they have installed a handler routine
 they can clean up and shut down gracefully on receipt of the event.
 they can clean up and shut down gracefully on receipt of the event.
 
 
+Since version 2.17, NSSM can redirect the managed application's I/O streams
+to an arbitrary path.
+
+
 Usage
 Usage
 -----
 -----
 In the usage notes below, arguments to the program may be written in angle 
 In the usage notes below, arguments to the program may be written in angle 
@@ -133,6 +137,33 @@ request to suicide if you explicitly configure a registry key for exit code 0.
 If only the default action is set to Suicide NSSM will instead exit gracefully.
 If only the default action is set to Suicide NSSM will instead exit gracefully.
 
 
 
 
+I/O redirection
+---------------
+NSSM can redirect the managed application's I/O to any path capable of being
+opened by CreateFile().  This enables, for example, capturing the log output
+of an application which would otherwise only write to the console or accepting
+input from a serial port.
+
+NSSM will look in the registry under
+HKLM\SYSTEM\CurrentControlSet\Services\<service>\Parameters for the keys
+corresponding to arguments to CreateFile().  All are optional.  If no path is
+given for a particular stream it will not be redirected.  If a path is given
+but any of the other values are omitted they will be receive sensible defaults.
+
+  AppStdin: Path to receive input.
+  AppStdout: Path to receive output.
+  AppStderr: Path to receive error output.
+
+Parameters for CreateFile() are providing with the "AppStdinShareMode",
+"AppStdinCreationDisposition" and "AppStdinFlagsAndAttributes" values (and
+analogously for stdout and stderr).
+
+In general, if you want the service to log its output, set AppStdout and
+AppStderr to the same path, eg C:\Users\Public\service.log, and it should
+work.  Remember, however, that the path must be accessible to the user
+running the service.
+
+
 Removing services using the GUI
 Removing services using the GUI
 -------------------------------
 -------------------------------
 NSSM can also remove services.  Run
 NSSM can also remove services.  Run

+ 46 - 0
messages.mc

@@ -1192,3 +1192,49 @@ Error detaching from console for service %1.
 FreeConsole() fallita:
 FreeConsole() fallita:
 %2
 %2
 .
 .
+
+MessageId = +1
+SymbolicName = NSSM_EVENT_CREATEFILE_FAILED
+Severity = Error
+Language = English
+CreateFile() failed to open %1:
+%2
+.
+Language = French
+CreateFile() a échoué %1:
+%2
+.
+Language = Italian
+Chiamata a CreateFile() fallita %1:
+%2
+.
+
+MessageId = +1
+SymbolicName = NSSM_EVENT_DUPLICATEHANDLE_FAILED
+Severity = Error
+Language = English
+Error duplicating the filehandle previously opened for %1 as %2.
+DuplicateHandle() failed:
+%3
+.
+Language = French
+DuplicateHandle() a échoué (%1 -> %2):
+%3
+.
+Language = Italian
+Chiamata a DuplicateHandle() fallita (%1 -> %2):
+%3
+.
+
+MessageId = +1
+SymbolicName = NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED
+Severity = Error
+Language = English
+Error setting up one or more I/O filehandles.  Service %1 will not be started.
+.
+Language = French
+Error setting up one or more I/O filehandles.  Service %1 will not be started.
+.
+Language = Italian
+Error setting up one or more I/O filehandles.  Service %1 will not be started.
+.

+ 1 - 0
nssm.h

@@ -10,6 +10,7 @@
 #include "messages.h"
 #include "messages.h"
 #include "process.h"
 #include "process.h"
 #include "registry.h"
 #include "registry.h"
+#include "io.h"
 #include "service.h"
 #include "service.h"
 #include "gui.h"
 #include "gui.h"
 
 

+ 8 - 0
nssm.vcproj

@@ -466,6 +466,10 @@
 					/>
 					/>
 				</FileConfiguration>
 				</FileConfiguration>
 			</File>
 			</File>
+			<File
+				RelativePath=".\io.cpp"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="nssm.cpp"
 				RelativePath="nssm.cpp"
 				>
 				>
@@ -623,6 +627,10 @@
 				RelativePath="gui.h"
 				RelativePath="gui.h"
 				>
 				>
 			</File>
 			</File>
+			<File
+				RelativePath=".\io.h"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="nssm.h"
 				RelativePath="nssm.h"
 				>
 				>

+ 8 - 1
registry.cpp

@@ -219,7 +219,7 @@ int get_number(HKEY key, char *value, unsigned long *number) {
   return get_number(key, value, number, true);
   return get_number(key, value, number, true);
 }
 }
 
 
-int get_parameters(char *service_name, char *exe, int exelen, char *flags, int flagslen, char *dir, int dirlen, char **env, unsigned long *throttle_delay) {
+int get_parameters(char *service_name, char *exe, int exelen, char *flags, int flagslen, char *dir, int dirlen, char **env, unsigned long *throttle_delay, STARTUPINFO *si) {
   unsigned long ret;
   unsigned long ret;
 
 
   /* Get registry */
   /* Get registry */
@@ -272,6 +272,13 @@ int get_parameters(char *service_name, char *exe, int exelen, char *flags, int f
   /* Try to get environment variables - may fail */
   /* Try to get environment variables - may fail */
   set_environment(service_name, key, env);
   set_environment(service_name, key, env);
 
 
+  /* Try to get stdout and stderr */
+  if (get_output_handles(key, si)) {
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service_name, 0);
+    RegCloseKey(key);
+    return 5;
+  }
+
   /* Try to get throttle restart delay */
   /* Try to get throttle restart delay */
   unsigned long type = REG_DWORD;
   unsigned long type = REG_DWORD;
   unsigned long buflen = sizeof(*throttle_delay);
   unsigned long buflen = sizeof(*throttle_delay);

+ 8 - 1
registry.h

@@ -8,6 +8,13 @@
 #define NSSM_REG_ENV "AppEnvironment"
 #define NSSM_REG_ENV "AppEnvironment"
 #define NSSM_REG_EXIT "AppExit"
 #define NSSM_REG_EXIT "AppExit"
 #define NSSM_REG_THROTTLE "AppThrottle"
 #define NSSM_REG_THROTTLE "AppThrottle"
+#define NSSM_REG_STDIN "AppStdin"
+#define NSSM_REG_STDOUT "AppStdout"
+#define NSSM_REG_STDERR "AppStderr"
+#define NSSM_REG_STDIO_SHARING "ShareMode"
+#define NSSM_REG_STDIO_DISPOSITION "CreationDisposition"
+#define NSSM_REG_STDIO_FLAGS "FlagsAndAttributes"
+#define NSSM_STDIO_LENGTH 29
 
 
 int create_messages();
 int create_messages();
 int create_parameters(char *, char *, char *, char *);
 int create_parameters(char *, char *, char *, char *);
@@ -17,7 +24,7 @@ int expand_parameter(HKEY, char *, char *, unsigned long, bool, bool);
 int expand_parameter(HKEY, char *, char *, unsigned long, bool);
 int expand_parameter(HKEY, char *, char *, unsigned long, bool);
 int get_number(HKEY, char *, unsigned long *, bool);
 int get_number(HKEY, char *, unsigned long *, bool);
 int get_number(HKEY, char *, unsigned long *);
 int get_number(HKEY, char *, unsigned long *);
-int get_parameters(char *, char *, int, char *, int, char *, int, char **, unsigned long *);
+int get_parameters(char *, char *, int, char *, int, char *, int, char **, unsigned long *, STARTUPINFO *);
 int get_exit_action(char *, unsigned long *, unsigned char *, bool *);
 int get_exit_action(char *, unsigned long *, unsigned char *, bool *);
 
 
 #endif
 #endif

+ 7 - 2
service.cpp

@@ -372,7 +372,7 @@ int start_service() {
 
 
   /* Get startup parameters */
   /* Get startup parameters */
   char *env = 0;
   char *env = 0;
-  int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay);
+  int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay, &si);
   if (ret) {
   if (ret) {
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);
     return stop_service(2, true, true);
     return stop_service(2, true, true);
@@ -382,15 +382,18 @@ int start_service() {
   char cmd[CMD_LENGTH];
   char cmd[CMD_LENGTH];
   if (_snprintf(cmd, sizeof(cmd), "\"%s\" %s", exe, flags) < 0) {
   if (_snprintf(cmd, sizeof(cmd), "\"%s\" %s", exe, flags) < 0) {
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
+    close_output_handles(&si);
     return stop_service(2, true, true);
     return stop_service(2, true, true);
   }
   }
 
 
   throttle_restart();
   throttle_restart();
 
 
-  if (! CreateProcess(0, cmd, 0, 0, false, 0, env, dir, &si, &pi)) {
+  bool inherit_handles = (si.dwFlags & STARTF_USESTDHANDLES);
+  if (! CreateProcess(0, cmd, 0, 0, inherit_handles, 0, env, dir, &si, &pi)) {
     unsigned long error = GetLastError();
     unsigned long error = GetLastError();
     if (error == ERROR_INVALID_PARAMETER && env) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service_name, exe, NSSM_REG_ENV, 0);
     if (error == ERROR_INVALID_PARAMETER && env) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service_name, exe, NSSM_REG_ENV, 0);
     else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(error), 0);
     else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(error), 0);
+    close_output_handles(&si);
     return stop_service(3, true, true);
     return stop_service(3, true, true);
   }
   }
   process_handle = pi.hProcess;
   process_handle = pi.hProcess;
@@ -398,6 +401,8 @@ int start_service() {
 
 
   if (get_process_creation_time(process_handle, &creation_time)) ZeroMemory(&creation_time, sizeof(creation_time));
   if (get_process_creation_time(process_handle, &creation_time)) ZeroMemory(&creation_time, sizeof(creation_time));
 
 
+  close_output_handles(&si);
+
   /* Signal successful start */
   /* Signal successful start */
   service_status.dwCurrentState = SERVICE_RUNNING;
   service_status.dwCurrentState = SERVICE_RUNNING;
   SetServiceStatus(service_handle, &service_status);
   SetServiceStatus(service_handle, &service_status);