|
|
@ -4,6 +4,7 @@ using CoreAgent.Domain.Interfaces.System.Command; |
|
|
|
using CoreAgent.Domain.Models.System; |
|
|
|
using CoreAgent.Infrastructure.Command.Base; |
|
|
|
using Microsoft.Extensions.Logging; |
|
|
|
using System.IO; |
|
|
|
|
|
|
|
namespace CoreAgent.Infrastructure.Command.Executors; |
|
|
|
|
|
|
@ -12,35 +13,92 @@ namespace CoreAgent.Infrastructure.Command.Executors; |
|
|
|
/// </summary>
|
|
|
|
public class LinuxCommandExecutor : BaseCommandExecutor |
|
|
|
{ |
|
|
|
private const string ShellPath = "/bin/bash"; |
|
|
|
private const string BashPath = "/bin/bash"; |
|
|
|
private const string ShPath = "/bin/sh"; |
|
|
|
private readonly string _shellPath; |
|
|
|
|
|
|
|
public LinuxCommandExecutor(ILogger<LinuxCommandExecutor> logger) : base(logger) |
|
|
|
{ |
|
|
|
// 优先使用 bash,如果不存在则使用 sh
|
|
|
|
_shellPath = File.Exists(BashPath) ? BashPath : ShPath; |
|
|
|
_logger.LogInformation("使用Shell: {ShellPath}", _shellPath); |
|
|
|
} |
|
|
|
|
|
|
|
protected override string DefaultShell => ShellPath; |
|
|
|
protected override string DefaultShell => _shellPath; |
|
|
|
|
|
|
|
public override async Task ExecuteCommandWithOutputAsync(string command, Action<string> outputHandler, CancellationTokenSource cancellationTokenSource) |
|
|
|
public override async Task ExecuteCommandWithOutputAsync(string command, Action<string> outputHandler, Action<string> errorHandler, CancellationTokenSource cancellationTokenSource, int timeout = -1) |
|
|
|
{ |
|
|
|
if (string.IsNullOrWhiteSpace(command)) |
|
|
|
{ |
|
|
|
throw new ArgumentException("命令不能为空", nameof(command)); |
|
|
|
} |
|
|
|
|
|
|
|
if (outputHandler == null) |
|
|
|
{ |
|
|
|
throw new ArgumentNullException(nameof(outputHandler)); |
|
|
|
} |
|
|
|
|
|
|
|
if (errorHandler == null) |
|
|
|
{ |
|
|
|
throw new ArgumentNullException(nameof(errorHandler)); |
|
|
|
} |
|
|
|
|
|
|
|
if (cancellationTokenSource == null) |
|
|
|
{ |
|
|
|
throw new ArgumentNullException(nameof(cancellationTokenSource)); |
|
|
|
} |
|
|
|
|
|
|
|
try |
|
|
|
{ |
|
|
|
_logger.LogDebug("开始执行命令: {Command}", command); |
|
|
|
var cmd = Cli.Wrap(DefaultShell) |
|
|
|
.WithArguments($"-c \"{command}\"") |
|
|
|
|
|
|
|
var cmd = Cli.Wrap(_shellPath) |
|
|
|
.WithArguments(GetCommandArguments(command)) |
|
|
|
.WithStandardOutputPipe(PipeTarget.ToDelegate(outputHandler)) |
|
|
|
.WithStandardErrorPipe(PipeTarget.ToDelegate(outputHandler)) |
|
|
|
.WithStandardErrorPipe(PipeTarget.ToDelegate(errorHandler)) |
|
|
|
.WithValidation(CommandResultValidation.None); |
|
|
|
var result = await cmd.ExecuteBufferedAsync(cancellationTokenSource.Token); |
|
|
|
|
|
|
|
// 如果设置了超时,创建一个新的 CancellationTokenSource
|
|
|
|
using var timeoutCts = timeout > 0 ? new CancellationTokenSource(timeout) : null; |
|
|
|
using var linkedCts = timeout > 0 |
|
|
|
? CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, timeoutCts!.Token) |
|
|
|
: cancellationTokenSource; |
|
|
|
|
|
|
|
var result = await cmd.ExecuteBufferedAsync(linkedCts.Token); |
|
|
|
_logger.LogInformation("命令执行结果: {IsSuccess}", result.IsSuccess); |
|
|
|
} |
|
|
|
catch (OperationCanceledException ex) |
|
|
|
{ |
|
|
|
_logger.LogError(ex, "命令执行超时: {Command}", command); |
|
|
|
throw; |
|
|
|
} |
|
|
|
catch (Exception ex) |
|
|
|
{ |
|
|
|
_logger.LogError(ex, "命令执行失败: {Command}", command); |
|
|
|
_logger.LogError(ex, "命令执行失败: {Command}, 错误: {ErrorMessage}", command, ex.Message); |
|
|
|
throw; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public override async Task<CommandExecutionResult> ExecuteCommandAsync(string command, CancellationTokenSource cancellationTokenSource, int timeout = -1) |
|
|
|
{ |
|
|
|
return await ExecuteCommandInternalAsync(command, GetCommandArguments(command), cancellationTokenSource, timeout); |
|
|
|
} |
|
|
|
|
|
|
|
public override async Task<CommandExecutionResult> ExecuteCommandAsync(string command, CancellationTokenSource cancellationTokenSource) |
|
|
|
protected override string GetCommandArguments(string command) |
|
|
|
{ |
|
|
|
if (string.IsNullOrWhiteSpace(command)) |
|
|
|
{ |
|
|
|
return await ExecuteCommandInternalAsync(command, $"-c \"{command}\"", cancellationTokenSource); |
|
|
|
throw new ArgumentException("命令不能为空", nameof(command)); |
|
|
|
} |
|
|
|
|
|
|
|
// 转义命令中的特殊字符
|
|
|
|
var escapedCommand = command |
|
|
|
.Replace("\"", "\\\"") |
|
|
|
.Replace("'", "\\'") |
|
|
|
.Replace("$", "\\$") |
|
|
|
.Replace("`", "\\`") |
|
|
|
.Replace("\\", "\\\\"); |
|
|
|
|
|
|
|
return $"-c '{escapedCommand}'"; |
|
|
|
} |
|
|
|
} |