/* * Copyright (c) 2024 ETH Zürich, IT Services * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ using System; using SafeExamBrowser.Communication.Contracts.Events; using SafeExamBrowser.Communication.Contracts.Hosts; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Lockdown.Contracts; using SafeExamBrowser.Logging.Contracts; namespace SafeExamBrowser.Service { internal class ServiceController { private ILogger logger; private Func logWriterFactory; private IOperationSequence bootstrapSequence; private IOperationSequence sessionSequence; private IServiceHost serviceHost; private SessionContext sessionContext; private ISystemConfigurationUpdate systemConfigurationUpdate; private ILogObserver sessionWriter; private ServiceConfiguration Session { get { return sessionContext.Configuration; } } private bool SessionIsRunning { get { return sessionContext.IsRunning; } } internal ServiceController( ILogger logger, Func logWriterFactory, IOperationSequence bootstrapSequence, IOperationSequence sessionSequence, IServiceHost serviceHost, SessionContext sessionContext, ISystemConfigurationUpdate systemConfigurationUpdate) { this.logger = logger; this.logWriterFactory = logWriterFactory; this.bootstrapSequence = bootstrapSequence; this.sessionSequence = sessionSequence; this.serviceHost = serviceHost; this.sessionContext = sessionContext; this.systemConfigurationUpdate = systemConfigurationUpdate; } internal bool TryStart() { logger.Info("Initiating startup procedure..."); var result = bootstrapSequence.TryPerform(); var success = result == OperationResult.Success; if (success) { RegisterEvents(); logger.Info("Service successfully initialized."); logger.Log(string.Empty); } else { logger.Info("Service startup aborted!"); logger.Log(string.Empty); } return success; } internal void Terminate() { DeregisterEvents(); if (SessionIsRunning) { StopSession(); } logger.Log(string.Empty); logger.Info("Initiating termination procedure..."); var result = bootstrapSequence.TryRevert(); var success = result == OperationResult.Success; if (success) { logger.Info("Service successfully terminated."); logger.Log(string.Empty); } else { logger.Info("Service termination failed!"); logger.Log(string.Empty); } } private void StartSession() { InitializeSessionLogging(); logger.Info(AppendDivider("Session Start Procedure")); var result = sessionSequence.TryPerform(); if (result == OperationResult.Success) { logger.Info(AppendDivider("Session Running")); } else { logger.Info(AppendDivider("Session Start Failed")); } } private void StopSession() { logger.Info(AppendDivider("Session Stop Procedure")); var result = sessionSequence.TryRevert(); if (result == OperationResult.Success) { logger.Info(AppendDivider("Session Terminated")); } else { logger.Info(AppendDivider("Session Stop Failed")); } FinalizeSessionLogging(); } private void RegisterEvents() { serviceHost.SessionStartRequested += ServiceHost_SessionStartRequested; serviceHost.SessionStopRequested += ServiceHost_SessionStopRequested; serviceHost.SystemConfigurationUpdateRequested += ServiceHost_SystemConfigurationUpdateRequested; } private void DeregisterEvents() { serviceHost.SessionStartRequested -= ServiceHost_SessionStartRequested; serviceHost.SessionStopRequested -= ServiceHost_SessionStopRequested; serviceHost.SystemConfigurationUpdateRequested -= ServiceHost_SystemConfigurationUpdateRequested; } private void ServiceHost_SessionStartRequested(SessionStartEventArgs args) { if (!SessionIsRunning) { sessionContext.Configuration = args.Configuration; StartSession(); } else { logger.Warn("Received session start request, even though a session is already running!"); } } private void ServiceHost_SessionStopRequested(SessionStopEventArgs args) { if (SessionIsRunning) { if (Session.SessionId == args.SessionId) { StopSession(); } else { logger.Warn("Received session stop request with wrong session ID!"); } } else { logger.Warn("Received session stop request, even though no session is currently running!"); } } private void ServiceHost_SystemConfigurationUpdateRequested() { logger.Info("Received request to initiate system configuration update."); systemConfigurationUpdate.ExecuteAsync(); } private string AppendDivider(string message) { var dashesLeft = new String('-', 48 - message.Length / 2 - message.Length % 2); var dashesRight = new String('-', 48 - message.Length / 2); return $"### {dashesLeft} {message} {dashesRight} ###"; } private void InitializeSessionLogging() { if (Session?.AppConfig?.ServiceLogFilePath != null) { sessionWriter = logWriterFactory.Invoke(Session.AppConfig.ServiceLogFilePath); logger.Subscribe(sessionWriter); logger.LogLevel = Session.Settings.LogLevel; } else { logger.Warn("Could not initialize session writer due to missing configuration data!"); } } private void FinalizeSessionLogging() { if (sessionWriter != null) { logger.Unsubscribe(sessionWriter); sessionWriter = null; } } } }