Run, RunWindows (cmd.exe /C), RunLinuxShell (/bin/sh -c) and RunOSX have had no callers since the backup moved to RunNoShell. Backup through the API and the integration suite unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
65 lines
2.9 KiB
C#
65 lines
2.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace AyaNova.Util
|
|
{
|
|
public static class RunProgram
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
/// Run a program directly, no shell: each argument is passed as-is (no quoting or escaping needed, nothing is interpreted by a shell)
|
|
/// and extra environment variables are set for this process only (e.g. PGPASSWORD so a password never appears on a command line)
|
|
/// Returns empty string on success (exit code 0) or the error text otherwise, same convention as the backup job expects
|
|
/// </summary>
|
|
public static string RunNoShell(string fileName, IEnumerable<string> arguments, IDictionary<string, string> environment = null, ILogger log = null, int waitForExitTimeOut = int.MaxValue)
|
|
{
|
|
try
|
|
{
|
|
using (var process = new Process())
|
|
{
|
|
process.StartInfo = new ProcessStartInfo
|
|
{
|
|
FileName = fileName,
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
UseShellExecute = false,
|
|
CreateNoWindow = true,
|
|
};
|
|
foreach (var a in arguments)
|
|
process.StartInfo.ArgumentList.Add(a);
|
|
if (environment != null)
|
|
foreach (var e in environment)
|
|
process.StartInfo.Environment[e.Key] = e.Value;
|
|
|
|
process.Start();
|
|
//read both streams at once, reading one to the end first can deadlock if the other fills its buffer
|
|
var stdOut = process.StandardOutput.ReadToEndAsync();
|
|
var stdErr = process.StandardError.ReadToEndAsync();
|
|
if (!process.WaitForExit(waitForExitTimeOut))
|
|
{
|
|
process.Kill(true);
|
|
return $"ERROR: TIMED OUT {waitForExitTimeOut}ms BEFORE COMPLETION running {fileName}";
|
|
}
|
|
process.WaitForExit();//ensures output streams are fully read after the timed wait above
|
|
if (process.ExitCode != 0)
|
|
return $"ERROR: {Path.GetFileName(fileName)} exit code {process.ExitCode}: {stdErr.Result}{stdOut.Result}".Trim();
|
|
return string.Empty;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
//arguments deliberately not logged, they can contain connection details
|
|
if (log != null)
|
|
{
|
|
log.LogError(ex, $"RunProgram error running command:{fileName}");
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
}
|
|
} |