// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // The exec package runs external commands. package exec import ( "os" "strconv" ) // Arguments to Run. const ( DevNull = iota PassThrough Pipe MergeWithStdout ) // A Cmd represents a running command. // Stdin, Stdout, and Stderr are Files representing pipes // connected to the running command's standard input, output, and error, // or else nil, depending on the arguments to Run. // Process represents the underlying operating system process. type Cmd struct { Stdin *os.File Stdout *os.File Stderr *os.File Process *os.Process } // PathError records the name of a binary that was not // found on the current $PATH. type PathError struct { Name string } func (e *PathError) String() string { return "command " + strconv.Quote(e.Name) + " not found in $PATH" } // Given mode (DevNull, etc), return file for child // and file to record in Cmd structure. func modeToFiles(mode, fd int) (*os.File, *os.File, os.Error) { switch mode { case DevNull: rw := os.O_WRONLY if fd == 0 { rw = os.O_RDONLY } f, err := os.Open(os.DevNull, rw, 0) return f, nil, err case PassThrough: switch fd { case 0: return os.Stdin, nil, nil case 1: return os.Stdout, nil, nil case 2: return os.Stderr, nil, nil } case Pipe: r, w, err := os.Pipe() if err != nil { return nil, nil, err } if fd == 0 { return r, w, nil } return w, r, nil } return nil, nil, os.EINVAL } // Run starts the named binary running with // arguments argv and environment envv. // If the dir argument is not empty, the child changes // into the directory before executing the binary. // It returns a pointer to a new Cmd representing // the command or an error. // // The arguments stdin, stdout, and stderr // specify how to handle standard input, output, and error. // The choices are DevNull (connect to /dev/null), // PassThrough (connect to the current process's standard stream), // Pipe (connect to an operating system pipe), and // MergeWithStdout (only for standard error; use the same // file descriptor as was used for standard output). // If an argument is Pipe, then the corresponding field (Stdin, Stdout, Stderr) // of the returned Cmd is the other end of the pipe. // Otherwise the field in Cmd is nil. func Run(name string, argv, envv []string, dir string, stdin, stdout, stderr int) (c *Cmd, err os.Error) { c = new(Cmd) var fd [3]*os.File if fd[0], c.Stdin, err = modeToFiles(stdin, 0); err != nil { goto Error } if fd[1], c.Stdout, err = modeToFiles(stdout, 1); err != nil { goto Error } if stderr == MergeWithStdout { fd[2] = fd[1] } else if fd[2], c.Stderr, err = modeToFiles(stderr, 2); err != nil { goto Error } // Run command. c.Process, err = os.StartProcess(name, argv, &os.ProcAttr{Dir: dir, Files: fd[:], Env: envv}) if err != nil { goto Error } if fd[0] != os.Stdin { fd[0].Close() } if fd[1] != os.Stdout { fd[1].Close() } if fd[2] != os.Stderr && fd[2] != fd[1] { fd[2].Close() } return c, nil Error: if fd[0] != os.Stdin && fd[0] != nil { fd[0].Close() } if fd[1] != os.Stdout && fd[1] != nil { fd[1].Close() } if fd[2] != os.Stderr && fd[2] != nil && fd[2] != fd[1] { fd[2].Close() } if c.Stdin != nil { c.Stdin.Close() } if c.Stdout != nil { c.Stdout.Close() } if c.Stderr != nil { c.Stderr.Close() } if c.Process != nil { c.Process.Release() } return nil, err } // Wait waits for the running command c, // returning the Waitmsg returned when the process exits. // The options are passed to the process's Wait method. // Setting options to 0 waits for c to exit; // other options cause Wait to return for other // process events; see package os for details. func (c *Cmd) Wait(options int) (*os.Waitmsg, os.Error) { if c.Process == nil { return nil, os.ErrorString("exec: invalid use of Cmd.Wait") } w, err := c.Process.Wait(options) if w != nil && (w.Exited() || w.Signaled()) { c.Process.Release() c.Process = nil } return w, err } // Close waits for the running command c to exit, // if it hasn't already, and then closes the non-nil file descriptors // c.Stdin, c.Stdout, and c.Stderr. func (c *Cmd) Close() os.Error { if c.Process != nil { // Loop on interrupt, but // ignore other errors -- maybe // caller has already waited for pid. _, err := c.Wait(0) for err == os.EINTR { _, err = c.Wait(0) } } // Close the FDs that are still open. var err os.Error if c.Stdin != nil && c.Stdin.Fd() >= 0 { if err1 := c.Stdin.Close(); err1 != nil { err = err1 } } if c.Stdout != nil && c.Stdout.Fd() >= 0 { if err1 := c.Stdout.Close(); err1 != nil && err != nil { err = err1 } } if c.Stderr != nil && c.Stderr != c.Stdout && c.Stderr.Fd() >= 0 { if err1 := c.Stderr.Close(); err1 != nil && err != nil { err = err1 } } return err }