diff options
Diffstat (limited to 'src/pkg/os/exec/exec.go')
-rw-r--r-- | src/pkg/os/exec/exec.go | 493 |
1 files changed, 0 insertions, 493 deletions
diff --git a/src/pkg/os/exec/exec.go b/src/pkg/os/exec/exec.go deleted file mode 100644 index a70ed0d20..000000000 --- a/src/pkg/os/exec/exec.go +++ /dev/null @@ -1,493 +0,0 @@ -// 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. - -// Package exec runs external commands. It wraps os.StartProcess to make it -// easier to remap stdin and stdout, connect I/O with pipes, and do other -// adjustments. -package exec - -import ( - "bytes" - "errors" - "io" - "os" - "path/filepath" - "runtime" - "strconv" - "strings" - "sync" - "syscall" -) - -// Error records the name of a binary that failed to be executed -// and the reason it failed. -type Error struct { - Name string - Err error -} - -func (e *Error) Error() string { - return "exec: " + strconv.Quote(e.Name) + ": " + e.Err.Error() -} - -// Cmd represents an external command being prepared or run. -type Cmd struct { - // Path is the path of the command to run. - // - // This is the only field that must be set to a non-zero - // value. If Path is relative, it is evaluated relative - // to Dir. - Path string - - // Args holds command line arguments, including the command as Args[0]. - // If the Args field is empty or nil, Run uses {Path}. - // - // In typical use, both Path and Args are set by calling Command. - Args []string - - // Env specifies the environment of the process. - // If Env is nil, Run uses the current process's environment. - Env []string - - // Dir specifies the working directory of the command. - // If Dir is the empty string, Run runs the command in the - // calling process's current directory. - Dir string - - // Stdin specifies the process's standard input. If Stdin is - // nil, the process reads from the null device (os.DevNull). - Stdin io.Reader - - // Stdout and Stderr specify the process's standard output and error. - // - // If either is nil, Run connects the corresponding file descriptor - // to the null device (os.DevNull). - // - // If Stdout and Stderr are the same writer, at most one - // goroutine at a time will call Write. - Stdout io.Writer - Stderr io.Writer - - // ExtraFiles specifies additional open files to be inherited by the - // new process. It does not include standard input, standard output, or - // standard error. If non-nil, entry i becomes file descriptor 3+i. - // - // BUG: on OS X 10.6, child processes may sometimes inherit unwanted fds. - // http://golang.org/issue/2603 - ExtraFiles []*os.File - - // SysProcAttr holds optional, operating system-specific attributes. - // Run passes it to os.StartProcess as the os.ProcAttr's Sys field. - SysProcAttr *syscall.SysProcAttr - - // Process is the underlying process, once started. - Process *os.Process - - // ProcessState contains information about an exited process, - // available after a call to Wait or Run. - ProcessState *os.ProcessState - - lookPathErr error // LookPath error, if any. - finished bool // when Wait was called - childFiles []*os.File - closeAfterStart []io.Closer - closeAfterWait []io.Closer - goroutine []func() error - errch chan error // one send per goroutine -} - -// Command returns the Cmd struct to execute the named program with -// the given arguments. -// -// It sets only the Path and Args in the returned structure. -// -// If name contains no path separators, Command uses LookPath to -// resolve the path to a complete name if possible. Otherwise it uses -// name directly. -// -// The returned Cmd's Args field is constructed from the command name -// followed by the elements of arg, so arg should not include the -// command name itself. For example, Command("echo", "hello") -func Command(name string, arg ...string) *Cmd { - cmd := &Cmd{ - Path: name, - Args: append([]string{name}, arg...), - } - if filepath.Base(name) == name { - if lp, err := LookPath(name); err != nil { - cmd.lookPathErr = err - } else { - cmd.Path = lp - } - } - return cmd -} - -// interfaceEqual protects against panics from doing equality tests on -// two interfaces with non-comparable underlying types. -func interfaceEqual(a, b interface{}) bool { - defer func() { - recover() - }() - return a == b -} - -func (c *Cmd) envv() []string { - if c.Env != nil { - return c.Env - } - return os.Environ() -} - -func (c *Cmd) argv() []string { - if len(c.Args) > 0 { - return c.Args - } - return []string{c.Path} -} - -func (c *Cmd) stdin() (f *os.File, err error) { - if c.Stdin == nil { - f, err = os.Open(os.DevNull) - if err != nil { - return - } - c.closeAfterStart = append(c.closeAfterStart, f) - return - } - - if f, ok := c.Stdin.(*os.File); ok { - return f, nil - } - - pr, pw, err := os.Pipe() - if err != nil { - return - } - - c.closeAfterStart = append(c.closeAfterStart, pr) - c.closeAfterWait = append(c.closeAfterWait, pw) - c.goroutine = append(c.goroutine, func() error { - _, err := io.Copy(pw, c.Stdin) - if err1 := pw.Close(); err == nil { - err = err1 - } - return err - }) - return pr, nil -} - -func (c *Cmd) stdout() (f *os.File, err error) { - return c.writerDescriptor(c.Stdout) -} - -func (c *Cmd) stderr() (f *os.File, err error) { - if c.Stderr != nil && interfaceEqual(c.Stderr, c.Stdout) { - return c.childFiles[1], nil - } - return c.writerDescriptor(c.Stderr) -} - -func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) { - if w == nil { - f, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0) - if err != nil { - return - } - c.closeAfterStart = append(c.closeAfterStart, f) - return - } - - if f, ok := w.(*os.File); ok { - return f, nil - } - - pr, pw, err := os.Pipe() - if err != nil { - return - } - - c.closeAfterStart = append(c.closeAfterStart, pw) - c.closeAfterWait = append(c.closeAfterWait, pr) - c.goroutine = append(c.goroutine, func() error { - _, err := io.Copy(w, pr) - return err - }) - return pw, nil -} - -func (c *Cmd) closeDescriptors(closers []io.Closer) { - for _, fd := range closers { - fd.Close() - } -} - -// Run starts the specified command and waits for it to complete. -// -// The returned error is nil if the command runs, has no problems -// copying stdin, stdout, and stderr, and exits with a zero exit -// status. -// -// If the command fails to run or doesn't complete successfully, the -// error is of type *ExitError. Other error types may be -// returned for I/O problems. -func (c *Cmd) Run() error { - if err := c.Start(); err != nil { - return err - } - return c.Wait() -} - -// lookExtensions finds windows executable by its dir and path. -// It uses LookPath to try appropriate extensions. -// lookExtensions does not search PATH, instead it converts `prog` into `.\prog`. -func lookExtensions(path, dir string) (string, error) { - if filepath.Base(path) == path { - path = filepath.Join(".", path) - } - if dir == "" { - return LookPath(path) - } - if filepath.VolumeName(path) != "" { - return LookPath(path) - } - if len(path) > 1 && os.IsPathSeparator(path[0]) { - return LookPath(path) - } - dirandpath := filepath.Join(dir, path) - // We assume that LookPath will only add file extension. - lp, err := LookPath(dirandpath) - if err != nil { - return "", err - } - ext := strings.TrimPrefix(lp, dirandpath) - return path + ext, nil -} - -// Start starts the specified command but does not wait for it to complete. -// -// The Wait method will return the exit code and release associated resources -// once the command exits. -func (c *Cmd) Start() error { - if c.lookPathErr != nil { - c.closeDescriptors(c.closeAfterStart) - c.closeDescriptors(c.closeAfterWait) - return c.lookPathErr - } - if runtime.GOOS == "windows" { - lp, err := lookExtensions(c.Path, c.Dir) - if err != nil { - c.closeDescriptors(c.closeAfterStart) - c.closeDescriptors(c.closeAfterWait) - return err - } - c.Path = lp - } - if c.Process != nil { - return errors.New("exec: already started") - } - - type F func(*Cmd) (*os.File, error) - for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} { - fd, err := setupFd(c) - if err != nil { - c.closeDescriptors(c.closeAfterStart) - c.closeDescriptors(c.closeAfterWait) - return err - } - c.childFiles = append(c.childFiles, fd) - } - c.childFiles = append(c.childFiles, c.ExtraFiles...) - - var err error - c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{ - Dir: c.Dir, - Files: c.childFiles, - Env: c.envv(), - Sys: c.SysProcAttr, - }) - if err != nil { - c.closeDescriptors(c.closeAfterStart) - c.closeDescriptors(c.closeAfterWait) - return err - } - - c.closeDescriptors(c.closeAfterStart) - - c.errch = make(chan error, len(c.goroutine)) - for _, fn := range c.goroutine { - go func(fn func() error) { - c.errch <- fn() - }(fn) - } - - return nil -} - -// An ExitError reports an unsuccessful exit by a command. -type ExitError struct { - *os.ProcessState -} - -func (e *ExitError) Error() string { - return e.ProcessState.String() -} - -// Wait waits for the command to exit. -// It must have been started by Start. -// -// The returned error is nil if the command runs, has no problems -// copying stdin, stdout, and stderr, and exits with a zero exit -// status. -// -// If the command fails to run or doesn't complete successfully, the -// error is of type *ExitError. Other error types may be -// returned for I/O problems. -// -// Wait releases any resources associated with the Cmd. -func (c *Cmd) Wait() error { - if c.Process == nil { - return errors.New("exec: not started") - } - if c.finished { - return errors.New("exec: Wait was already called") - } - c.finished = true - state, err := c.Process.Wait() - c.ProcessState = state - - var copyError error - for _ = range c.goroutine { - if err := <-c.errch; err != nil && copyError == nil { - copyError = err - } - } - - c.closeDescriptors(c.closeAfterWait) - - if err != nil { - return err - } else if !state.Success() { - return &ExitError{state} - } - - return copyError -} - -// Output runs the command and returns its standard output. -func (c *Cmd) Output() ([]byte, error) { - if c.Stdout != nil { - return nil, errors.New("exec: Stdout already set") - } - var b bytes.Buffer - c.Stdout = &b - err := c.Run() - return b.Bytes(), err -} - -// CombinedOutput runs the command and returns its combined standard -// output and standard error. -func (c *Cmd) CombinedOutput() ([]byte, error) { - if c.Stdout != nil { - return nil, errors.New("exec: Stdout already set") - } - if c.Stderr != nil { - return nil, errors.New("exec: Stderr already set") - } - var b bytes.Buffer - c.Stdout = &b - c.Stderr = &b - err := c.Run() - return b.Bytes(), err -} - -// StdinPipe returns a pipe that will be connected to the command's -// standard input when the command starts. -// The pipe will be closed automatically after Wait sees the command exit. -// A caller need only call Close to force the pipe to close sooner. -// For example, if the command being run will not exit until standard input -// is closed, the caller must close the pipe. -func (c *Cmd) StdinPipe() (io.WriteCloser, error) { - if c.Stdin != nil { - return nil, errors.New("exec: Stdin already set") - } - if c.Process != nil { - return nil, errors.New("exec: StdinPipe after process started") - } - pr, pw, err := os.Pipe() - if err != nil { - return nil, err - } - c.Stdin = pr - c.closeAfterStart = append(c.closeAfterStart, pr) - wc := &closeOnce{File: pw} - c.closeAfterWait = append(c.closeAfterWait, wc) - return wc, nil -} - -type closeOnce struct { - *os.File - - once sync.Once - err error -} - -func (c *closeOnce) Close() error { - c.once.Do(c.close) - return c.err -} - -func (c *closeOnce) close() { - c.err = c.File.Close() -} - -// StdoutPipe returns a pipe that will be connected to the command's -// standard output when the command starts. -// -// Wait will close the pipe after seeing the command exit, so most callers -// need not close the pipe themselves; however, an implication is that -// it is incorrect to call Wait before all reads from the pipe have completed. -// For the same reason, it is incorrect to call Run when using StdoutPipe. -// See the example for idiomatic usage. -func (c *Cmd) StdoutPipe() (io.ReadCloser, error) { - if c.Stdout != nil { - return nil, errors.New("exec: Stdout already set") - } - if c.Process != nil { - return nil, errors.New("exec: StdoutPipe after process started") - } - pr, pw, err := os.Pipe() - if err != nil { - return nil, err - } - c.Stdout = pw - c.closeAfterStart = append(c.closeAfterStart, pw) - c.closeAfterWait = append(c.closeAfterWait, pr) - return pr, nil -} - -// StderrPipe returns a pipe that will be connected to the command's -// standard error when the command starts. -// -// Wait will close the pipe after seeing the command exit, so most callers -// need not close the pipe themselves; however, an implication is that -// it is incorrect to call Wait before all reads from the pipe have completed. -// For the same reason, it is incorrect to use Run when using StderrPipe. -// See the StdoutPipe example for idiomatic usage. -func (c *Cmd) StderrPipe() (io.ReadCloser, error) { - if c.Stderr != nil { - return nil, errors.New("exec: Stderr already set") - } - if c.Process != nil { - return nil, errors.New("exec: StderrPipe after process started") - } - pr, pw, err := os.Pipe() - if err != nil { - return nil, err - } - c.Stderr = pw - c.closeAfterStart = append(c.closeAfterStart, pw) - c.closeAfterWait = append(c.closeAfterWait, pr) - return pr, nil -} |