private Int32 StartJobExecution() { Int32 ErrorCode = Constants.INVALID; try { string JobsFolder = Path.Combine( AgentApplication.Options.CacheFolder, "Jobs" ); string JobSpecificFolder = Path.Combine( JobsFolder, "Job-" + JobGuid.ToString() ); // Assert that the folders we need are there Debug.Assert( Directory.Exists( JobsFolder ) && Directory.Exists( JobSpecificFolder ) ); // Copy all required files into the Job directory foreach( KeyValuePair<string, string> Pair in Specification.DependenciesOriginalNames ) { string SrcFile = Path.Combine( AgentApplication.Options.CacheFolder, Pair.Key ); string DestFile = Path.Combine( JobSpecificFolder, Pair.Value ); // Before the file is copied into the job directory, security check bool bIsAuthorized = true; bool bNeedToCheckFile = ( Path.GetExtension( SrcFile ) == ".exe" ) || ( Path.GetExtension( SrcFile ) == ".dll" ); // Microsoft redistributable binaries won't be signed by Epic, so allow them to slide if( Path.GetFileName( SrcFile ).StartsWith( "msvc", StringComparison.InvariantCultureIgnoreCase ) ) { bNeedToCheckFile = false; } if( ( bNeedToCheckFile ) && ( Manager.Certificate != null ) ) { // If the Agent is signed, then everything else must be signed as well. // Start off pessimistic bIsAuthorized = false; X509Certificate NextCertificate = null; try { NextCertificate = X509Certificate.CreateFromSignedFile( SrcFile ); } catch( Exception ) { // Any exception means that either the file isn't signed or has an invalid certificate } if( NextCertificate != null ) { if( NextCertificate.Equals( Manager.Certificate ) ) { bIsAuthorized = true; } } } if( bIsAuthorized ) { File.Copy( SrcFile, DestFile, true ); } else { Manager.Log( EVerbosityLevel.Informative, ELogColour.Red, "[Job] Failed to use file \"" + Pair.Key + "\" because of a security violation" ); } } // Used to indicate the Job was started successfully bool JobStartedSuccessfully = false; // First determine if we're even supposed to start the executable EJobTaskFlags JobFlags = Specification.JobFlags; if( ( JobFlags & EJobTaskFlags.FLAG_MANUAL_START ) != 0 ) { // User has asked to start the executable on their own (useful // for debugging). At connection time, we'll try to match the // full executable path name to establish the parent // connection. Simply update the state of the Job. JobStartedSuccessfully = true; } else { string OriginalExecutableName; if( Specification.DependenciesOriginalNames.TryGetValue( Specification.ExecutableName, out OriginalExecutableName ) ) { string FullExecutableName = Path.Combine( JobSpecificFolder, OriginalExecutableName ); Process TaskProcess = new Process(); TaskProcess.StartInfo.FileName = FullExecutableName; TaskProcess.StartInfo.Arguments = Specification.Parameters; TaskProcess.StartInfo.WorkingDirectory = JobSpecificFolder; TaskProcess.StartInfo.CreateNoWindow = true; // Other properties worth looking into setting // TaskProcess.StartInfo.RedirectStandardInput // Set up the redirect for output TaskProcess.StartInfo.UseShellExecute = false; TaskProcess.StartInfo.RedirectStandardOutput = true; TaskProcess.StartInfo.RedirectStandardError = true; TaskProcess.OutputDataReceived += new DataReceivedEventHandler( OutputReceivedDataEventHandler ); TaskProcess.ErrorDataReceived += new DataReceivedEventHandler( ErrorReceivedDataEventHandler ); // If not already set, set the number of allowed cores to use, // which should be respected by swarm-aware applications if( OwnerIsInstigator ) { // Use local settings, if it's not already set if( !TaskProcess.StartInfo.EnvironmentVariables.ContainsKey( "Swarm_MaxCores" ) ) { TaskProcess.StartInfo.EnvironmentVariables.Add( "Swarm_MaxCores", AgentApplication.DeveloperOptions.LocalJobsDefaultProcessorCount.ToString() ); } } else { // Use remote settings TaskProcess.StartInfo.EnvironmentVariables.Add( "Swarm_MaxCores", AgentApplication.DeveloperOptions.RemoteJobsDefaultProcessorCount.ToString() ); } // Set up our exited callback TaskProcess.Exited += new EventHandler( ExitedProcessEventHandler ); TaskProcess.EnableRaisingEvents = true; ProcessObject = TaskProcess; if( TaskProcess.Start() ) { if( OwnerIsInstigator ) { if( AgentApplication.Options.AvoidLocalExecution ) { // If we're avoiding local execution, make it as non-invasive as possible TaskProcess.PriorityClass = ProcessPriorityClass.Idle; } else { // Use local settings TaskProcess.PriorityClass = ( ProcessPriorityClass )AgentApplication.DeveloperOptions.LocalJobsDefaultProcessPriority; } } else { // Use remote settings TaskProcess.PriorityClass = ( ProcessPriorityClass )AgentApplication.DeveloperOptions.RemoteJobsDefaultProcessPriority; } TaskProcess.BeginOutputReadLine(); TaskProcess.BeginErrorReadLine(); Manager.Log( EVerbosityLevel.Informative, ELogColour.Green, "[Job] Launched Job " + Specification.ExecutableName ); Manager.Log( EVerbosityLevel.Informative, ELogColour.Green, "[Job] PID is " + ProcessObject.Id.ToString() ); Manager.Log( EVerbosityLevel.Informative, ELogColour.Green, "[Job] GUID is \"" + JobGuid.ToString() + "\"" ); // Success JobStartedSuccessfully = true; } } else { //@TODO: Error handling... } } // If the job was started successfully, however that may be, update // the state of the job if( JobStartedSuccessfully ) { CurrentState = JobState.AGENT_JOB_RUNNING; StartTime = DateTime.UtcNow; ErrorCode = Constants.SUCCESS; } // Send a message indicating the Job has started, if this is the original owner if( ( OwnerIsInstigator ) && ( CurrentState == JobState.AGENT_JOB_RUNNING ) ) { AgentJobState JobStartedMessage = new AgentJobState( JobGuid, EJobTaskState.STATE_RUNNING ); Manager.SendMessageInternal( Owner, JobStartedMessage ); // Also, if enabled, request that the agent window be brought // to the front, but only for the Instigator if (AgentApplication.Options.BringToFront && ((JobFlags & EJobTaskFlags.FLAG_MINIMIZED) == 0)) { AgentApplication.ShowWindow = true; } } } catch( Exception Ex ) { // Be sure to null out the process object if anything went wrong (usually starting the process) ProcessObject = null; Manager.Log( EVerbosityLevel.Critical, ELogColour.Red, "[StartJobExecution] Exception was: " + Ex.ToString() ); ErrorCode = Constants.ERROR_EXCEPTION; } return ( ErrorCode ); }
private void SendJobCompletedMessage( AgentInfoMessage AdditionalInfoMessage ) { // Only send a message back if we're the Instigator - remote workers don't have // enough information to make this determination Debug.Assert( OwnerIsInstigator ); AgentJobState UpdatedStateMessage = null; if( CurrentSuccessState == JobSuccessState.AGENT_JOB_SUCCESS ) { Manager.Log( EVerbosityLevel.Informative, ELogColour.Green, "[Job] " + AdditionalInfoMessage.TextMessage ); UpdatedStateMessage = new AgentJobState( JobGuid, EJobTaskState.STATE_COMPLETE_SUCCESS ); } else if( CurrentSuccessState == JobSuccessState.AGENT_JOB_FAILURE ) { Manager.Log( EVerbosityLevel.Informative, ELogColour.Red, "[Job] " + AdditionalInfoMessage.TextMessage ); UpdatedStateMessage = new AgentJobState( JobGuid, EJobTaskState.STATE_COMPLETE_FAILURE ); } // Only if we have an actual update should we send one if( UpdatedStateMessage != null ) { // Set the running time TimeSpan JobRunningTime = DateTime.UtcNow - StartTime; UpdatedStateMessage.JobRunningTime = JobRunningTime.TotalSeconds; // Set the exit code, if there is one UpdatedStateMessage.JobExitCode = ProcessObjectExitCode; // Send the actual message and the additional state message if( AdditionalInfoMessage != null ) { Manager.SendMessageInternal( Owner, AdditionalInfoMessage ); } Manager.SendMessageInternal( Owner, UpdatedStateMessage ); } }