// execute executes the binary pointed to by the given path using the given // arguments and options. If the wait flag is set, the function waits for the // completion of the binary and the timeout value can optionally specify for // how long should the function wait before timing out. func (e *executor) execute(wait bool, timeout time.Duration, opts opts, path string, args ...string) (*exec.Cmd, error) { e.increaseIndent() defer e.decreaseIndent() // Check if <path> identifies a binary in the PATH environment // variable of the opts.Env. if binary, err := lookpath.Look(opts.env, path); err == nil { // If so, make sure to execute this binary. This step // enables us to "shadow" binaries included in the // PATH environment variable of the host OS (which // would be otherwise used to lookup <path>). // // This mechanism is used instead of modifying the // PATH environment variable of the host OS as the // latter is not thread-safe. path = binary } command := exec.Command(path, args...) command.Dir = opts.dir command.Stdin = opts.stdin command.Stdout = opts.stdout command.Stderr = opts.stderr command.Env = envvar.MapToSlice(opts.env) if out := e.verboseStdout(opts); out != ioutil.Discard { args := []string{} for _, arg := range command.Args { // Quote any arguments that contain '"', ''', '|', or ' '. if strings.IndexAny(arg, "\"' |") != -1 { args = append(args, strconv.Quote(arg)) } else { args = append(args, arg) } } e.printf(out, strings.Replace(strings.Join(args, " "), "%", "%%", -1)) } var err error switch { case !wait: err = command.Start() e.printf(e.verboseStdout(opts), okOrFailed(err)) case timeout == 0: err = command.Run() e.printf(e.verboseStdout(opts), okOrFailed(err)) default: err = e.timedCommand(timeout, opts, command) // Verbose output handled in timedCommand. } return command, err }
func (r *runner) Map(mr *simplemr.MR, key string, val interface{}) error { mi := val.(*mapInput) output := &mapOutput{ key: key, mi: mi} jirix := mi.jirix path := os.Getenv("SHELL") if path == "" { path = "sh" } var wg sync.WaitGroup cmd := exec.Command(path, "-c", strings.Join(r.args, " ")) cmd.Env = envvar.MapToSlice(jirix.Env()) cmd.Dir = mi.ProjectState.Project.Path cmd.Stdin = mi.jirix.Stdin() var stdoutCloser, stderrCloser io.Closer if runpFlags.interactive { cmd.Stdout = jirix.Stdout() cmd.Stderr = jirix.Stderr() } else { var stdout io.Writer stderr := r.serializedWriter(jirix.Stderr()) var cleanup func() if runpFlags.collateOutput { // Write standard output to a file, stderr // is not collated. f, err := ioutil.TempFile("", mi.ProjectState.Project.Name+"-") if err != nil { return err } stdout = f output.outputFilename = f.Name() cleanup = func() { os.Remove(output.outputFilename) } // The child process will have exited by the // time this method returns so it's safe to close the file // here. defer f.Close() } else { stdout = r.serializedWriter(jirix.Stdout()) cleanup = func() {} } if !runpFlags.showNamePrefix && !runpFlags.showKeyPrefix { // write directly to stdout, stderr if there's no prefix cmd.Stdout = stdout cmd.Stderr = stderr } else { stdoutReader, stdoutWriter, err := os.Pipe() if err != nil { cleanup() return err } stderrReader, stderrWriter, err := os.Pipe() if err != nil { cleanup() stdoutReader.Close() stdoutWriter.Close() return err } cmd.Stdout = stdoutWriter cmd.Stderr = stderrWriter // Record the write end of the pipe so that it can be closed // after the child has exited, this ensures that all goroutines // will finish. stdoutCloser = stdoutWriter stderrCloser = stderrWriter prefix := key if runpFlags.showNamePrefix { prefix = mi.ProjectState.Project.Name } wg.Add(2) go func() { copyWithPrefix(prefix, stdout, stdoutReader); wg.Done() }() go func() { copyWithPrefix(prefix, stderr, stderrReader); wg.Done() }() } } if err := cmd.Start(); err != nil { mi.result = err } done := make(chan error) go func() { done <- cmd.Wait() }() select { case output.err = <-done: if output.err != nil && runpFlags.exitOnError { mr.Cancel() } case <-mr.CancelCh(): output.err = cmd.Process.Kill() } for _, closer := range []io.Closer{stdoutCloser, stderrCloser} { if closer != nil { closer.Close() } } wg.Wait() mr.MapOut(key, output) return nil }