2018-01-23 15:33:54 +01:00
/ *
2019-01-09 11:25:21 +01:00
* Copyright ( c ) 2019 ETH Zürich , Educational Development and Technology ( LET )
2018-01-23 15:33:54 +01:00
*
* 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/.
* /
2019-06-18 10:18:56 +02:00
using System ;
using System.Security.AccessControl ;
2019-06-06 15:44:03 +02:00
using System.Threading ;
2019-08-30 09:55:26 +02:00
using SafeExamBrowser.Communication.Contracts.Hosts ;
using SafeExamBrowser.Communication.Contracts.Proxies ;
using SafeExamBrowser.Configuration.Contracts ;
2019-09-06 09:39:28 +02:00
using SafeExamBrowser.Settings.Service ;
2019-08-30 09:55:26 +02:00
using SafeExamBrowser.Core.Contracts.OperationModel ;
using SafeExamBrowser.Core.Contracts.OperationModel.Events ;
using SafeExamBrowser.I18n.Contracts ;
using SafeExamBrowser.Logging.Contracts ;
2019-09-06 08:32:29 +02:00
using SafeExamBrowser.Runtime.Operations.Events ;
2019-08-30 09:55:26 +02:00
using SafeExamBrowser.SystemComponents.Contracts ;
using SafeExamBrowser.UserInterface.Contracts.MessageBox ;
2018-01-23 15:33:54 +01:00
2018-08-31 10:06:27 +02:00
namespace SafeExamBrowser.Runtime.Operations
2018-01-23 15:33:54 +01:00
{
2018-10-12 11:16:59 +02:00
internal class ServiceOperation : SessionOperation
2018-01-23 15:33:54 +01:00
{
private ILogger logger ;
2019-06-06 15:44:03 +02:00
private IRuntimeHost runtimeHost ;
2018-01-24 12:34:32 +01:00
private IServiceProxy service ;
2019-06-06 15:44:03 +02:00
private int timeout_ms ;
2019-06-21 15:05:31 +02:00
private IUserInfo userInfo ;
2018-01-23 15:33:54 +01:00
2019-06-11 09:53:33 +02:00
public override event ActionRequiredEventHandler ActionRequired ;
2018-10-12 11:16:59 +02:00
public override event StatusChangedEventHandler StatusChanged ;
2018-01-23 15:33:54 +01:00
2019-06-06 15:44:03 +02:00
public ServiceOperation (
ILogger logger ,
IRuntimeHost runtimeHost ,
IServiceProxy service ,
SessionContext sessionContext ,
2019-06-21 15:05:31 +02:00
int timeout_ms ,
IUserInfo userInfo ) : base ( sessionContext )
2018-01-23 15:33:54 +01:00
{
this . logger = logger ;
2019-06-06 15:44:03 +02:00
this . runtimeHost = runtimeHost ;
2018-10-12 11:16:59 +02:00
this . service = service ;
2019-06-06 15:44:03 +02:00
this . timeout_ms = timeout_ms ;
2019-06-21 15:05:31 +02:00
this . userInfo = userInfo ;
2018-01-23 15:33:54 +01:00
}
2018-10-12 11:16:59 +02:00
public override OperationResult Perform ( )
2018-01-23 15:33:54 +01:00
{
2019-06-18 10:18:56 +02:00
logger . Info ( $"Initializing service..." ) ;
2018-10-03 15:42:50 +02:00
StatusChanged ? . Invoke ( TextKey . OperationStatus_InitializeServiceSession ) ;
2018-01-23 15:33:54 +01:00
2019-06-06 15:44:03 +02:00
var success = TryEstablishConnection ( ) ;
2018-01-24 12:34:32 +01:00
2019-06-06 15:44:03 +02:00
if ( service . IsConnected )
2018-01-24 12:34:32 +01:00
{
2019-06-06 15:44:03 +02:00
success = TryStartSession ( ) ;
}
return success ? OperationResult . Success : OperationResult . Failed ;
}
public override OperationResult Repeat ( )
{
2019-06-18 10:18:56 +02:00
logger . Info ( $"Initializing service..." ) ;
2019-06-06 15:44:03 +02:00
StatusChanged ? . Invoke ( TextKey . OperationStatus_InitializeServiceSession ) ;
var success = false ;
if ( service . IsConnected )
{
success = TryStopSession ( ) ;
}
else
{
success = TryEstablishConnection ( ) ;
}
2018-02-28 15:49:06 +01:00
2019-06-06 15:44:03 +02:00
if ( success & & service . IsConnected )
{
success = TryStartSession ( ) ;
2018-01-24 12:34:32 +01:00
}
2018-02-28 15:49:06 +01:00
2019-06-06 15:44:03 +02:00
return success ? OperationResult . Success : OperationResult . Failed ;
}
2018-02-28 15:49:06 +01:00
2019-06-06 15:44:03 +02:00
public override OperationResult Revert ( )
{
2019-06-18 10:18:56 +02:00
logger . Info ( "Finalizing service..." ) ;
2019-06-06 15:44:03 +02:00
StatusChanged ? . Invoke ( TextKey . OperationStatus_FinalizeServiceSession ) ;
var success = true ;
if ( service . IsConnected )
2018-03-21 10:23:15 +01:00
{
2019-06-18 10:18:56 +02:00
if ( Context . Current ! = null )
{
2019-07-04 09:12:28 +02:00
success & = TryStopSession ( true ) ;
2019-06-18 10:18:56 +02:00
}
2019-06-06 15:44:03 +02:00
success & = TryTerminateConnection ( ) ;
2018-03-21 10:23:15 +01:00
}
2019-06-06 15:44:03 +02:00
return success ? OperationResult . Success : OperationResult . Failed ;
2018-01-23 15:33:54 +01:00
}
2019-06-06 15:44:03 +02:00
private bool TryEstablishConnection ( )
2018-02-01 08:37:12 +01:00
{
2019-06-20 10:55:24 +02:00
var mandatory = Context . Next . Settings . Service . Policy = = ServicePolicy . Mandatory ;
var warn = Context . Next . Settings . Service . Policy = = ServicePolicy . Warn ;
2019-06-06 15:44:03 +02:00
var connected = service . Connect ( ) ;
var success = connected | | ! mandatory ;
2018-10-10 09:19:03 +02:00
2019-06-06 15:44:03 +02:00
if ( success )
2018-03-21 10:23:15 +01:00
{
2019-06-06 15:44:03 +02:00
service . Ignore = ! connected ;
logger . Info ( $"The service is {(mandatory ? " mandatory " : " optional ")} and {(connected ? " connected . " : " not connected . All service - related operations will be ignored ! ")}" ) ;
2019-06-11 09:53:33 +02:00
if ( ! connected & & warn )
{
ActionRequired ? . Invoke ( new MessageEventArgs
{
Icon = MessageBoxIcon . Warning ,
Message = TextKey . MessageBox_ServiceUnavailableWarning ,
Title = TextKey . MessageBox_ServiceUnavailableWarningTitle
} ) ;
}
2019-06-06 15:44:03 +02:00
}
else
{
logger . Error ( "The service is mandatory but no connection could be established!" ) ;
2019-06-11 09:53:33 +02:00
ActionRequired ? . Invoke ( new MessageEventArgs
{
Icon = MessageBoxIcon . Error ,
Message = TextKey . MessageBox_ServiceUnavailableError ,
Title = TextKey . MessageBox_ServiceUnavailableErrorTitle
} ) ;
2018-03-21 10:23:15 +01:00
}
2018-02-28 15:49:06 +01:00
2019-06-06 15:44:03 +02:00
return success ;
2018-02-01 08:37:12 +01:00
}
2019-06-06 15:44:03 +02:00
private bool TryTerminateConnection ( )
2018-01-23 15:33:54 +01:00
{
2019-06-18 10:18:56 +02:00
var disconnected = service . Disconnect ( ) ;
2019-06-06 15:44:03 +02:00
2019-06-18 10:18:56 +02:00
if ( disconnected )
2018-01-24 12:34:32 +01:00
{
2019-06-18 10:18:56 +02:00
logger . Info ( "Successfully disconnected from service." ) ;
2018-01-24 12:34:32 +01:00
}
2019-06-06 15:44:03 +02:00
else
{
logger . Error ( "Failed to disconnect from service!" ) ;
}
2018-10-10 09:19:03 +02:00
2019-06-18 10:18:56 +02:00
return disconnected ;
2018-01-23 15:33:54 +01:00
}
2018-02-01 08:37:12 +01:00
2019-06-06 15:44:03 +02:00
private bool TryStartSession ( )
2018-03-21 10:23:15 +01:00
{
2019-06-11 09:53:33 +02:00
var configuration = new ServiceConfiguration
{
AppConfig = Context . Next . AppConfig ,
SessionId = Context . Next . SessionId ,
2019-06-21 15:05:31 +02:00
Settings = Context . Next . Settings ,
UserName = userInfo . GetUserName ( ) ,
UserSid = userInfo . GetUserSid ( )
2019-06-11 09:53:33 +02:00
} ;
2019-06-18 10:18:56 +02:00
var started = false ;
2019-06-06 15:44:03 +02:00
logger . Info ( "Starting new service session..." ) ;
2019-06-11 09:53:33 +02:00
var communication = service . StartSession ( configuration ) ;
2019-06-06 15:44:03 +02:00
if ( communication . Success )
{
2019-06-18 10:18:56 +02:00
started = TryWaitForServiceEvent ( Context . Next . AppConfig . ServiceEventName ) ;
2019-06-06 15:44:03 +02:00
2019-06-18 10:18:56 +02:00
if ( started )
2019-06-06 15:44:03 +02:00
{
logger . Info ( "Successfully started new service session." ) ;
}
else
{
logger . Error ( $"Failed to start new service session within {timeout_ms / 1000} seconds!" ) ;
}
}
else
{
2019-07-04 09:12:28 +02:00
logger . Error ( "Failed to communicate session start command to service!" ) ;
2019-06-06 15:44:03 +02:00
}
2019-06-18 10:18:56 +02:00
return started ;
2018-03-21 10:23:15 +01:00
}
2019-07-04 09:12:28 +02:00
private bool TryStopSession ( bool isFinalSession = false )
2018-03-21 10:23:15 +01:00
{
2019-07-04 09:12:28 +02:00
var success = false ;
2019-06-06 15:44:03 +02:00
logger . Info ( "Stopping current service session..." ) ;
2019-06-11 09:53:33 +02:00
var communication = service . StopSession ( Context . Current . SessionId ) ;
2019-06-06 15:44:03 +02:00
if ( communication . Success )
{
2019-07-04 09:12:28 +02:00
success = TryWaitForServiceEvent ( Context . Current . AppConfig . ServiceEventName ) ;
2019-06-06 15:44:03 +02:00
2019-07-04 09:12:28 +02:00
if ( success )
2019-06-06 15:44:03 +02:00
{
logger . Info ( "Successfully stopped service session." ) ;
}
else
{
logger . Error ( $"Failed to stop service session within {timeout_ms / 1000} seconds!" ) ;
}
}
else
{
2019-07-04 09:12:28 +02:00
logger . Error ( "Failed to communicate session stop command to service!" ) ;
2019-06-06 15:44:03 +02:00
}
2019-07-04 09:12:28 +02:00
if ( success & & isFinalSession )
{
communication = service . RunSystemConfigurationUpdate ( ) ;
success = communication . Success ;
if ( communication . Success )
{
logger . Info ( "Instructed service to perform system configuration update." ) ;
}
else
{
logger . Error ( "Failed to communicate system configuration update command to service!" ) ;
}
}
return success ;
2019-06-18 10:18:56 +02:00
}
2019-06-06 15:44:03 +02:00
2019-06-18 10:18:56 +02:00
private bool TryWaitForServiceEvent ( string eventName )
{
var serviceEvent = default ( EventWaitHandle ) ;
var startTime = DateTime . Now ;
do
{
if ( EventWaitHandle . TryOpenExisting ( eventName , EventWaitHandleRights . Synchronize , out serviceEvent ) )
{
break ;
}
} while ( startTime . AddMilliseconds ( timeout_ms ) > DateTime . Now ) ;
if ( serviceEvent ! = default ( EventWaitHandle ) )
{
using ( serviceEvent )
{
return serviceEvent . WaitOne ( timeout_ms ) ;
}
}
return false ;
2018-03-21 10:23:15 +01:00
}
2018-01-23 15:33:54 +01:00
}
}