Browse Source

Allocate a new console for stdin.

Allocate a new console for processes which require stdin instead of
using the pipe hack.
Iain Patterson 8 years ago
parent
commit
54438652fa
5 changed files with 55 additions and 69 deletions
  1. 0 7
      README.txt
  2. 53 49
      io.cpp
  3. 2 7
      process.cpp
  4. 0 5
      service.cpp
  5. 0 1
      service.h

+ 0 - 7
README.txt

@@ -288,13 +288,6 @@ 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.
 
-Note that if you set AppStdout and/or AppStderr, applications which attempt
-to read stdin will fail due to a combination of factors including the way I/O
-redirection is configured on Windows and how a console application starts in
-a service context.  NSSM can fake a stdin stream so that applications can
-still work when they would otherwise exit when at end of file on stdin.  Set
-AppStdin to "|" (a single pipe character) to invoke the fake stdin.
-
 
 File rotation
 -------------

+ 53 - 49
io.cpp

@@ -231,12 +231,23 @@ void rotate_file(TCHAR *service_name, TCHAR *path, unsigned long seconds, unsign
 }
 
 int get_output_handles(nssm_service_t *service, HKEY key, STARTUPINFO *si) {
-  bool set_flags = false;
+  bool redirect = false;
 
-  /* Standard security attributes allowing inheritance. */
-  SECURITY_ATTRIBUTES attributes;
-  ZeroMemory(&attributes, sizeof(attributes));
-  attributes.bInheritHandle = true;
+  /* stdin */
+  if (get_createfile_parameters(key, NSSM_REG_STDIN, service->stdin_path, &service->stdin_sharing, NSSM_STDIN_SHARING, &service->stdin_disposition, NSSM_STDIN_DISPOSITION, &service->stdin_flags, NSSM_STDIN_FLAGS)) {
+    service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
+    ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
+    return 1;
+  }
+  if (si && service->stdin_path[0]) {
+    si->hStdInput = CreateFile(service->stdin_path, FILE_READ_DATA, service->stdin_sharing, 0, service->stdin_disposition, service->stdin_flags, 0);
+    if (! si->hStdInput) {
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stdin_path, error_string(GetLastError()), 0);
+      return 2;
+    }
+
+    redirect = true;
+  }
 
   /* stdout */
   if (get_createfile_parameters(key, NSSM_REG_STDOUT, service->stdout_path, &service->stdout_sharing, NSSM_STDOUT_SHARING, &service->stdout_disposition, NSSM_STDOUT_DISPOSITION, &service->stdout_flags, NSSM_STDOUT_FLAGS)) {
@@ -267,7 +278,7 @@ int get_output_handles(nssm_service_t *service, HKEY key, STARTUPINFO *si) {
       service->rotate_stdout_online = NSSM_ROTATE_OFFLINE;
     }
 
-    set_flags = true;
+    redirect = true;
   }
 
   /* stderr */
@@ -315,55 +326,28 @@ int get_output_handles(nssm_service_t *service, HKEY key, STARTUPINFO *si) {
         service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
       }
 
-      set_flags = true;
+      redirect = true;
     }
   }
 
-  /* stdin */
-  if (get_createfile_parameters(key, NSSM_REG_STDIN, service->stdin_path, &service->stdin_sharing, NSSM_STDIN_SHARING, &service->stdin_disposition, NSSM_STDIN_DISPOSITION, &service->stdin_flags, NSSM_STDIN_FLAGS)) {
-    service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
-    ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
-    return 1;
-  }
-  if (si && service->stdin_path[0]) {
-    if (str_equiv(service->stdin_path, _T("|"))) {
-      /* Fake stdin with a pipe. */
-      if (set_flags) {
-        /*
-          None of this is necessary if we aren't redirecting stdout and/or
-          stderr as well.
-
-          If we don't redirect any handles the application will start and be
-          quite happy with its console.  If we start it with
-          STARTF_USESTDHANDLES set it will interpret a NULL value for
-          hStdInput as meaning no input.  Because the service starts with
-          no stdin we can't just pass GetStdHandle(STD_INPUT_HANDLE) to
-          the application.
-
-          The only way we can successfully redirect the application's output
-          while preventing programs which exit after reading all input from
-          exiting prematurely is to create a pipe between ourselves and the
-          application but write nothing to it.
-        */
-        if (! CreatePipe(&si->hStdInput, &service->stdin_pipe, 0, 0)) {
-          log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_STDIN_CREATEPIPE_FAILED, service->name, error_string(GetLastError()), 0);
-          return 2;
-        }
-        SetHandleInformation(si->hStdInput, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
-      }
-    }
-    else {
-      si->hStdInput = CreateFile(service->stdin_path, FILE_READ_DATA, service->stdin_sharing, &attributes, service->stdin_disposition, service->stdin_flags, 0);
-      if (! si->hStdInput) {
-        log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stdin_path, error_string(GetLastError()), 0);
-        return 2;
-      }
+  if (! redirect || ! si) return 0;
 
-      set_flags = true;
-    }
+  /* Allocate a new console so we get a fresh stdin, stdout and stderr. */
+  FreeConsole();
+  AllocConsole();
+
+  /* Set a title like "[NSSM] Jenkins" */
+  TCHAR displayname[SERVICE_NAME_LENGTH];
+  unsigned long len = _countof(displayname);
+  SC_HANDLE services = open_service_manager();
+  if (services) {
+    if (! GetServiceDisplayName(services, service->name, displayname, &len)) _sntprintf_s(displayname, _countof(displayname), _TRUNCATE, _T("%s"), service->name);
+    CloseServiceHandle(services);
   }
 
-  if (! set_flags) return 0;
+  TCHAR title[65535];
+  _sntprintf_s(title, _countof(title), _TRUNCATE, _T("[%s] %s\n"), NSSM, displayname);
+  SetConsoleTitle(title);
 
   /*
     We need to set the startup_info flags to make the new handles
@@ -371,6 +355,26 @@ int get_output_handles(nssm_service_t *service, HKEY key, STARTUPINFO *si) {
   */
   if (si) si->dwFlags |= STARTF_USESTDHANDLES;
 
+  /* Redirect other handles. */
+  if (! si->hStdInput) {
+    if (! DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), GetCurrentProcess(), &si->hStdInput, 0, true, DUPLICATE_SAME_ACCESS)) {
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, _T("STD_INPUT_HANDLE"), _T("stdin"), error_string(GetLastError()), 0);
+      return 8;
+    }
+  }
+  if (! si->hStdOutput) {
+    if (! DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_OUTPUT_HANDLE), GetCurrentProcess(), &si->hStdOutput, 0, true, DUPLICATE_SAME_ACCESS)) {
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, _T("STD_OUTPUT_HANDLE"), _T("stdout"), error_string(GetLastError()), 0);
+      return 9;
+    }
+  }
+  if (! si->hStdError)  {
+    if (! DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_ERROR_HANDLE), GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_SAME_ACCESS)) {
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, _T("STD_ERROR_HANDLE"), _T("stderr"), error_string(GetLastError()), 0);
+      return 10;
+    }
+  }
+
   return 0;
 }
 

+ 2 - 7
process.cpp

@@ -149,13 +149,6 @@ int kill_process(nssm_service_t *service, HANDLE process_handle, unsigned long p
 
   kill_t k = { pid, exitcode, 0 };
 
-  /* Close the stdin pipe. */
-  if (service->stdin_pipe) {
-    CloseHandle(service->stdin_pipe);
-    service->stdin_pipe = 0;
-    if (! await_shutdown(service, _T(__FUNCTION__), service->kill_console_delay)) return 1;
-  }
-
   /* Try to send a Control-C event to the console. */
   if (service->stop_method & NSSM_STOP_METHOD_CONSOLE) {
     if (! kill_console(service)) return 1;
@@ -215,6 +208,8 @@ int kill_console(nssm_service_t *service) {
         return 2;
 
       case ERROR_ACCESS_DENIED:
+        /* Maybe we already allocated a console for output. */
+        if (service->stdin_path[0] || service->stdout_path[0] || service->stderr_path[0]) break;
       default:
         /* We already have a console. */
         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ATTACHCONSOLE_FAILED, service->name, error_string(ret), 0);

+ 0 - 5
service.cpp

@@ -1530,7 +1530,6 @@ int start_service(nssm_service_t *service) {
   bool inherit_handles = false;
   if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
   unsigned long flags = service->priority & priority_mask();
-  if (service->stdin_pipe) flags |= DETACHED_PROCESS;
   if (service->affinity) flags |= CREATE_SUSPENDED;
   if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
     unsigned long exitcode = 3;
@@ -1620,10 +1619,6 @@ int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful,
     UnregisterWait(service->wait_handle);
     service->wait_handle = 0;
   }
-  if (service->stdin_pipe) {
-    CloseHandle(service->stdin_pipe);
-    service->stdin_pipe = 0;
-  }
 
   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
 

+ 0 - 1
service.h

@@ -57,7 +57,6 @@ typedef struct {
   unsigned long stdin_sharing;
   unsigned long stdin_disposition;
   unsigned long stdin_flags;
-  HANDLE stdin_pipe;
   TCHAR stdout_path[PATH_LENGTH];
   unsigned long stdout_sharing;
   unsigned long stdout_disposition;