/// <inheritdoc /> protected override async Task InitControllers( Task chatTask, ReattachInformation reattachInfo, CancellationToken cancellationToken) { // don't need a new dmb if reattaching var reattachInProgress = reattachInfo != null; var dmbToUse = reattachInProgress ? null : DmbFactory.LockNextDmb(1); // if this try catches something, both servers are killed try { // start the alpha server task, either by launch a new process or attaching to an existing one // The tasks returned are mainly for writing interop files to the directories among other things and should generally never fail // The tasks pertaining to server startup times are in the ISessionControllers Task <ISessionController> serverLaunchTask; if (!reattachInProgress) { dmbToUse = await PrepServerForLaunch(dmbToUse, cancellationToken).ConfigureAwait(false); serverLaunchTask = SessionControllerFactory.LaunchNew( dmbToUse, null, ActiveLaunchParameters, false, cancellationToken); } else { serverLaunchTask = SessionControllerFactory.Reattach(reattachInfo, cancellationToken); } // retrieve the session controller Server = await serverLaunchTask.ConfigureAwait(false); // failed reattaches will return null Server?.SetHighPriority(); // possiblity of null servers due to failed reattaches if (Server == null) { await ReattachFailure( chatTask, cancellationToken) .ConfigureAwait(false); return; } await CheckLaunchResult(Server, "Server", cancellationToken).ConfigureAwait(false); Server.EnableCustomChatCommands(); } catch { // kill the controllers bool serverWasActive = Server != null; // DCT: Operation must always run await DisposeAndNullControllers(default).ConfigureAwait(false);
/// <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; } } if (reattachInfo != null) { Logger.LogTrace("Skipping symlink due to reattach"); return; } Logger.LogTrace("Symlinking compile job..."); await ActiveSwappable.MakeActive(cancellationToken).ConfigureAwait(false); Server.Resume(); }
/// <summary> /// Construct a <see cref="SessionController"/> /// </summary> /// <param name="reattachInformation">The value of <see cref="reattachInformation"/></param> /// <param name="process">The value of <see cref="process"/></param> /// <param name="byondLock">The value of <see cref="byondLock"/></param> /// <param name="byondTopicSender">The value of <see cref="byondTopicSender"/></param> /// <param name="interopContext">The value of <see cref="interopContext"/></param> /// <param name="chat">The value of <see cref="chat"/></param> /// <param name="chatJsonTrackingContext">The value of <see cref="chatJsonTrackingContext"/></param> /// <param name="logger">The value of <see cref="logger"/></param> /// <param name="launchSecurityLevel">The value of <see cref="launchSecurityLevel"/></param> /// <param name="startupTimeout">The optional time to wait before failing the <see cref="LaunchResult"/></param> public SessionController( ReattachInformation reattachInformation, IProcess process, IByondExecutableLock byondLock, IByondTopicSender byondTopicSender, IJsonTrackingContext chatJsonTrackingContext, ICommContext interopContext, IChat chat, ILogger <SessionController> logger, DreamDaemonSecurity?launchSecurityLevel, uint?startupTimeout) { this.chatJsonTrackingContext = chatJsonTrackingContext; // null valid this.reattachInformation = reattachInformation ?? throw new ArgumentNullException(nameof(reattachInformation)); this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender)); this.process = process ?? throw new ArgumentNullException(nameof(process)); this.byondLock = byondLock ?? throw new ArgumentNullException(nameof(byondLock)); this.interopContext = interopContext ?? throw new ArgumentNullException(nameof(interopContext)); this.chat = chat ?? throw new ArgumentNullException(nameof(chat)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); this.launchSecurityLevel = launchSecurityLevel; interopContext.RegisterHandler(this); portClosedForReboot = false; disposed = false; apiValidationStatus = ApiValidationStatus.NeverValidated; released = false; rebootTcs = new TaskCompletionSource <object>(); process.Lifetime.ContinueWith(x => chatJsonTrackingContext.Active = false, TaskScheduler.Current); async Task <LaunchResult> GetLaunchResult() { var startTime = DateTimeOffset.Now; Task toAwait = process.Startup; if (startupTimeout.HasValue) { toAwait = Task.WhenAny(process.Startup, Task.Delay(startTime.AddSeconds(startupTimeout.Value) - startTime)); } await toAwait.ConfigureAwait(false); var result = new LaunchResult { ExitCode = process.Lifetime.IsCompleted ? (int?)await process.Lifetime.ConfigureAwait(false) : null, StartupTime = process.Startup.IsCompleted ? (TimeSpan?)(DateTimeOffset.Now - startTime) : null }; return(result); } LaunchResult = GetLaunchResult(); logger.LogDebug("Created session controller. Primary: {0}, CommsKey: {1}, Port: {2}", IsPrimary, reattachInformation.AccessIdentifier, Port); }
#pragma warning restore CA1506 /// <inheritdoc /> public async Task <ISessionController> Reattach(ReattachInformation reattachInformation, CancellationToken cancellationToken) { if (reattachInformation == null) { throw new ArgumentNullException(nameof(reattachInformation)); } SessionController result = null; var basePath = reattachInformation.IsPrimary ? reattachInformation.Dmb.PrimaryDirectory : reattachInformation.Dmb.SecondaryDirectory; var chatJsonTrackingContext = await chat.TrackJsons(basePath, reattachInformation.ChatChannelsJson, reattachInformation.ChatCommandsJson, cancellationToken).ConfigureAwait(false); try { var byondLock = await byond.UseExecutables(Version.Parse(reattachInformation.Dmb.CompileJob.ByondVersion), cancellationToken).ConfigureAwait(false); try { var context = new CommContext(ioManager, loggerFactory.CreateLogger <CommContext>(), basePath, reattachInformation.ServerCommandsJson); try { var process = processExecutor.GetProcess(reattachInformation.ProcessId); if (process != null) { try { networkPromptReaper.RegisterProcess(process); result = new SessionController(reattachInformation, process, byondLock, byondTopicSender, chatJsonTrackingContext, context, chat, loggerFactory.CreateLogger <SessionController>(), null, null); process = null; context = null; byondLock = null; chatJsonTrackingContext = null; } finally { process?.Dispose(); } } } finally { context?.Dispose(); } } finally { byondLock?.Dispose(); } } finally { chatJsonTrackingContext?.Dispose(); } return(result); }
/// <summary> /// Construct a <see cref="WatchdogReattachInformation"/> from a given <paramref name="copy"/> with a given <paramref name="dmbAlpha"/> and <paramref name="dmbBravo"/> /// </summary> /// <param name="copy">The <see cref="WatchdogReattachInformationBase"/> to copy information from</param> /// <param name="dmbAlpha">The <see cref="IDmbProvider"/> used to build <see cref="Alpha"/></param> /// <param name="dmbBravo">The <see cref="IDmbProvider"/> used to build <see cref="Bravo"/></param> public WatchdogReattachInformation(Models.WatchdogReattachInformation copy, IDmbProvider dmbAlpha, IDmbProvider dmbBravo) : base(copy) { if (copy.Alpha != null) { Alpha = new ReattachInformation(copy.Alpha, dmbAlpha); } if (copy.Bravo != null) { Bravo = new ReattachInformation(copy.Bravo, dmbBravo); } }
/// <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 (hardLinkedDmb != null) { try { Logger.LogTrace("Unhardlinking compile job..."); Server?.Suspend(); var hardLink = hardLinkedDmb.Directory; var originalPosition = hardLinkedDmb.CompileJob.DirectoryName.ToString(); await GameIOManager.MoveDirectory( hardLink, originalPosition, default) .ConfigureAwait(false); } catch (Exception ex) { Logger.LogError( ex, "Failed to un-hard link compile job #{0} ({1})", hardLinkedDmb.CompileJob.Id, hardLinkedDmb.CompileJob.DirectoryName); } hardLinkedDmb = null; } } if (reattachInfo != null) { Logger.LogTrace("Skipping symlink due to reattach"); return; } Logger.LogTrace("Symlinking compile job..."); await ActiveSwappable.MakeActive(cancellationToken).ConfigureAwait(false); Server.Resume(); }
/// <summary> /// Construct a <see cref="SessionController"/> /// </summary> /// <param name="reattachInformation">The value of <see cref="reattachInformation"/></param> /// <param name="process">The value of <see cref="process"/></param> /// <param name="byondLock">The value of <see cref="byondLock"/></param> /// <param name="byondTopicSender">The value of <see cref="byondTopicSender"/></param> /// <param name="interopContext">The value of <see cref="interopContext"/></param> /// <param name="chat">The value of <see cref="chat"/></param> /// <param name="chatJsonTrackingContext">The value of <see cref="chatJsonTrackingContext"/></param> /// <param name="logger">The value of <see cref="logger"/></param> /// <param name="startupTimeout">The optional time to wait before failing the <see cref="LaunchResult"/></param> public SessionController(ReattachInformation reattachInformation, IProcess process, IByondExecutableLock byondLock, IByondTopicSender byondTopicSender, IJsonTrackingContext chatJsonTrackingContext, ICommContext interopContext, IChat chat, ILogger <SessionController> logger, uint?startupTimeout) { this.chatJsonTrackingContext = chatJsonTrackingContext; //null valid this.reattachInformation = reattachInformation ?? throw new ArgumentNullException(nameof(reattachInformation)); this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender)); this.process = process ?? throw new ArgumentNullException(nameof(process)); this.byondLock = byondLock ?? throw new ArgumentNullException(nameof(byondLock)); this.interopContext = interopContext ?? throw new ArgumentNullException(nameof(interopContext)); this.chat = chat ?? throw new ArgumentNullException(nameof(chat)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); interopContext.RegisterHandler(this); portClosedForReboot = false; disposed = false; apiValidated = false; released = false; rebootTcs = new TaskCompletionSource <object>(); async Task <LaunchResult> GetLaunchResult() { var startTime = DateTimeOffset.Now; Task toAwait = process.Startup; if (startupTimeout.HasValue) { toAwait = Task.WhenAny(process.Startup, Task.Delay(startTime.AddSeconds(startupTimeout.Value) - startTime)); } await toAwait.ConfigureAwait(false); var result = new LaunchResult { ExitCode = process.Lifetime.IsCompleted ? (int?)await process.Lifetime.ConfigureAwait(false) : null, StartupTime = process.Startup.IsCompleted ? (TimeSpan?)(DateTimeOffset.Now - startTime) : null }; return(result); }; LaunchResult = GetLaunchResult(); }