diff --git a/src/Tgstation.Server.Host/Components/EventConsumer.cs b/src/Tgstation.Server.Host/Components/EventConsumer.cs
new file mode 100644
index 0000000000..83e1c5ae69
--- /dev/null
+++ b/src/Tgstation.Server.Host/Components/EventConsumer.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Tgstation.Server.Host.Components.StaticFiles;
+using Tgstation.Server.Host.Components.Watchdog;
+
+namespace Tgstation.Server.Host.Components
+{
+ ///
+ sealed class EventConsumer : IEventConsumer
+ {
+ ///
+ /// The for the
+ ///
+ readonly IConfiguration configuration;
+
+ ///
+ /// The for the
+ ///
+ IWatchdog watchdog;
+
+ ///
+ /// Construct an
+ ///
+ /// The value of
+ public EventConsumer(IConfiguration configuration)
+ {
+ this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
+ }
+
+ ///
+ public async Task HandleEvent(EventType eventType, IEnumerable parameters, CancellationToken cancellationToken)
+ {
+ if (watchdog == null)
+ throw new InvalidOperationException("EventConsumer used without watchdog set!");
+
+ if (!await configuration.HandleEvent(eventType, parameters, cancellationToken).ConfigureAwait(false))
+ return false;
+ return await watchdog.HandleEvent(eventType, parameters, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Set the for the
+ ///
+ /// The value of
+ public void SetWatchdog(IWatchdog watchdog)
+ {
+ if (watchdog != null)
+ throw new InvalidOperationException("watchdog already set!");
+ this.watchdog = watchdog;
+ }
+ }
+}
diff --git a/src/Tgstation.Server.Host/Components/IEventConsumer.cs b/src/Tgstation.Server.Host/Components/IEventConsumer.cs
index 986f9f2213..55945548c2 100644
--- a/src/Tgstation.Server.Host/Components/IEventConsumer.cs
+++ b/src/Tgstation.Server.Host/Components/IEventConsumer.cs
@@ -15,7 +15,7 @@ namespace Tgstation.Server.Host.Components
/// The
/// The parameters for
/// The for the operation
- /// A representing the running operation
- Task HandleEvent(EventType eventType, IEnumerable parameters, CancellationToken cancellationToken);
+ /// A resulting in if more should run, otherwise
+ Task HandleEvent(EventType eventType, IEnumerable parameters, CancellationToken cancellationToken);
}
}
diff --git a/src/Tgstation.Server.Host/Components/InstanceFactory.cs b/src/Tgstation.Server.Host/Components/InstanceFactory.cs
index 9bb2a2b73d..f6c402c1fa 100644
--- a/src/Tgstation.Server.Host/Components/InstanceFactory.cs
+++ b/src/Tgstation.Server.Host/Components/InstanceFactory.cs
@@ -6,6 +6,7 @@ using Tgstation.Server.Host.Components.Chat;
using Tgstation.Server.Host.Components.Chat.Commands;
using Tgstation.Server.Host.Components.Compiler;
using Tgstation.Server.Host.Components.Repository;
+using Tgstation.Server.Host.Components.StaticFiles;
using Tgstation.Server.Host.Components.Watchdog;
using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.IO;
@@ -81,6 +82,11 @@ namespace Tgstation.Server.Host.Components
///
readonly IProviderFactory providerFactory;
+ ///
+ /// The for the
+ ///
+ readonly IScriptExecutor scriptExecutor;
+
///
/// Construct an
///
@@ -97,7 +103,8 @@ namespace Tgstation.Server.Host.Components
/// The value of
/// The value of
/// The value of
- public InstanceFactory(IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, ILoggerFactory loggerFactory, IByondTopicSender byondTopicSender, IServerUpdater serverUpdater, ICryptographySuite cryptographySuite, IExecutor executor, ICommandFactory commandFactory, ISynchronousIOManager synchronousIOManager, ISymlinkFactory symlinkFactory, IByondInstaller byondInstaller, IProviderFactory providerFactory)
+ /// The value of
+ public InstanceFactory(IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, ILoggerFactory loggerFactory, IByondTopicSender byondTopicSender, IServerUpdater serverUpdater, ICryptographySuite cryptographySuite, IExecutor executor, ICommandFactory commandFactory, ISynchronousIOManager synchronousIOManager, ISymlinkFactory symlinkFactory, IByondInstaller byondInstaller, IProviderFactory providerFactory, IScriptExecutor scriptExecutor)
{
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
@@ -112,6 +119,7 @@ namespace Tgstation.Server.Host.Components
this.symlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory));
this.byondInstaller = byondInstaller ?? throw new ArgumentNullException(nameof(byondInstaller));
this.providerFactory = providerFactory ?? throw new ArgumentNullException(nameof(providerFactory));
+ this.scriptExecutor = scriptExecutor ?? throw new ArgumentNullException(nameof(scriptExecutor));
}
///
@@ -126,6 +134,9 @@ namespace Tgstation.Server.Host.Components
var gameIoManager = new ResolvingIOManager(instanceIoManager, "Game");
var configurationIoManager = new ResolvingIOManager(instanceIoManager, "Configuration");
+ var configuration = new StaticFiles.Configuration(configurationIoManager, synchronousIOManager, symlinkFactory, scriptExecutor, loggerFactory.CreateLogger());
+ var eventConsumer = new EventConsumer(configuration);
+
var dmbFactory = new DmbFactory(databaseContextFactory, gameIoManager, metadata.CloneMetadata());
try
{
@@ -136,18 +147,18 @@ namespace Tgstation.Server.Host.Components
try
{
var byond = new ByondManager(byondIOManager, byondInstaller, loggerFactory.CreateLogger());
- var configuration = new StaticFiles.Configuration(configurationIoManager, synchronousIOManager, symlinkFactory, loggerFactory.CreateLogger());
var chat = chatFactory.CreateChat(metadata.ChatSettings);
try
{
var sessionControllerFactory = new SessionControllerFactory(executor, byond, byondTopicSender, interopRegistrar, cryptographySuite, application, gameIoManager, chat, loggerFactory, metadata.CloneMetadata());
var reattachInfoHandler = new ReattachInfoHandler(databaseContextFactory, dmbFactory, metadata.CloneMetadata());
- var watchdogFactory = new WatchdogFactory(chat, sessionControllerFactory, serverUpdater, loggerFactory, reattachInfoHandler, databaseContextFactory, byondTopicSender, metadata.CloneMetadata());
+ var watchdogFactory = new WatchdogFactory(chat, sessionControllerFactory, serverUpdater, loggerFactory, reattachInfoHandler, databaseContextFactory, byondTopicSender, eventConsumer, metadata.CloneMetadata());
var watchdog = watchdogFactory.CreateWatchdog(dmbFactory, metadata.DreamDaemonSettings);
+ eventConsumer.SetWatchdog(watchdog);
try
{
- var dreamMaker = new DreamMaker(byond, ioManager, configuration, sessionControllerFactory, dmbFactory, application, watchdog, loggerFactory.CreateLogger());
+ var dreamMaker = new DreamMaker(byond, ioManager, configuration, sessionControllerFactory, dmbFactory, application, eventConsumer, loggerFactory.CreateLogger());
return new Instance(metadata.CloneMetadata(), repoManager, byond, dreamMaker, watchdog, chat, configuration, dmbFactory, databaseContextFactory, dmbFactory);
}
diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs
index d014ccd90d..6442c22e7c 100644
--- a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs
+++ b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
@@ -23,6 +24,12 @@ namespace Tgstation.Server.Host.Components.StaticFiles
const string CodeModificationsHeadFile = "HeadInclude.dm";
const string CodeModificationsTailFile = "TailInclude.dm";
+ static readonly IReadOnlyDictionary EventTypeScriptFileNameMap = new Dictionary
+ {
+ };
+
+ static readonly string SystemScriptFileExtension = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".bat" : ".sh";
+
///
/// The for
///
@@ -38,6 +45,11 @@ namespace Tgstation.Server.Host.Components.StaticFiles
///
readonly ISymlinkFactory symlinkFactory;
+ ///
+ /// The for
+ ///
+ readonly IScriptExecutor scriptExecutor;
+
///
/// The for
///
@@ -49,12 +61,14 @@ namespace Tgstation.Server.Host.Components.StaticFiles
/// The value of
/// The value of
/// The value of
+ /// The value of
/// The value of
- public Configuration(IIOManager ioManager, ISynchronousIOManager synchronousIOManager, ISymlinkFactory symlinkFactory, ILogger logger)
+ public Configuration(IIOManager ioManager, ISynchronousIOManager synchronousIOManager, ISymlinkFactory symlinkFactory, IScriptExecutor scriptExecutor, ILogger logger)
{
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
this.synchronousIOManager = synchronousIOManager ?? throw new ArgumentNullException(nameof(synchronousIOManager));
this.symlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory));
+ this.scriptExecutor = scriptExecutor ?? throw new ArgumentNullException(nameof(scriptExecutor));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
@@ -253,5 +267,21 @@ namespace Tgstation.Server.Host.Components.StaticFiles
///
public Task StopAsync(CancellationToken cancellationToken) => EnsureDirectories(cancellationToken);
+
+ ///
+ public async Task HandleEvent(EventType eventType, IEnumerable parameters, CancellationToken cancellationToken)
+ {
+ await EnsureDirectories(cancellationToken).ConfigureAwait(false);
+
+ if (!EventTypeScriptFileNameMap.TryGetValue(eventType, out var scriptName))
+ return true;
+
+ var files = await ioManager.GetFilesWithExtension(EventScriptsSubdirectory, SystemScriptFileExtension, cancellationToken).ConfigureAwait(false);
+ var resolvedScriptsDir = ioManager.ResolvePath(EventScriptsSubdirectory);
+ foreach (var I in files.Where(x => x.StartsWith(scriptName, StringComparison.Ordinal)))
+ if ((await scriptExecutor.ExecuteScript(ioManager.ConcatPath(resolvedScriptsDir, I), parameters, cancellationToken).ConfigureAwait(false)) != 0)
+ return false;
+ return true;
+ }
}
}
diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs
index 433e532413..883568558e 100644
--- a/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs
+++ b/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs
@@ -10,7 +10,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles
///
/// For managing the Configuration directory
///
- public interface IConfiguration : IHostedService
+ public interface IConfiguration : IHostedService, IEventConsumer
{
///
/// Copies all files in the CodeModifications directory to
diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/IScriptExecutor.cs b/src/Tgstation.Server.Host/Components/StaticFiles/IScriptExecutor.cs
new file mode 100644
index 0000000000..a9e26ff72f
--- /dev/null
+++ b/src/Tgstation.Server.Host/Components/StaticFiles/IScriptExecutor.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Tgstation.Server.Host.Components.StaticFiles
+{
+ ///
+ /// For executing system shell scripts
+ ///
+ interface IScriptExecutor
+ {
+ ///
+ /// Execute a shell script and get the result
+ ///
+ /// The absolute path to the script
+ /// Command line parameters for the script
+ /// The for the operation
+ /// A resulting in the or if an error occurred
+ Task ExecuteScript(string scriptPath, IEnumerable parameters, CancellationToken cancellationToken);
+ }
+}
diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/ScriptExecutor.cs b/src/Tgstation.Server.Host/Components/StaticFiles/ScriptExecutor.cs
new file mode 100644
index 0000000000..7aa7409d4c
--- /dev/null
+++ b/src/Tgstation.Server.Host/Components/StaticFiles/ScriptExecutor.cs
@@ -0,0 +1,75 @@
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using Tgstation.Server.Host.IO;
+
+namespace Tgstation.Server.Host.Components.StaticFiles
+{
+ ///
+ sealed class ScriptExecutor : IScriptExecutor
+ {
+ ///
+ /// The for the
+ ///
+ readonly IIOManager ioManager;
+
+ ///
+ /// The for the
+ ///
+ readonly ILogger logger;
+
+ ///
+ /// Construct a
+ ///
+ /// The value of
+ /// The value of
+ public ScriptExecutor(IIOManager ioManager, ILogger logger)
+ {
+ this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
+ this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ ///
+ public async Task ExecuteScript(string scriptPath, IEnumerable parameters, CancellationToken cancellationToken)
+ {
+ try
+ {
+ using (var process = new Process())
+ {
+ process.StartInfo.FileName = scriptPath;
+ process.StartInfo.WorkingDirectory = ioManager.GetDirectoryName(scriptPath);
+ process.StartInfo.Arguments = String.Join(" ", parameters);
+ process.EnableRaisingEvents = true;
+
+ var tcs = new TaskCompletionSource
readonly IByondTopicSender byondTopicSender;
+ ///
+ /// The for the
+ ///
+ readonly IEventConsumer eventConsumer;
+
///
/// The for the
///
@@ -134,7 +139,8 @@ namespace Tgstation.Server.Host.Components.Watchdog
/// The initial value of
/// The value of
/// The value of
- public Watchdog(IChat chat, ISessionControllerFactory sessionControllerFactory, IDmbFactory dmbFactory, IServerUpdater serverUpdater, ILogger logger, IReattachInfoHandler reattachInfoHandler, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, bool autoStart)
+ /// The value of
+ public Watchdog(IChat chat, ISessionControllerFactory sessionControllerFactory, IDmbFactory dmbFactory, IServerUpdater serverUpdater, ILogger logger, IReattachInfoHandler reattachInfoHandler, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IEventConsumer eventConsumer, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, bool autoStart)
{
this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
this.sessionControllerFactory = sessionControllerFactory ?? throw new ArgumentNullException(nameof(sessionControllerFactory));
@@ -143,6 +149,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
this.reattachInfoHandler = reattachInfoHandler ?? throw new ArgumentNullException(nameof(reattachInfoHandler));
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender));
+ this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
this.autoStart = autoStart;
@@ -775,13 +782,13 @@ namespace Tgstation.Server.Host.Components.Watchdog
}
///
- public async Task HandleEvent(EventType eventType, IEnumerable parameters, CancellationToken cancellationToken)
+ public async Task HandleEvent(EventType eventType, IEnumerable parameters, CancellationToken cancellationToken)
{
string results;
using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
{
if (!Running)
- return;
+ return true;
var builder = new StringBuilder(InteropConstants.DMTopicEvent);
foreach (var I in parameters)
@@ -795,7 +802,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
}
if (results == null)
- return;
+ return true;
List responses;
try
@@ -805,10 +812,12 @@ namespace Tgstation.Server.Host.Components.Watchdog
catch
{
logger.LogInformation("Recieved invalid response from DD when parsing event {0}:{1}{2}", eventType, Environment.NewLine, results);
- return;
+ return true;
}
await Task.WhenAll(responses.Select(x => chat.SendMessage(x.Message, x.ChannelIds, cancellationToken))).ConfigureAwait(false);
+
+ return true;
}
///
diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogFactory.cs
index 2df5df1d9e..2cf4f407af 100644
--- a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogFactory.cs
+++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogFactory.cs
@@ -46,11 +46,16 @@ namespace Tgstation.Server.Host.Components.Watchdog
///
readonly IByondTopicSender byondTopicSender;
+ ///
+ /// The for the
+ ///
+ readonly IEventConsumer eventConsumer;
+
///
/// The for the
///
readonly Api.Models.Instance instance;
-
+
///
/// Construct a
@@ -62,8 +67,9 @@ namespace Tgstation.Server.Host.Components.Watchdog
/// The value of
/// The value of
/// The value of
+ /// The value of
/// The value of
- public WatchdogFactory(IChat chat, ISessionControllerFactory sessionControllerFactory, IServerUpdater serverUpdater, ILoggerFactory loggerFactory, IReattachInfoHandler reattachInfoHandler, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, Api.Models.Instance instance)
+ public WatchdogFactory(IChat chat, ISessionControllerFactory sessionControllerFactory, IServerUpdater serverUpdater, ILoggerFactory loggerFactory, IReattachInfoHandler reattachInfoHandler, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IEventConsumer eventConsumer, Api.Models.Instance instance)
{
this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
this.sessionControllerFactory = sessionControllerFactory ?? throw new ArgumentNullException(nameof(sessionControllerFactory));
@@ -72,10 +78,11 @@ namespace Tgstation.Server.Host.Components.Watchdog
this.reattachInfoHandler = reattachInfoHandler ?? throw new ArgumentNullException(nameof(reattachInfoHandler));
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender));
+ this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
}
///
- public IWatchdog CreateWatchdog(IDmbFactory dmbFactory, DreamDaemonSettings settings) => new Watchdog(chat, sessionControllerFactory, dmbFactory, serverUpdater, loggerFactory.CreateLogger(), reattachInfoHandler, databaseContextFactory, byondTopicSender, settings, instance, settings.AutoStart.Value);
+ public IWatchdog CreateWatchdog(IDmbFactory dmbFactory, DreamDaemonSettings settings) => new Watchdog(chat, sessionControllerFactory, dmbFactory, serverUpdater, loggerFactory.CreateLogger(), reattachInfoHandler, databaseContextFactory, byondTopicSender, eventConsumer, settings, instance, settings.AutoStart.Value);
}
}