From a0ee1b35a42c257d04bdba3ead32c1f66ef652a9 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Tue, 14 Nov 2023 22:10:15 -0500 Subject: [PATCH 1/6] Enable NRT on `Tgstation.Server.Common` --- src/Tgstation.Server.Common/Tgstation.Server.Common.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Tgstation.Server.Common/Tgstation.Server.Common.csproj b/src/Tgstation.Server.Common/Tgstation.Server.Common.csproj index 07b8b364a2..b77bdc4dc1 100644 --- a/src/Tgstation.Server.Common/Tgstation.Server.Common.csproj +++ b/src/Tgstation.Server.Common/Tgstation.Server.Common.csproj @@ -4,6 +4,7 @@ $(TgsNugetNetFramework) $(TgsCommonLibraryVersion) + enable Common functions for tgstation-server. web tgstation-server tgstation ss13 byond client http $(TGS_NUGET_RELEASE_NOTES_COMMON) From c5fff694d79d997d7caf517ed4c7c6efb0e5a664 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Tue, 14 Nov 2023 23:11:33 -0500 Subject: [PATCH 2/6] Make the host watchdog use nullable references --- build/Version.props | 2 +- .../PosixSignalChecker.cs | 2 +- src/Tgstation.Server.Host.Console/Program.cs | 4 +- .../Tgstation.Server.Host.Console.csproj | 1 + src/Tgstation.Server.Host.Service/Program.cs | 13 +- .../ServerService.cs | 201 +++--------------- .../ServiceLifetime.cs | 200 +++++++++++++++++ .../Tgstation.Server.Host.Service.csproj | 1 + .../ISignalChecker.cs | 4 +- .../IWatchdog.cs | 4 +- .../NoopSignalChecker.cs | 6 +- .../Tgstation.Server.Host.Watchdog.csproj | 1 + .../Watchdog.cs | 44 +++- .../TestServerService.cs | 17 +- 14 files changed, 297 insertions(+), 203 deletions(-) create mode 100644 src/Tgstation.Server.Host.Service/ServiceLifetime.cs diff --git a/build/Version.props b/build/Version.props index f75f23d239..c27dc59c94 100644 --- a/build/Version.props +++ b/build/Version.props @@ -11,7 +11,7 @@ 15.0.0 7.0.0 5.7.0 - 1.4.0 + 1.4.1 1.2.1 2.0.0 netstandard2.0 diff --git a/src/Tgstation.Server.Host.Console/PosixSignalChecker.cs b/src/Tgstation.Server.Host.Console/PosixSignalChecker.cs index e037b5936f..b18ece3c03 100644 --- a/src/Tgstation.Server.Host.Console/PosixSignalChecker.cs +++ b/src/Tgstation.Server.Host.Console/PosixSignalChecker.cs @@ -31,7 +31,7 @@ namespace Tgstation.Server.Host.Console } /// - public async ValueTask CheckSignals(Func startChild, CancellationToken cancellationToken) + public async ValueTask CheckSignals(Func startChild, CancellationToken cancellationToken) { var (childPid, _) = startChild?.Invoke(null) ?? throw new ArgumentNullException(nameof(startChild)); var signalTcs = new TaskCompletionSource(); diff --git a/src/Tgstation.Server.Host.Console/Program.cs b/src/Tgstation.Server.Host.Console/Program.cs index e142de81be..c8383865af 100644 --- a/src/Tgstation.Server.Host.Console/Program.cs +++ b/src/Tgstation.Server.Host.Console/Program.cs @@ -38,7 +38,7 @@ namespace Tgstation.Server.Host.Console /// A representing the running operation. internal static async Task Main(string[] args) { - System.Console.Title = $"{Constants.CanonicalPackageName} Host Watchdog v{Assembly.GetExecutingAssembly().GetName().Version.Semver()}"; + System.Console.Title = $"{Constants.CanonicalPackageName} Host Watchdog v{Assembly.GetExecutingAssembly().GetName().Version?.Semver()}"; var arguments = new List(args); var trace = arguments.Remove("--trace-host-watchdog"); @@ -61,7 +61,7 @@ namespace Tgstation.Server.Host.Console } using var cts = new CancellationTokenSource(); - void AppDomainHandler(object a, EventArgs b) => cts.Cancel(); + void AppDomainHandler(object? a, EventArgs b) => cts.Cancel(); AppDomain.CurrentDomain.ProcessExit += AppDomainHandler; try { diff --git a/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj b/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj index 92902912bb..3cbf1c1a16 100644 --- a/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj +++ b/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj @@ -5,6 +5,7 @@ Exe $(TgsFrameworkVersion) $(TgsCoreVersion) + enable false ../../build/uac_elevation_manifest.xml diff --git a/src/Tgstation.Server.Host.Service/Program.cs b/src/Tgstation.Server.Host.Service/Program.cs index 9e6deaeb63..c2f72b0901 100644 --- a/src/Tgstation.Server.Host.Service/Program.cs +++ b/src/Tgstation.Server.Host.Service/Program.cs @@ -77,7 +77,7 @@ namespace Tgstation.Server.Host.Service /// The --passthroughargs or -p option. /// [Option(ShortName = "p", Description = "Arguments passed to main host process")] - public string PassthroughArgs { get; set; } + public string? PassthroughArgs { get; set; } /// /// Entrypoint for the application. @@ -157,7 +157,7 @@ namespace Tgstation.Server.Host.Service /// Runs sc.exe to either uninstall a given or install the running . /// /// The name of a service to uninstall. - void InvokeSC(string serviceToUninstall) + void InvokeSC(string? serviceToUninstall) { using var installer = new ServiceInstaller(); if (serviceToUninstall != null) @@ -172,6 +172,9 @@ namespace Tgstation.Server.Host.Service Assembly.GetExecutingAssembly().Location); var assemblyDirectory = Path.GetDirectoryName(fullPathToAssembly); + if (assemblyDirectory == null) + throw new InvalidOperationException($"Failed to resolve directory name of {assemblyDirectory}"); + var assemblyNameWithoutExtension = Path.GetFileNameWithoutExtension(fullPathToAssembly); var exePath = Path.Combine(assemblyDirectory, $"{assemblyNameWithoutExtension}.exe"); @@ -260,10 +263,8 @@ namespace Tgstation.Server.Host.Service var stop = !Detach; if (!stop) { - serviceController.ExecuteCommand( - PipeCommands.GetServiceCommandId( - PipeCommands.CommandDetachingShutdown) - .Value); + var serviceControllerCommand = PipeCommands.GetServiceCommandId(PipeCommands.CommandDetachingShutdown); + serviceController.ExecuteCommand(serviceControllerCommand!.Value); serviceController.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(30)); if (serviceController.Status != ServiceControllerStatus.Stopped) stop = true; diff --git a/src/Tgstation.Server.Host.Service/ServerService.cs b/src/Tgstation.Server.Host.Service/ServerService.cs index 8f5554304f..56491187fb 100644 --- a/src/Tgstation.Server.Host.Service/ServerService.cs +++ b/src/Tgstation.Server.Host.Service/ServerService.cs @@ -1,20 +1,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; -using System.IO.Pipes; -using System.Linq; using System.Runtime.Versioning; using System.ServiceProcess; -using System.Text; using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.EventLog; using Tgstation.Server.Common; -using Tgstation.Server.Host.Common; using Tgstation.Server.Host.Watchdog; namespace Tgstation.Server.Host.Service @@ -23,7 +17,7 @@ namespace Tgstation.Server.Host.Service /// Represents a as a . /// [SupportedOSPlatform("windows")] - sealed class ServerService : ServiceBase, ISignalChecker + sealed class ServerService : ServiceBase { /// /// The canonical windows service name. @@ -35,45 +29,22 @@ namespace Tgstation.Server.Host.Service /// readonly IWatchdogFactory watchdogFactory; + /// + /// The used by the . + /// + readonly Lazy loggerFactory; + /// /// The of command line arguments the service was invoked with. /// readonly string[] commandLineArguments; /// - /// The minimum for the . + /// The active . /// - readonly LogLevel minimumLogLevel; - - /// - /// The used by the . - /// - ILoggerFactory loggerFactory; - - /// - /// The for the . - /// - ILogger logger; - - /// - /// The that represents the running . - /// - Task watchdogTask; - - /// - /// The for the . - /// - CancellationTokenSource cancellationTokenSource; - - /// - /// The for sending to the server process. - /// - AnonymousPipeServerStream commandPipeServer; - - /// - /// The for receiving the . - /// - AnonymousPipeServerStream readyPipeServer; +#pragma warning disable CA2213 // Disposable fields should be disposed + volatile ServiceLifetime? serviceLifetime; +#pragma warning restore CA2213 // Disposable fields should be disposed /// /// Initializes a new instance of the class. @@ -85,21 +56,15 @@ namespace Tgstation.Server.Host.Service { this.watchdogFactory = watchdogFactory ?? throw new ArgumentNullException(nameof(watchdogFactory)); this.commandLineArguments = commandLineArguments ?? throw new ArgumentNullException(nameof(commandLineArguments)); - this.minimumLogLevel = minimumLogLevel; - ServiceName = Name; - } - /// - public async ValueTask CheckSignals(Func startChildAndGetPid, CancellationToken cancellationToken) - { - await using (commandPipeServer = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable)) - await using (readyPipeServer = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable)) + ServiceName = Name; + loggerFactory = new Lazy(() => LoggerFactory.Create(builder => builder.AddEventLog(new EventLogSettings { - var (_, lifetimeTask) = startChildAndGetPid($"--Internal:CommandPipe={commandPipeServer.GetClientHandleAsString()} --Internal:ReadyPipe={readyPipeServer.GetClientHandleAsString()}"); - commandPipeServer.DisposeLocalCopyOfClientHandle(); - readyPipeServer.DisposeLocalCopyOfClientHandle(); - await lifetimeTask; - } + LogName = EventLog.Log, + MachineName = EventLog.MachineName, + SourceName = EventLog.Source, + Filter = (message, logLevel) => logLevel >= minimumLogLevel, + }))); } /// @@ -112,53 +77,21 @@ namespace Tgstation.Server.Host.Service { if (disposing) { - loggerFactory?.Dispose(); - cancellationTokenSource?.Dispose(); - commandPipeServer?.Dispose(); - readyPipeServer?.Dispose(); + OnStop(); + + if (loggerFactory.IsValueCreated) + loggerFactory.Value.Dispose(); } base.Dispose(disposing); } /// - protected override void OnCustomCommand(int command) - { - var commandsToCheck = PipeCommands.AllCommands; - foreach (var stringCommand in commandsToCheck) - { - var commandId = PipeCommands.GetServiceCommandId(stringCommand); - if (command == commandId) - { - SendCommandToHostThroughPipe(stringCommand); - return; - } - } - - logger.LogWarning("Received unknown service command: {command}", command); - } + protected override void OnCustomCommand(int command) => serviceLifetime!.HandleCustomCommand(command); /// protected override void OnStart(string[] args) { - if (loggerFactory == null) - { - loggerFactory = LoggerFactory.Create(builder => builder.AddEventLog(new EventLogSettings - { - LogName = EventLog.Log, - MachineName = EventLog.MachineName, - SourceName = EventLog.Source, - Filter = (message, logLevel) => logLevel >= minimumLogLevel, - })); - - logger = loggerFactory.CreateLogger(); - } - - var watchdog = watchdogFactory.CreateWatchdog(this, loggerFactory); - - cancellationTokenSource?.Dispose(); - cancellationTokenSource = new CancellationTokenSource(); - var newArgs = new List(commandLineArguments.Length + args.Length + 1) { "--General:SetupWizardMode=Never", @@ -167,94 +100,18 @@ namespace Tgstation.Server.Host.Service newArgs.AddRange(commandLineArguments); newArgs.AddRange(args); - watchdogTask = RunWatchdog(watchdog, newArgs.ToArray(), cancellationTokenSource.Token); - - if (!watchdogTask.IsCompleted && watchdog.InitialHostVersion >= new Version(5, 14, 0)) - { - logger.LogInformation("Waiting for host to finish starting..."); - using var streamReader = new StreamReader( - readyPipeServer, - Encoding.UTF8, - leaveOpen: true); - - var line = streamReader.ReadLine(); // Intentionally blocking service startup - logger.LogDebug("Pipe read: {line}", line); - } - - // Maybe we'll use this pipe more in the future, but for now leaving it open is just a resource waste - readyPipeServer.Dispose(); + serviceLifetime = new ServiceLifetime( + Stop, + signalChecker => watchdogFactory.CreateWatchdog(signalChecker, loggerFactory.Value), + loggerFactory.Value.CreateLogger(), + args); } /// protected override void OnStop() { - cancellationTokenSource.Cancel(); - watchdogTask.GetAwaiter().GetResult(); - } - - /// - /// Executes the , stopping the service if it exits. - /// - /// The to run. - /// The arguments for the . - /// The for the operation. - /// A representing the running operation. - async Task RunWatchdog(IWatchdog watchdog, string[] args, CancellationToken cancellationToken) - { - await watchdog.RunAsync(false, args, cancellationToken); - - async void StopServiceAsync() - { - try - { - await Task.Run(Stop, cancellationToken); // DCT intentional - } - catch (OperationCanceledException ex) - { - logger.LogTrace(ex, "Stopping service cancelled!"); - } - catch (Exception ex) - { - logger.LogError(ex, "Error stopping service!"); - } - } - - StopServiceAsync(); - } - - /// - /// Sends a command to the main server process. - /// - /// One of the . - void SendCommandToHostThroughPipe(string command) - { - var localPipeServer = commandPipeServer; - if (localPipeServer == null) - { - logger.LogWarning("Unable to send command \"{command}\" to main server process. Is the service running?", command); - return; - } - - logger.LogDebug("Send command: {command}", command); - try - { - var encoding = Encoding.UTF8; - using var streamWriter = new StreamWriter( - localPipeServer, - encoding, - PipeCommands - .AllCommands - .Select( - command => encoding.GetByteCount( - command + Environment.NewLine)) - .Max(), - true); - streamWriter.WriteLine(command); - } - catch (Exception ex) - { - logger.LogError(ex, "Error attempting to send command \"{command}\"", command); - } + var oldLifetime = Interlocked.Exchange(ref serviceLifetime, null); + oldLifetime?.DisposeAsync().GetAwaiter().GetResult(); } } } diff --git a/src/Tgstation.Server.Host.Service/ServiceLifetime.cs b/src/Tgstation.Server.Host.Service/ServiceLifetime.cs new file mode 100644 index 0000000000..efc058fbaa --- /dev/null +++ b/src/Tgstation.Server.Host.Service/ServiceLifetime.cs @@ -0,0 +1,200 @@ +using System; +using System.IO; +using System.IO.Pipes; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.Extensions.Logging; + +using Tgstation.Server.Host.Common; +using Tgstation.Server.Host.Watchdog; + +namespace Tgstation.Server.Host.Service +{ + /// + /// Represents the lifetime of the service. + /// + sealed class ServiceLifetime : ISignalChecker, IAsyncDisposable + { + /// + /// The for the . + /// + readonly ILogger logger; + + /// + /// The that represents the running . + /// + readonly Task watchdogTask; + + /// + /// The for the . + /// + readonly CancellationTokenSource cancellationTokenSource; + + /// + /// The for sending to the server process. + /// + AnonymousPipeServerStream? commandPipeServer; + + /// + /// The for receiving the . + /// + AnonymousPipeServerStream? readyPipeServer; + + /// + /// Initializes a new instance of the class. + /// + /// An to manually stop the service. + /// A taking a and returning the to run. + /// The value of . + /// The arguments for the . + public ServiceLifetime(Action stopService, Func watchdogFactory, ILogger logger, string[] args) + { + ArgumentNullException.ThrowIfNull(stopService); + ArgumentNullException.ThrowIfNull(watchdogFactory); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + ArgumentNullException.ThrowIfNull(args); + + cancellationTokenSource = new CancellationTokenSource(); + watchdogTask = RunWatchdog( + stopService, + watchdogFactory(this), + args, + cancellationTokenSource.Token); + } + + /// + public async ValueTask DisposeAsync() + { + cancellationTokenSource.Cancel(); + await watchdogTask; + cancellationTokenSource.Dispose(); + + if (commandPipeServer != null) + await commandPipeServer.DisposeAsync(); + + if (readyPipeServer != null) + await readyPipeServer.DisposeAsync(); + } + + /// + public async ValueTask CheckSignals(Func startChildAndGetPid, CancellationToken cancellationToken) + { + try + { + await using (commandPipeServer = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable)) + await using (readyPipeServer = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable)) + { + var (_, lifetimeTask) = startChildAndGetPid($"--Internal:CommandPipe={commandPipeServer.GetClientHandleAsString()} --Internal:ReadyPipe={readyPipeServer.GetClientHandleAsString()}"); + commandPipeServer.DisposeLocalCopyOfClientHandle(); + readyPipeServer.DisposeLocalCopyOfClientHandle(); + await lifetimeTask; + } + } + finally + { + readyPipeServer = null; + commandPipeServer = null; + } + } + + /// + /// Handle a custom service . + /// + /// The command sent to the service. + public void HandleCustomCommand(int command) + { + var commandsToCheck = PipeCommands.AllCommands; + foreach (var stringCommand in commandsToCheck) + { + var commandId = PipeCommands.GetServiceCommandId(stringCommand); + if (command == commandId) + { + SendCommandToHostThroughPipe(stringCommand); + return; + } + } + + logger.LogWarning("Received unknown service command: {command}", command); + } + + /// + /// Executes the , stopping the service if it exits. + /// + /// An to manually stop the service. + /// The to run. + /// The arguments for the . + /// The for the operation. + /// A representing the running operation. + async Task RunWatchdog(Action stopService, IWatchdog watchdog, string[] args, CancellationToken cancellationToken) + { + var localWatchdogTask = watchdog.RunAsync(false, args, cancellationToken); + + if (!localWatchdogTask.IsCompleted && (await watchdog.InitialHostVersion) >= new Version(5, 14, 0)) + if (readyPipeServer != null) + { + logger.LogInformation("Waiting for host to finish starting..."); + using var streamReader = new StreamReader( + readyPipeServer, + Encoding.UTF8, + leaveOpen: true); + + var line = streamReader.ReadLine(); // Intentionally blocking service startup + logger.LogDebug("Pipe read: {line}", line); + + // Maybe we'll use this pipe more in the future, but for now leaving it open is just a resource waste + readyPipeServer.Dispose(); + } + else + logger.LogError("Watchdog started and ready pipe was not initialized!"); + + await localWatchdogTask; + + try + { + stopService(); + } + catch (Exception ex) + { + logger.LogError(ex, "Error stopping service!"); + } + } + + /// + /// Sends a command to the main server process. + /// + /// One of the . + void SendCommandToHostThroughPipe(string command) + { + var localPipeServer = commandPipeServer; + if (localPipeServer == null) + { + logger.LogWarning("Unable to send command \"{command}\" to main server process. Is the service running?", command); + return; + } + + logger.LogDebug("Send command: {command}", command); + try + { + var encoding = Encoding.UTF8; + using var streamWriter = new StreamWriter( + localPipeServer, + encoding, + PipeCommands + .AllCommands + .Select( + command => encoding.GetByteCount( + command + Environment.NewLine)) + .Max(), + true); + streamWriter.WriteLine(command); + } + catch (Exception ex) + { + logger.LogError(ex, "Error attempting to send command \"{command}\"", command); + } + } + } +} diff --git a/src/Tgstation.Server.Host.Service/Tgstation.Server.Host.Service.csproj b/src/Tgstation.Server.Host.Service/Tgstation.Server.Host.Service.csproj index aa98536f38..54710f6b47 100644 --- a/src/Tgstation.Server.Host.Service/Tgstation.Server.Host.Service.csproj +++ b/src/Tgstation.Server.Host.Service/Tgstation.Server.Host.Service.csproj @@ -4,6 +4,7 @@ WinExe win-x86;win-x64 + enable $(TgsFrameworkVersion) $(TgsCoreVersion) diff --git a/src/Tgstation.Server.Host.Watchdog/ISignalChecker.cs b/src/Tgstation.Server.Host.Watchdog/ISignalChecker.cs index b23295dd0f..2b8d42ee80 100644 --- a/src/Tgstation.Server.Host.Watchdog/ISignalChecker.cs +++ b/src/Tgstation.Server.Host.Watchdog/ISignalChecker.cs @@ -12,9 +12,9 @@ namespace Tgstation.Server.Host.Watchdog /// /// Relays signals received to the host process. /// - /// An to start the main process. It accepts an optional additional command line argument as a paramter and returns it's and lifetime . + /// An to start the main process. It accepts an optional additional command line argument as a paramter and returns it's and lifetime . Must be called. /// The for the operation. /// A representing the running operation. - ValueTask CheckSignals(Func startChild, CancellationToken cancellationToken); + ValueTask CheckSignals(Func startChildAndGetPid, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Host.Watchdog/IWatchdog.cs b/src/Tgstation.Server.Host.Watchdog/IWatchdog.cs index dadff2c41b..c9f9e89da9 100644 --- a/src/Tgstation.Server.Host.Watchdog/IWatchdog.cs +++ b/src/Tgstation.Server.Host.Watchdog/IWatchdog.cs @@ -10,9 +10,9 @@ namespace Tgstation.Server.Host.Watchdog public interface IWatchdog { /// - /// Gets the current version of the host process. Set once begins and doesn't immediately return . + /// Gets a resulting in the current version of the host process. Guaranteed to complete once begins and doesn't immediately return . /// - Version InitialHostVersion { get; } + Task InitialHostVersion { get; } /// /// Run the . diff --git a/src/Tgstation.Server.Host.Watchdog/NoopSignalChecker.cs b/src/Tgstation.Server.Host.Watchdog/NoopSignalChecker.cs index fa5dd56f94..270cf63380 100644 --- a/src/Tgstation.Server.Host.Watchdog/NoopSignalChecker.cs +++ b/src/Tgstation.Server.Host.Watchdog/NoopSignalChecker.cs @@ -10,10 +10,10 @@ namespace Tgstation.Server.Host.Watchdog public sealed class NoopSignalChecker : ISignalChecker { /// - public ValueTask CheckSignals(Func startChild, CancellationToken cancellationToken) + public ValueTask CheckSignals(Func startChildAndGetPid, CancellationToken cancellationToken) { - ArgumentNullException.ThrowIfNull(startChild); - startChild(null); + ArgumentNullException.ThrowIfNull(startChildAndGetPid); + startChildAndGetPid(null); return ValueTask.CompletedTask; } } diff --git a/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj b/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj index ae3b89cdb2..04f187c08e 100644 --- a/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj +++ b/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj @@ -3,6 +3,7 @@ $(TgsFrameworkVersion) + enable false $(TgsHostWatchdogVersion) diff --git a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs index bab5e9a07b..a574a6ab6b 100644 --- a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs @@ -20,7 +20,7 @@ namespace Tgstation.Server.Host.Watchdog sealed class Watchdog : IWatchdog { /// - public Version InitialHostVersion { get; private set; } + public Task InitialHostVersion => initialHostVersionTcs.Task; /// /// The for the . @@ -32,6 +32,11 @@ namespace Tgstation.Server.Host.Watchdog /// readonly ILogger logger; + /// + /// Backing for . + /// + readonly TaskCompletionSource initialHostVersionTcs; + /// /// Initializes a new instance of the class. /// @@ -41,6 +46,8 @@ namespace Tgstation.Server.Host.Watchdog { this.signalChecker = signalChecker ?? throw new ArgumentNullException(nameof(signalChecker)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + initialHostVersionTcs = new TaskCompletionSource(); } /// @@ -54,7 +61,7 @@ namespace Tgstation.Server.Host.Watchdog currentProcessId = currentProc.Id; logger.LogDebug("PID: {pid}", currentProcessId); - string updateDirectory = null; + string? updateDirectory = null; try { var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); @@ -76,6 +83,11 @@ namespace Tgstation.Server.Host.Watchdog var executingAssembly = Assembly.GetExecutingAssembly(); var rootLocation = Path.GetDirectoryName(executingAssembly.Location); + if (rootLocation == null) + { + logger.LogCritical("Failed to get the directory name of the executing assembly: {location}", executingAssembly.Location); + return false; + } var assemblyStoragePath = Path.Combine(rootLocation, "lib"); // always always next to watchdog @@ -119,9 +131,18 @@ namespace Tgstation.Server.Host.Watchdog return false; } - InitialHostVersion = Version.Parse(FileVersionInfo.GetVersionInfo(assemblyPath).FileVersion); + var fileVersion = FileVersionInfo.GetVersionInfo(assemblyPath).FileVersion; + if (fileVersion == null) + { + logger.LogCritical("Failed to parse version info from {assemblyPath}!", assemblyPath); + return false; + } - var watchdogVersion = executingAssembly.GetName().Version.Semver().ToString(); + initialHostVersionTcs.SetResult( + Version.Parse( + fileVersion)); + + var watchdogVersion = executingAssembly.GetName().Version?.Semver().ToString(); while (!cancellationToken.IsCancellationRequested) using (logger.BeginScope("Host invocation")) @@ -158,8 +179,8 @@ namespace Tgstation.Server.Host.Watchdog var killedHostProcess = false; try { - Task processTask = null; - (int, Task) StartProcess(string additionalArg) + Task? processTask = null; + (int, Task) StartProcess(string? additionalArg) { if (additionalArg != null) process.StartInfo.Arguments += $" {additionalArg}"; @@ -199,7 +220,7 @@ namespace Tgstation.Server.Host.Watchdog var checkerTask = signalChecker.CheckSignals(StartProcess, cts.Token); try { - await processTask; + await processTask!; } finally { @@ -338,10 +359,11 @@ namespace Tgstation.Server.Host.Watchdog catch (OperationCanceledException ex) { logger.LogDebug(ex, "Exiting due to cancellation..."); - if (!Directory.Exists(updateDirectory)) - File.Delete(updateDirectory); - else - Directory.Delete(updateDirectory, true); + if (updateDirectory != null) + if (!Directory.Exists(updateDirectory)) + File.Delete(updateDirectory); + else + Directory.Delete(updateDirectory, true); } catch (Exception ex) { diff --git a/tests/Tgstation.Server.Host.Service.Tests/TestServerService.cs b/tests/Tgstation.Server.Host.Service.Tests/TestServerService.cs index 855870d4c8..6faf32f95e 100644 --- a/tests/Tgstation.Server.Host.Service.Tests/TestServerService.cs +++ b/tests/Tgstation.Server.Host.Service.Tests/TestServerService.cs @@ -39,15 +39,25 @@ namespace Tgstation.Server.Host.Service.Tests var childStarted = false; ISignalChecker signalChecker = null; - mockWatchdog.Setup(x => x.RunAsync(false, It.IsNotNull(), It.IsAny())).Callback((bool x, string[] _, CancellationToken token) => + var hostVersionTcs = new TaskCompletionSource(); + var hostLifetimeTcs = new TaskCompletionSource(); + + mockWatchdog.Setup(x => x.RunAsync(false, It.IsNotNull(), It.IsAny())).Returns(async (bool x, string[] _, CancellationToken token) => { + hostVersionTcs.SetResult(typeof(ServerService).Assembly.GetName().Version); + cancellationToken = token; + cancellationToken.Register(() => hostLifetimeTcs.SetResult(true)); signalCheckerTask = signalChecker.CheckSignals(additionalArgs => { childStarted = true; - return (123, Task.CompletedTask); + return (123, hostLifetimeTcs.Task); }, cancellationToken).AsTask(); - }).ReturnsAsync(true).Verifiable(); + + await signalCheckerTask; + return true; + }).Verifiable(); + mockWatchdog.SetupGet(x => x.InitialHostVersion).Returns(hostVersionTcs.Task); var mockWatchdogFactory = new Mock(); mockWatchdogFactory.Setup(x => x.CreateWatchdog(It.IsNotNull(), It.IsNotNull())) @@ -68,6 +78,7 @@ namespace Tgstation.Server.Host.Service.Tests mockWatchdogFactory.VerifyAll(); Assert.IsTrue(signalCheckerTask.IsCompleted); + Assert.IsTrue(cancellationToken.IsCancellationRequested); } } } From 5c5b54e094b9f6c549e7a50accddf2d4a384d294 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Fri, 17 Nov 2023 12:23:37 -0500 Subject: [PATCH 3/6] Update to beta Stylecop Cleanup warnings --- build/SrcCommon.props | 2 +- src/Tgstation.Server.Api/Models/EngineType.cs | 2 +- src/Tgstation.Server.Api/Models/ErrorCode.cs | 8 ++-- .../Models/FieldPresence.cs | 2 +- .../Models/IrcPasswordType.cs | 6 +-- .../Models/OAuthProvider.cs | 10 ++--- .../Models/RemoteGitProvider.cs | 4 +- .../Models/Response/TokenResponse.cs | 2 +- .../Rights/AdministrationRights.cs | 2 +- .../Rights/ChatBotRights.cs | 4 +- .../Rights/DreamDaemonRights.cs | 2 +- .../Rights/InstancePermissionSetRights.cs | 2 +- src/Tgstation.Server.Api/Rights/RightsType.cs | 18 ++++----- src/Tgstation.Server.Client/ApiClient.cs | 8 +--- .../Extensions/ValueTaskExtensions.cs | 2 +- .../ISignalChecker.cs | 2 +- .../Chat/Providers/DiscordProvider.cs | 2 +- .../Components/Chat/Providers/IrcProvider.cs | 2 +- .../Components/Engine/ByondInstallerBase.cs | 4 +- .../Engine/WindowsByondInstaller.cs | 2 +- .../Components/Events/EventType.cs | 40 +++++++++---------- .../Interop/Bridge/BridgeCommandType.cs | 2 +- .../Components/Interop/DMApiConstants.cs | 2 +- .../Interop/Topic/TopicParameters.cs | 4 +- .../Components/Repository/Repository.cs | 2 +- .../Components/Session/ApiValidationStatus.cs | 12 +++--- .../Components/Session/RebootState.cs | 6 +-- .../Session/SessionControllerFactory.cs | 2 +- .../Components/Watchdog/MonitorAction.cs | 8 ++-- .../Watchdog/MonitorActivationReason.cs | 8 ++-- .../Configuration/DatabaseType.cs | 10 ++--- .../Extensions/TaskExtensions.cs | 2 +- .../IO/BufferedFileStreamProvider.cs | 8 ++-- .../IO/DefaultIOManager.cs | 2 +- .../Models/CompileJob.cs | 2 +- src/Tgstation.Server.Host/Models/Instance.cs | 2 +- src/Tgstation.Server.Host/Models/Job.cs | 6 +-- src/Tgstation.Server.Host/Models/UserGroup.cs | 2 +- .../Security/OAuth/DiscordOAuthValidator.cs | 6 +-- .../Security/OAuth/GenericOAuthValidator.cs | 4 +- .../Security/OAuth/GitHubOAuthValidator.cs | 2 +- .../OAuth/InvisionCommunityOAuthValidator.cs | 6 +-- .../Security/OAuth/KeycloakOAuthValidator.cs | 6 +-- .../Security/OAuth/TGForumsOAuthValidator.cs | 6 +-- .../Swarm/SwarmConstants.cs | 2 +- .../Swarm/SwarmService.cs | 20 +++++----- .../System/AssemblyInformationProvider.cs | 2 +- .../System/ProcessExecutor.cs | 2 +- .../System/WindowsNetworkPromptReaper.cs | 13 +++--- .../Transfer/FileTransferService.cs | 2 +- .../Utils/GitHub/GitHubClientFactory.cs | 12 +++--- .../Utils/GitHub/GitHubServiceFactory.cs | 2 +- 52 files changed, 144 insertions(+), 147 deletions(-) diff --git a/build/SrcCommon.props b/build/SrcCommon.props index fb1f8c29d5..d6e835dad0 100644 --- a/build/SrcCommon.props +++ b/build/SrcCommon.props @@ -17,7 +17,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Tgstation.Server.Api/Models/EngineType.cs b/src/Tgstation.Server.Api/Models/EngineType.cs index 67410c1d47..df4b3ab3aa 100644 --- a/src/Tgstation.Server.Api/Models/EngineType.cs +++ b/src/Tgstation.Server.Api/Models/EngineType.cs @@ -6,7 +6,7 @@ public enum EngineType { /// - /// Build your own net dream, + /// Build your own net dream. /// Byond, diff --git a/src/Tgstation.Server.Api/Models/ErrorCode.cs b/src/Tgstation.Server.Api/Models/ErrorCode.cs index 52de54d77e..886816346a 100644 --- a/src/Tgstation.Server.Api/Models/ErrorCode.cs +++ b/src/Tgstation.Server.Api/Models/ErrorCode.cs @@ -36,7 +36,7 @@ namespace Tgstation.Server.Api.Models IOError, /// - /// The failed to validate! + /// The failed to validate. /// [Description("A header validation error occurred!")] BadHeaders, @@ -324,7 +324,7 @@ namespace Tgstation.Server.Api.Models EngineNoVersionsInstalled, /// - /// The DMAPI never validated itself + /// The DMAPI never validated itself. /// [Description("DMAPI validation failed! See FAQ at https://github.com/tgstation/tgstation-server/discussions/1695")] DeploymentNeverValidated, @@ -360,7 +360,7 @@ namespace Tgstation.Server.Api.Models DeploymentExitCode, /// - /// Deployment already in progress + /// Deployment already in progress. /// [Description("There is already a deployment operation in progress!")] DeploymentInProgress, @@ -600,7 +600,7 @@ namespace Tgstation.Server.Api.Models FileUploadExpired, /// - /// Tried to update a user to have both a and + /// Tried to update a user to have both a and . /// [Description("A user may not have both a permissionSet and group!")] UserGroupAndPermissionSet, diff --git a/src/Tgstation.Server.Api/Models/FieldPresence.cs b/src/Tgstation.Server.Api/Models/FieldPresence.cs index 11db2cd2cb..e09ac60975 100644 --- a/src/Tgstation.Server.Api/Models/FieldPresence.cs +++ b/src/Tgstation.Server.Api/Models/FieldPresence.cs @@ -6,7 +6,7 @@ public enum FieldPresence { /// - /// The field is optional + /// The field is optional. /// Optional, diff --git a/src/Tgstation.Server.Api/Models/IrcPasswordType.cs b/src/Tgstation.Server.Api/Models/IrcPasswordType.cs index ad6ac6d9c2..add54b1565 100644 --- a/src/Tgstation.Server.Api/Models/IrcPasswordType.cs +++ b/src/Tgstation.Server.Api/Models/IrcPasswordType.cs @@ -6,17 +6,17 @@ public enum IrcPasswordType { /// - /// Use server authentication + /// Use server authentication. /// Server, /// - /// Use PLAIN sasl authentication + /// Use PLAIN sasl authentication. /// Sasl, /// - /// Use NickServ authentication + /// Use NickServ authentication. /// NickServ, } diff --git a/src/Tgstation.Server.Api/Models/OAuthProvider.cs b/src/Tgstation.Server.Api/Models/OAuthProvider.cs index a6dfbe07bb..0581cf3369 100644 --- a/src/Tgstation.Server.Api/Models/OAuthProvider.cs +++ b/src/Tgstation.Server.Api/Models/OAuthProvider.cs @@ -10,27 +10,27 @@ namespace Tgstation.Server.Api.Models public enum OAuthProvider { /// - /// https://github.com + /// https://github.com. /// GitHub, /// - /// https://discord.com + /// https://discord.com. /// Discord, /// - /// https://tgstation13.org + /// https://tgstation13.org. /// TGForums, /// - /// https://www.keycloak.org + /// https://www.keycloak.org. /// Keycloak, /// - /// https://invisioncommunity.com/ + /// https://invisioncommunity.com. /// InvisionCommunity, } diff --git a/src/Tgstation.Server.Api/Models/RemoteGitProvider.cs b/src/Tgstation.Server.Api/Models/RemoteGitProvider.cs index c6df292adf..b90016a422 100644 --- a/src/Tgstation.Server.Api/Models/RemoteGitProvider.cs +++ b/src/Tgstation.Server.Api/Models/RemoteGitProvider.cs @@ -11,12 +11,12 @@ Unknown, /// - /// Remote provider is GitHub.com + /// Remote provider is GitHub.com. /// GitHub, /// - /// Remote provider is GitLab.com + /// Remote provider is GitLab.com. /// GitLab, } diff --git a/src/Tgstation.Server.Api/Models/Response/TokenResponse.cs b/src/Tgstation.Server.Api/Models/Response/TokenResponse.cs index c3908e9518..f81066741a 100644 --- a/src/Tgstation.Server.Api/Models/Response/TokenResponse.cs +++ b/src/Tgstation.Server.Api/Models/Response/TokenResponse.cs @@ -16,6 +16,6 @@ namespace Tgstation.Server.Api.Models.Response /// Parses the as a . /// /// A new based on . - public JsonWebToken ParseJwt() => new (Bearer); + public JsonWebToken ParseJwt() => new(Bearer); } } diff --git a/src/Tgstation.Server.Api/Rights/AdministrationRights.cs b/src/Tgstation.Server.Api/Rights/AdministrationRights.cs index 018a239e8e..ed530eee44 100644 --- a/src/Tgstation.Server.Api/Rights/AdministrationRights.cs +++ b/src/Tgstation.Server.Api/Rights/AdministrationRights.cs @@ -9,7 +9,7 @@ namespace Tgstation.Server.Api.Rights public enum AdministrationRights : ulong { /// - /// User has no rights + /// User has no rights. /// None = 0, diff --git a/src/Tgstation.Server.Api/Rights/ChatBotRights.cs b/src/Tgstation.Server.Api/Rights/ChatBotRights.cs index d94106ead8..d00af08c86 100644 --- a/src/Tgstation.Server.Api/Rights/ChatBotRights.cs +++ b/src/Tgstation.Server.Api/Rights/ChatBotRights.cs @@ -29,7 +29,7 @@ namespace Tgstation.Server.Api.Rights WriteChannels = 1 << 2, /// - /// User can change + /// User can change . /// WriteConnectionString = 1 << 3, @@ -39,7 +39,7 @@ namespace Tgstation.Server.Api.Rights ReadConnectionString = 1 << 4, /// - /// User can read all chat bot properties except + /// User can read all chat bot properties except . /// Read = 1 << 5, diff --git a/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs b/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs index 378783bf1e..9af0522856 100644 --- a/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs +++ b/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs @@ -74,7 +74,7 @@ namespace Tgstation.Server.Api.Rights SetStartupTimeout = 1 << 11, /// - /// User can change + /// User can change . /// SetHealthCheckInterval = 1 << 12, diff --git a/src/Tgstation.Server.Api/Rights/InstancePermissionSetRights.cs b/src/Tgstation.Server.Api/Rights/InstancePermissionSetRights.cs index 6562e38398..0b6e3b5f9a 100644 --- a/src/Tgstation.Server.Api/Rights/InstancePermissionSetRights.cs +++ b/src/Tgstation.Server.Api/Rights/InstancePermissionSetRights.cs @@ -9,7 +9,7 @@ namespace Tgstation.Server.Api.Rights public enum InstancePermissionSetRights : ulong { /// - /// User has no rights/ + /// User has no rights. /// None = 0, diff --git a/src/Tgstation.Server.Api/Rights/RightsType.cs b/src/Tgstation.Server.Api/Rights/RightsType.cs index 1959a56e5b..7c60417def 100644 --- a/src/Tgstation.Server.Api/Rights/RightsType.cs +++ b/src/Tgstation.Server.Api/Rights/RightsType.cs @@ -6,47 +6,47 @@ public enum RightsType : ulong { /// - /// + /// . /// Administration, /// - /// + /// . /// InstanceManager, /// - /// + /// . /// Repository, /// - /// + /// . /// Engine, /// - /// + /// . /// DreamMaker, /// - /// + /// . /// DreamDaemon, /// - /// + /// . /// ChatBots, /// - /// + /// . /// Configuration, /// - /// + /// . /// InstancePermissionSet, } diff --git a/src/Tgstation.Server.Client/ApiClient.cs b/src/Tgstation.Server.Client/ApiClient.cs index f691dd0e90..e9824a433d 100644 --- a/src/Tgstation.Server.Client/ApiClient.cs +++ b/src/Tgstation.Server.Client/ApiClient.cs @@ -37,7 +37,7 @@ namespace Tgstation.Server.Client /// PATCH . /// /// HOW IS THIS NOT INCLUDED IN THE FRAMEWORK??!?!? - static readonly HttpMethod HttpPatch = new ("PATCH"); + static readonly HttpMethod HttpPatch = new("PATCH"); /// public Uri Url { get; } @@ -59,7 +59,7 @@ namespace Tgstation.Server.Client /// /// The to use. /// - static readonly JsonSerializerSettings SerializerSettings = new () + static readonly JsonSerializerSettings SerializerSettings = new() { ContractResolver = new CamelCasePropertyNamesContractResolver(), Converters = new[] @@ -126,9 +126,7 @@ namespace Tgstation.Server.Client } #pragma warning disable IDE0010 // Add missing cases -#pragma warning disable IDE0066 // Convert switch statement to expression switch (response.StatusCode) -#pragma warning restore IDE0066 // Convert switch statement to expression #pragma warning restore IDE0010 // Add missing cases { case HttpStatusCode.Unauthorized: @@ -310,9 +308,7 @@ namespace Tgstation.Server.Client using (memoryStream) { -#pragma warning disable CA2000 // Dispose objects before losing scope var streamContent = new StreamContent(uploadStream ?? memoryStream); -#pragma warning restore CA2000 // Dispose objects before losing scope try { await RunRequest( diff --git a/src/Tgstation.Server.Common/Extensions/ValueTaskExtensions.cs b/src/Tgstation.Server.Common/Extensions/ValueTaskExtensions.cs index 6cb8e6ad8a..1dead09d93 100644 --- a/src/Tgstation.Server.Common/Extensions/ValueTaskExtensions.cs +++ b/src/Tgstation.Server.Common/Extensions/ValueTaskExtensions.cs @@ -138,7 +138,7 @@ namespace Tgstation.Server.Common.Extensions } catch (Exception ex) { - exceptions ??= new (tasks.Count - i); + exceptions ??= new(tasks.Count - i); exceptions.Add(ex); } diff --git a/src/Tgstation.Server.Host.Watchdog/ISignalChecker.cs b/src/Tgstation.Server.Host.Watchdog/ISignalChecker.cs index 2b8d42ee80..5ba7f8dda5 100644 --- a/src/Tgstation.Server.Host.Watchdog/ISignalChecker.cs +++ b/src/Tgstation.Server.Host.Watchdog/ISignalChecker.cs @@ -15,6 +15,6 @@ namespace Tgstation.Server.Host.Watchdog /// An to start the main process. It accepts an optional additional command line argument as a paramter and returns it's and lifetime . Must be called. /// The for the operation. /// A representing the running operation. - ValueTask CheckSignals(Func startChildAndGetPid, CancellationToken cancellationToken); + ValueTask CheckSignals(Func startChildAndGetPid, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs index 943e8e1005..bda2228065 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs @@ -337,7 +337,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers { var completionString = errorMessage == null ? "Pending" : "Failed"; - Embed CreateUpdatedEmbed(string message, Color color) => new () + Embed CreateUpdatedEmbed(string message, Color color) => new() { Author = embed.Author, Colour = color, diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs index 8bca821cf1..e4fe37a93e 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs @@ -351,7 +351,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers dbChannel, new List { - new () + new() { RealId = id.Value, IsAdminChannel = dbChannel.IsAdminChannel == true, diff --git a/src/Tgstation.Server.Host/Components/Engine/ByondInstallerBase.cs b/src/Tgstation.Server.Host/Components/Engine/ByondInstallerBase.cs index dcbbd9b0cd..cf20d498a9 100644 --- a/src/Tgstation.Server.Host/Components/Engine/ByondInstallerBase.cs +++ b/src/Tgstation.Server.Host/Components/Engine/ByondInstallerBase.cs @@ -41,12 +41,12 @@ namespace Tgstation.Server.Host.Components.Engine /// /// The first of BYOND that supports the '-map-threads' parameter on DreamDaemon. /// - static readonly Version MapThreadsVersion = new (515, 1609); + static readonly Version MapThreadsVersion = new(515, 1609); /// /// for writing to files in the user's BYOND directory. /// - static readonly SemaphoreSlim UserFilesSemaphore = new (1); + static readonly SemaphoreSlim UserFilesSemaphore = new(1); /// protected override EngineType TargetEngineType => EngineType.Byond; diff --git a/src/Tgstation.Server.Host/Components/Engine/WindowsByondInstaller.cs b/src/Tgstation.Server.Host/Components/Engine/WindowsByondInstaller.cs index 2af9b96a35..0ebf63ff9f 100644 --- a/src/Tgstation.Server.Host/Components/Engine/WindowsByondInstaller.cs +++ b/src/Tgstation.Server.Host/Components/Engine/WindowsByondInstaller.cs @@ -50,7 +50,7 @@ namespace Tgstation.Server.Host.Components.Engine /// /// The first version of BYOND to ship with dd.exe on the Windows build. /// - public static Version DDExeVersion => new (515, 1598); + public static Version DDExeVersion => new(515, 1598); /// protected override string DreamMakerName => "dm.exe"; diff --git a/src/Tgstation.Server.Host/Components/Events/EventType.cs b/src/Tgstation.Server.Host/Components/Events/EventType.cs index 2d34b552c2..fa8321c7e0 100644 --- a/src/Tgstation.Server.Host/Components/Events/EventType.cs +++ b/src/Tgstation.Server.Host/Components/Events/EventType.cs @@ -6,92 +6,92 @@ public enum EventType { /// - /// Parameters: Reference name, commit sha + /// Parameters: Reference name, commit sha. /// [EventScript("RepoResetOrigin")] RepoResetOrigin, /// - /// Parameters: Checkout target + /// Parameters: Checkout target. /// [EventScript("RepoCheckout")] RepoCheckout, /// - /// No parameters + /// No parameters. /// [EventScript("RepoFetch")] RepoFetch, /// - /// Parameters: Test merge number, test merge target sha, merger message + /// Parameters: Test merge number, test merge target sha, merger message. /// [EventScript("RepoMergePullRequest")] RepoAddTestMerge, /// - /// Parameters: Absolute path to repository root + /// Parameters: Absolute path to repository root. /// /// Changes made to the repository during this event will be pushed to the tracked branch if no test merges are present. [EventScript("PreSynchronize")] RepoPreSynchronize, /// - /// Parameters: Version being installed + /// Parameters: Version being installed. /// [EventScript("ByondInstallStart", "EngineInstallStart")] EngineInstallStart, /// - /// Parameters: Error string + /// Parameters: Error string. /// [EventScript("ByondInstallFail", "EngineInstallFail")] EngineInstallFail, /// - /// Parameters: Old active version, new active version + /// Parameters: Old active version, new active version. /// [EventScript("ByondActiveVersionChange", "EngineActiveVersionChange")] EngineActiveVersionChange, /// - /// After the repo is copied, before CodeModifications are applied. Parameters: Game directory path, origin commit sha, engine version string + /// After the repo is copied, before CodeModifications are applied. Parameters: Game directory path, origin commit sha, engine version string. /// [EventScript("PreCompile")] CompileStart, /// - /// No parameters + /// No parameters. /// [EventScript("CompileCancelled")] CompileCancelled, /// - /// Parameters: Game directory path, "1" if compile succeeded and api validation failed, "0" otherwise, engine version string + /// Parameters: Game directory path, "1" if compile succeeded and api validation failed, "0" otherwise, engine version string. /// [EventScript("CompileFailure")] CompileFailure, /// - /// Parameters: Game directory path, engine version string + /// Parameters: Game directory path, engine version string. /// [EventScript("PostCompile")] CompileComplete, /// - /// No parameters + /// No parameters. /// [EventScript("InstanceAutoUpdateStart")] InstanceAutoUpdateStart, /// - /// Parameters: Base sha, target sha, base reference, target reference, all conflicting files + /// Parameters: Base sha, target sha, base reference, target reference, all conflicting files. /// [EventScript("RepoMergeConflict")] RepoMergeConflict, /// - /// No parameters + /// No parameters. /// [EventScript("DeploymentComplete")] DeploymentComplete, @@ -139,31 +139,31 @@ WorldPrime, /// - /// After DD has launched. Not the same as WatchdogLaunch. Parameters: PID of DreamDaemon + /// After DD has launched. Not the same as WatchdogLaunch. Parameters: PID of DreamDaemon. /// [EventScript("DreamDaemonLaunch")] DreamDaemonLaunch, /// - /// After a single submodule update is performed. Parameters: Updated submodule name + /// After a single submodule update is performed. Parameters: Updated submodule name. /// [EventScript("RepoSubmoduleUpdate")] RepoSubmoduleUpdate, /// - /// After CodeModifications are applied, before DreamMaker is run. Parameters: Game directory path, origin commit sha, engine version string + /// After CodeModifications are applied, before DreamMaker is run. Parameters: Game directory path, origin commit sha, engine version string. /// [EventScript("PreDreamMaker")] PreDreamMaker, /// - /// Whenever a deployment folder is deleted from disk. Parameters: Game directory path + /// Whenever a deployment folder is deleted from disk. Parameters: Game directory path. /// [EventScript("DeploymentCleanup")] DeploymentCleanup, /// - /// Whenever a deployment is about to be used by the game server. May fire multiple times per deployment. Parameters: Game directory path + /// Whenever a deployment is about to be used by the game server. May fire multiple times per deployment. Parameters: Game directory path. /// [EventScript("DeploymentActivation")] DeploymentActivation, diff --git a/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeCommandType.cs b/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeCommandType.cs index 627d55d994..8c18f74c39 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeCommandType.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeCommandType.cs @@ -16,7 +16,7 @@ Startup, /// - /// DreamDaemon notifying the server is primed + /// DreamDaemon notifying the server is primed. /// Prime, diff --git a/src/Tgstation.Server.Host/Components/Interop/DMApiConstants.cs b/src/Tgstation.Server.Host/Components/Interop/DMApiConstants.cs index 9cca307361..2a59892180 100644 --- a/src/Tgstation.Server.Host/Components/Interop/DMApiConstants.cs +++ b/src/Tgstation.Server.Host/Components/Interop/DMApiConstants.cs @@ -57,7 +57,7 @@ namespace Tgstation.Server.Host.Components.Interop /// /// for use when communicating with the DMAPI. /// - public static readonly JsonSerializerSettings SerializerSettings = new () + public static readonly JsonSerializerSettings SerializerSettings = new() { ContractResolver = new DefaultContractResolver { diff --git a/src/Tgstation.Server.Host/Components/Interop/Topic/TopicParameters.cs b/src/Tgstation.Server.Host/Components/Interop/Topic/TopicParameters.cs index fff5453b0d..65c060b412 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Topic/TopicParameters.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Topic/TopicParameters.cs @@ -88,7 +88,7 @@ namespace Tgstation.Server.Host.Components.Interop.Topic /// The value of . /// The created . public static TopicParameters CreateInstanceRenamedTopicParameters(string newInstanceName) - => new ( + => new( newInstanceName ?? throw new ArgumentNullException(nameof(newInstanceName)), TopicCommandType.InstanceRenamed); @@ -98,7 +98,7 @@ namespace Tgstation.Server.Host.Components.Interop.Topic /// The value of . /// The created . public static TopicParameters CreateBroadcastParameters(string broadcastMessage) - => new ( + => new( broadcastMessage ?? throw new ArgumentNullException(nameof(broadcastMessage)), TopicCommandType.Broadcast); diff --git a/src/Tgstation.Server.Host/Components/Repository/Repository.cs b/src/Tgstation.Server.Host/Components/Repository/Repository.cs index 6c4a619bf0..a3b81d2bad 100644 --- a/src/Tgstation.Server.Host/Components/Repository/Repository.cs +++ b/src/Tgstation.Server.Host/Components/Repository/Repository.cs @@ -68,7 +68,7 @@ namespace Tgstation.Server.Host.Components.Repository public string Reference => libGitRepo.Head.FriendlyName; /// - public Uri Origin => new (libGitRepo.Network.Remotes.First().Url); + public Uri Origin => new(libGitRepo.Network.Remotes.First().Url); /// /// The for the . diff --git a/src/Tgstation.Server.Host/Components/Session/ApiValidationStatus.cs b/src/Tgstation.Server.Host/Components/Session/ApiValidationStatus.cs index 74ea6b4c54..6b3c6db357 100644 --- a/src/Tgstation.Server.Host/Components/Session/ApiValidationStatus.cs +++ b/src/Tgstation.Server.Host/Components/Session/ApiValidationStatus.cs @@ -6,32 +6,32 @@ enum ApiValidationStatus { /// - /// The DMAPI never contacted the server for validation + /// The DMAPI never contacted the server for validation. /// NeverValidated, /// - /// The server was contacted for validation but it was never requested + /// The server was contacted for validation but it was never requested. /// UnaskedValidationRequest, /// - /// The validation request was malformed + /// The validation request was malformed. /// BadValidationRequest, /// - /// Valid API. The game must be run with a minimum security level of + /// Valid API. The game must be run with a minimum security level of . /// RequiresSafe, /// - /// Valid API. The game must be run with a security level of + /// Valid API. The game must be run with a security level of . /// RequiresTrusted, /// - /// Valid API. The game must be run with a minimum security level of + /// Valid API. The game must be run with a minimum security level of . /// RequiresUltrasafe, diff --git a/src/Tgstation.Server.Host/Components/Session/RebootState.cs b/src/Tgstation.Server.Host/Components/Session/RebootState.cs index 0b58f492dd..030e8be98e 100644 --- a/src/Tgstation.Server.Host/Components/Session/RebootState.cs +++ b/src/Tgstation.Server.Host/Components/Session/RebootState.cs @@ -6,17 +6,17 @@ public enum RebootState : int { /// - /// Run DreamDaemon's normal reboot process + /// Run DreamDaemon's normal reboot process. /// Normal = 0, /// - /// Shutdown DreamDaemon + /// Shutdown DreamDaemon. /// Shutdown = 1, /// - /// Restart the DreamDaemon process + /// Restart the DreamDaemon process. /// Restart = 2, } diff --git a/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs index 9cd7fa15aa..a54f9f8874 100644 --- a/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs @@ -614,7 +614,7 @@ namespace Tgstation.Server.Host.Components.Session DreamDaemonSecurity securityLevel, DreamDaemonVisibility visibility, bool apiValidateOnly) - => new ( + => new( chatTrackingContext, dmbProvider, assemblyInformationProvider.Version, diff --git a/src/Tgstation.Server.Host/Components/Watchdog/MonitorAction.cs b/src/Tgstation.Server.Host/Components/Watchdog/MonitorAction.cs index 50c33760bb..999df90fd1 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/MonitorAction.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/MonitorAction.cs @@ -6,22 +6,22 @@ enum MonitorAction { /// - /// The monitor should continue as normal + /// The monitor should continue as normal. /// Continue, /// - /// Skips the next call to HandleMonitorWakeup action + /// Skips the next call to HandleMonitorWakeup action. /// Skip, /// - /// The monitor should kill and restart both servers + /// The monitor should kill and restart both servers. /// Restart, /// - /// The monitor should stop checking actions for this iteration and continue its loop + /// The monitor should stop checking actions for this iteration and continue its loop. /// Break, diff --git a/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs b/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs index 9cd09acd7a..1d9c760d13 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs @@ -6,22 +6,22 @@ enum MonitorActivationReason { /// - /// The active server crashed or exited + /// The active server crashed or exited. /// ActiveServerCrashed, /// - /// The active server called /world/Reboot() + /// The active server called /world/Reboot(). /// ActiveServerRebooted, /// - /// A new .dmb was deployed + /// A new .dmb was deployed. /// NewDmbAvailable, /// - /// Server launch parameters were changed + /// Server launch parameters were changed. /// ActiveLaunchParametersUpdated, diff --git a/src/Tgstation.Server.Host/Configuration/DatabaseType.cs b/src/Tgstation.Server.Host/Configuration/DatabaseType.cs index 9b3f1c89ca..ab2e6fcae6 100644 --- a/src/Tgstation.Server.Host/Configuration/DatabaseType.cs +++ b/src/Tgstation.Server.Host/Configuration/DatabaseType.cs @@ -6,27 +6,27 @@ public enum DatabaseType { /// - /// Use Microsoft SQL Server + /// Use Microsoft SQL Server. /// SqlServer, /// - /// Use MySQL + /// Use MySQL. /// MySql, /// - /// Use MariaDB + /// Use MariaDB. /// MariaDB, /// - /// Use Sqlite + /// Use Sqlite. /// Sqlite, /// - /// Use PostgresSql + /// Use PostgresSql. /// PostgresSql, } diff --git a/src/Tgstation.Server.Host/Extensions/TaskExtensions.cs b/src/Tgstation.Server.Host/Extensions/TaskExtensions.cs index 68f8f69874..55b6701dbb 100644 --- a/src/Tgstation.Server.Host/Extensions/TaskExtensions.cs +++ b/src/Tgstation.Server.Host/Extensions/TaskExtensions.cs @@ -10,7 +10,7 @@ namespace Tgstation.Server.Host.Extensions /// /// A that never completes. /// - static readonly TaskCompletionSource InfiniteTaskCompletionSource = new (); + static readonly TaskCompletionSource InfiniteTaskCompletionSource = new(); /// /// Gets a that never completes. diff --git a/src/Tgstation.Server.Host/IO/BufferedFileStreamProvider.cs b/src/Tgstation.Server.Host/IO/BufferedFileStreamProvider.cs index a369f41047..c324869860 100644 --- a/src/Tgstation.Server.Host/IO/BufferedFileStreamProvider.cs +++ b/src/Tgstation.Server.Host/IO/BufferedFileStreamProvider.cs @@ -102,7 +102,7 @@ namespace Tgstation.Server.Host.IO /// /// The for the operation. /// A resulting in and its . - async ValueTask<(MemoryStream, long)> GetResultInternal(CancellationToken cancellationToken) + async ValueTask<(MemoryStream Stream, long StreamLength)> GetResultInternal(CancellationToken cancellationToken) { if (!buffered) using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken)) @@ -115,15 +115,15 @@ namespace Tgstation.Server.Host.IO await input.CopyToAsync(localBuffer, cancellationToken); localBuffer.Seek(0, SeekOrigin.Begin); buffered = true; - return (localBuffer, localBuffer.Length); + return (Stream: localBuffer, StreamLength: localBuffer.Length); } lock (semaphore) { var localBuffer = buffer ?? throw new ObjectDisposedException(nameof(BufferedFileStreamProvider)); return ( - localBuffer, - localBuffer.Length); + Stream: localBuffer, + StreamLength: localBuffer.Length); } } } diff --git a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs index 4d3089d4c8..4841980dd3 100644 --- a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs +++ b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs @@ -323,7 +323,7 @@ namespace Tgstation.Server.Host.IO TaskScheduler.Current); /// - public FileStream GetFileStream(string path, bool shareWrite) => new ( + public FileStream GetFileStream(string path, bool shareWrite) => new( ResolvePath(path), FileMode.Open, FileAccess.Read, diff --git a/src/Tgstation.Server.Host/Models/CompileJob.cs b/src/Tgstation.Server.Host/Models/CompileJob.cs index 8ff0ab466d..59bc7c4d01 100644 --- a/src/Tgstation.Server.Host/Models/CompileJob.cs +++ b/src/Tgstation.Server.Host/Models/CompileJob.cs @@ -82,7 +82,7 @@ namespace Tgstation.Server.Host.Models } /// - public CompileJobResponse ToApi() => new () + public CompileJobResponse ToApi() => new() { DirectoryName = DirectoryName, DmeName = DmeName, diff --git a/src/Tgstation.Server.Host/Models/Instance.cs b/src/Tgstation.Server.Host/Models/Instance.cs index 8acf4f1570..82cfd5c707 100644 --- a/src/Tgstation.Server.Host/Models/Instance.cs +++ b/src/Tgstation.Server.Host/Models/Instance.cs @@ -55,7 +55,7 @@ namespace Tgstation.Server.Host.Models public ICollection Jobs { get; set; } /// - public InstanceResponse ToApi() => new () + public InstanceResponse ToApi() => new() { AutoUpdateInterval = AutoUpdateInterval, ConfigurationType = ConfigurationType, diff --git a/src/Tgstation.Server.Host/Models/Job.cs b/src/Tgstation.Server.Host/Models/Job.cs index 8193402a6a..674330ee15 100644 --- a/src/Tgstation.Server.Host/Models/Job.cs +++ b/src/Tgstation.Server.Host/Models/Job.cs @@ -42,7 +42,7 @@ namespace Tgstation.Server.Host.Models /// A new ready to be registered with the . public static Job Create(JobCode code, User startedBy, Api.Models.Instance instance, TRight cancelRight) where TRight : Enum - => new ( + => new( code, startedBy, instance, @@ -57,7 +57,7 @@ namespace Tgstation.Server.Host.Models /// The used to generate the value of . /// A new ready to be registered with the . public static Job Create(JobCode code, User startedBy, Api.Models.Instance instance) - => new ( + => new( code, startedBy, instance, @@ -109,7 +109,7 @@ namespace Tgstation.Server.Host.Models } /// - public JobResponse ToApi() => new () + public JobResponse ToApi() => new() { Id = Id, JobCode = JobCode.Value, diff --git a/src/Tgstation.Server.Host/Models/UserGroup.cs b/src/Tgstation.Server.Host/Models/UserGroup.cs index 11329e9397..63f351c329 100644 --- a/src/Tgstation.Server.Host/Models/UserGroup.cs +++ b/src/Tgstation.Server.Host/Models/UserGroup.cs @@ -28,7 +28,7 @@ namespace Tgstation.Server.Host.Models /// /// If should be populated. /// A new . - public UserGroupResponse ToApi(bool showUsers) => new () + public UserGroupResponse ToApi(bool showUsers) => new() { Id = Id, Name = Name, diff --git a/src/Tgstation.Server.Host/Security/OAuth/DiscordOAuthValidator.cs b/src/Tgstation.Server.Host/Security/OAuth/DiscordOAuthValidator.cs index ccb4d7972a..3195ec9dcf 100644 --- a/src/Tgstation.Server.Host/Security/OAuth/DiscordOAuthValidator.cs +++ b/src/Tgstation.Server.Host/Security/OAuth/DiscordOAuthValidator.cs @@ -17,10 +17,10 @@ namespace Tgstation.Server.Host.Security.OAuth public override OAuthProvider Provider => OAuthProvider.Discord; /// - protected override Uri TokenUrl => new ("https://discord.com/api/oauth2/token"); + protected override Uri TokenUrl => new("https://discord.com/api/oauth2/token"); /// - protected override Uri UserInformationUrl => new ("https://discord.com/api/users/@me"); + protected override Uri UserInformationUrl => new("https://discord.com/api/users/@me"); /// /// Initializes a new instance of the class. @@ -37,7 +37,7 @@ namespace Tgstation.Server.Host.Security.OAuth } /// - protected override OAuthTokenRequest CreateTokenRequest(string code) => new (OAuthConfiguration, code, "identify"); + protected override OAuthTokenRequest CreateTokenRequest(string code) => new(OAuthConfiguration, code, "identify"); /// protected override string DecodeTokenPayload(dynamic responseJson) => responseJson.access_token; diff --git a/src/Tgstation.Server.Host/Security/OAuth/GenericOAuthValidator.cs b/src/Tgstation.Server.Host/Security/OAuth/GenericOAuthValidator.cs index 8606d0921d..668a96240f 100644 --- a/src/Tgstation.Server.Host/Security/OAuth/GenericOAuthValidator.cs +++ b/src/Tgstation.Server.Host/Security/OAuth/GenericOAuthValidator.cs @@ -55,7 +55,7 @@ namespace Tgstation.Server.Host.Security.OAuth /// Gets that should be used. /// /// A new . - protected static JsonSerializerSettings SerializerSettings() => new () + protected static JsonSerializerSettings SerializerSettings() => new() { ContractResolver = new DefaultContractResolver { @@ -141,7 +141,7 @@ namespace Tgstation.Server.Host.Security.OAuth /// public OAuthProviderInfo GetProviderInfo() - => new () + => new() { ClientId = OAuthConfiguration.ClientId, RedirectUri = OAuthConfiguration.RedirectUrl, diff --git a/src/Tgstation.Server.Host/Security/OAuth/GitHubOAuthValidator.cs b/src/Tgstation.Server.Host/Security/OAuth/GitHubOAuthValidator.cs index f53fe8137c..567887b06f 100644 --- a/src/Tgstation.Server.Host/Security/OAuth/GitHubOAuthValidator.cs +++ b/src/Tgstation.Server.Host/Security/OAuth/GitHubOAuthValidator.cs @@ -85,7 +85,7 @@ namespace Tgstation.Server.Host.Security.OAuth /// public OAuthProviderInfo GetProviderInfo() - => new () + => new() { ClientId = oAuthConfiguration.ClientId, RedirectUri = oAuthConfiguration.RedirectUrl, diff --git a/src/Tgstation.Server.Host/Security/OAuth/InvisionCommunityOAuthValidator.cs b/src/Tgstation.Server.Host/Security/OAuth/InvisionCommunityOAuthValidator.cs index 54d6883e3d..a0c8cdd0e2 100644 --- a/src/Tgstation.Server.Host/Security/OAuth/InvisionCommunityOAuthValidator.cs +++ b/src/Tgstation.Server.Host/Security/OAuth/InvisionCommunityOAuthValidator.cs @@ -17,10 +17,10 @@ namespace Tgstation.Server.Host.Security.OAuth public override OAuthProvider Provider => OAuthProvider.InvisionCommunity; /// - protected override Uri TokenUrl => new ($"{OAuthConfiguration.ServerUrl}/oauth/token/"); // This needs the trailing slash or it doesnt get the token. Do not remove. + protected override Uri TokenUrl => new($"{OAuthConfiguration.ServerUrl}/oauth/token/"); // This needs the trailing slash or it doesnt get the token. Do not remove. /// - protected override Uri UserInformationUrl => new ($"{OAuthConfiguration.ServerUrl}/api/core/me"); + protected override Uri UserInformationUrl => new($"{OAuthConfiguration.ServerUrl}/api/core/me"); /// /// Initializes a new instance of the class. @@ -37,7 +37,7 @@ namespace Tgstation.Server.Host.Security.OAuth } /// - protected override OAuthTokenRequest CreateTokenRequest(string code) => new (OAuthConfiguration, code, "profile"); + protected override OAuthTokenRequest CreateTokenRequest(string code) => new(OAuthConfiguration, code, "profile"); /// protected override string DecodeTokenPayload(dynamic responseJson) => responseJson.access_token; diff --git a/src/Tgstation.Server.Host/Security/OAuth/KeycloakOAuthValidator.cs b/src/Tgstation.Server.Host/Security/OAuth/KeycloakOAuthValidator.cs index 03b3ea8732..f4e61b4b5c 100644 --- a/src/Tgstation.Server.Host/Security/OAuth/KeycloakOAuthValidator.cs +++ b/src/Tgstation.Server.Host/Security/OAuth/KeycloakOAuthValidator.cs @@ -17,10 +17,10 @@ namespace Tgstation.Server.Host.Security.OAuth public override OAuthProvider Provider => OAuthProvider.Keycloak; /// - protected override Uri TokenUrl => new ($"{BaseProtocolPath}/token"); + protected override Uri TokenUrl => new($"{BaseProtocolPath}/token"); /// - protected override Uri UserInformationUrl => new ($"{BaseProtocolPath}/userinfo"); + protected override Uri UserInformationUrl => new($"{BaseProtocolPath}/userinfo"); /// /// Base path to the server's OAuth endpoint. @@ -42,7 +42,7 @@ namespace Tgstation.Server.Host.Security.OAuth } /// - protected override OAuthTokenRequest CreateTokenRequest(string code) => new (OAuthConfiguration, code, "openid"); + protected override OAuthTokenRequest CreateTokenRequest(string code) => new(OAuthConfiguration, code, "openid"); /// protected override string DecodeTokenPayload(dynamic responseJson) => responseJson.access_token; diff --git a/src/Tgstation.Server.Host/Security/OAuth/TGForumsOAuthValidator.cs b/src/Tgstation.Server.Host/Security/OAuth/TGForumsOAuthValidator.cs index a65b8346a4..c81a54ec73 100644 --- a/src/Tgstation.Server.Host/Security/OAuth/TGForumsOAuthValidator.cs +++ b/src/Tgstation.Server.Host/Security/OAuth/TGForumsOAuthValidator.cs @@ -17,10 +17,10 @@ namespace Tgstation.Server.Host.Security.OAuth public override OAuthProvider Provider => OAuthProvider.TGForums; /// - protected override Uri TokenUrl => new ("https://tgstation13.org/phpBB/app.php/tgapi/oauth/token"); + protected override Uri TokenUrl => new("https://tgstation13.org/phpBB/app.php/tgapi/oauth/token"); /// - protected override Uri UserInformationUrl => new ("https://tgstation13.org/phpBB/app.php/tgapi/user/me"); + protected override Uri UserInformationUrl => new("https://tgstation13.org/phpBB/app.php/tgapi/user/me"); /// /// Initializes a new instance of the class. @@ -46,6 +46,6 @@ namespace Tgstation.Server.Host.Security.OAuth protected override string DecodeUserInformationPayload(dynamic responseJson) => responseJson.phpbb_username; /// - protected override OAuthTokenRequest CreateTokenRequest(string code) => new (OAuthConfiguration, code, "user"); + protected override OAuthTokenRequest CreateTokenRequest(string code) => new(OAuthConfiguration, code, "user"); } } diff --git a/src/Tgstation.Server.Host/Swarm/SwarmConstants.cs b/src/Tgstation.Server.Host/Swarm/SwarmConstants.cs index f5485cc280..988c60243e 100644 --- a/src/Tgstation.Server.Host/Swarm/SwarmConstants.cs +++ b/src/Tgstation.Server.Host/Swarm/SwarmConstants.cs @@ -66,7 +66,7 @@ namespace Tgstation.Server.Host.Swarm /// static SwarmConstants() { - SerializerSettings = new () + SerializerSettings = new() { ContractResolver = new DefaultContractResolver { diff --git a/src/Tgstation.Server.Host/Swarm/SwarmService.cs b/src/Tgstation.Server.Host/Swarm/SwarmService.cs index ba0a117b58..dc4dfa2251 100644 --- a/src/Tgstation.Server.Host/Swarm/SwarmService.cs +++ b/src/Tgstation.Server.Host/Swarm/SwarmService.cs @@ -109,7 +109,7 @@ namespace Tgstation.Server.Host.Swarm /// /// of s to registration s and when they were created. /// - readonly Dictionary registrationIdsAndTimes; + readonly Dictionary registrationIdsAndTimes; /// /// If the current server is the swarm controller. @@ -193,7 +193,7 @@ namespace Tgstation.Server.Host.Swarm serverHealthCheckCancellationTokenSource = new CancellationTokenSource(); forceHealthCheckTcs = new TaskCompletionSource(); if (swarmController) - registrationIdsAndTimes = new (); + registrationIdsAndTimes = new(); swarmServers = new List { @@ -520,7 +520,7 @@ namespace Tgstation.Server.Host.Swarm { if (swarmController) lock (swarmServers) - return registrationIdsAndTimes.Values.Any(x => x.Item1 == registrationId); + return registrationIdsAndTimes.Values.Any(x => x.RegistrationId == registrationId); if (registrationId != controllerRegistration) return false; @@ -549,9 +549,9 @@ namespace Tgstation.Server.Host.Swarm lock (swarmServers) { - if (registrationIdsAndTimes.Any(x => x.Value.Item1 == registrationId)) + if (registrationIdsAndTimes.Any(x => x.Value.RegistrationId == registrationId)) { - var preExistingRegistrationKvp = registrationIdsAndTimes.FirstOrDefault(x => x.Value.Item1 == registrationId); + var preExistingRegistrationKvp = registrationIdsAndTimes.FirstOrDefault(x => x.Value.RegistrationId == registrationId); if (preExistingRegistrationKvp.Key == node.Identifier) { logger.LogWarning("Node {nodeId} has already registered!", node.Identifier); @@ -580,7 +580,7 @@ namespace Tgstation.Server.Host.Swarm Identifier = node.Identifier, Controller = false, }); - registrationIdsAndTimes.Add(node.Identifier, (registrationId, DateTimeOffset.UtcNow)); + registrationIdsAndTimes.Add(node.Identifier, (RegistrationId: registrationId, DateTimeOffset.UtcNow)); } logger.LogInformation("Registered node {nodeId} ({nodeIP}) with ID {registrationId}", node.Identifier, node.Address, registrationId); @@ -1139,7 +1139,7 @@ namespace Tgstation.Server.Host.Swarm currentSwarmServers .Where(node => !node.Controller && registrationIdsAndTimes.TryGetValue(node.Identifier, out var registrationAndTime) - && registrationAndTime.Item2.AddMinutes(SwarmConstants.ControllerHealthCheckIntervalMinutes) < DateTimeOffset.UtcNow) + && registrationAndTime.RegisteredAt.AddMinutes(SwarmConstants.ControllerHealthCheckIntervalMinutes) < DateTimeOffset.UtcNow) .Select(HealthRequestForServer)); lock (swarmServers) @@ -1390,7 +1390,7 @@ namespace Tgstation.Server.Host.Swarm { lock (swarmServers) if (registrationIdsAndTimes.TryGetValue(swarmServer.Identifier, out var registrationIdAndTime)) - request.Headers.Add(SwarmConstants.RegistrationIdHeader, registrationIdAndTime.Item1.ToString()); + request.Headers.Add(SwarmConstants.RegistrationIdHeader, registrationIdAndTime.RegistrationId.ToString()); } else if (controllerRegistration.HasValue) request.Headers.Add(SwarmConstants.RegistrationIdHeader, controllerRegistration.Value.ToString()); @@ -1505,14 +1505,14 @@ namespace Tgstation.Server.Host.Swarm lock (swarmServers) { - var exists = registrationIdsAndTimes.Any(x => x.Value.Item1 == registrationId); + var exists = registrationIdsAndTimes.Any(x => x.Value.RegistrationId == registrationId); if (!exists) { logger.LogWarning("A node that was to be looked up ({registrationId}) disappeared from our records!", registrationId); return null; } - return registrationIdsAndTimes.First(x => x.Value.Item1 == registrationId).Key; + return registrationIdsAndTimes.First(x => x.Value.RegistrationId == registrationId).Key; } } } diff --git a/src/Tgstation.Server.Host/System/AssemblyInformationProvider.cs b/src/Tgstation.Server.Host/System/AssemblyInformationProvider.cs index 34d2a9a940..88d1a3a627 100644 --- a/src/Tgstation.Server.Host/System/AssemblyInformationProvider.cs +++ b/src/Tgstation.Server.Host/System/AssemblyInformationProvider.cs @@ -26,7 +26,7 @@ namespace Tgstation.Server.Host.System public string VersionString { get; } /// - public ProductInfoHeaderValue ProductInfoHeaderValue => new ( + public ProductInfoHeaderValue ProductInfoHeaderValue => new( VersionPrefix, Version.ToString()); diff --git a/src/Tgstation.Server.Host/System/ProcessExecutor.cs b/src/Tgstation.Server.Host/System/ProcessExecutor.cs index abe5063684..7a00b31d10 100644 --- a/src/Tgstation.Server.Host/System/ProcessExecutor.cs +++ b/src/Tgstation.Server.Host/System/ProcessExecutor.cs @@ -17,7 +17,7 @@ namespace Tgstation.Server.Host.System /// /// for . /// - static readonly ReaderWriterLockSlim ExclusiveProcessLaunchLock = new (); + static readonly ReaderWriterLockSlim ExclusiveProcessLaunchLock = new(); /// /// The for the . diff --git a/src/Tgstation.Server.Host/System/WindowsNetworkPromptReaper.cs b/src/Tgstation.Server.Host/System/WindowsNetworkPromptReaper.cs index c30f107ac3..4892415571 100644 --- a/src/Tgstation.Server.Host/System/WindowsNetworkPromptReaper.cs +++ b/src/Tgstation.Server.Host/System/WindowsNetworkPromptReaper.cs @@ -74,7 +74,7 @@ namespace Tgstation.Server.Host.System try { var pointerChildHandlesList = GCHandle.ToIntPtr(gcChildhandlesList); - NativeMethods.EnumWindowProc childProc = new (EnumWindow); + NativeMethods.EnumWindowProc childProc = new(EnumWindow); NativeMethods.EnumChildWindows(mainWindow, childProc, pointerChildHandlesList); } finally @@ -113,11 +113,12 @@ namespace Tgstation.Server.Host.System process.Lifetime.ContinueWith( x => - { - logger.LogTrace("Unregistering process {0}...", process.Id); - lock (registeredProcesses) - registeredProcesses.Remove(process); - }, TaskScheduler.Current); + { + logger.LogTrace("Unregistering process {pid}...", process.Id); + lock (registeredProcesses) + registeredProcesses.Remove(process); + }, + TaskScheduler.Current); } /// diff --git a/src/Tgstation.Server.Host/Transfer/FileTransferService.cs b/src/Tgstation.Server.Host/Transfer/FileTransferService.cs index 263bd2ba9c..fc0201b48d 100644 --- a/src/Tgstation.Server.Host/Transfer/FileTransferService.cs +++ b/src/Tgstation.Server.Host/Transfer/FileTransferService.cs @@ -240,7 +240,7 @@ namespace Tgstation.Server.Host.Transfer /// Creates a new . /// /// A new . - FileTicketResponse CreateTicket() => new () + FileTicketResponse CreateTicket() => new() { FileTicket = cryptographySuite.GetSecureString(), }; diff --git a/src/Tgstation.Server.Host/Utils/GitHub/GitHubClientFactory.cs b/src/Tgstation.Server.Host/Utils/GitHub/GitHubClientFactory.cs index 7595a70a43..38fb72ffd6 100644 --- a/src/Tgstation.Server.Host/Utils/GitHub/GitHubClientFactory.cs +++ b/src/Tgstation.Server.Host/Utils/GitHub/GitHubClientFactory.cs @@ -43,7 +43,7 @@ namespace Tgstation.Server.Host.Utils.GitHub /// /// Cache of created s and last used times, keyed by access token. /// - readonly Dictionary clientCache; + readonly Dictionary clientCache; /// /// Initializes a new instance of the class. @@ -105,15 +105,15 @@ namespace Tgstation.Server.Host.Utils.GitHub if (accessToken != null) client.Credentials = new Credentials(accessToken); - clientCache.Add(cacheKey, (client, now)); + clientCache.Add(cacheKey, (Client: client, LastUsed: now)); lastUsed = null; } else { logger.LogTrace("Cache hit for GitHubClient"); - client = tuple.Item1; - lastUsed = tuple.Item2; - tuple.Item2 = now; + client = tuple.Client; + lastUsed = tuple.LastUsed; + tuple.LastUsed = now; } // Prune the cache @@ -125,7 +125,7 @@ namespace Tgstation.Server.Host.Utils.GitHub continue; // save the hash lookup tuple = clientCache[key]; - if (tuple.Item2 <= purgeAfter) + if (tuple.LastUsed <= purgeAfter) { clientCache.Remove(key); ++purgeCount; diff --git a/src/Tgstation.Server.Host/Utils/GitHub/GitHubServiceFactory.cs b/src/Tgstation.Server.Host/Utils/GitHub/GitHubServiceFactory.cs index 8ff53440a6..efa08d13bb 100644 --- a/src/Tgstation.Server.Host/Utils/GitHub/GitHubServiceFactory.cs +++ b/src/Tgstation.Server.Host/Utils/GitHub/GitHubServiceFactory.cs @@ -58,7 +58,7 @@ namespace Tgstation.Server.Host.Utils.GitHub /// The for the . /// A new . GitHubService CreateServiceImpl(IGitHubClient gitHubClient) - => new ( + => new( gitHubClient, loggerFactory.CreateLogger(), updatesConfiguration); From 12d0c19794066ecdf3ec7114f10efa93469bb55a Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 25 Nov 2023 11:11:12 -0500 Subject: [PATCH 4/6] Fix debugging issue with host watchdogs --- src/Tgstation.Server.Host.Watchdog/Watchdog.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs index a574a6ab6b..4813383440 100644 --- a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs @@ -101,7 +101,10 @@ namespace Tgstation.Server.Host.Watchdog Directory.Delete(assemblyStoragePath, true); Directory.CreateDirectory(defaultAssemblyPath); - var sourcePath = "../../../../Tgstation.Server.Host/bin/Debug/net8.0"; + var sourcePath = Path.GetFullPath( + Path.Combine( + rootLocation, + "../../../../Tgstation.Server.Host/bin/Debug/net8.0")); foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories)) Directory.CreateDirectory(dirPath.Replace(sourcePath, defaultAssemblyPath, StringComparison.Ordinal)); From c4ce4dad542993444230e15d9eab806b69c88bbc Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 25 Nov 2023 12:20:42 -0500 Subject: [PATCH 5/6] Fix deadlock in `ServiceLifetime` --- .../ServiceLifetime.cs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/Tgstation.Server.Host.Service/ServiceLifetime.cs b/src/Tgstation.Server.Host.Service/ServiceLifetime.cs index efc058fbaa..f29cd0e919 100644 --- a/src/Tgstation.Server.Host.Service/ServiceLifetime.cs +++ b/src/Tgstation.Server.Host.Service/ServiceLifetime.cs @@ -152,14 +152,24 @@ namespace Tgstation.Server.Host.Service await localWatchdogTask; - try + async void StopServiceAsync() { - stopService(); - } - catch (Exception ex) - { - logger.LogError(ex, "Error stopping service!"); + try + { + // This can call OnStop which waits on this task to complete, must be threaded off or it will deadlock + await Task.Run(stopService, cancellationToken); + } + catch (OperationCanceledException ex) + { + logger.LogDebug(ex, "Stopping service cancelled!"); + } + catch (Exception ex) + { + logger.LogError(ex, "Error stopping service!"); + } } + + StopServiceAsync(); } /// From 9069d9a958a17b120ec225721c7b029a18c3043d Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sat, 25 Nov 2023 15:45:03 -0500 Subject: [PATCH 6/6] Fix appsettings base path not getting passed in service --- src/Tgstation.Server.Host.Service/ServerService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host.Service/ServerService.cs b/src/Tgstation.Server.Host.Service/ServerService.cs index 56491187fb..0ab3a73cb6 100644 --- a/src/Tgstation.Server.Host.Service/ServerService.cs +++ b/src/Tgstation.Server.Host.Service/ServerService.cs @@ -104,7 +104,7 @@ namespace Tgstation.Server.Host.Service Stop, signalChecker => watchdogFactory.CreateWatchdog(signalChecker, loggerFactory.Value), loggerFactory.Value.CreateLogger(), - args); + newArgs.ToArray()); } ///