Allocate a new console for stdin.
authorIain Patterson <me@iain.cx>
Sat, 25 Jan 2014 21:59:57 +0000 (21:59 +0000)
committerIain Patterson <me@iain.cx>
Sat, 25 Jan 2014 22:17:58 +0000 (22:17 +0000)
Allocate a new console for processes which require stdin instead of
using the pipe hack.

README.txt
io.cpp
process.cpp
service.cpp
service.h

index 85bf62e..aa1fc9f 100644 (file)
@@ -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\r
 running the service.\r
 \r
 work.  Remember, however, that the path must be accessible to the user\r
 running the service.\r
 \r
-Note that if you set AppStdout and/or AppStderr, applications which attempt\r
-to read stdin will fail due to a combination of factors including the way I/O\r
-redirection is configured on Windows and how a console application starts in\r
-a service context.  NSSM can fake a stdin stream so that applications can\r
-still work when they would otherwise exit when at end of file on stdin.  Set\r
-AppStdin to "|" (a single pipe character) to invoke the fake stdin.\r
-\r
 \r
 File rotation\r
 -------------\r
 \r
 File rotation\r
 -------------\r
diff --git a/io.cpp b/io.cpp
index 464e290..42e4447 100644 (file)
--- a/io.cpp
+++ b/io.cpp
@@ -231,12 +231,23 @@ void rotate_file(TCHAR *service_name, TCHAR *path, unsigned long seconds, unsign
 }\r
 \r
 int get_output_handles(nssm_service_t *service, HKEY key, STARTUPINFO *si) {\r
 }\r
 \r
 int get_output_handles(nssm_service_t *service, HKEY key, STARTUPINFO *si) {\r
-  bool set_flags = false;\r
+  bool redirect = false;\r
 \r
 \r
-  /* Standard security attributes allowing inheritance. */\r
-  SECURITY_ATTRIBUTES attributes;\r
-  ZeroMemory(&attributes, sizeof(attributes));\r
-  attributes.bInheritHandle = true;\r
+  /* stdin */\r
+  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)) {\r
+    service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;\r
+    ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));\r
+    return 1;\r
+  }\r
+  if (si && service->stdin_path[0]) {\r
+    si->hStdInput = CreateFile(service->stdin_path, FILE_READ_DATA, service->stdin_sharing, 0, service->stdin_disposition, service->stdin_flags, 0);\r
+    if (! si->hStdInput) {\r
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stdin_path, error_string(GetLastError()), 0);\r
+      return 2;\r
+    }\r
+\r
+    redirect = true;\r
+  }\r
 \r
   /* stdout */\r
   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)) {\r
 \r
   /* stdout */\r
   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)) {\r
@@ -267,7 +278,7 @@ int get_output_handles(nssm_service_t *service, HKEY key, STARTUPINFO *si) {
       service->rotate_stdout_online = NSSM_ROTATE_OFFLINE;\r
     }\r
 \r
       service->rotate_stdout_online = NSSM_ROTATE_OFFLINE;\r
     }\r
 \r
-    set_flags = true;\r
+    redirect = true;\r
   }\r
 \r
   /* stderr */\r
   }\r
 \r
   /* stderr */\r
@@ -315,55 +326,28 @@ int get_output_handles(nssm_service_t *service, HKEY key, STARTUPINFO *si) {
         service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
       }\r
 \r
         service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
       }\r
 \r
-      set_flags = true;\r
+      redirect = true;\r
     }\r
   }\r
 \r
     }\r
   }\r
 \r
-  /* stdin */\r
-  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)) {\r
-    service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;\r
-    ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));\r
-    return 1;\r
-  }\r
-  if (si && service->stdin_path[0]) {\r
-    if (str_equiv(service->stdin_path, _T("|"))) {\r
-      /* Fake stdin with a pipe. */\r
-      if (set_flags) {\r
-        /*\r
-          None of this is necessary if we aren't redirecting stdout and/or\r
-          stderr as well.\r
-\r
-          If we don't redirect any handles the application will start and be\r
-          quite happy with its console.  If we start it with\r
-          STARTF_USESTDHANDLES set it will interpret a NULL value for\r
-          hStdInput as meaning no input.  Because the service starts with\r
-          no stdin we can't just pass GetStdHandle(STD_INPUT_HANDLE) to\r
-          the application.\r
-\r
-          The only way we can successfully redirect the application's output\r
-          while preventing programs which exit after reading all input from\r
-          exiting prematurely is to create a pipe between ourselves and the\r
-          application but write nothing to it.\r
-        */\r
-        if (! CreatePipe(&si->hStdInput, &service->stdin_pipe, 0, 0)) {\r
-          log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_STDIN_CREATEPIPE_FAILED, service->name, error_string(GetLastError()), 0);\r
-          return 2;\r
-        }\r
-        SetHandleInformation(si->hStdInput, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);\r
-      }\r
-    }\r
-    else {\r
-      si->hStdInput = CreateFile(service->stdin_path, FILE_READ_DATA, service->stdin_sharing, &attributes, service->stdin_disposition, service->stdin_flags, 0);\r
-      if (! si->hStdInput) {\r
-        log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stdin_path, error_string(GetLastError()), 0);\r
-        return 2;\r
-      }\r
+  if (! redirect || ! si) return 0;\r
 \r
 \r
-      set_flags = true;\r
-    }\r
+  /* Allocate a new console so we get a fresh stdin, stdout and stderr. */\r
+  FreeConsole();\r
+  AllocConsole();\r
+\r
+  /* Set a title like "[NSSM] Jenkins" */\r
+  TCHAR displayname[SERVICE_NAME_LENGTH];\r
+  unsigned long len = _countof(displayname);\r
+  SC_HANDLE services = open_service_manager();\r
+  if (services) {\r
+    if (! GetServiceDisplayName(services, service->name, displayname, &len)) _sntprintf_s(displayname, _countof(displayname), _TRUNCATE, _T("%s"), service->name);\r
+    CloseServiceHandle(services);\r
   }\r
 \r
   }\r
 \r
-  if (! set_flags) return 0;\r
+  TCHAR title[65535];\r
+  _sntprintf_s(title, _countof(title), _TRUNCATE, _T("[%s] %s\n"), NSSM, displayname);\r
+  SetConsoleTitle(title);\r
 \r
   /*\r
     We need to set the startup_info flags to make the new handles\r
 \r
   /*\r
     We need to set the startup_info flags to make the new handles\r
@@ -371,6 +355,26 @@ int get_output_handles(nssm_service_t *service, HKEY key, STARTUPINFO *si) {
   */\r
   if (si) si->dwFlags |= STARTF_USESTDHANDLES;\r
 \r
   */\r
   if (si) si->dwFlags |= STARTF_USESTDHANDLES;\r
 \r
+  /* Redirect other handles. */\r
+  if (! si->hStdInput) {\r
+    if (! DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), GetCurrentProcess(), &si->hStdInput, 0, true, DUPLICATE_SAME_ACCESS)) {\r
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, _T("STD_INPUT_HANDLE"), _T("stdin"), error_string(GetLastError()), 0);\r
+      return 8;\r
+    }\r
+  }\r
+  if (! si->hStdOutput) {\r
+    if (! DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_OUTPUT_HANDLE), GetCurrentProcess(), &si->hStdOutput, 0, true, DUPLICATE_SAME_ACCESS)) {\r
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, _T("STD_OUTPUT_HANDLE"), _T("stdout"), error_string(GetLastError()), 0);\r
+      return 9;\r
+    }\r
+  }\r
+  if (! si->hStdError)  {\r
+    if (! DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_ERROR_HANDLE), GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_SAME_ACCESS)) {\r
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, _T("STD_ERROR_HANDLE"), _T("stderr"), error_string(GetLastError()), 0);\r
+      return 10;\r
+    }\r
+  }\r
+\r
   return 0;\r
 }\r
 \r
   return 0;\r
 }\r
 \r
index 59707c4..0a8c18b 100644 (file)
@@ -149,13 +149,6 @@ int kill_process(nssm_service_t *service, HANDLE process_handle, unsigned long p
 
   kill_t k = { pid, exitcode, 0 };
 
 
   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;
   /* 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:
         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);
       default:
         /* We already have a console. */
         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ATTACHCONSOLE_FAILED, service->name, error_string(ret), 0);
index ee60c10..284b5ec 100644 (file)
@@ -1530,7 +1530,6 @@ int start_service(nssm_service_t *service) {
   bool inherit_handles = false;\r
   if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;\r
   unsigned long flags = service->priority & priority_mask();\r
   bool inherit_handles = false;\r
   if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;\r
   unsigned long flags = service->priority & priority_mask();\r
-  if (service->stdin_pipe) flags |= DETACHED_PROCESS;\r
   if (service->affinity) flags |= CREATE_SUSPENDED;\r
   if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {\r
     unsigned long exitcode = 3;\r
   if (service->affinity) flags |= CREATE_SUSPENDED;\r
   if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {\r
     unsigned long exitcode = 3;\r
@@ -1620,10 +1619,6 @@ int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful,
     UnregisterWait(service->wait_handle);\r
     service->wait_handle = 0;\r
   }\r
     UnregisterWait(service->wait_handle);\r
     service->wait_handle = 0;\r
   }\r
-  if (service->stdin_pipe) {\r
-    CloseHandle(service->stdin_pipe);\r
-    service->stdin_pipe = 0;\r
-  }\r
 \r
   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
 \r
 \r
   service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;\r
 \r
index fe59c9a..4eed6ad 100644 (file)
--- a/service.h
+++ b/service.h
@@ -57,7 +57,6 @@ typedef struct {
   unsigned long stdin_sharing;\r
   unsigned long stdin_disposition;\r
   unsigned long stdin_flags;\r
   unsigned long stdin_sharing;\r
   unsigned long stdin_disposition;\r
   unsigned long stdin_flags;\r
-  HANDLE stdin_pipe;\r
   TCHAR stdout_path[PATH_LENGTH];\r
   unsigned long stdout_sharing;\r
   unsigned long stdout_disposition;\r
   TCHAR stdout_path[PATH_LENGTH];\r
   unsigned long stdout_sharing;\r
   unsigned long stdout_disposition;\r