From: Iain Patterson Date: Sat, 25 Jan 2014 21:45:56 +0000 (+0000) Subject: Allocate new console by default. X-Git-Tag: v2.22~36 X-Git-Url: http://git.iain.cx/?p=nssm.git;a=commitdiff_plain;h=c03d51cbbfe9f4f8a28dbabbfc068573a1842b74 Allocate new console by default. Since the whole purpose of NSSM is to be able to make services out of applications which expect to have a standard desktop environment, let's simply allocate a new console for each one rather than do so only when we try to redirect I/O. A funky ASCII art banner identifies the console as belonging to the service. NSSM itself will detach from the console as soon as possible, so if the service runs a graphical application the console may never be seen. The console can be suppressed by setting the REG_DWORD value AppNoConsole to 1. Note that services running console applications on Windows 2000 may fail if the console is disabled. --- diff --git a/ChangeLog.txt b/ChangeLog.txt index ddfb5f7..26810d5 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,226 +1,230 @@ -Changes since 2.21 ------------------- - * Existing services can now be managed using the GUI - or on the command line. - - * NSSM can now set the priority class and processor - affinity of the managed application. - - * NSSM can now apply an unconditional delay before - restarting the application. - - * NSSM can now optionally rotate existing files when - redirecting I/O. - - * Unqualified path names are now relative to the - application startup directory when redirecting I/O. - - * NSSM can now set the service display name, description, - startup type and log on details. - -Changes since 2.20 ------------------- - * Services installed from the GUI no longer have incorrect - AppParameters set in the registry. - -Changes since 2.19 ------------------- - * Services installed from the commandline without using the - GUI no longer have incorrect AppStopMethod* registry - entries set. - -Changes since 2.18 ------------------- - * Support AppEnvironmentExtra to append to the environment - instead of replacing it. - - * The GUI is significantly less sucky. - -Changes since 2.17 ------------------- - * Timeouts for each shutdown method can be configured in - the registry. - - * The GUI is slightly less sucky. - -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. - - * Silently ignore INTERROGATE control. - - * Try to send Control-C events to console applications when - shutting them down. - -Changes since 2.15 ------------------- - * Fixed case where NSSM could kill unrelated processes when - shutting down. - -Changes since 2.14 ------------------- - * NSSM is now translated into Italian. - - * Fixed GUI not allowing paths longer than 256 characters. - -Changes since 2.13 ------------------- - * Fixed default GUI language being French not English. - -Changes since 2.12 ------------------- - * Fixed failure to run on Windows 2000. - -Changes since 2.11 ------------------- - * NSSM is now translated into French. - - * Really ensure systems recovery actions can happen. - - The change supposedly introduced in v2.4 to allow service recovery - actions to be activated when the application exits gracefully with - a non-zero error code didn't actually work. - -Changes since 2.10 ------------------- - * Support AppEnvironment for compatibility with srvany. - -Changes since 2.9 ------------------ - * Fixed failure to compile messages.mc in paths containing spaces. - - * Fixed edge case with CreateProcess(). - - Correctly handle the case where the application executable is under - a path which contains space and an executable sharing the initial - part of that path (up to a space) exists. - -Changes since 2.8 ------------------ - * Fixed failure to run on Windows versions prior to Vista. - -Changes since 2.7 ------------------ - * Read Application, AppDirectory and AppParameters before each restart so - a change to any one doesn't require restarting NSSM itself. - - * Fixed messages not being sent to the event log correctly in some - cases. - - * Try to handle (strictly incorrect) quotes in AppDirectory. - - Windows directories aren't allowed to contain quotes so CreateProcess() - will fail if the AppDirectory is quoted. Note that it succeeds even if - Application itself is quoted as the application plus parameters are - interpreted as a command line. - - * Fixed failed to write full arguments to AppParameters when - installing a service. - - * Throttle restarts. - - Back off from restarting the application immediately if it starts - successfully but exits too soon. The default value of "too soon" is - 1500 milliseconds. This can be configured by adding a DWORD value - AppThrottle to the registry. - - Handle resume messages from the service console to restart the - application immediately even if it is throttled. - - * Try to kill the process tree gracefully. - - Before calling TerminateProcess() on all processes assocatiated with - the monitored application, enumerate all windows and threads and - post appropriate messages to them. If the application bothers to - listen for such messages it has a chance to shut itself down gracefully. - -Changes since 2.6 ------------------ - * Handle missing registry values. - - Warn if AppParameters is missing. Warn if AppDirectory is missing or - unset and choose a fallback directory. - First try to find the parent directory of the application. If that - fails, eg because the application path is just "notepad" or something, - start in the Windows directory. - - * Kill process tree when stopping service. - - Ensure that all child processes of the monitored application are - killed when the service stops by recursing through all running - processes and terminating those whose parent is the application - or one of its descendents. - -Changes since 2.5 ------------------ - * Removed incorrect ExpandEnvironmentStrings() error. - - A log_event() call was inadvertently left in the code causing an error - to be set to the eventlog saying that ExpandEnvironmentStrings() had - failed when it had actually succeeded. - -Changes since 2.4 ------------------ - * Allow use of REG_EXPAND_SZ values in the registry. - - * Don't suicide on exit status 0 by default. - - Suiciding when the application exits 0 will cause recovery actions to be - taken. Usually this is inappropriate. Only suicide if there is an - explicit AppExit value for 0 in the registry. - - Technically such behaviour could be abused to do something like run a - script after successful completion of a service but in most cases a - suicide is undesirable when no actual failure occurred. - - * Don't hang if startup parameters couldn't be determined. - Instead, signal that the service entered the STOPPED state. - Set START_PENDING state prior to actual startup. - -Changes since 2.3 ------------------ - * Ensure systems recovery actions can happen. - - In Windows versions earlier than Vista the service manager would only - consider a service failed (and hence eligible for recovery action) if - the service exited without setting its state to SERVICE_STOPPED, even if - it signalled an error exit code. - In Vista and later the service manager can be configured to treat a - graceful shutdown with error code as a failure but this is not the - default behaviour. - - Try to configure the service manager to use the new behaviour when - starting the service so users who set AppExit to Exit can use recovery - actions as expected. - - Also recognise the new AppExit option Suicide for use on pre-Vista - systems. When AppExit is Suicide don't stop the service but exit - inelegantly, which should be seen as a failure. - -Changes since 2.2 ------------------ - * Send properly formatted messages to the event log. - - * Fixed truncation of very long path lengths in the registry. - -Changes since 2.1 ------------------ - * Decide how to handle application exit. - - When the service exits with exit code n look in - HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppExit\, - falling back to the unnamed value if no such code is listed. Parse the - (string) value of this entry as follows: - - Restart: Start the application again (NSSM default). - Ignore: Do nothing (srvany default). - Exit: Stop the service. - -Changes since 2.0 ------------------ - * Added support for building a 64-bit executable. - - * Added project files for newer versions of Visual Studio. +Changes since 2.21 +------------------ + * Existing services can now be managed using the GUI + or on the command line. + + * NSSM can now set the priority class and processor + affinity of the managed application. + + * NSSM can now apply an unconditional delay before + restarting the application. + + * NSSM can now optionally rotate existing files when + redirecting I/O. + + * Unqualified path names are now relative to the + application startup directory when redirecting I/O. + + * NSSM can now set the service display name, description, + startup type and log on details. + + * All services now receive a standard console window, + allowing them to read input correctly (if running in + interactive mode). + +Changes since 2.20 +------------------ + * Services installed from the GUI no longer have incorrect + AppParameters set in the registry. + +Changes since 2.19 +------------------ + * Services installed from the commandline without using the + GUI no longer have incorrect AppStopMethod* registry + entries set. + +Changes since 2.18 +------------------ + * Support AppEnvironmentExtra to append to the environment + instead of replacing it. + + * The GUI is significantly less sucky. + +Changes since 2.17 +------------------ + * Timeouts for each shutdown method can be configured in + the registry. + + * The GUI is slightly less sucky. + +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. + + * Silently ignore INTERROGATE control. + + * Try to send Control-C events to console applications when + shutting them down. + +Changes since 2.15 +------------------ + * Fixed case where NSSM could kill unrelated processes when + shutting down. + +Changes since 2.14 +------------------ + * NSSM is now translated into Italian. + + * Fixed GUI not allowing paths longer than 256 characters. + +Changes since 2.13 +------------------ + * Fixed default GUI language being French not English. + +Changes since 2.12 +------------------ + * Fixed failure to run on Windows 2000. + +Changes since 2.11 +------------------ + * NSSM is now translated into French. + + * Really ensure systems recovery actions can happen. + + The change supposedly introduced in v2.4 to allow service recovery + actions to be activated when the application exits gracefully with + a non-zero error code didn't actually work. + +Changes since 2.10 +------------------ + * Support AppEnvironment for compatibility with srvany. + +Changes since 2.9 +----------------- + * Fixed failure to compile messages.mc in paths containing spaces. + + * Fixed edge case with CreateProcess(). + + Correctly handle the case where the application executable is under + a path which contains space and an executable sharing the initial + part of that path (up to a space) exists. + +Changes since 2.8 +----------------- + * Fixed failure to run on Windows versions prior to Vista. + +Changes since 2.7 +----------------- + * Read Application, AppDirectory and AppParameters before each restart so + a change to any one doesn't require restarting NSSM itself. + + * Fixed messages not being sent to the event log correctly in some + cases. + + * Try to handle (strictly incorrect) quotes in AppDirectory. + + Windows directories aren't allowed to contain quotes so CreateProcess() + will fail if the AppDirectory is quoted. Note that it succeeds even if + Application itself is quoted as the application plus parameters are + interpreted as a command line. + + * Fixed failed to write full arguments to AppParameters when + installing a service. + + * Throttle restarts. + + Back off from restarting the application immediately if it starts + successfully but exits too soon. The default value of "too soon" is + 1500 milliseconds. This can be configured by adding a DWORD value + AppThrottle to the registry. + + Handle resume messages from the service console to restart the + application immediately even if it is throttled. + + * Try to kill the process tree gracefully. + + Before calling TerminateProcess() on all processes assocatiated with + the monitored application, enumerate all windows and threads and + post appropriate messages to them. If the application bothers to + listen for such messages it has a chance to shut itself down gracefully. + +Changes since 2.6 +----------------- + * Handle missing registry values. + + Warn if AppParameters is missing. Warn if AppDirectory is missing or + unset and choose a fallback directory. + First try to find the parent directory of the application. If that + fails, eg because the application path is just "notepad" or something, + start in the Windows directory. + + * Kill process tree when stopping service. + + Ensure that all child processes of the monitored application are + killed when the service stops by recursing through all running + processes and terminating those whose parent is the application + or one of its descendents. + +Changes since 2.5 +----------------- + * Removed incorrect ExpandEnvironmentStrings() error. + + A log_event() call was inadvertently left in the code causing an error + to be set to the eventlog saying that ExpandEnvironmentStrings() had + failed when it had actually succeeded. + +Changes since 2.4 +----------------- + * Allow use of REG_EXPAND_SZ values in the registry. + + * Don't suicide on exit status 0 by default. + + Suiciding when the application exits 0 will cause recovery actions to be + taken. Usually this is inappropriate. Only suicide if there is an + explicit AppExit value for 0 in the registry. + + Technically such behaviour could be abused to do something like run a + script after successful completion of a service but in most cases a + suicide is undesirable when no actual failure occurred. + + * Don't hang if startup parameters couldn't be determined. + Instead, signal that the service entered the STOPPED state. + Set START_PENDING state prior to actual startup. + +Changes since 2.3 +----------------- + * Ensure systems recovery actions can happen. + + In Windows versions earlier than Vista the service manager would only + consider a service failed (and hence eligible for recovery action) if + the service exited without setting its state to SERVICE_STOPPED, even if + it signalled an error exit code. + In Vista and later the service manager can be configured to treat a + graceful shutdown with error code as a failure but this is not the + default behaviour. + + Try to configure the service manager to use the new behaviour when + starting the service so users who set AppExit to Exit can use recovery + actions as expected. + + Also recognise the new AppExit option Suicide for use on pre-Vista + systems. When AppExit is Suicide don't stop the service but exit + inelegantly, which should be seen as a failure. + +Changes since 2.2 +----------------- + * Send properly formatted messages to the event log. + + * Fixed truncation of very long path lengths in the registry. + +Changes since 2.1 +----------------- + * Decide how to handle application exit. + + When the service exits with exit code n look in + HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppExit\, + falling back to the unnamed value if no such code is listed. Parse the + (string) value of this entry as follows: + + Restart: Start the application again (NSSM default). + Ignore: Do nothing (srvany default). + Exit: Stop the service. + +Changes since 2.0 +----------------- + * Added support for building a 64-bit executable. + + * Added project files for newer versions of Visual Studio. diff --git a/README.txt b/README.txt index aa1fc9f..edddf04 100644 --- a/README.txt +++ b/README.txt @@ -262,6 +262,17 @@ so the actual time to shutdown may be longer than the sum of all configured timeouts if the application spawns multiple subprocesses. +Console window +-------------- +By default, NSSM will create a console window so that applications which +are capable of reading user input can do so - subject to the service being +allowed to interact with the desktop. + +Creation of the console can be suppressed by setting the integer (REG_DWORD) +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppNoConsole +registry value to 1. + + I/O redirection --------------- NSSM can redirect the managed application's I/O to any path capable of being diff --git a/gui.cpp b/gui.cpp index 694e0e2..dfa1a22 100644 --- a/gui.cpp +++ b/gui.cpp @@ -117,6 +117,10 @@ int nssm_gui(int resource, nssm_service_t *service) { } } + if (service->no_console) { + SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_SETCHECK, BST_UNCHECKED, 0); + } + /* Shutdown tab. */ if (! (service->stop_method & NSSM_STOP_METHOD_CONSOLE)) { SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_CONSOLE, BM_SETCHECK, BST_UNCHECKED, 0); @@ -478,6 +482,9 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service } } + if (SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_GETCHECK, 0, 0) & BST_CHECKED) service->no_console = 0; + else service->no_console = 1; + /* Get stop method stuff. */ check_stop_method(service, NSSM_STOP_METHOD_CONSOLE, IDC_METHOD_CONSOLE); check_stop_method(service, NSSM_STOP_METHOD_WINDOW, IDC_METHOD_WINDOW); @@ -956,6 +963,8 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) { SendMessage(combo, CB_INSERTSTRING, NSSM_IDLE_PRIORITY, (LPARAM) message_string(NSSM_GUI_IDLE_PRIORITY_CLASS)); SendMessage(combo, CB_SETCURSEL, NSSM_NORMAL_PRIORITY, 0); + SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_SETCHECK, BST_CHECKED, 0); + list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY); n = num_cpus(); SendMessage(list, LB_SETCOLUMNWIDTH, 16, 0); diff --git a/io.cpp b/io.cpp index 42e4447..41139a0 100644 --- a/io.cpp +++ b/io.cpp @@ -231,7 +231,26 @@ 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 redirect = false; + /* Allocate a new console so we get a fresh stdin, stdout and stderr. */ + if (si && ! service->no_console) { + FreeConsole(); + AllocConsole(); + banner(); + + /* 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)) ZeroMemory(displayname, sizeof(displayname)); + CloseServiceHandle(services); + } + if (! displayname[0]) _sntprintf_s(displayname, _countof(displayname), _TRUNCATE, _T("%s"), service->name); + + TCHAR title[65535]; + _sntprintf_s(title, _countof(title), _TRUNCATE, _T("[%s] %s"), NSSM, displayname); + SetConsoleTitle(title); + } /* 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)) { @@ -245,8 +264,6 @@ int get_output_handles(nssm_service_t *service, HKEY key, STARTUPINFO *si) { log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stdin_path, error_string(GetLastError()), 0); return 2; } - - redirect = true; } /* stdout */ @@ -277,8 +294,6 @@ int get_output_handles(nssm_service_t *service, HKEY key, STARTUPINFO *si) { } service->rotate_stdout_online = NSSM_ROTATE_OFFLINE; } - - redirect = true; } /* stderr */ @@ -325,35 +340,18 @@ int get_output_handles(nssm_service_t *service, HKEY key, STARTUPINFO *si) { } service->rotate_stderr_online = NSSM_ROTATE_OFFLINE; } - - redirect = true; } } - if (! redirect || ! si) return 0; - - /* 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); - } - - TCHAR title[65535]; - _sntprintf_s(title, _countof(title), _TRUNCATE, _T("[%s] %s\n"), NSSM, displayname); - SetConsoleTitle(title); + if (! si) return 0; /* We need to set the startup_info flags to make the new handles inheritable by the new process. */ - if (si) si->dwFlags |= STARTF_USESTDHANDLES; + si->dwFlags |= STARTF_USESTDHANDLES; + + if (service->no_console) return 0; /* Redirect other handles. */ if (! si->hStdInput) { diff --git a/nssm.cpp b/nssm.cpp index 931496a..8c36780 100644 --- a/nssm.cpp +++ b/nssm.cpp @@ -82,6 +82,135 @@ int num_cpus() { return (int) i; } +static inline void block(unsigned int a, short x, short y, unsigned long n) { + HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); + TCHAR s = _T(' '); + + unsigned long out; + COORD c = { x, y }; + FillConsoleOutputAttribute(h, a, n, c, &out); + FillConsoleOutputCharacter(h, s, n, c, &out); +} + +static inline void R(short x, short y, unsigned long n) { + block(BACKGROUND_RED | BACKGROUND_INTENSITY, x, y, n); +} + +static inline void r(short x, short y, unsigned long n) { + block(BACKGROUND_RED, x, y, n); +} + +static inline void b(short x, short y, unsigned long n) { + block(0, x, y, n); +} + +void banner() { + short y = 0; + + b(0, y, 80); + y++; + + b(0, y, 80); + y++; + + b(0, y, 80); + y++; + + b(0, y, 80); + y++; + + b(0, y, 80); + r(18, y, 5); r(28, y, 4); r(41, y, 4); r(68, y, 1); + R(6, y, 5); R(19, y, 4); R(29, y, 1); R(32, y, 3); R(42, y, 1); R(45, y, 3); R(52, y, 5); R(69, y, 4); + y++; + + b(0, y, 80); + r(8, y, 4); r(20, y, 1); r(28, y, 1); r(33, y, 3); r(41, y, 1); r(46, y, 3); r (57, y, 1); + R(9, y, 2); R(21, y, 1); R(27, y, 1); R(34, y, 1); R(40, y, 1); R(47, y, 1); R(54, y, 3); R(68, y, 3); + y++; + + b(0, y, 80); + r(12, y, 1); r(20, y, 1); r(26, y, 1); r(34, y, 2); r(39, y, 1); r(47, y, 2); r(67, y, 2); + R(9, y, 3); R(21, y, 1); R(27, y, 1); R(40, y, 1); R(54, y, 1); R(56, y, 2); R(67, y, 1); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(20, y, 1); r(26, y, 1); r (35, y, 1); r(39, y, 1); r(48, y, 1); r(58, y, 1); + R(10, y, 3); R(21, y, 1); R(27, y, 1); R(40, y, 1); R(54, y, 1); R(56, y, 2); R(67, y, 1); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(56, y, 1); r(66, y, 2); + R(11, y, 3); R(21, y, 1); R(26, y, 2); R(39, y, 2); R(54, y, 1); R(57, y, 2); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(26, y, 1); r(39, y, 1); r(59, y, 1); + R(12, y, 3); R(21, y, 1); R(27, y, 2); R(40, y, 2); R(54, y, 1); R(57, y, 2); R(66, y, 1); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(12, y, 4); r(30, y, 1); r(43, y, 1); r(57, y, 1); r(65, y, 2); + R(13, y, 2); R(21, y, 1); R(27, y, 3); R(40, y, 3); R(54, y, 1); R(58, y, 2); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(13, y, 4); r(27, y, 7); r(40, y, 7); + R(14, y, 2); R(21, y, 1); R(28, y, 5); R(41, y, 5); R(54, y, 1); R(58, y, 2); R(65, y, 1); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(60, y, 1); r(65, y, 1); + R(14, y, 3); R(21, y, 1); R(29, y, 6); R(42, y, 6); R(54, y, 1); R(58, y, 2); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(31, y, 1); r(44, y, 1); r(58, y, 1); r(64, y, 1); + R(15, y, 3); R(21, y, 1); R(32, y, 4); R(45, y, 4); R(54, y, 1); R(59, y, 2); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(33, y, 1); r(46, y, 1); r(61, y, 1); r(64, y, 1); + R(16, y, 3); R(21, y, 1); R(34, y, 2); R(47, y, 2); R(54, y, 1); R(59, y, 2); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(16, y, 4); r(36, y, 1); r(49, y, 1); r(59, y, 1); r(63, y, 1); + R(17, y, 2); R(21, y, 1); R(34, y, 2); R(47, y, 2); R(54, y, 1); R(60, y, 2); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(17, y, 4); r(26, y, 1); r(36, y, 1); r(39, y, 1); r(49, y, 1); + R(18, y, 2); R(21, y, 1); R(35, y, 1); R(48, y, 1); R(54, y, 1); R(60, y, 2); R(63, y, 1); R(69, y, 2); + y++; + + b(0, y, 80); + r(26, y, 2); r(39, y, 2); r(63, y, 1); + R(9, y, 1); R(18, y, 4); R(35, y, 1); R(48, y, 1); R(54, y, 1); R(60, y, 3); R(69, y, 2); + y++; + + b(0, y, 80); + r(34, y, 1); r(47, y, 1); r(60, y, 1); + R(9, y, 1); R(19, y, 3); R(26, y, 2); R(35, y, 1); R(39, y, 2); R(48, y, 1); R(54, y, 1); R(61, y, 2); R(69, y, 2); + y++; + + b(0, y, 80); + r(8, y, 1); r(35, y, 1); r(48, y, 1); r(62, y, 1); r(71, y, 1); + R(9, y, 1); R(20, y, 2); R(26, y, 3); R(34, y, 1); R(39, y, 3); R(47, y, 1); R(54, y, 1); R(61, y, 1); R(69, y, 2); + y++; + + b(0, y, 80); + r(11, y, 1); r(26, y, 1); r(28, y, 5); r(39, y, 1); r(41, y, 5); r(51, y, 7); r(61, y, 1); r(66, y, 8); + R(7, y, 4); R(21, y, 1); R(29, y, 1); R(33, y, 1); R(42, y, 1); R(46, y, 1); R(52, y, 5); R(67, y, 7); + y++; + + b(0, y, 80); + y++; + + b(0, y, 80); + y++; +} + int _tmain(int argc, TCHAR **argv) { check_console(); diff --git a/nssm.h b/nssm.h index 6d1f315..cbfa15f 100644 --- a/nssm.h +++ b/nssm.h @@ -57,6 +57,7 @@ int str_number(const TCHAR *, unsigned long *, TCHAR **); int str_number(const TCHAR *, unsigned long *); int num_cpus(); int usage(int); +void banner(); #define NSSM _T("NSSM") #ifdef _WIN64 diff --git a/nssm.rc b/nssm.rc index e6c5871..e2193a1 100644 Binary files a/nssm.rc and b/nssm.rc differ diff --git a/process.cpp b/process.cpp index 0a8c18b..1cc6a73 100644 --- a/process.cpp +++ b/process.cpp @@ -208,8 +208,6 @@ 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); diff --git a/registry.cpp b/registry.cpp index bd79dc3..3410e4a 100644 --- a/registry.cpp +++ b/registry.cpp @@ -119,6 +119,8 @@ int create_parameters(nssm_service_t *service, bool editing) { else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW); if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high); else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH); + if (service->no_console) set_number(key, NSSM_REG_NO_CONSOLE, 1); + else if (editing) RegDeleteValue(key, NSSM_REG_NO_CONSOLE); /* Environment */ if (service->env) { @@ -489,6 +491,9 @@ int get_parameters(nssm_service_t *service, STARTUPINFO *si) { if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0; if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0; + /* Try to get force new console setting - may fail. */ + if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0; + /* Change to startup directory in case stdout/stderr are relative paths. */ TCHAR cwd[PATH_LENGTH]; GetCurrentDirectory(_countof(cwd), cwd); diff --git a/registry.h b/registry.h index aa14e00..1734d4e 100644 --- a/registry.h +++ b/registry.h @@ -27,6 +27,7 @@ #define NSSM_REG_ROTATE_BYTES_HIGH _T("AppRotateBytesHigh") #define NSSM_REG_PRIORITY _T("AppPriority") #define NSSM_REG_AFFINITY _T("AppAffinity") +#define NSSM_REG_NO_CONSOLE _T("AppNoConsole") #define NSSM_STDIO_LENGTH 29 HKEY open_registry(const TCHAR *, const TCHAR *, REGSAM sam); diff --git a/resource.h b/resource.h index 0fa1f5c..0ddd90d 100644 --- a/resource.h +++ b/resource.h @@ -61,6 +61,7 @@ #define IDC_PRIORITY 1042 #define IDC_AFFINITY_ALL 1043 #define IDC_AFFINITY 1044 +#define IDC_CONSOLE 1045 // Next default values for new objects // @@ -68,7 +69,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 115 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1045 +#define _APS_NEXT_CONTROL_VALUE 1046 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/service.cpp b/service.cpp index 284b5ec..e8a1569 100644 --- a/service.cpp +++ b/service.cpp @@ -1697,6 +1697,8 @@ void CALLBACK end_service(void *arg, unsigned char why) { if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid); service->pid = 0; + if (! service->no_console) FreeConsole(); + /* The why argument is true if our wait timed out or false otherwise. Our wait is infinite so why will never be true when called by the system. diff --git a/service.h b/service.h index 4eed6ad..496b54a 100644 --- a/service.h +++ b/service.h @@ -53,6 +53,7 @@ typedef struct { TCHAR *env_extra; unsigned long env_extralen; unsigned long priority; + unsigned long no_console; TCHAR stdin_path[PATH_LENGTH]; unsigned long stdin_sharing; unsigned long stdin_disposition; diff --git a/settings.cpp b/settings.cpp index a3d301b..73b8d0d 100644 --- a/settings.cpp +++ b/settings.cpp @@ -810,6 +810,7 @@ settings_t settings[] = { { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity }, { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment }, { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment }, + { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number }, { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority }, { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number }, { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },