<commit_before>using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.Components.Chat;
using Tgstation.Server.Host.Components.Deployment;
using Tgstation.Server.Host.Components.Events;
using Tgstation.Server.Host.Components.Session;
using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.IO;
using Tgstation.Server.Host.Jobs;

namespace Tgstation.Server.Host.Components.Watchdog
{
	/// <summary>
	/// A variant of the <see cref="WindowsWatchdog"/> that works on POSIX systems.
	/// </summary>
	sealed class PosixWatchdog : WindowsWatchdog
	{
		/// <summary>
		/// If the swappable game directory is currently a rename of the compile job.
		/// </summary>
		bool directoryHardLinked;

		/// <summary>
		/// Initializes a new instance of the <see cref="PosixWatchdog"/> <see langword="class"/>.
		/// </summary>
		/// <param name="chat">The <see cref="IChatManager"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="sessionControllerFactory">The <see cref="ISessionControllerFactory"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="dmbFactory">The <see cref="IDmbFactory"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="sessionPersistor">The <see cref="ISessionPersistor"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="jobManager">The <see cref="IJobManager"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="serverControl">The <see cref="IServerControl"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="asyncDelayer">The <see cref="IAsyncDelayer"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="diagnosticsIOManager">The <see cref="IIOManager"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="eventConsumer">The <see cref="IEventConsumer"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="gitHubDeploymentManager">The <see cref="IGitHubDeploymentManager"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="gameIOManager">The <see cref="IIOManager"/> pointing to the game directory for the <see cref="WindowsWatchdog"/>..</param>
		/// <param name="symlinkFactory">The <see cref="ISymlinkFactory"/> for the <see cref="WindowsWatchdog"/>.</param>
		/// <param name="logger">The <see cref="ILogger"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="initialLaunchParameters">The <see cref="DreamDaemonLaunchParameters"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="instance">The <see cref="Api.Models.Instance"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="autoStart">The autostart value for the <see cref="WatchdogBase"/>.</param>
		public PosixWatchdog(
			IChatManager chat,
			ISessionControllerFactory sessionControllerFactory,
			IDmbFactory dmbFactory,
			ISessionPersistor sessionPersistor,
			IJobManager jobManager,
			IServerControl serverControl,
			IAsyncDelayer asyncDelayer,
			IIOManager diagnosticsIOManager,
			IEventConsumer eventConsumer,
			IGitHubDeploymentManager gitHubDeploymentManager,
			IIOManager gameIOManager,
			ISymlinkFactory symlinkFactory,
			ILogger<PosixWatchdog> logger,
			DreamDaemonLaunchParameters initialLaunchParameters,
			Api.Models.Instance instance,
			bool autoStart)
			: base(
				  chat,
				  sessionControllerFactory,
				  dmbFactory,
				  sessionPersistor,
				  jobManager,
				  serverControl,
				  asyncDelayer,
				  diagnosticsIOManager,
				  eventConsumer,
				  gitHubDeploymentManager,
				  gameIOManager,
				  symlinkFactory,
				  logger,
				  initialLaunchParameters,
				  instance,
				  autoStart)
		{ }

		/// <inheritdoc />
		protected override async Task InitialLink(CancellationToken cancellationToken)
		{
			// The logic to check for an active live directory is in SwappableDmbProvider, so we just do it again here for safety
			Logger.LogTrace("Hard linking compile job...");

			// Symlinks are counted as a file on linux??
			if (await GameIOManager.DirectoryExists(ActiveSwappable.Directory, cancellationToken).ConfigureAwait(false))
				await GameIOManager.DeleteDirectory(ActiveSwappable.Directory, cancellationToken).ConfigureAwait(false);
			else
				await GameIOManager.DeleteFile(ActiveSwappable.Directory, cancellationToken).ConfigureAwait(false);

			// Instead of symlinking to begin with we actually rename the directory
			await GameIOManager.MoveDirectory(
				ActiveSwappable.CompileJob.DirectoryName.ToString(),
				ActiveSwappable.Directory,
				cancellationToken)
				.ConfigureAwait(false);

			directoryHardLinked = true;
		}

		/// <inheritdoc />
		protected override async Task InitControllers(Task chatTask, ReattachInformation reattachInfo, CancellationToken cancellationToken)
		{
			try
			{
				await base.InitControllers(chatTask, reattachInfo, cancellationToken).ConfigureAwait(false);
			}
			finally
			{
				// Then we move it back and apply the symlink
				if (directoryHardLinked)
				{
					Logger.LogTrace("Unhardlinking compile job...");
					Server?.Suspend();
					await GameIOManager.MoveDirectory(
						ActiveSwappable.Directory,
						ActiveSwappable.CompileJob.DirectoryName.ToString(),
						default)
						.ConfigureAwait(false);
					directoryHardLinked = false;<commit_msg>Minor adjustment since this can be null apparently<commit_after>using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.Components.Chat;
using Tgstation.Server.Host.Components.Deployment;
using Tgstation.Server.Host.Components.Events;
using Tgstation.Server.Host.Components.Session;
using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.IO;
using Tgstation.Server.Host.Jobs;

namespace Tgstation.Server.Host.Components.Watchdog
{
	/// <summary>
	/// A variant of the <see cref="WindowsWatchdog"/> that works on POSIX systems.
	/// </summary>
	sealed class PosixWatchdog : WindowsWatchdog
	{
		/// <summary>
		/// If the swappable game directory is currently a rename of the compile job.
		/// </summary>
		bool directoryHardLinked;

		/// <summary>
		/// Initializes a new instance of the <see cref="PosixWatchdog"/> <see langword="class"/>.
		/// </summary>
		/// <param name="chat">The <see cref="IChatManager"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="sessionControllerFactory">The <see cref="ISessionControllerFactory"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="dmbFactory">The <see cref="IDmbFactory"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="sessionPersistor">The <see cref="ISessionPersistor"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="jobManager">The <see cref="IJobManager"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="serverControl">The <see cref="IServerControl"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="asyncDelayer">The <see cref="IAsyncDelayer"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="diagnosticsIOManager">The <see cref="IIOManager"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="eventConsumer">The <see cref="IEventConsumer"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="gitHubDeploymentManager">The <see cref="IGitHubDeploymentManager"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="gameIOManager">The <see cref="IIOManager"/> pointing to the game directory for the <see cref="WindowsWatchdog"/>..</param>
		/// <param name="symlinkFactory">The <see cref="ISymlinkFactory"/> for the <see cref="WindowsWatchdog"/>.</param>
		/// <param name="logger">The <see cref="ILogger"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="initialLaunchParameters">The <see cref="DreamDaemonLaunchParameters"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="instance">The <see cref="Api.Models.Instance"/> for the <see cref="WatchdogBase"/>.</param>
		/// <param name="autoStart">The autostart value for the <see cref="WatchdogBase"/>.</param>
		public PosixWatchdog(
			IChatManager chat,
			ISessionControllerFactory sessionControllerFactory,
			IDmbFactory dmbFactory,
			ISessionPersistor sessionPersistor,
			IJobManager jobManager,
			IServerControl serverControl,
			IAsyncDelayer asyncDelayer,
			IIOManager diagnosticsIOManager,
			IEventConsumer eventConsumer,
			IGitHubDeploymentManager gitHubDeploymentManager,
			IIOManager gameIOManager,
			ISymlinkFactory symlinkFactory,
			ILogger<PosixWatchdog> logger,
			DreamDaemonLaunchParameters initialLaunchParameters,
			Api.Models.Instance instance,
			bool autoStart)
			: base(
				  chat,
				  sessionControllerFactory,
				  dmbFactory,
				  sessionPersistor,
				  jobManager,
				  serverControl,
				  asyncDelayer,
				  diagnosticsIOManager,
				  eventConsumer,
				  gitHubDeploymentManager,
				  gameIOManager,
				  symlinkFactory,
				  logger,
				  initialLaunchParameters,
				  instance,
				  autoStart)
		{ }

		/// <inheritdoc />
		protected override async Task InitialLink(CancellationToken cancellationToken)
		{
			// The logic to check for an active live directory is in SwappableDmbProvider, so we just do it again here for safety
			Logger.LogTrace("Hard linking compile job...");

			// Symlinks are counted as a file on linux??
			if (await GameIOManager.DirectoryExists(ActiveSwappable.Directory, cancellationToken).ConfigureAwait(false))
				await GameIOManager.DeleteDirectory(ActiveSwappable.Directory, cancellationToken).ConfigureAwait(false);
			else
				await GameIOManager.DeleteFile(ActiveSwappable.Directory, cancellationToken).ConfigureAwait(false);

			// Instead of symlinking to begin with we actually rename the directory
			await GameIOManager.MoveDirectory(
				ActiveSwappable.CompileJob.DirectoryName.ToString(),
				ActiveSwappable.Directory,
				cancellationToken)
				.ConfigureAwait(false);

			directoryHardLinked = true;
		}

		/// <inheritdoc />
		protected override async Task InitControllers(Task chatTask, ReattachInformation reattachInfo, CancellationToken cancellationToken)
		{
			try
			{
				await base.InitControllers(chatTask, reattachInfo, cancellationToken).ConfigureAwait(false);
			}
			finally
			{
				// Then we move it back and apply the symlink
				if (directoryHardLinked)
				{
					Logger.LogTrace("Unhardlinking compile job...");
					Server?.Suspend();
					var hardLink = ActiveSwappable.Directory;
					var originalPosition = ActiveSwappable.CompileJob.DirectoryName.ToString();
					await GameIOManager.MoveDirectory(
						hardLink,
						originalPosition,
						default)
						.ConfigureAwait(false);
					directoryHardLinked = false;
{'fraction_non_alphanumeric': 0.12535982913919583, 'fraction_numerical': 0.0, 'mean_word_length': 4.342261904761905, 'pattern_counts': {'":': 0, '<': 153, '<?xml version=': 0, '>': 153, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1371433', 'n_tokens_mistral': 3516, 'n_tokens_neox': 3089, 'n_words': 740}