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)
{