/// <summary> /// Called by schedulers to execute an action. /// </summary> /// <param name="action">The action to be executed.</param> /// <returns>An SMVActionResult object representing the result of executing the action.</returns> public static SMVActionResult ExecuteAction(SMVAction action, bool fromWorker) { // NOTE: The code in this function must be thread safe. if (action == null) { return(null); } // If there is a plugin, call PreAction first. if (plugin != null) { plugin.PreAction(action); } using (MemoryStream stream = new MemoryStream()) { // We use a logger for writing messages since we can't output to the console in this function (As this // may be running in multiple threads). StreamWriter logger = new StreamWriter(stream); IDictionary <string, string> variables = action.variables; DateTime startTime = DateTime.Now; string actionPath = variables["workingDir"]; string actionOutput = string.Empty; int cumulativeExitCode = 0; // Get the name of the action. string name = action.name; if (variables.ContainsKey("analysisProperty")) { name = action.name + " - " + variables["analysisProperty"]; } variables["name"] = action.name; Log.LogInfo("Running action: " + name, logger); // Get the path to the action. if (action.Path != null) { actionPath = action.Path.value; } actionPath = ExpandVariables(actionPath, variables); variables["actionPath"] = actionPath; // Launch a cmd.exe process to run commands in. if (Console.InputEncoding.Equals(Encoding.UTF8)) { Console.InputEncoding = new UTF8Encoding(false); } // Run the commands. if (action.Command != null) { foreach (SMVCommand cmd in action.Command) { JobObject jobObject = null; if (useJobObject) { //Update maxTime and maxMemory allowed int maxMemory = int.MaxValue; int maxTime = int.MaxValue; updateAttribute(ref maxTime, cmd.maxTime, "Time"); Log.LogDebug("Maximum time allowed for this command = " + maxTime); updateAttribute(ref maxMemory, cmd.maxMemory, "Memory"); //Converting memory from MB to bytes, if input is valid if (maxMemory < int.MaxValue) { maxMemory *= (1024 * 1024); } Log.LogDebug("Maximum memory allowed for this command = " + maxMemory); jobObject = new JobObject(); jobObject.setConstraints(maxMemory, maxTime); } Process process = LaunchProcess("cmd.exe", "", actionPath, action.Env, logger, jobObject); process.OutputDataReceived += (sender, e) => { Log.LogMessage(e.Data, logger); }; process.ErrorDataReceived += (sender, e) => { Log.LogMessage(e.Data, logger); }; // Get the command and arguments, and expand all environment as well as SMV variables. string cmdAttr = ExpandVariables(Environment.ExpandEnvironmentVariables(cmd.value), variables); string argumentsAttr = string.Empty; if (!string.IsNullOrEmpty(cmd.arguments)) { argumentsAttr = ExpandVariables(Environment.ExpandEnvironmentVariables(cmd.arguments), variables); } try { Log.LogInfo(String.Format(CultureInfo.InvariantCulture, "Launching {0} with arguments: {1}", cmdAttr, argumentsAttr), logger); process.StandardInput.WriteLine(String.Join(" ", new String[] { cmdAttr, argumentsAttr })); process.StandardInput.WriteLine("Exit %errorlevel%"); process.StandardInput.Close(); process.BeginOutputReadLine(); process.BeginErrorReadLine(); process.WaitForExit(); TimeSpan span = process.ExitTime - process.StartTime; Log.LogMessage(string.Format("Command Exit code: {0}", process.ExitCode), logger); cumulativeExitCode += Math.Abs(process.ExitCode); if (useDb) { try { using (var database = new SmvDbEntities()) { var masterEntry = new TaskAction { ActionID = Guid.NewGuid().ToString(), TaskID = taskId, ActionName = action.name, Success = cumulativeExitCode.ToString(), ActionTime = span.ToString(), WorkingDirectory = variables["workingDir"] }; database.TaskActions.Add(masterEntry); database.SaveChanges(); } } catch (Exception e) { if (fromWorker) { Log.LogError("Exception while updating database " + e); } else { Log.LogFatalError("Exception while updating database " + e); } } } if (useJobObject) { jobObject.QueryExtendedLimitInformation(); jobObject.Close(); jobObject.Dispose(); } } catch (Exception e) { Log.LogInfo(e.ToString(), logger); Log.LogInfo("Could not start process: " + cmdAttr, logger); if (useJobObject) { jobObject.Close(); jobObject.Dispose(); } return(null); } } } logger.Flush(); stream.Position = 0; string output = new StreamReader(stream).ReadToEnd(); if (debugMode) { Log.WriteToFile(Path.Combine(actionPath, string.Format("smvexecute-{0}.log", action.name)), output, false); } Log.LogDebug("cumulative exit code is " + cumulativeExitCode); DateTime endTime = DateTime.Now; action.result = new SMVActionResult(action.name, output, (cumulativeExitCode == 0), cumulativeExitCode != 0 && action.breakOnError, (int)(endTime - startTime).TotalSeconds); // Call plugin post action only if we were successful in executing the action. if (cumulativeExitCode == 0) { // get the output directory and set the output of the action from the build log. if (action.name.Equals("NormalBuild")) { string logPath = Path.Combine(variables["workingDir"], variables["smvLogFileNamePrefix"] + ".log"); action.result.output = Utility.ReadFile(logPath); variables["outputDir"] = ExtractBuildPath(variables["workingDir"], action.result.output, logger); Utility.SetSmvVar("outputDir", variables["outputDir"]); } // Get the output directory and the analysis directory. if (action.name.Equals("InterceptedBuild")) { string logPath = Path.Combine(variables["workingDir"], variables["smvLogFileNamePrefix"] + ".log"); action.result.output = Utility.ReadFile(logPath); } // Call the plugin's post action. if (plugin != null) { plugin.PostAction(action); } } else { // are we sure we want to exit here... the cloud worker instance becomes // unhealthy after exiting here... if (action.breakOnError) { if (fromWorker) { Log.LogError(String.Format("Action: {0}, failed.", name)); } else { Log.LogFatalError(String.Format("Action: {0}, failed.", name)); } } else { Log.LogError(String.Format("Action: {0}, failed.", name)); } } return(action.result); } }
/// <summary> /// Launches a process and executes the given command with the args provided. /// </summary> /// <param name="cmd">Command to execute.</param> /// <param name="args">Arguments to pass.</param> /// <param name="startDirectory">Directory in which to start the process</param> /// <param name="env">Environment variables</param> /// <returns>The process on success, null on failure.</returns> public static Process LaunchProcess(String cmd, String args, string startDirectory, SMVEnvVar[] env, TextWriter logger, JobObject jobObject) { try { var psi = new ProcessStartInfo(cmd, args); psi.RedirectStandardError = true; psi.RedirectStandardInput = true; psi.RedirectStandardOutput = true; psi.UseShellExecute = false; // Set environment variables for this process. SetEnvironmentVariables(psi, env, logger); Log.LogInfo(String.Format(CultureInfo.InvariantCulture, "Launching {0} with arguments: {1} ", cmd, args), logger); if (!String.IsNullOrEmpty(startDirectory)) { psi.WorkingDirectory = startDirectory; Log.LogInfo("PATH: " + startDirectory, logger); } Process process = Process.Start(psi); if (process == null) { Log.LogFatalError(String.Format(CultureInfo.InvariantCulture, "Could not create process: {0} with args {1}, working directory: {2}", cmd, args, startDirectory)); } if (jobObject != null) { jobObject.AddProcess(process.Id); } return(process); } catch (Exception e) { Log.LogError(e.ToString()); Log.LogFatalError(String.Format(CultureInfo.InvariantCulture, "Could not start process: {0} with args {1}, working directory: {2}", cmd, args, startDirectory)); return(null); } }