This commit is contained in:
Jordan Brown
2020-05-15 15:27:48 -04:00
parent 9a4f86cf98
commit 448c658ed6
6 changed files with 90 additions and 35 deletions
+2 -2
View File
@@ -3,8 +3,8 @@
<!-- This is the authorative version list -->
<!-- Integration tests will ensure they match across the board -->
<TgsCoreVersion>4.2.1</TgsCoreVersion>
<TgsApiVersion>6.2.0</TgsApiVersion>
<TgsClientVersion>6.1.0</TgsClientVersion>
<TgsApiVersion>6.3.0</TgsApiVersion>
<TgsClientVersion>6.2.0</TgsClientVersion>
<TgsDmapiVersion>5.1.1</TgsDmapiVersion>
<TgsControlPanelVersion>0.4.0</TgsControlPanelVersion>
<TgsHostWatchdogVersion>1.1.0</TgsHostWatchdogVersion>
@@ -471,5 +471,11 @@ namespace Tgstation.Server.Api.Models
/// </summary>
[Description("Cannot set both softShutdown and softReboot at once!")]
DreamDaemonDoubleSoft,
/// <summary>
/// Attempted to launch DreamDaemon on a user account that had the BYOND pager running.
/// </summary>
[Description("Cannot start DreamDaemon headless with the BYOND pager running!")]
DeploymentPagerRunning,
}
}
@@ -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
/// <summary>
/// Make sure the BYOND pager is not running.
/// </summary>
void CheckPagerIsNotRunning()
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
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);
}
}
}
@@ -18,17 +18,23 @@
IProcess LaunchProcess(string fileName, string workingDirectory, string arguments = null, bool readOutput = false, bool readError = false, bool noShellExecute = false);
/// <summary>
/// Get a <see cref="IProcess"/> by <paramref name="id"/>
/// Get a <see cref="IProcess"/> representing the running executable.
/// </summary>
/// <param name="id">The <see cref="IProcess.Id"/></param>
/// <returns>The <see cref="IProcess"/> represented by <paramref name="id"/> on success, <see langword="null"/> on failure</returns>
/// <returns>The current <see cref="IProcess"/>.</returns>
IProcess GetCurrentProcess();
/// <summary>
/// Get a <see cref="IProcess"/> by <paramref name="id"/>.
/// </summary>
/// <param name="id">The <see cref="IProcess.Id"/>.</param>
/// <returns>The <see cref="IProcess"/> represented by <paramref name="id"/> on success, <see langword="null"/> on failure.</returns>
IProcess GetProcess(int id);
/// <summary>
/// Check if a <see cref="IProcess"/> with a given <paramref name="name"/> is running.
/// Get a <see cref="IProcess"/> with a given <paramref name="name"/>.
/// </summary>
/// <param name="name">The name of the process without the extension.</param>
/// <returns><see langword="true"/> if the process is running, <see langword="false"/> otherwise.</returns>
bool IsProcessWithNameRunning(string name);
/// <param name="name">The name of the process executable without the extension.</param>
/// <returns>The <see cref="IProcess"/> represented by <paramref name="name"/> on success, <see langword="null"/> on failure.</returns>
IProcess GetProcessByName(string name);
}
}
@@ -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<Process>(),
true);
}
catch
{
handle.Dispose();
throw;
}
return CreateFromExistingHandle(handle);
}
/// <inheritdoc />
public IProcess GetCurrentProcess()
{
logger.LogTrace("Getting current process...");
var handle = global::System.Diagnostics.Process.GetCurrentProcess();
return CreateFromExistingHandle(handle);
}
/// <inheritdoc />
@@ -231,13 +222,50 @@ namespace Tgstation.Server.Host.System
}
/// <inheritdoc />
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);
}
/// <summary>
/// Create a <see cref="IProcess"/> given an existing <paramref name="handle"/>.
/// </summary>
/// <param name="handle">The <see cref="global::System.Diagnostics.Process"/> to create a <see cref="IProcess"/> from.</param>
/// <returns>The <see cref="IProcess"/> based on <paramref name="handle"/>.</returns>
private IProcess CreateFromExistingHandle(global::System.Diagnostics.Process handle)
{
try
{
return new Process(
processFeatures,
handle,
AttachExitHandler(handle),
null,
null,
null,
loggerFactory.CreateLogger<Process>(),
true);
}
catch
{
handle.Dispose();
throw;
}
}
}
}
@@ -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<ILogger<WindowsProcessFeatures>>())
: new PosixProcessFeatures(Mock.Of<ILogger<PosixProcessFeatures>>()),
: new PosixProcessFeatures(Mock.Of<IIOManager>(), Mock.Of<ILogger<PosixProcessFeatures>>()),
Mock.Of<ILogger<ProcessExecutor>>(),
LoggerFactory.Create(x => { }))
.GetProcess(ddProc.Id);