mirror of https://github.com/winsw/winsw
Add stdout/stderr path settings
parent
2a576e102e
commit
a16c93e558
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,5 +9,7 @@
|
||||||
internal const string Prestart = "prestart";
|
internal const string Prestart = "prestart";
|
||||||
internal const string Prestop = "prestop";
|
internal const string Prestop = "prestop";
|
||||||
internal const string Service = "service";
|
internal const string Service = "service";
|
||||||
|
internal const string StdoutPath = "stdoutPath";
|
||||||
|
internal const string StderrPath = "stderrPath";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ProcessCommand Poststop => this.GetProcessCommand(Names.Poststop);
|
||||||
|
|
||||||
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 override string WorkingDirectory
|
public override string WorkingDirectory
|
||||||
{
|
{
|
||||||
|
@ -746,16 +738,22 @@ namespace WinSW
|
||||||
return environment;
|
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;
|
XmlNode? node = this.dom.SelectSingleNode(Names.Service)?.SelectSingleNode(name);
|
||||||
return text is null ? null : Environment.ExpandEnvironmentVariables(text);
|
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? GetInnerText(string name)
|
||||||
{
|
{
|
||||||
string? text = this.dom.SelectSingleNode(Names.Service)?.SelectSingleNode(name)?.SelectSingleNode(Names.Arguments)?.InnerText;
|
string? text = node.SelectSingleNode(name)?.InnerText;
|
||||||
return text is null ? null : Environment.ExpandEnvironmentVariables(text);
|
return text is null ? null : Environment.ExpandEnvironmentVariables(text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,31 @@ namespace WinSW
|
||||||
void WriteEntry(string message, EventLogEntryType type);
|
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)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Abstraction for handling log.
|
/// Abstraction for handling log.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -346,45 +346,77 @@ $@"<service>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Additional_Executable_And_Arguments()
|
public void Additional_Executable_And_Arguments()
|
||||||
{
|
{
|
||||||
const string prestartExecutable = "1";
|
var prestart = new ProcessCommand
|
||||||
const string prestartArguments = "2";
|
{
|
||||||
const string poststartExecutable = "3";
|
Executable = "a1",
|
||||||
const string poststartArguments = "4";
|
Arguments = "a2",
|
||||||
const string prestopExecutable = "5";
|
StdoutPath = "a3",
|
||||||
const string prestopArguments = "6";
|
StderrPath = "a4",
|
||||||
const string poststopExecutable = "7";
|
};
|
||||||
const string poststopArguments = "8";
|
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 =
|
string seedXml =
|
||||||
$@"<service>
|
$@"<service>
|
||||||
<prestart>
|
<prestart>
|
||||||
<executable>{prestartExecutable}</executable>
|
<executable>{prestart.Executable}</executable>
|
||||||
<arguments>{prestartArguments}</arguments>
|
<arguments>{prestart.Arguments}</arguments>
|
||||||
|
<stdoutPath>{prestart.StdoutPath}</stdoutPath>
|
||||||
|
<stderrPath>{prestart.StderrPath}</stderrPath>
|
||||||
</prestart>
|
</prestart>
|
||||||
<poststart>
|
<poststart>
|
||||||
<executable>{poststartExecutable}</executable>
|
<executable>{poststart.Executable}</executable>
|
||||||
<arguments>{poststartArguments}</arguments>
|
<arguments>{poststart.Arguments}</arguments>
|
||||||
|
<stdoutPath>{poststart.StdoutPath}</stdoutPath>
|
||||||
|
<stderrPath>{poststart.StderrPath}</stderrPath>
|
||||||
</poststart>
|
</poststart>
|
||||||
<prestop>
|
<prestop>
|
||||||
<executable>{prestopExecutable}</executable>
|
<executable>{prestop.Executable}</executable>
|
||||||
<arguments>{prestopArguments}</arguments>
|
<arguments>{prestop.Arguments}</arguments>
|
||||||
|
<stdoutPath>{prestop.StdoutPath}</stdoutPath>
|
||||||
|
<stderrPath>{prestop.StderrPath}</stderrPath>
|
||||||
</prestop>
|
</prestop>
|
||||||
<poststop>
|
<poststop>
|
||||||
<executable>{poststopExecutable}</executable>
|
<executable>{poststop.Executable}</executable>
|
||||||
<arguments>{poststopArguments}</arguments>
|
<arguments>{poststop.Arguments}</arguments>
|
||||||
|
<stdoutPath>{poststop.StdoutPath}</stdoutPath>
|
||||||
|
<stderrPath>{poststop.StderrPath}</stderrPath>
|
||||||
</poststop>
|
</poststop>
|
||||||
</service>";
|
</service>";
|
||||||
|
|
||||||
XmlServiceConfig config = XmlServiceConfig.FromXml(seedXml);
|
XmlServiceConfig config = XmlServiceConfig.FromXml(seedXml);
|
||||||
|
|
||||||
Assert.Equal(prestartExecutable, config.PrestartExecutable);
|
VerifyEqual(prestart, config.Prestart);
|
||||||
Assert.Equal(prestartArguments, config.PrestartArguments);
|
VerifyEqual(poststart, config.Poststart);
|
||||||
Assert.Equal(poststartExecutable, config.PoststartExecutable);
|
VerifyEqual(prestop, config.Prestop);
|
||||||
Assert.Equal(poststartArguments, config.PoststartArguments);
|
VerifyEqual(poststop, config.Poststop);
|
||||||
Assert.Equal(prestopExecutable, config.PrestopExecutable);
|
|
||||||
Assert.Equal(prestopArguments, config.PrestopArguments);
|
static void VerifyEqual(ProcessCommand expected, ProcessCommand actual)
|
||||||
Assert.Equal(poststopExecutable, config.PoststopExecutable);
|
{
|
||||||
Assert.Equal(poststopArguments, config.PoststopArguments);
|
Assert.Equal(expected.Executable, actual.Executable);
|
||||||
|
Assert.Equal(expected.Arguments, actual.Arguments);
|
||||||
|
Assert.Equal(expected.StdoutPath, actual.StdoutPath);
|
||||||
|
Assert.Equal(expected.StderrPath, actual.StderrPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -298,12 +298,13 @@ namespace WinSW
|
||||||
throw new AggregateException(exceptions);
|
throw new AggregateException(exceptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
string? prestartExecutable = this.config.PrestartExecutable;
|
ProcessCommand prestart = this.config.Prestart;
|
||||||
|
string? prestartExecutable = prestart.Executable;
|
||||||
if (prestartExecutable != null)
|
if (prestartExecutable != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using Process process = this.StartProcess(prestartExecutable, this.config.PrestartArguments);
|
using Process process = this.StartProcess(prestartExecutable, prestart.Arguments, prestart.CreateLogHandler());
|
||||||
this.WaitForProcessToExit(process);
|
this.WaitForProcessToExit(process);
|
||||||
this.LogExited($"Pre-start process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode);
|
this.LogExited($"Pre-start process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode);
|
||||||
process.StopDescendants(additionalStopTimeout);
|
process.StopDescendants(additionalStopTimeout);
|
||||||
|
@ -323,10 +324,11 @@ namespace WinSW
|
||||||
this.ExtensionManager.FireOnWrapperStarted();
|
this.ExtensionManager.FireOnWrapperStarted();
|
||||||
|
|
||||||
LogHandler executableLogHandler = this.CreateExecutableLogHandler();
|
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);
|
this.ExtensionManager.FireOnProcessStarted(this.process);
|
||||||
|
|
||||||
string? poststartExecutable = this.config.PoststartExecutable;
|
ProcessCommand poststart = this.config.Poststart;
|
||||||
|
string? poststartExecutable = poststart.Executable;
|
||||||
if (poststartExecutable != null)
|
if (poststartExecutable != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -341,7 +343,7 @@ namespace WinSW
|
||||||
{
|
{
|
||||||
lock (this)
|
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
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void DoStop()
|
private void DoStop()
|
||||||
{
|
{
|
||||||
string? prestopExecutable = this.config.PrestopExecutable;
|
ProcessCommand prestop = this.config.Prestop;
|
||||||
|
string? prestopExecutable = prestop.Executable;
|
||||||
if (prestopExecutable != null)
|
if (prestopExecutable != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using Process process = StartProcessLocked(prestopExecutable, this.config.PrestopArguments);
|
using Process process = StartProcessLocked(prestopExecutable, prestop.Arguments, prestop.CreateLogHandler());
|
||||||
this.WaitForProcessToExit(process);
|
this.WaitForProcessToExit(process);
|
||||||
this.LogExited($"Pre-stop process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode);
|
this.LogExited($"Pre-stop process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode);
|
||||||
process.StopDescendants(additionalStopTimeout);
|
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)
|
if (poststopExecutable != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using Process process = StartProcessLocked(poststopExecutable, this.config.PoststopArguments);
|
using Process process = StartProcessLocked(poststopExecutable, poststop.Arguments, poststop.CreateLogHandler());
|
||||||
this.WaitForProcessToExit(process);
|
this.WaitForProcessToExit(process);
|
||||||
this.LogExited($"Post-Stop process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode);
|
this.LogExited($"Post-Stop process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode);
|
||||||
process.StopDescendants(additionalStopTimeout);
|
process.StopDescendants(additionalStopTimeout);
|
||||||
|
@ -446,11 +450,11 @@ namespace WinSW
|
||||||
|
|
||||||
Log.Info("Finished " + this.config.Name);
|
Log.Info("Finished " + this.config.Name);
|
||||||
|
|
||||||
Process StartProcessLocked(string executable, string? arguments)
|
Process StartProcessLocked(string executable, string? arguments, LogHandler? logHandler = null)
|
||||||
{
|
{
|
||||||
lock (this)
|
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<Process>? onExited = null, LogHandler? logHandler = null)
|
private Process StartProcess(string executable, string? arguments, LogHandler? logHandler = null, Action<Process>? onExited = null)
|
||||||
{
|
{
|
||||||
var startInfo = new ProcessStartInfo(executable, arguments)
|
var startInfo = new ProcessStartInfo(executable, arguments)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue