diff --git a/SECURITY.md b/SECURITY.md index 5d79c18579..1e624e68bb 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -16,6 +16,6 @@ Vulnerabilities should ideally be reported by directly messaging one of the TGS Here is a list of their discord IDs. -@Cyberboss - Dominion#0444 (<@133295178197893120>) +@Cyberboss - dominion (<@133295178197893120>) Once reported, they will handle the processing of the security advisory. diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/Provider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/Provider.cs index f9ef3396c6..bfc541e6f9 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/Provider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/Provider.cs @@ -309,7 +309,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers cancellationToken); // DCT: Always wait for the job to complete here - await jobManager.WaitForJobCompletion(job, null, cancellationToken, default); + await jobManager.WaitForJobCompletion(job, null, cancellationToken, cancellationToken); } } catch (OperationCanceledException e) diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs index 95ca246c42..0e441edb15 100644 --- a/src/Tgstation.Server.Host/Components/Instance.cs +++ b/src/Tgstation.Server.Host/Components/Instance.cs @@ -514,11 +514,9 @@ namespace Tgstation.Server.Host.Components await jobManager.RegisterOperation( repositoryUpdateJob, RepositoryAutoUpdateJob, - cancellationToken) - ; + cancellationToken); - // DCT: First token will cancel the job, second is for cancelling the cancellation, unwanted - await jobManager.WaitForJobCompletion(repositoryUpdateJob, null, cancellationToken, default); + await jobManager.WaitForJobCompletion(repositoryUpdateJob, null, cancellationToken, cancellationToken); Job compileProcessJob; using (var repo = await RepositoryManager.LoadRepository(cancellationToken)) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs index 9bad7226ad..07e0ebbe89 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs @@ -853,7 +853,7 @@ namespace Tgstation.Server.Host.Components.Watchdog var healthCheckSeconds = ActiveLaunchParameters.HeartbeatSeconds.Value; var healthCheck = healthCheckSeconds == 0 || !controller.DMApiAvailable - ? Extensions.TaskExtensions.InfiniteTask() + ? Extensions.TaskExtensions.InfiniteTask : Task.Delay( TimeSpan.FromSeconds(healthCheckSeconds), cancellationToken); diff --git a/src/Tgstation.Server.Host/Extensions/TaskExtensions.cs b/src/Tgstation.Server.Host/Extensions/TaskExtensions.cs index a8ee467d1e..bd65a629d9 100644 --- a/src/Tgstation.Server.Host/Extensions/TaskExtensions.cs +++ b/src/Tgstation.Server.Host/Extensions/TaskExtensions.cs @@ -14,6 +14,11 @@ namespace Tgstation.Server.Host.Extensions /// static readonly TaskCompletionSource InfiniteTaskCompletionSource = new (); + /// + /// Gets a that never completes. + /// + public static Task InfiniteTask => InfiniteTaskCompletionSource.Task; + /// /// Create a that can be awaited while respecting a given . /// @@ -53,11 +58,5 @@ namespace Tgstation.Server.Host.Extensions return await task; } - - /// - /// Creates a that never completes. - /// - /// A never ending . - public static Task InfiniteTask() => InfiniteTaskCompletionSource.Task; } } diff --git a/src/Tgstation.Server.Host/Jobs/JobHandler.cs b/src/Tgstation.Server.Host/Jobs/JobHandler.cs index 19941fbefd..98cbe88651 100644 --- a/src/Tgstation.Server.Host/Jobs/JobHandler.cs +++ b/src/Tgstation.Server.Host/Jobs/JobHandler.cs @@ -11,6 +11,21 @@ namespace Tgstation.Server.Host.Jobs /// sealed class JobHandler : IDisposable { + /// + /// If the job has started. + /// + public bool Started => task != null; + + /// + /// The progress of the job. + /// + public int? Progress { get; set; } + + /// + /// The stage of the job. + /// + public string Stage { get; set; } + /// /// The for . /// @@ -39,16 +54,6 @@ namespace Tgstation.Server.Host.Jobs /// public void Dispose() => cancellationTokenSource.Dispose(); - /// - /// The progress of the job. - /// - public int? Progress { get; set; } - - /// - /// The stage of the job. - /// - public string Stage { get; set; } - /// /// Wait for to complete. /// diff --git a/src/Tgstation.Server.Host/Jobs/JobService.cs b/src/Tgstation.Server.Host/Jobs/JobService.cs index c0d01f2dcf..971150061a 100644 --- a/src/Tgstation.Server.Host/Jobs/JobService.cs +++ b/src/Tgstation.Server.Host/Jobs/JobService.cs @@ -256,13 +256,23 @@ namespace Tgstation.Server.Host.Jobs { if (job == null) throw new ArgumentNullException(nameof(job)); + + if (!cancellationToken.CanBeCanceled) + throw new ArgumentException("A cancellable CancellationToken should be provided!", nameof(cancellationToken)); + JobHandler handler; + bool noMoreJobsShouldStart; lock (synchronizationLock) { if (!jobs.TryGetValue(job.Id.Value, out handler)) return; + + noMoreJobsShouldStart = this.noMoreJobsShouldStart; } + if (noMoreJobsShouldStart && !handler.Started) + await Extensions.TaskExtensions.InfiniteTask.WithToken(cancellationToken); + Task cancelTask = null; using (jobCancellationToken.Register(() => cancelTask = CancelJob(job, canceller, true, cancellationToken))) await handler.Wait(cancellationToken); diff --git a/src/Tgstation.Server.Host/Swarm/SwarmService.cs b/src/Tgstation.Server.Host/Swarm/SwarmService.cs index d53456c8ac..8f8a5ce7a9 100644 --- a/src/Tgstation.Server.Host/Swarm/SwarmService.cs +++ b/src/Tgstation.Server.Host/Swarm/SwarmService.cs @@ -380,7 +380,7 @@ namespace Tgstation.Server.Host.Swarm ? asyncDelayer.Delay( TimeSpan.FromMinutes(UpdateCommitTimeoutMinutes), cancellationToken) - : Extensions.TaskExtensions.InfiniteTask().WithToken(cancellationToken); + : Extensions.TaskExtensions.InfiniteTask.WithToken(cancellationToken); var commitTask = Task.WhenAny(commitTcsTask, timeoutTask); diff --git a/tests/Tgstation.Server.Tests/Live/Instance/RepositoryTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/RepositoryTest.cs index c35ccd70dd..05f3b90ec2 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/RepositoryTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/RepositoryTest.cs @@ -59,8 +59,8 @@ namespace Tgstation.Server.Tests.Live.Instance if (!targetActiveJob.Progress.HasValue) { - // give it 15 more seconds - targetActiveJob = await WaitForJobProgress(targetActiveJob, 15, cancellationToken); + // give it a few more seconds + targetActiveJob = await WaitForJobProgress(targetActiveJob, 30, cancellationToken); allJobs = await JobsClient.List(null, cancellationToken); } diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index eb5c6946cc..146b9f24ba 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -246,7 +246,14 @@ namespace Tgstation.Server.Tests.Live.Instance jobTcs.SetResult(); await killTask; } - Assert.IsTrue(job.ErrorCode == ErrorCode.DreamDaemonOffline || job.ErrorCode == ErrorCode.GCoreFailure, $"{job.ErrorCode}: {job.ExceptionDetails}"); + + // these can also happen + if (!(new PlatformIdentifier().IsWindows + && (job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_ACCESSDENIED: Access is denied.") + || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_HANDLE: The handle is invalid.") + || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: 3489660936: Unknown error (0xd0000008)")))) // kek + Assert.IsTrue(job.ErrorCode == ErrorCode.DreamDaemonOffline || job.ErrorCode == ErrorCode.GCoreFailure, $"{job.ErrorCode}: {job.ExceptionDetails}"); + await Task.Delay(TimeSpan.FromSeconds(20), cancellationToken); var ddStatus = await instanceClient.DreamDaemon.Read(cancellationToken); @@ -543,7 +550,7 @@ namespace Tgstation.Server.Tests.Live.Instance TopicResponse topicRequestResult = null; try { - System.Console.WriteLine($"Topic limit test S:{payloadSize}..."); + System.Console.WriteLine($"Topic send limit test S:{currentSize}..."); topicRequestResult = await TopicClientNoLogger.SendTopic( IPAddress.Loopback, $"tgs_integration_test_tactics3={TopicClient.SanitizeString(JsonConvert.SerializeObject(topic, DMApiConstants.SerializerSettings))}", @@ -575,6 +582,8 @@ namespace Tgstation.Server.Tests.Live.Instance Assert.AreEqual(DMApiConstants.MaximumTopicRequestLength, (uint)lastSize); + System.Console.WriteLine("TEST: Receiving Topic tests topics..."); + // Receive baseSize = 1; nextPow = 0; @@ -582,6 +591,7 @@ namespace Tgstation.Server.Tests.Live.Instance while (!cancellationToken.IsCancellationRequested) { var currentSize = baseSize + (int)Math.Pow(2, nextPow); + System.Console.WriteLine($"Topic recieve limit test S:{currentSize}..."); var topicRequestResult = await TopicClientNoLogger.SendTopic( IPAddress.Loopback, $"tgs_integration_test_tactics4={TopicClient.SanitizeString(currentSize.ToString())}",