Adding option for enabling argument parsing in scripts

This commit is contained in:
Kenneth Skovhede
2024-06-17 09:32:04 +02:00
parent 828ba240f3
commit 1b7e1349ca
4 changed files with 40 additions and 17 deletions
+22 -17
View File
@@ -55,6 +55,7 @@ namespace Duplicati.Library.Modules.Builtin
private const string FINISH_OPTION = "run-script-after";
private const string REQUIRED_OPTION = "run-script-before-required";
private const string TIMEOUT_OPTION = "run-script-timeout";
private const string ENABLE_ARGUMENTS_OPTION = "run-script-with-arguments";
/// <summary>
/// Option used to set the log level for mail reports
/// </summary>
@@ -72,6 +73,7 @@ namespace Duplicati.Library.Modules.Builtin
private string m_startScript = null;
private string m_finishScript = null;
private int m_timeout = 0;
private bool m_enableArguments = false;
private string m_operationName;
private string m_remoteurl;
@@ -86,7 +88,7 @@ namespace Duplicati.Library.Modules.Builtin
/// <summary>
/// The log storage
/// </summary>
private Utility.FileBackedStringList m_logstorage;
private FileBackedStringList m_logstorage;
#region IGenericModule implementation
@@ -95,10 +97,10 @@ namespace Duplicati.Library.Modules.Builtin
commandlineOptions.TryGetValue(STARTUP_OPTION, out m_startScript);
commandlineOptions.TryGetValue(REQUIRED_OPTION, out m_requiredScript);
commandlineOptions.TryGetValue(FINISH_OPTION, out m_finishScript);
m_enableArguments = Utility.Utility.ParseBoolOption(commandlineOptions, ENABLE_ARGUMENTS_OPTION);
string tmpResultFormat;
ResultExportFormat resultFormat;
if (!commandlineOptions.TryGetValue(RESULT_FORMAT_OPTION, out tmpResultFormat))
if (!commandlineOptions.TryGetValue(RESULT_FORMAT_OPTION, out var tmpResultFormat))
{
resultFormat = ResultExportFormat.Duplicati;
}
@@ -109,8 +111,7 @@ namespace Duplicati.Library.Modules.Builtin
resultFormatSerializer = ResultFormatSerializerProvider.GetSerializer(resultFormat);
string t;
if (!commandlineOptions.TryGetValue(TIMEOUT_OPTION, out t))
if (!commandlineOptions.TryGetValue(TIMEOUT_OPTION, out var t))
t = DEFAULT_TIMEOUT;
m_timeout = (int)Utility.Timeparser.ParseTimeSpan(t).TotalMilliseconds;
@@ -118,7 +119,7 @@ namespace Duplicati.Library.Modules.Builtin
m_options = commandlineOptions;
m_options.TryGetValue(OPTION_LOG_FILTER, out var logfilterstring);
var filter = Utility.FilterExpression.ParseLogFilter(logfilterstring);
var filter = FilterExpression.ParseLogFilter(logfilterstring);
var logLevel = Utility.Utility.ParseEnumOption(m_options, OPTION_LOG_LEVEL, DEFAULT_LOG_LEVEL);
m_logstorage = new FileBackedStringList();
@@ -148,6 +149,7 @@ namespace Duplicati.Library.Modules.Builtin
new CommandLineArgument(STARTUP_OPTION, CommandLineArgument.ArgumentType.Path, Strings.RunScript.StartupoptionShort, Strings.RunScript.StartupoptionLong),
new CommandLineArgument(FINISH_OPTION, CommandLineArgument.ArgumentType.Path, Strings.RunScript.FinishoptionShort, Strings.RunScript.FinishoptionLong),
new CommandLineArgument(REQUIRED_OPTION, CommandLineArgument.ArgumentType.Path, Strings.RunScript.RequiredoptionShort, Strings.RunScript.RequiredoptionLong),
new CommandLineArgument(ENABLE_ARGUMENTS_OPTION, CommandLineArgument.ArgumentType.Boolean, Strings.RunScript.EnableArgumentsShort, Strings.RunScript.EnableArgumentsLong),
new CommandLineArgument(RESULT_FORMAT_OPTION,
CommandLineArgument.ArgumentType.Enumeration,
Strings.RunScript.ResultFormatShort,
@@ -169,10 +171,10 @@ namespace Duplicati.Library.Modules.Builtin
public void OnStart(string operationname, ref string remoteurl, ref string[] localpath)
{
if (!string.IsNullOrEmpty(m_requiredScript))
Execute(m_requiredScript, "BEFORE", operationname, ref remoteurl, ref localpath, m_timeout, true, m_options, null, null);
Execute(m_requiredScript, "BEFORE", operationname, ref remoteurl, ref localpath, m_timeout, true, m_enableArguments, m_options, null, null);
if (!string.IsNullOrEmpty(m_startScript))
Execute(m_startScript, "BEFORE", operationname, ref remoteurl, ref localpath, m_timeout, false, m_options, null, null);
Execute(m_startScript, "BEFORE", operationname, ref remoteurl, ref localpath, m_timeout, false, m_enableArguments, m_options, null, null);
// Save options that might be set by a --run-script-before script so that the OnFinish method
// references the same values.
@@ -226,25 +228,28 @@ namespace Duplicati.Library.Modules.Builtin
using (var streamWriter = new StreamWriter(tmpfile))
streamWriter.Write(resultFormatSerializer.Serialize(result, exception, m_logstorage, null));
Execute(m_finishScript, "AFTER", m_operationName, ref m_remoteurl, ref m_localpath, m_timeout, false, m_options, tmpfile, level);
Execute(m_finishScript, "AFTER", m_operationName, ref m_remoteurl, ref m_localpath, m_timeout, false, m_enableArguments, m_options, tmpfile, level);
}
}
#endregion
private static void Execute(string scriptpath, string eventname, string operationname, ref string remoteurl, ref string[] localpath, int timeout, bool requiredScript, IDictionary<string, string> options, string datafile, ParsedResultType? level)
private static void Execute(string scriptpath, string eventname, string operationname, ref string remoteurl, ref string[] localpath, int timeout, bool requiredScript, bool enableArguments, IDictionary<string, string> options, string datafile, ParsedResultType? level)
{
try
{
var args = ARGREGEX.Matches(scriptpath);
var arguments = new List<string>();
if (args.Any())
var arguments = new List<string>() { scriptpath };
if (enableArguments)
{
arguments = args.AsEnumerable().Select(m => m.Groups["value"].Value).ToList();
scriptpath = arguments[0];
arguments.RemoveAt(0);
var args = ARGREGEX.Matches(scriptpath);
if (args.Any())
{
arguments = args.AsEnumerable().Select(m => m.Groups["value"].Value).ToList();
scriptpath = arguments[0];
arguments.RemoveAt(0);
}
}
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(scriptpath, arguments)
var psi = new System.Diagnostics.ProcessStartInfo(scriptpath, arguments)
{
WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden,
CreateNoWindow = true,
@@ -93,6 +93,8 @@ namespace Duplicati.Library.Modules.Builtin.Strings
public static string StdErrorReport(string script, string message) { return LC.L(@"The script ""{0}"" reported error messages: {1}", script, message); }
public static string TimeoutoptionLong { get { return LC.L(@"Sets the maximum time a script is allowed to execute. If the script has not completed within this time, it will continue to execute but the operation will continue too, and no script output will be processed."); } }
public static string TimeoutoptionShort { get { return LC.L(@"Sets the script timeout"); } }
public static string EnableArgumentsShort { get { return LC.L(@"Enable script arguments"); } }
public static string EnableArgumentsLong { get { return LC.L(@"This option enables the use of script arguments. If this option is enabled, the script arguments are treated as commandline strings. Use single or double quotes to separate arguments."); } }
}
internal static class SendMail
{
@@ -11,6 +11,7 @@ REM --run-script-before = <filename>
REM --run-script-before-required = <filename>
REM --run-script-timeout = <time>
REM --run-script-after = <filename>
REM --run-script-with-arguments = <boolean>
REM
REM --run-script-before = <filename>
REM Duplicati will run the script before the backup job and waits for its
@@ -44,6 +45,13 @@ REM warning is logged.
REM The same exit codes as in --run-script-before are supported, but
REM the operation will always continue (i.e. 1 => 0, 3 => 2, 5 => 4)
REM as it has already completed so stopping it during stop is useless.
REM
REM --run-script-with-arguments = <boolean>
REM If set to true, the script path will be parsed as a command line, and the
REM arguments will be passed to the script. If set to false (default),
REM the script path will used as a single path.
REM Use double- or single-quotes to pass arguments with spaces.
REM Example: --run-script-before="\"C:\path to\script.bat\" \"arg1 \" arg2"
@@ -11,6 +11,7 @@
# --run-script-before-required = <filename>
# --run-script-timeout = <time>
# --run-script-after = <filename>
# --run-script-with-arguments = <boolean>
#
# --run-script-before = <filename>
# Duplicati will run the script before the backup job and waits for its
@@ -44,6 +45,13 @@
# The same exit codes as in --run-script-before are supported, but
# the operation will always continue (i.e. 1 => 0, 3 => 2, 5 => 4)
# as it has already completed so stopping it during stop is useless.
#
# --run-script-with-arguments = <boolean>
# If set to true, the script path will be parsed as a command line, and the
# arguments will be passed to the script. If set to false (default),
# the script path will used as a single path.
# Use double- or single-quotes to pass arguments with spaces.
# Example: --run-script-before="\"/path to/script.sh\" \"arg1 \" arg2"
###############################################################################