From a16c93e55814710ec751a7e6d47b0485c07ecc85 Mon Sep 17 00:00:00 2001 From: NextTurn <45985406+NextTurn@users.noreply.github.com> Date: Wed, 5 Aug 2020 00:00:00 +0800 Subject: [PATCH] Add stdout/stderr path settings --- .../Configuration/ProcessCommand.cs | 12 +++ src/WinSW.Core/Configuration/SettingNames.cs | 2 + .../Configuration/XmlServiceConfig.cs | 38 +++++---- src/WinSW.Core/LogAppenders.cs | 25 ++++++ src/WinSW.Tests/ServiceConfigTests.cs | 80 +++++++++++++------ src/WinSW/WrapperService.cs | 28 ++++--- 6 files changed, 129 insertions(+), 56 deletions(-) create mode 100644 src/WinSW.Core/Configuration/ProcessCommand.cs diff --git a/src/WinSW.Core/Configuration/ProcessCommand.cs b/src/WinSW.Core/Configuration/ProcessCommand.cs new file mode 100644 index 0000000..e1bae44 --- /dev/null +++ b/src/WinSW.Core/Configuration/ProcessCommand.cs @@ -0,0 +1,12 @@ +namespace WinSW +{ + public struct ProcessCommand + { + public string? Executable; + public string? Arguments; + public string? StdoutPath; + public string? StderrPath; + + public LogHandler CreateLogHandler() => new TempLogHandler(this.StdoutPath, this.StderrPath); + } +} diff --git a/src/WinSW.Core/Configuration/SettingNames.cs b/src/WinSW.Core/Configuration/SettingNames.cs index 21db14f..cecce6b 100644 --- a/src/WinSW.Core/Configuration/SettingNames.cs +++ b/src/WinSW.Core/Configuration/SettingNames.cs @@ -9,5 +9,7 @@ internal const string Prestart = "prestart"; internal const string Prestop = "prestop"; internal const string Service = "service"; + internal const string StdoutPath = "stdoutPath"; + internal const string StderrPath = "stderrPath"; } } diff --git a/src/WinSW.Core/Configuration/XmlServiceConfig.cs b/src/WinSW.Core/Configuration/XmlServiceConfig.cs index 6021a9f..43b8a8f 100644 --- a/src/WinSW.Core/Configuration/XmlServiceConfig.cs +++ b/src/WinSW.Core/Configuration/XmlServiceConfig.cs @@ -253,21 +253,13 @@ namespace WinSW } } - public string? PrestartExecutable => this.GetExecutable(Names.Prestart); + public ProcessCommand Prestart => this.GetProcessCommand(Names.Prestart); - public string? PrestartArguments => this.GetArguments(Names.Prestart); + public ProcessCommand Poststart => this.GetProcessCommand(Names.Poststart); - public string? PoststartExecutable => this.GetExecutable(Names.Poststart); + public ProcessCommand Prestop => this.GetProcessCommand(Names.Prestop); - public string? PoststartArguments => this.GetArguments(Names.Poststart); - - public string? PrestopExecutable => this.GetExecutable(Names.Prestop); - - public string? PrestopArguments => this.GetArguments(Names.Prestop); - - public string? PoststopExecutable => this.GetExecutable(Names.Poststop); - - public string? PoststopArguments => this.GetArguments(Names.Poststop); + public ProcessCommand Poststop => this.GetProcessCommand(Names.Poststop); public override string WorkingDirectory { @@ -746,16 +738,22 @@ namespace WinSW return environment; } - private string? GetExecutable(string name) + private ProcessCommand GetProcessCommand(string name) { - string? text = this.dom.SelectSingleNode(Names.Service)?.SelectSingleNode(name)?.SelectSingleNode(Names.Executable)?.InnerText; - return text is null ? null : Environment.ExpandEnvironmentVariables(text); - } + XmlNode? node = this.dom.SelectSingleNode(Names.Service)?.SelectSingleNode(name); + return node is null ? default : new ProcessCommand + { + Executable = GetInnerText(Names.Executable), + Arguments = GetInnerText(Names.Arguments), + StdoutPath = GetInnerText(Names.StdoutPath), + StderrPath = GetInnerText(Names.StderrPath), + }; - private string? GetArguments(string name) - { - string? text = this.dom.SelectSingleNode(Names.Service)?.SelectSingleNode(name)?.SelectSingleNode(Names.Arguments)?.InnerText; - return text is null ? null : Environment.ExpandEnvironmentVariables(text); + string? GetInnerText(string name) + { + string? text = node.SelectSingleNode(name)?.InnerText; + return text is null ? null : Environment.ExpandEnvironmentVariables(text); + } } } } diff --git a/src/WinSW.Core/LogAppenders.cs b/src/WinSW.Core/LogAppenders.cs index 213f0ed..02bab95 100644 --- a/src/WinSW.Core/LogAppenders.cs +++ b/src/WinSW.Core/LogAppenders.cs @@ -14,6 +14,31 @@ namespace WinSW void WriteEntry(string message, EventLogEntryType type); } + internal sealed class TempLogHandler : AbstractFileLogAppender + { + private readonly string? outputPath; + private readonly string? errorPath; + + public TempLogHandler(string? outputPath, string? errorPath) + : base(string.Empty, string.Empty, IsDisabled(outputPath), IsDisabled(errorPath), string.Empty, string.Empty) + { + this.outputPath = outputPath; + this.errorPath = errorPath; + } + + private static bool IsDisabled(string? path) => string.IsNullOrEmpty(path) || path!.Equals("NUL", StringComparison.OrdinalIgnoreCase); + + protected override Task LogOutput(StreamReader outputReader) + { + return this.CopyStreamAsync(outputReader, this.CreateWriter(new FileStream(this.outputPath!, FileMode.OpenOrCreate))); + } + + protected override Task LogError(StreamReader errorReader) + { + return this.CopyStreamAsync(errorReader, this.CreateWriter(new FileStream(this.errorPath!, FileMode.OpenOrCreate))); + } + } + /// /// Abstraction for handling log. /// diff --git a/src/WinSW.Tests/ServiceConfigTests.cs b/src/WinSW.Tests/ServiceConfigTests.cs index 3f9fd7a..d229b24 100644 --- a/src/WinSW.Tests/ServiceConfigTests.cs +++ b/src/WinSW.Tests/ServiceConfigTests.cs @@ -346,45 +346,77 @@ $@" [Fact] public void Additional_Executable_And_Arguments() { - const string prestartExecutable = "1"; - const string prestartArguments = "2"; - const string poststartExecutable = "3"; - const string poststartArguments = "4"; - const string prestopExecutable = "5"; - const string prestopArguments = "6"; - const string poststopExecutable = "7"; - const string poststopArguments = "8"; + var prestart = new ProcessCommand + { + Executable = "a1", + Arguments = "a2", + StdoutPath = "a3", + StderrPath = "a4", + }; + var poststart = new ProcessCommand + { + Executable = "a1", + Arguments = "a2", + StdoutPath = "a3", + StderrPath = "a4", + }; + var prestop = new ProcessCommand + { + Executable = "a1", + Arguments = "a2", + StdoutPath = "a3", + StderrPath = "a4", + }; + var poststop = new ProcessCommand + { + Executable = "a1", + Arguments = "a2", + StdoutPath = "a3", + StderrPath = "a4", + }; string seedXml = $@" - {prestartExecutable} - {prestartArguments} + {prestart.Executable} + {prestart.Arguments} + {prestart.StdoutPath} + {prestart.StderrPath} - {poststartExecutable} - {poststartArguments} + {poststart.Executable} + {poststart.Arguments} + {poststart.StdoutPath} + {poststart.StderrPath} - {prestopExecutable} - {prestopArguments} + {prestop.Executable} + {prestop.Arguments} + {prestop.StdoutPath} + {prestop.StderrPath} - {poststopExecutable} - {poststopArguments} + {poststop.Executable} + {poststop.Arguments} + {poststop.StdoutPath} + {poststop.StderrPath} "; XmlServiceConfig config = XmlServiceConfig.FromXml(seedXml); - Assert.Equal(prestartExecutable, config.PrestartExecutable); - Assert.Equal(prestartArguments, config.PrestartArguments); - Assert.Equal(poststartExecutable, config.PoststartExecutable); - Assert.Equal(poststartArguments, config.PoststartArguments); - Assert.Equal(prestopExecutable, config.PrestopExecutable); - Assert.Equal(prestopArguments, config.PrestopArguments); - Assert.Equal(poststopExecutable, config.PoststopExecutable); - Assert.Equal(poststopArguments, config.PoststopArguments); + VerifyEqual(prestart, config.Prestart); + VerifyEqual(poststart, config.Poststart); + VerifyEqual(prestop, config.Prestop); + VerifyEqual(poststop, config.Poststop); + + static void VerifyEqual(ProcessCommand expected, ProcessCommand actual) + { + Assert.Equal(expected.Executable, actual.Executable); + Assert.Equal(expected.Arguments, actual.Arguments); + Assert.Equal(expected.StdoutPath, actual.StdoutPath); + Assert.Equal(expected.StderrPath, actual.StderrPath); + } } } } diff --git a/src/WinSW/WrapperService.cs b/src/WinSW/WrapperService.cs index 32010a7..174f024 100644 --- a/src/WinSW/WrapperService.cs +++ b/src/WinSW/WrapperService.cs @@ -298,12 +298,13 @@ namespace WinSW throw new AggregateException(exceptions); } - string? prestartExecutable = this.config.PrestartExecutable; + ProcessCommand prestart = this.config.Prestart; + string? prestartExecutable = prestart.Executable; if (prestartExecutable != null) { try { - using Process process = this.StartProcess(prestartExecutable, this.config.PrestartArguments); + using Process process = this.StartProcess(prestartExecutable, prestart.Arguments, prestart.CreateLogHandler()); this.WaitForProcessToExit(process); this.LogExited($"Pre-start process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode); process.StopDescendants(additionalStopTimeout); @@ -323,10 +324,11 @@ namespace WinSW this.ExtensionManager.FireOnWrapperStarted(); LogHandler executableLogHandler = this.CreateExecutableLogHandler(); - this.process = this.StartProcess(this.config.Executable, startArguments, this.OnMainProcessExited, executableLogHandler); + this.process = this.StartProcess(this.config.Executable, startArguments, executableLogHandler, this.OnMainProcessExited); this.ExtensionManager.FireOnProcessStarted(this.process); - string? poststartExecutable = this.config.PoststartExecutable; + ProcessCommand poststart = this.config.Poststart; + string? poststartExecutable = poststart.Executable; if (poststartExecutable != null) { try @@ -341,7 +343,7 @@ namespace WinSW { lock (this) { - return this.startingProcess = this.StartProcess(poststartExecutable, this.config.PoststartArguments); + return this.startingProcess = this.StartProcess(poststartExecutable, poststart.Arguments, poststart.CreateLogHandler()); } } } @@ -357,12 +359,13 @@ namespace WinSW /// private void DoStop() { - string? prestopExecutable = this.config.PrestopExecutable; + ProcessCommand prestop = this.config.Prestop; + string? prestopExecutable = prestop.Executable; if (prestopExecutable != null) { try { - using Process process = StartProcessLocked(prestopExecutable, this.config.PrestopArguments); + using Process process = StartProcessLocked(prestopExecutable, prestop.Arguments, prestop.CreateLogHandler()); this.WaitForProcessToExit(process); this.LogExited($"Pre-stop process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode); process.StopDescendants(additionalStopTimeout); @@ -419,12 +422,13 @@ namespace WinSW } } - string? poststopExecutable = this.config.PoststopExecutable; + ProcessCommand poststop = this.config.Poststop; + string? poststopExecutable = poststop.Executable; if (poststopExecutable != null) { try { - using Process process = StartProcessLocked(poststopExecutable, this.config.PoststopArguments); + using Process process = StartProcessLocked(poststopExecutable, poststop.Arguments, poststop.CreateLogHandler()); this.WaitForProcessToExit(process); this.LogExited($"Post-Stop process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode); process.StopDescendants(additionalStopTimeout); @@ -446,11 +450,11 @@ namespace WinSW Log.Info("Finished " + this.config.Name); - Process StartProcessLocked(string executable, string? arguments) + Process StartProcessLocked(string executable, string? arguments, LogHandler? logHandler = null) { lock (this) { - return this.stoppingProcess = this.StartProcess(executable, arguments); + return this.stoppingProcess = this.StartProcess(executable, arguments, logHandler); } } } @@ -528,7 +532,7 @@ namespace WinSW } } - private Process StartProcess(string executable, string? arguments, Action? onExited = null, LogHandler? logHandler = null) + private Process StartProcess(string executable, string? arguments, LogHandler? logHandler = null, Action? onExited = null) { var startInfo = new ProcessStartInfo(executable, arguments) {