// Execute starts the shell with its given parameters. func (sec *ShellExecCommand) Execute(pluginLogger plugin.Logger, pluginCom plugin.PluginCommunicator, conf *model.TaskConfig, stop chan bool) error { pluginLogger.LogExecution(slogger.DEBUG, "Preparing script...") logWriterInfo := pluginLogger.GetTaskLogWriter(slogger.INFO) logWriterErr := pluginLogger.GetTaskLogWriter(slogger.ERROR) if sec.SystemLog { logWriterInfo = pluginLogger.GetSystemLogWriter(slogger.INFO) logWriterErr = pluginLogger.GetSystemLogWriter(slogger.ERROR) } outBufferWriter := util.NewLineBufferingWriter(logWriterInfo) errorBufferWriter := util.NewLineBufferingWriter(logWriterErr) defer outBufferWriter.Flush() defer errorBufferWriter.Flush() localCmd := &command.LocalCommand{ CmdString: sec.Script, Stdout: outBufferWriter, Stderr: errorBufferWriter, ScriptMode: true, } if sec.WorkingDir != "" { localCmd.WorkingDirectory = filepath.Join(conf.WorkDir, sec.WorkingDir) } else { localCmd.WorkingDirectory = conf.WorkDir } if sec.Shell != "" { localCmd.Shell = sec.Shell } err := localCmd.PrepToRun(conf.Expansions) if err != nil { return fmt.Errorf("Failed to apply expansions: %v", err) } if sec.Silent { pluginLogger.LogExecution(slogger.INFO, "Executing script with %s (source hidden)...", localCmd.Shell) } else { pluginLogger.LogExecution(slogger.INFO, "Executing script with %s: %v", localCmd.Shell, localCmd.CmdString) } doneStatus := make(chan error) go func() { var err error env := os.Environ() env = append(env, fmt.Sprintf("EVR_TASK_ID=%v", conf.Task.Id)) env = append(env, fmt.Sprintf("EVR_AGENT_PID=%v", os.Getpid())) localCmd.Environment = env err = localCmd.Start() if err == nil { pluginLogger.LogSystem(slogger.DEBUG, "spawned shell process with pid %v", localCmd.Cmd.Process.Pid) // Call the platform's process-tracking function. On some OSes this will be a noop, // on others this may need to do some additional work to track the process so that // it can be cleaned up later. projTask := conf.Project.FindProjectTask(conf.Task.DisplayName) if conf.Project.DisableCleanup || (projTask != nil && projTask.DisableCleanup) { pluginLogger.LogSystem(slogger.DEBUG, "skipping process tracking for pid %v", localCmd.Cmd.Process.Pid) } else { trackProcess(conf.Task.Id, localCmd.Cmd.Process.Pid, pluginLogger) } if !sec.Background { err = localCmd.Cmd.Wait() } } else { pluginLogger.LogSystem(slogger.DEBUG, "error spawning shell process: %v", err) } doneStatus <- err }() defer pluginLogger.Flush() select { case err = <-doneStatus: if err != nil { if sec.ContinueOnError { pluginLogger.LogExecution(slogger.INFO, "(ignoring) Script finished with error: %v", err) return nil } else { pluginLogger.LogExecution(slogger.INFO, "Script finished with error: %v", err) return err } } else { pluginLogger.LogExecution(slogger.INFO, "Script execution complete.") } case <-stop: pluginLogger.LogExecution(slogger.INFO, "Got kill signal") // need to check command has started if localCmd.Cmd != nil { pluginLogger.LogExecution(slogger.INFO, "Stopping process: %v", localCmd.Cmd.Process.Pid) // try and stop the process if err := localCmd.Stop(); err != nil { pluginLogger.LogExecution(slogger.ERROR, "Error occurred stopping process: %v", err) } } return fmt.Errorf("Shell command interrupted.") } return nil }