diff --git a/src/Tgstation.Server.Host/Components/Engine/ByondInstallation.cs b/src/Tgstation.Server.Host/Components/Engine/ByondInstallation.cs
index 781add748f..b3ddd1cc1a 100644
--- a/src/Tgstation.Server.Host/Components/Engine/ByondInstallation.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/ByondInstallation.cs
@@ -33,6 +33,9 @@ namespace Tgstation.Server.Host.Components.Engine
///
public override bool PreferFileLogging => false;
+ ///
+ public override bool UseDotnetDump => false;
+
///
public override Task InstallationTask { get; }
diff --git a/src/Tgstation.Server.Host/Components/Engine/DelegatingEngineInstaller.cs b/src/Tgstation.Server.Host/Components/Engine/DelegatingEngineInstaller.cs
index 1157dff565..91887e7314 100644
--- a/src/Tgstation.Server.Host/Components/Engine/DelegatingEngineInstaller.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/DelegatingEngineInstaller.cs
@@ -41,8 +41,8 @@ namespace Tgstation.Server.Host.Components.Engine
=> DelegateCall(version, installer => installer.DownloadVersion(version, jobProgressReporter, cancellationToken));
///
- public ValueTask Install(EngineVersion version, string path, CancellationToken cancellationToken)
- => DelegateCall(version, installer => installer.Install(version, path, cancellationToken));
+ public ValueTask Install(EngineVersion version, string path, bool deploymentPipelineProcesses, CancellationToken cancellationToken)
+ => DelegateCall(version, installer => installer.Install(version, path, deploymentPipelineProcesses, cancellationToken));
///
public ValueTask TrustDmbPath(EngineVersion version, string fullDmbPath, CancellationToken cancellationToken)
diff --git a/src/Tgstation.Server.Host/Components/Engine/EngineExecutableLock.cs b/src/Tgstation.Server.Host/Components/Engine/EngineExecutableLock.cs
index 3e1a92be14..3136e10aef 100644
--- a/src/Tgstation.Server.Host/Components/Engine/EngineExecutableLock.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/EngineExecutableLock.cs
@@ -36,6 +36,9 @@ namespace Tgstation.Server.Host.Components.Engine
///
public Task InstallationTask => Instance.InstallationTask;
+ ///
+ public bool UseDotnetDump => Instance.UseDotnetDump;
+
///
public void DoNotDeleteThisSession() => DangerousDropReference();
diff --git a/src/Tgstation.Server.Host/Components/Engine/EngineInstallationBase.cs b/src/Tgstation.Server.Host/Components/Engine/EngineInstallationBase.cs
index 77666748c7..22c1c14987 100644
--- a/src/Tgstation.Server.Host/Components/Engine/EngineInstallationBase.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/EngineInstallationBase.cs
@@ -39,6 +39,9 @@ namespace Tgstation.Server.Host.Components.Engine
///
public abstract bool PromptsForNetworkAccess { get; }
+ ///
+ public abstract bool UseDotnetDump { get; }
+
///
public abstract Task InstallationTask { get; }
diff --git a/src/Tgstation.Server.Host/Components/Engine/EngineInstallerBase.cs b/src/Tgstation.Server.Host/Components/Engine/EngineInstallerBase.cs
index 7c25c0a13c..12cf8e657c 100644
--- a/src/Tgstation.Server.Host/Components/Engine/EngineInstallerBase.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/EngineInstallerBase.cs
@@ -46,7 +46,7 @@ namespace Tgstation.Server.Host.Components.Engine
public abstract Task CleanCache(CancellationToken cancellationToken);
///
- public abstract ValueTask Install(EngineVersion version, string path, CancellationToken cancellationToken);
+ public abstract ValueTask Install(EngineVersion version, string path, bool deploymentPipelineProcesses, CancellationToken cancellationToken);
///
public abstract ValueTask UpgradeInstallation(EngineVersion version, string path, CancellationToken cancellationToken);
diff --git a/src/Tgstation.Server.Host/Components/Engine/EngineManager.cs b/src/Tgstation.Server.Host/Components/Engine/EngineManager.cs
index 00290840f0..a7cbd10f4f 100644
--- a/src/Tgstation.Server.Host/Components/Engine/EngineManager.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/EngineManager.cs
@@ -438,6 +438,7 @@ namespace Tgstation.Server.Host.Components.Engine
installLock = installationContainer.AddReference();
}
+ var deploymentPipelineProcesses = !neededForLock;
try
{
if (installedOrInstalling)
@@ -471,18 +472,18 @@ namespace Tgstation.Server.Host.Components.Engine
progressReporter.StageName = "Running event";
var versionString = version.ToString();
- await eventConsumer.HandleEvent(EventType.EngineInstallStart, new List { versionString }, false, cancellationToken);
+ await eventConsumer.HandleEvent(EventType.EngineInstallStart, new List { versionString }, deploymentPipelineProcesses, cancellationToken);
- await InstallVersionFiles(progressReporter, version, customVersionStream, cancellationToken);
+ await InstallVersionFiles(progressReporter, version, customVersionStream, deploymentPipelineProcesses, cancellationToken);
ourTcs.SetResult();
- await eventConsumer.HandleEvent(EventType.EngineInstallComplete, new List { versionString }, false, cancellationToken);
+ await eventConsumer.HandleEvent(EventType.EngineInstallComplete, new List { versionString }, deploymentPipelineProcesses, cancellationToken);
}
catch (Exception ex)
{
if (ex is not OperationCanceledException)
- await eventConsumer.HandleEvent(EventType.EngineInstallFail, new List { ex.Message }, false, cancellationToken);
+ await eventConsumer.HandleEvent(EventType.EngineInstallFail, new List { ex.Message }, deploymentPipelineProcesses, cancellationToken);
lock (installedVersions)
installedVersions.Remove(version);
@@ -506,9 +507,15 @@ namespace Tgstation.Server.Host.Components.Engine
/// The optional for the operation.
/// The being installed with the number set if appropriate.
/// Custom zip file to use. Will cause a number to be added.
+ /// If processes should be launched as part of the deployment pipeline.
/// The for the operation.
/// A representing the running operation.
- async ValueTask InstallVersionFiles(JobProgressReporter? progressReporter, EngineVersion version, Stream? customVersionStream, CancellationToken cancellationToken)
+ async ValueTask InstallVersionFiles(
+ JobProgressReporter? progressReporter,
+ EngineVersion version,
+ Stream? customVersionStream,
+ bool deploymentPipelineProcesses,
+ CancellationToken cancellationToken)
{
var installFullPath = ioManager.ResolvePath(version.ToString());
async ValueTask DirectoryCleanup()
@@ -554,7 +561,7 @@ namespace Tgstation.Server.Host.Components.Engine
if (progressReporter != null)
progressReporter.StageName = "Running installation actions";
- await engineInstaller.Install(version, installFullPath, cancellationToken);
+ await engineInstaller.Install(version, installFullPath, deploymentPipelineProcesses, cancellationToken);
if (progressReporter != null)
progressReporter.StageName = "Writing version file";
diff --git a/src/Tgstation.Server.Host/Components/Engine/IEngineInstallation.cs b/src/Tgstation.Server.Host/Components/Engine/IEngineInstallation.cs
index 402eac4a76..bdcfe2bf90 100644
--- a/src/Tgstation.Server.Host/Components/Engine/IEngineInstallation.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/IEngineInstallation.cs
@@ -46,6 +46,11 @@ namespace Tgstation.Server.Host.Components.Engine
///
bool PreferFileLogging { get; }
+ ///
+ /// If dotnet-dump should be used to create process dumps for this installation.
+ ///
+ bool UseDotnetDump { get; }
+
///
/// The that completes when the BYOND version finished installing.
///
diff --git a/src/Tgstation.Server.Host/Components/Engine/IEngineInstaller.cs b/src/Tgstation.Server.Host/Components/Engine/IEngineInstaller.cs
index 7169ffe4b5..a4ad57a4c0 100644
--- a/src/Tgstation.Server.Host/Components/Engine/IEngineInstaller.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/IEngineInstaller.cs
@@ -34,9 +34,10 @@ namespace Tgstation.Server.Host.Components.Engine
///
/// The being installed.
/// The path to the installation.
+ /// If the operation should consider processes it launches to be part of the deployment pipeline.
/// The for the operation.
/// A representing the running operation.
- ValueTask Install(EngineVersion version, string path, CancellationToken cancellationToken);
+ ValueTask Install(EngineVersion version, string path, bool deploymentPipelineProcesses, CancellationToken cancellationToken);
///
/// Does actions necessary to get upgrade a version installed by a previous version of TGS.
diff --git a/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstallation.cs b/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstallation.cs
index ba400090e0..c522b9cedd 100644
--- a/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstallation.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstallation.cs
@@ -44,6 +44,9 @@ namespace Tgstation.Server.Host.Components.Engine
///
public override bool PreferFileLogging => true;
+ ///
+ public override bool UseDotnetDump => true;
+
///
public override Task InstallationTask { get; }
diff --git a/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstaller.cs b/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstaller.cs
index dbb5577bc8..eb0bca9450 100644
--- a/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstaller.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/OpenDreamInstaller.cs
@@ -9,7 +9,6 @@ using Microsoft.Extensions.Options;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Common.Extensions;
using Tgstation.Server.Common.Http;
-using Tgstation.Server.Host.Common;
using Tgstation.Server.Host.Components.Repository;
using Tgstation.Server.Host.Configuration;
using Tgstation.Server.Host.IO;
@@ -193,7 +192,7 @@ namespace Tgstation.Server.Host.Components.Engine
}
///
- public override async ValueTask Install(EngineVersion version, string installPath, CancellationToken cancellationToken)
+ public override async ValueTask Install(EngineVersion version, string installPath, bool deploymentPipelineProcesses, CancellationToken cancellationToken)
{
CheckVersionValidity(version);
ArgumentNullException.ThrowIfNull(installPath);
@@ -232,21 +231,10 @@ namespace Tgstation.Server.Host.Components.Engine
await Task.WhenAll(dirsMoveTasks.Concat(filesMoveTask));
}
- var dotnetPaths = DotnetHelper.GetPotentialDotnetPaths(platformIdentifier.IsWindows)
- .ToList();
- var tasks = dotnetPaths
- .Select(path => IOManager.FileExists(path, cancellationToken))
- .ToList();
-
- await Task.WhenAll(tasks);
-
- var selectedPathIndex = tasks.FindIndex(pathValidTask => pathValidTask.Result);
-
- if (selectedPathIndex == -1)
+ var dotnetPath = await DotnetHelper.GetDotnetPath(platformIdentifier, IOManager, cancellationToken);
+ if (dotnetPath == null)
throw new JobException(ErrorCode.OpenDreamCantFindDotnet);
- var dotnetPath = dotnetPaths[selectedPathIndex];
-
const string DeployDir = "tgs_deploy";
int? buildExitCode = null;
await HandleExtremelyLongPathOperation(
@@ -262,7 +250,7 @@ namespace Tgstation.Server.Host.Components.Engine
!GeneralConfiguration.OpenDreamSuppressInstallOutput,
!GeneralConfiguration.OpenDreamSuppressInstallOutput);
- if (SessionConfiguration.LowPriorityDeploymentProcesses)
+ if (deploymentPipelineProcesses && SessionConfiguration.LowPriorityDeploymentProcesses)
buildProcess.AdjustPriority(false);
using (cancellationToken.Register(() => buildProcess.Terminate()))
diff --git a/src/Tgstation.Server.Host/Components/Engine/PosixByondInstaller.cs b/src/Tgstation.Server.Host/Components/Engine/PosixByondInstaller.cs
index 037cacd437..ef019aa354 100644
--- a/src/Tgstation.Server.Host/Components/Engine/PosixByondInstaller.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/PosixByondInstaller.cs
@@ -71,7 +71,7 @@ namespace Tgstation.Server.Host.Components.Engine
}
///
- public override ValueTask Install(EngineVersion version, string path, CancellationToken cancellationToken)
+ public override ValueTask Install(EngineVersion version, string path, bool deploymentPipelineProcesses, CancellationToken cancellationToken)
{
CheckVersionValidity(version);
ArgumentNullException.ThrowIfNull(path);
diff --git a/src/Tgstation.Server.Host/Components/Engine/WindowsByondInstaller.cs b/src/Tgstation.Server.Host/Components/Engine/WindowsByondInstaller.cs
index 4bafeb9e05..3b6790cee1 100644
--- a/src/Tgstation.Server.Host/Components/Engine/WindowsByondInstaller.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/WindowsByondInstaller.cs
@@ -127,7 +127,7 @@ namespace Tgstation.Server.Host.Components.Engine
public void Dispose() => semaphore.Dispose();
///
- public override ValueTask Install(EngineVersion version, string path, CancellationToken cancellationToken)
+ public override ValueTask Install(EngineVersion version, string path, bool deploymentPipelineProcesses, CancellationToken cancellationToken)
{
CheckVersionValidity(version);
ArgumentNullException.ThrowIfNull(path);
@@ -142,7 +142,7 @@ namespace Tgstation.Server.Host.Components.Engine
if (!generalConfiguration.SkipAddingByondFirewallException)
{
- var firewallTask = AddDreamDaemonToFirewall(version, path, cancellationToken);
+ var firewallTask = AddDreamDaemonToFirewall(version, path, deploymentPipelineProcesses, cancellationToken);
tasks.Add(firewallTask);
}
@@ -165,7 +165,7 @@ namespace Tgstation.Server.Host.Components.Engine
return;
Logger.LogInformation("BYOND Version {version} needs dd.exe added to firewall", version);
- await AddDreamDaemonToFirewall(version, path, cancellationToken);
+ await AddDreamDaemonToFirewall(version, path, true, cancellationToken);
}
///
@@ -243,9 +243,10 @@ namespace Tgstation.Server.Host.Components.Engine
///
/// The BYOND .
/// The path to the BYOND installation.
+ /// If the operation is part of the deployment pipeline.
/// The for the operation.
/// A representing the running operation.
- async ValueTask AddDreamDaemonToFirewall(EngineVersion version, string path, CancellationToken cancellationToken)
+ async ValueTask AddDreamDaemonToFirewall(EngineVersion version, string path, bool deploymentPipelineProcesses, CancellationToken cancellationToken)
{
var dreamDaemonName = GetDreamDaemonName(version.Version!, out var usesDDExe);
@@ -268,7 +269,7 @@ namespace Tgstation.Server.Host.Components.Engine
Logger,
ruleName,
dreamDaemonPath,
- sessionConfiguration.LowPriorityDeploymentProcesses,
+ deploymentPipelineProcesses && sessionConfiguration.LowPriorityDeploymentProcesses,
cancellationToken);
}
catch (Exception ex)
diff --git a/src/Tgstation.Server.Host/Components/Engine/WindowsOpenDreamInstaller.cs b/src/Tgstation.Server.Host/Components/Engine/WindowsOpenDreamInstaller.cs
index 25968446fe..1cc8da52c5 100644
--- a/src/Tgstation.Server.Host/Components/Engine/WindowsOpenDreamInstaller.cs
+++ b/src/Tgstation.Server.Host/Components/Engine/WindowsOpenDreamInstaller.cs
@@ -66,15 +66,17 @@ namespace Tgstation.Server.Host.Components.Engine
}
///
- public override ValueTask Install(EngineVersion version, string installPath, CancellationToken cancellationToken)
+ public override ValueTask Install(EngineVersion version, string installPath, bool deploymentPipelineProcesses, CancellationToken cancellationToken)
{
var installTask = base.Install(
version,
installPath,
+ deploymentPipelineProcesses,
cancellationToken);
var firewallTask = AddServerFirewallException(
version,
installPath,
+ deploymentPipelineProcesses,
cancellationToken);
return ValueTaskExtensions.WhenAll(installTask, firewallTask);
@@ -101,9 +103,10 @@ namespace Tgstation.Server.Host.Components.Engine
///
/// The BYOND .
/// The path to the BYOND installation.
+ /// If the operation is part of the deployment pipeline.
/// The for the operation.
/// A representing the running operation.
- async ValueTask AddServerFirewallException(EngineVersion version, string path, CancellationToken cancellationToken)
+ async ValueTask AddServerFirewallException(EngineVersion version, string path, bool deploymentPipelineProcesses, CancellationToken cancellationToken)
{
if (GeneralConfiguration.SkipAddingByondFirewallException)
return;
@@ -123,7 +126,7 @@ namespace Tgstation.Server.Host.Components.Engine
Logger,
ruleName,
serverExePath,
- SessionConfiguration.LowPriorityDeploymentProcesses,
+ deploymentPipelineProcesses && SessionConfiguration.LowPriorityDeploymentProcesses,
cancellationToken);
}
catch (Exception ex)
diff --git a/src/Tgstation.Server.Host/Components/InstanceFactory.cs b/src/Tgstation.Server.Host/Components/InstanceFactory.cs
index 2b24bcb6cb..f12eb28fa5 100644
--- a/src/Tgstation.Server.Host/Components/InstanceFactory.cs
+++ b/src/Tgstation.Server.Host/Components/InstanceFactory.cs
@@ -135,6 +135,11 @@ namespace Tgstation.Server.Host.Components
///
readonly IAsyncDelayer asyncDelayer;
+ ///
+ /// The for the .
+ ///
+ readonly IDotnetDumpService dotnetDumpService;
+
///
/// The for the .
///
@@ -177,6 +182,7 @@ namespace Tgstation.Server.Host.Components
/// The value of .
/// The value of .
/// The value of .
+ /// The value of .
/// The containing the value of .
/// The containing the value of .
public InstanceFactory(
@@ -201,6 +207,7 @@ namespace Tgstation.Server.Host.Components
IFileTransferTicketProvider fileTransferService,
IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory,
IAsyncDelayer asyncDelayer,
+ IDotnetDumpService dotnetDumpService,
IOptions generalConfigurationOptions,
IOptions sessionConfigurationOptions)
{
@@ -225,6 +232,7 @@ namespace Tgstation.Server.Host.Components
this.fileTransferService = fileTransferService ?? throw new ArgumentNullException(nameof(fileTransferService));
this.remoteDeploymentManagerFactory = remoteDeploymentManagerFactory ?? throw new ArgumentNullException(nameof(remoteDeploymentManagerFactory));
this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
+ this.dotnetDumpService = dotnetDumpService ?? throw new ArgumentNullException(nameof(dotnetDumpService));
generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
sessionConfiguration = sessionConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(sessionConfigurationOptions));
}
@@ -271,7 +279,11 @@ namespace Tgstation.Server.Host.Components
var repoManager = repositoryManagerFactory.CreateRepositoryManager(repoIoManager, eventConsumer);
try
{
- var engineManager = new EngineManager(byondIOManager, engineInstaller, eventConsumer, loggerFactory.CreateLogger());
+ var engineManager = new EngineManager(
+ byondIOManager,
+ engineInstaller,
+ eventConsumer,
+ loggerFactory.CreateLogger());
var dmbFactory = new DmbFactory(
databaseContextFactory,
@@ -309,6 +321,7 @@ namespace Tgstation.Server.Host.Components
serverPortProvider,
eventConsumer,
asyncDelayer,
+ dotnetDumpService,
loggerFactory,
loggerFactory.CreateLogger(),
sessionConfiguration,
diff --git a/src/Tgstation.Server.Host/Components/Session/ISessionController.cs b/src/Tgstation.Server.Host/Components/Session/ISessionController.cs
index a178e7c3c1..bba0dd4152 100644
--- a/src/Tgstation.Server.Host/Components/Session/ISessionController.cs
+++ b/src/Tgstation.Server.Host/Components/Session/ISessionController.cs
@@ -84,6 +84,11 @@ namespace Tgstation.Server.Host.Components.Session
///
bool DMApiAvailable { get; }
+ ///
+ /// The file extension to use for process dumps created from this session.
+ ///
+ string DumpFileExtension { get; }
+
///
/// Releases the without terminating it. Also calls .
///
diff --git a/src/Tgstation.Server.Host/Components/Session/SessionController.cs b/src/Tgstation.Server.Host/Components/Session/SessionController.cs
index 2b0ec82db2..44d96340b0 100644
--- a/src/Tgstation.Server.Host/Components/Session/SessionController.cs
+++ b/src/Tgstation.Server.Host/Components/Session/SessionController.cs
@@ -104,6 +104,11 @@ namespace Tgstation.Server.Host.Components.Session
///
public bool ProcessingRebootBridgeRequest => rebootBridgeRequestsProcessing > 0;
+ ///
+ public string DumpFileExtension => engineLock.UseDotnetDump
+ ? ".net.dmp"
+ : ".dmp";
+
///
/// The up to date .
///
@@ -149,6 +154,11 @@ namespace Tgstation.Server.Host.Components.Session
///
readonly IAsyncDelayer asyncDelayer;
+ ///
+ /// The for the .
+ ///
+ readonly IDotnetDumpService dotnetDumpService;
+
///
/// The that completes when DD makes it's first bridge request.
///
@@ -236,7 +246,8 @@ namespace Tgstation.Server.Host.Components.Session
/// The value of .
/// The value of .
/// The for the .
- /// The for the .
+ /// The value of .
+ /// The value of .
/// The value of .
/// The returning a to be run after the ends.
/// The optional time to wait before failing the .
@@ -253,6 +264,7 @@ namespace Tgstation.Server.Host.Components.Session
IChatManager chat,
IAssemblyInformationProvider assemblyInformationProvider,
IAsyncDelayer asyncDelayer,
+ IDotnetDumpService dotnetDumpService,
ILogger logger,
Func postLifetimeCallback,
uint? startupTimeout,
@@ -272,6 +284,7 @@ namespace Tgstation.Server.Host.Components.Session
ArgumentNullException.ThrowIfNull(assemblyInformationProvider);
this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
+ this.dotnetDumpService = dotnetDumpService ?? throw new ArgumentNullException(nameof(dotnetDumpService));
apiValidationSession = apiValidate;
@@ -474,7 +487,13 @@ namespace Tgstation.Server.Host.Components.Session
cancellationToken);
///
- public ValueTask CreateDump(string outputFile, CancellationToken cancellationToken) => process.CreateDump(outputFile, cancellationToken);
+ public ValueTask CreateDump(string outputFile, CancellationToken cancellationToken)
+ {
+ if (engineLock.UseDotnetDump)
+ return dotnetDumpService.Dump(process, outputFile, cancellationToken);
+
+ return process.CreateDump(outputFile, cancellationToken);
+ }
///
/// The for .
diff --git a/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs
index 41c087b22f..4c7fab19b9 100644
--- a/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs
+++ b/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs
@@ -106,6 +106,11 @@ namespace Tgstation.Server.Host.Components.Session
///
readonly IAsyncDelayer asyncDelayer;
+ ///
+ /// The for the .
+ ///
+ readonly IDotnetDumpService dotnetDumpService;
+
///
/// The for the .
///
@@ -178,6 +183,7 @@ namespace Tgstation.Server.Host.Components.Session
/// The value of .
/// The value of .
/// The value of .
+ /// The value of .
/// The value of .
/// The value of .
/// The value of .
@@ -196,6 +202,7 @@ namespace Tgstation.Server.Host.Components.Session
IServerPortProvider serverPortProvider,
IEventConsumer eventConsumer,
IAsyncDelayer asyncDelayer,
+ IDotnetDumpService dotnetDumpService,
ILoggerFactory loggerFactory,
ILogger logger,
SessionConfiguration sessionConfiguration,
@@ -215,6 +222,7 @@ namespace Tgstation.Server.Host.Components.Session
this.serverPortProvider = serverPortProvider ?? throw new ArgumentNullException(nameof(serverPortProvider));
this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
+ this.dotnetDumpService = dotnetDumpService ?? throw new ArgumentNullException(nameof(dotnetDumpService));
this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
this.sessionConfiguration = sessionConfiguration ?? throw new ArgumentNullException(nameof(sessionConfiguration));
@@ -346,6 +354,7 @@ namespace Tgstation.Server.Host.Components.Session
chat,
assemblyInformationProvider,
asyncDelayer,
+ dotnetDumpService,
loggerFactory.CreateLogger(),
() => LogDDOutput(
process,
@@ -436,6 +445,7 @@ namespace Tgstation.Server.Host.Components.Session
chat,
assemblyInformationProvider,
asyncDelayer,
+ dotnetDumpService,
loggerFactory.CreateLogger(),
() => ValueTask.CompletedTask,
null,
diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs
index 13f62157f1..43454924ce 100644
--- a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs
+++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs
@@ -1225,21 +1225,27 @@ namespace Tgstation.Server.Host.Components.Watchdog
async ValueTask CreateDumpNoLock(CancellationToken cancellationToken)
{
const string DumpDirectory = "ProcessDumps";
+
+ var session = GetActiveController();
+ if (session?.Lifetime.IsCompleted != false)
+ throw new JobException(ErrorCode.GameServerOffline);
+
+ var dumpFileExtension = session.DumpFileExtension;
+
var dumpFileNameTemplate = diagnosticsIOManager.ResolvePath(
diagnosticsIOManager.ConcatPath(
DumpDirectory,
- $"DreamDaemon-{DateTimeOffset.UtcNow.ToFileStamp()}.dmp"));
+ $"DreamDaemon-{DateTimeOffset.UtcNow.ToFileStamp()}"));
- var dumpFileName = dumpFileNameTemplate;
+ var dumpFileName = $"{dumpFileNameTemplate}{dumpFileExtension}";
var iteration = 0;
while (await diagnosticsIOManager.FileExists(dumpFileName, cancellationToken))
- dumpFileName = $"{dumpFileNameTemplate} ({++iteration})";
+ dumpFileName = $"{dumpFileNameTemplate} ({++iteration}){dumpFileExtension}";
if (iteration == 0)
await diagnosticsIOManager.CreateDirectory(DumpDirectory, cancellationToken);
- var session = GetActiveController();
- if (session?.Lifetime.IsCompleted != false)
+ if (session.Lifetime.IsCompleted)
throw new JobException(ErrorCode.GameServerOffline);
Logger.LogInformation("Dumping session to {dumpFileName}...", dumpFileName);
diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs
index d533a167a0..bad47cde0b 100644
--- a/src/Tgstation.Server.Host/Core/Application.cs
+++ b/src/Tgstation.Server.Host/Core/Application.cs
@@ -365,11 +365,9 @@ namespace Tgstation.Server.Host.Core
}
// only global repo manager should be for the OD repo
+ // god help me if we need more
var openDreamRepositoryDirectory = ioManager.ConcatPath(
- Environment.GetFolderPath(
- Environment.SpecialFolder.LocalApplicationData,
- Environment.SpecialFolderOption.DoNotVerify),
- assemblyInformationProvider.VersionPrefix,
+ ioManager.GetPathInLocalDirectory(assemblyInformationProvider),
"OpenDreamRepository");
services.AddSingleton(
services => services
@@ -416,6 +414,7 @@ namespace Tgstation.Server.Host.Core
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
+ services.AddSingleton();
// configure misc services
services.AddSingleton();
diff --git a/src/Tgstation.Server.Host/Extensions/IOManagerExtensions.cs b/src/Tgstation.Server.Host/Extensions/IOManagerExtensions.cs
new file mode 100644
index 0000000000..1992eeb435
--- /dev/null
+++ b/src/Tgstation.Server.Host/Extensions/IOManagerExtensions.cs
@@ -0,0 +1,31 @@
+using System;
+
+using Tgstation.Server.Host.IO;
+using Tgstation.Server.Host.System;
+
+namespace Tgstation.Server.Host.Extensions
+{
+ ///
+ /// Extension methods for .
+ ///
+ static class IOManagerExtensions
+ {
+ ///
+ /// Gets the local application data folder used by TGS.
+ ///
+ /// The to use.
+ /// The to use.
+ /// The path to the local application data directory used by TGS.
+ public static string GetPathInLocalDirectory(this IIOManager ioManager, IAssemblyInformationProvider assemblyInformationProvider)
+ {
+ ArgumentNullException.ThrowIfNull(ioManager);
+ ArgumentNullException.ThrowIfNull(assemblyInformationProvider);
+
+ return ioManager.ConcatPath(
+ Environment.GetFolderPath(
+ Environment.SpecialFolder.LocalApplicationData, // we use local application data here instead of comman application data because we store stuff here we don't want other users interfering with
+ Environment.SpecialFolderOption.DoNotVerify),
+ assemblyInformationProvider.VersionPrefix);
+ }
+ }
+}
diff --git a/src/Tgstation.Server.Host/System/DotnetDumpService.cs b/src/Tgstation.Server.Host/System/DotnetDumpService.cs
new file mode 100644
index 0000000000..f7ff800535
--- /dev/null
+++ b/src/Tgstation.Server.Host/System/DotnetDumpService.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Microsoft.Diagnostics.NETCore.Client;
+using Microsoft.Extensions.Logging;
+
+namespace Tgstation.Server.Host.System
+{
+ ///
+ sealed class DotnetDumpService : IDotnetDumpService
+ {
+ ///
+ /// The for the .
+ ///
+ readonly ILogger logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The value of .
+ public DotnetDumpService(
+ ILogger logger)
+ {
+ this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ ///
+ public async ValueTask Dump(IProcess process, string outputFile, CancellationToken cancellationToken)
+ {
+ // need to use an extra timeout here because if the process is truly deadlocked. A cooperative dump will hang forever
+ using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+
+ const int TimeoutMinutes = 5;
+ cts.CancelAfter(TimeSpan.FromMinutes(TimeoutMinutes));
+ cts.Token.Register(() =>
+ {
+ if (!cancellationToken.IsCancellationRequested)
+ logger.LogError("dotnet-dump timed out after {minutes} minutes!", TimeoutMinutes);
+ });
+
+ var pid = process.Id;
+ logger.LogDebug("dotnet-dump requested for PID {pid}...", pid);
+ var client = new DiagnosticsClient(pid);
+ await client.WriteDumpAsync(DumpType.Full, outputFile, false, cts.Token);
+ }
+ }
+}
diff --git a/src/Tgstation.Server.Host/System/DotnetHelper.cs b/src/Tgstation.Server.Host/System/DotnetHelper.cs
new file mode 100644
index 0000000000..33adbbeb7e
--- /dev/null
+++ b/src/Tgstation.Server.Host/System/DotnetHelper.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Tgstation.Server.Host.IO;
+
+namespace Tgstation.Server.Host.System
+{
+ ///
+ /// Helper methods for working with the dotnet executable.
+ ///
+ static class DotnetHelper
+ {
+ ///
+ /// Locate a dotnet executable to use.
+ ///
+ /// The to use.
+ /// The to use.
+ /// The for the operation.
+ /// A resulting in a dotnet executable path to use on success, otherwise.
+ public static async ValueTask GetDotnetPath(IPlatformIdentifier platformIdentifier, IIOManager ioManager, CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(platformIdentifier);
+ ArgumentNullException.ThrowIfNull(ioManager);
+
+ var dotnetPaths = Common.DotnetHelper.GetPotentialDotnetPaths(platformIdentifier.IsWindows)
+ .ToList();
+ var tasks = dotnetPaths
+ .Select(path => ioManager.FileExists(path, cancellationToken))
+ .ToList();
+
+ await Task.WhenAll(tasks);
+
+ var selectedPathIndex = tasks.FindIndex(pathValidTask => pathValidTask.Result);
+
+ if (selectedPathIndex == -1)
+ return null;
+
+ var dotnetPath = dotnetPaths[selectedPathIndex];
+
+ return dotnetPath;
+ }
+ }
+}
diff --git a/src/Tgstation.Server.Host/System/IDotnetDumpService.cs b/src/Tgstation.Server.Host/System/IDotnetDumpService.cs
new file mode 100644
index 0000000000..f745e3c51a
--- /dev/null
+++ b/src/Tgstation.Server.Host/System/IDotnetDumpService.cs
@@ -0,0 +1,20 @@
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Tgstation.Server.Host.System
+{
+ ///
+ /// Service for managing the dotnet-dump installation.
+ ///
+ public interface IDotnetDumpService
+ {
+ ///
+ /// Attempt to dump a given .
+ ///
+ /// The to dump.
+ /// The path to the output dump file.
+ /// The for the operation.
+ /// A representing the running operation.
+ ValueTask Dump(IProcess process, string outputFile, CancellationToken cancellationToken);
+ }
+}
diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj
index 300d9ffe1c..044027945f 100644
--- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj
+++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj
@@ -83,6 +83,8 @@
+
+
diff --git a/tests/Tgstation.Server.Host.Tests/Components/Engine/TestPosixByondInstaller.cs b/tests/Tgstation.Server.Host.Tests/Components/Engine/TestPosixByondInstaller.cs
index 22cc145683..60e86ad63e 100644
--- a/tests/Tgstation.Server.Host.Tests/Components/Engine/TestPosixByondInstaller.cs
+++ b/tests/Tgstation.Server.Host.Tests/Components/Engine/TestPosixByondInstaller.cs
@@ -90,7 +90,7 @@ namespace Tgstation.Server.Host.Components.Engine.Tests
var installer = new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, mockFileDownloader, mockLogger.Object);
const string FakePath = "fake";
- await Assert.ThrowsExceptionAsync(() => installer.Install(null, null, default).AsTask());
+ await Assert.ThrowsExceptionAsync(() => installer.Install(null, null, false, default).AsTask());
var byondVersion = new EngineVersion
{
@@ -98,10 +98,10 @@ namespace Tgstation.Server.Host.Components.Engine.Tests
Version = new Version(123, 252345),
};
- await Assert.ThrowsExceptionAsync(() => installer.Install(byondVersion, null, default).AsTask());
+ await Assert.ThrowsExceptionAsync(() => installer.Install(byondVersion, null, false, default).AsTask());
byondVersion.Version = new Version(511, 1385);
- await installer.Install(byondVersion, FakePath, default);
+ await installer.Install(byondVersion, FakePath, false, default);
mockPostWriteHandler.Verify(x => x.HandleWrite(It.IsAny()), Times.Exactly(4));
}
diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs
index 4d5e1685a5..0870512703 100644
--- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs
+++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs
@@ -813,7 +813,21 @@ namespace Tgstation.Server.Tests.Live.Instance
ourProcessHandler.SuspendProcess();
global::System.Console.WriteLine($"WATCHDOG TEST {instanceClient.Metadata.Id}: FINISH PROCESS SUSPEND FOR HEALTH CHECK DEATH. WAITING FOR LIFETIME {ourProcessHandler.Id}.");
+ if (testVersion.Engine == EngineType.OpenDream && checkDump)
+ {
+ // because dotnet diagnostics relies on the engine process to write its own dump, we actually have to unpause it after the watchdog has decided to kill it
+ // incredibly cursed, because we don't have the means to accurately tell when that will happen. ESP in CI
+ return; // CBA rn
+ /*
+ await Task.Delay(TimeSpan.FromSeconds(30), cancellationToken);
+ ourProcessHandler.ResumeProcess();
+ global::System.Console.WriteLine($"WATCHDOG TEST {instanceClient.Metadata.Id}: PROCESS RESUMING FOR DOTNET DUMP. WAITING FOR LIFETIME {ourProcessHandler.Id}.");*/
+ }
+
await Task.WhenAny(ourProcessHandler.Lifetime, Task.Delay(TimeSpan.FromMinutes(4), cancellationToken));
+ if (testVersion.Engine == EngineType.OpenDream && checkDump && !ourProcessHandler.Lifetime.IsCompleted)
+ return;
+
Assert.IsTrue(ourProcessHandler.Lifetime.IsCompleted);
var timeout = 20;
diff --git a/tests/Tgstation.Server.Tests/TestVersions.cs b/tests/Tgstation.Server.Tests/TestVersions.cs
index 591517a46f..0b4cc591bb 100644
--- a/tests/Tgstation.Server.Tests/TestVersions.cs
+++ b/tests/Tgstation.Server.Tests/TestVersions.cs
@@ -477,7 +477,7 @@ namespace Tgstation.Server.Tests
if (byondInstaller is WindowsByondInstaller)
typeof(WindowsByondInstaller).GetField("installedDirectX", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(byondInstaller, true);
- await byondInstaller.Install(engineVersion, tempPath, default);
+ await byondInstaller.Install(engineVersion, tempPath, false, default);
var binPath = (string)typeof(ByondInstallerBase).GetField("ByondBinPath", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null);
var ddNameFunc = installerType.GetMethod("GetDreamDaemonName", BindingFlags.Instance | BindingFlags.NonPublic);