AnsonBot/AnsonBot.Core/IRconService.cs
2026-07-30 20:47:39 +01:00

70 lines
2.3 KiB
C#

using Docker.DotNet;
using Docker.DotNet.Models;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace AnsonBot.Core;
public interface IRconService
{
Task<string> SendAsync(string command, CancellationToken ct = default);
}
public class DockerRconService : IRconService, IDisposable
{
private readonly DockerOptions _options;
private readonly ILogger<DockerRconService> _logger;
private readonly DockerClient _client;
public DockerRconService(
IOptions<DockerOptions> options,
ILogger<DockerRconService> logger)
{
_options = options.Value;
_logger = logger;
var config = string.IsNullOrWhiteSpace(_options.DaemonUri)
? new DockerClientConfiguration()
: new DockerClientConfiguration(new Uri(_options.DaemonUri));
_client = config.CreateClient();
}
public async Task<string> SendAsync(string command, CancellationToken ct = default)
{
using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
cts.CancelAfter(TimeSpan.FromSeconds(_options.TimeoutSeconds));
_logger.LogInformation("Exec in {Container}: {Cli} {Command}",
_options.ContainerName, _options.RconCliPath, command);
var exec = await _client.Exec.ExecCreateContainerAsync(
_options.ContainerName,
new ContainerExecCreateParameters
{
// Passed as argv, so no shell quoting concerns.
Cmd = new List<string> { _options.RconCliPath, command },
AttachStdout = true,
AttachStderr = true
},
cts.Token);
using var stream = await _client.Exec.StartAndAttachContainerExecAsync(
exec.ID, tty: false, cts.Token);
var (stdout, stderr) = await stream.ReadOutputToEndAsync(cts.Token);
var inspect = await _client.Exec.InspectContainerExecAsync(exec.ID, cts.Token);
if (inspect.ExitCode != 0)
{
_logger.LogError("rcon exec failed (exit {Code}): {Error}", inspect.ExitCode, stderr);
throw new InvalidOperationException(
$"'{_options.RconCliPath} {command}' exited with code {inspect.ExitCode}: {stderr}");
}
return stdout;
}
public void Dispose() => _client.Dispose();
}