diff --git a/build/Version.props b/build/Version.props index b169916252..34c362b277 100644 --- a/build/Version.props +++ b/build/Version.props @@ -3,8 +3,8 @@ 4.2.1 - 6.2.0 - 6.1.0 + 6.3.0 + 6.2.0 5.1.1 0.4.0 1.1.0 diff --git a/src/Tgstation.Server.Api/Models/ErrorCode.cs b/src/Tgstation.Server.Api/Models/ErrorCode.cs index 9de878067c..f36699047d 100644 --- a/src/Tgstation.Server.Api/Models/ErrorCode.cs +++ b/src/Tgstation.Server.Api/Models/ErrorCode.cs @@ -471,5 +471,11 @@ namespace Tgstation.Server.Api.Models /// [Description("Cannot set both softShutdown and softReboot at once!")] DreamDaemonDoubleSoft, + + /// + /// Attempted to launch DreamDaemon on a user account that had the BYOND pager running. + /// + [Description("Cannot start DreamDaemon headless with the BYOND pager running!")] + DeploymentPagerRunning, } } \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs index 92a76a973b..8f5bb7db31 100644 --- a/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs @@ -201,7 +201,7 @@ namespace Tgstation.Server.Host.Components.Session if (launchParameters.SecurityLevel == DreamDaemonSecurity.Trusted) await byondLock.TrustDmbPath(ioManager.ConcatPath(basePath, dmbProvider.DmbName), cancellationToken).ConfigureAwait(false); - CheckPagerIsNotRunning(); + await CheckPagerIsNotRunning(cancellationToken).ConfigureAwait(false); var accessIdentifier = cryptographySuite.GetSecureString(); @@ -403,10 +403,24 @@ namespace Tgstation.Server.Host.Components.Session /// /// Make sure the BYOND pager is not running. /// - void CheckPagerIsNotRunning() + /// The for the operation. + /// A representing the running operation. + async Task CheckPagerIsNotRunning(CancellationToken cancellationToken) { - if (platformIdentifier.IsWindows && processExecutor.IsProcessWithNameRunning("byond")) - throw new JobException("Cannot start DreamDaemon headless with the BYOND pager running!"); + if (!platformIdentifier.IsWindows) + return; + + using var otherProcess = processExecutor.GetProcessByName("byond"); + if (otherProcess == null) + return; + + var otherUsernameTask = otherProcess.GetExecutingUsername(cancellationToken); + using var ourProcess = processExecutor.GetCurrentProcess(); + var ourUserName = await ourProcess.GetExecutingUsername(cancellationToken).ConfigureAwait(false); + var otherUserName = await otherUsernameTask.ConfigureAwait(false); + + if(otherUserName.Equals(ourUserName, StringComparison.Ordinal)) + throw new JobException(ErrorCode.DeploymentPagerRunning); } } } diff --git a/src/Tgstation.Server.Host/System/IProcessExecutor.cs b/src/Tgstation.Server.Host/System/IProcessExecutor.cs index 6b732915dd..3dea91680b 100644 --- a/src/Tgstation.Server.Host/System/IProcessExecutor.cs +++ b/src/Tgstation.Server.Host/System/IProcessExecutor.cs @@ -18,17 +18,23 @@ IProcess LaunchProcess(string fileName, string workingDirectory, string arguments = null, bool readOutput = false, bool readError = false, bool noShellExecute = false); /// - /// Get a by + /// Get a representing the running executable. /// - /// The - /// The represented by on success, on failure + /// The current . + IProcess GetCurrentProcess(); + + /// + /// Get a by . + /// + /// The . + /// The represented by on success, on failure. IProcess GetProcess(int id); /// - /// Check if a with a given is running. + /// Get a with a given . /// - /// The name of the process without the extension. - /// if the process is running, otherwise. - bool IsProcessWithNameRunning(string name); + /// The name of the process executable without the extension. + /// The represented by on success, on failure. + IProcess GetProcessByName(string name); } } diff --git a/src/Tgstation.Server.Host/System/ProcessExecutor.cs b/src/Tgstation.Server.Host/System/ProcessExecutor.cs index 66841aad75..9284a0703e 100644 --- a/src/Tgstation.Server.Host/System/ProcessExecutor.cs +++ b/src/Tgstation.Server.Host/System/ProcessExecutor.cs @@ -1,6 +1,5 @@ using Microsoft.Extensions.Logging; using System; -using System.Linq; using System.Text; using System.Threading.Tasks; @@ -83,23 +82,15 @@ namespace Tgstation.Server.Host.System return null; } - try - { - return new Process( - processFeatures, - handle, - AttachExitHandler(handle), - null, - null, - null, - loggerFactory.CreateLogger(), - true); - } - catch - { - handle.Dispose(); - throw; - } + return CreateFromExistingHandle(handle); + } + + /// + public IProcess GetCurrentProcess() + { + logger.LogTrace("Getting current process..."); + var handle = global::System.Diagnostics.Process.GetCurrentProcess(); + return CreateFromExistingHandle(handle); } /// @@ -231,13 +222,50 @@ namespace Tgstation.Server.Host.System } /// - public bool IsProcessWithNameRunning(string name) + public IProcess GetProcessByName(string name) { + logger.LogTrace("GetProcessByName: {0}...", name ?? throw new ArgumentNullException(nameof(name))); var procs = global::System.Diagnostics.Process.GetProcessesByName(name); + global::System.Diagnostics.Process handle = null; foreach (var proc in procs) - proc.Dispose(); + if (handle == null) + handle = proc; + else + { + logger.LogTrace("Disposing extra found PID: {0}", proc.Id); + proc.Dispose(); + } - return procs.Any(); + if (handle == null) + return null; + + return CreateFromExistingHandle(handle); + } + + /// + /// Create a given an existing . + /// + /// The to create a from. + /// The based on . + private IProcess CreateFromExistingHandle(global::System.Diagnostics.Process handle) + { + try + { + return new Process( + processFeatures, + handle, + AttachExitHandler(handle), + null, + null, + null, + loggerFactory.CreateLogger(), + true); + } + catch + { + handle.Dispose(); + throw; + } } } } diff --git a/tests/Tgstation.Server.Tests/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Instance/WatchdogTest.cs index 20e691b1e2..f7a8ed3e55 100644 --- a/tests/Tgstation.Server.Tests/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Instance/WatchdogTest.cs @@ -12,6 +12,7 @@ using Tgstation.Server.Api.Models; using Tgstation.Server.Client; using Tgstation.Server.Client.Components; using Tgstation.Server.Host.Components.Interop; +using Tgstation.Server.Host.IO; using Tgstation.Server.Host.System; namespace Tgstation.Server.Tests.Instance @@ -102,7 +103,7 @@ namespace Tgstation.Server.Tests.Instance using var ourProcessHandler = new ProcessExecutor( new PlatformIdentifier().IsWindows ? (IProcessFeatures)new WindowsProcessFeatures(Mock.Of>()) - : new PosixProcessFeatures(Mock.Of>()), + : new PosixProcessFeatures(Mock.Of(), Mock.Of>()), Mock.Of>(), LoggerFactory.Create(x => { })) .GetProcess(ddProc.Id);