src/pkg/os/exec/exec.go - The Go Programming Language

Golang

Source file src/pkg/os/exec/exec.go

     1	// Copyright 2009 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	// Package exec runs external commands. It wraps os.StartProcess to make it
     6	// easier to remap stdin and stdout, connect I/O with pipes, and do other
     7	// adjustments.
     8	package exec
     9	
    10	import (
    11		"bytes"
    12		"errors"
    13		"io"
    14		"os"
    15		"strconv"
    16		"syscall"
    17	)
    18	
    19	// Error records the name of a binary that failed to be be executed
    20	// and the reason it failed.
    21	type Error struct {
    22		Name string
    23		Err  error
    24	}
    25	
    26	func (e *Error) Error() string {
    27		return "exec: " + strconv.Quote(e.Name) + ": " + e.Err.Error()
    28	}
    29	
    30	// Cmd represents an external command being prepared or run.
    31	type Cmd struct {
    32		// Path is the path of the command to run.
    33		//
    34		// This is the only field that must be set to a non-zero
    35		// value.
    36		Path string
    37	
    38		// Args holds command line arguments, including the command as Args[0].
    39		// If the Args field is empty or nil, Run uses {Path}.
    40		// 
    41		// In typical use, both Path and Args are set by calling Command.
    42		Args []string
    43	
    44		// Env specifies the environment of the process.
    45		// If Env is nil, Run uses the current process's environment.
    46		Env []string
    47	
    48		// Dir specifies the working directory of the command.
    49		// If Dir is the empty string, Run runs the command in the
    50		// calling process's current directory.
    51		Dir string
    52	
    53		// Stdin specifies the process's standard input. If Stdin is
    54		// nil, the process reads from the null device (os.DevNull).
    55		Stdin io.Reader
    56	
    57		// Stdout and Stderr specify the process's standard output and error.
    58		//
    59		// If either is nil, Run connects the corresponding file descriptor
    60		// to the null device (os.DevNull).
    61		//
    62		// If Stdout and Stderr are the same writer, at most one
    63		// goroutine at a time will call Write.
    64		Stdout io.Writer
    65		Stderr io.Writer
    66	
    67		// ExtraFiles specifies additional open files to be inherited by the
    68		// new process. It does not include standard input, standard output, or
    69		// standard error. If non-nil, entry i becomes file descriptor 3+i.
    70		//
    71		// BUG: on OS X 10.6, child processes may sometimes inherit unwanted fds.
    72		// http://golang.org/issue/2603
    73		ExtraFiles []*os.File
    74	
    75		// SysProcAttr holds optional, operating system-specific attributes.
    76		// Run passes it to os.StartProcess as the os.ProcAttr's Sys field.
    77		SysProcAttr *syscall.SysProcAttr
    78	
    79		// Process is the underlying process, once started.
    80		Process *os.Process
    81	
    82		// ProcessState contains information about an exited process,
    83		// available after a call to Wait or Run.
    84		ProcessState *os.ProcessState
    85	
    86		err             error // last error (from LookPath, stdin, stdout, stderr)
    87		finished        bool  // when Wait was called
    88		childFiles      []*os.File
    89		closeAfterStart []io.Closer
    90		closeAfterWait  []io.Closer
    91		goroutine       []func() error
    92		errch           chan error // one send per goroutine
    93	}
    94	
    95	// Command returns the Cmd struct to execute the named program with
    96	// the given arguments.
    97	//
    98	// It sets Path and Args in the returned structure and zeroes the
    99	// other fields.
   100	//
   101	// If name contains no path separators, Command uses LookPath to
   102	// resolve the path to a complete name if possible. Otherwise it uses
   103	// name directly.
   104	//
   105	// The returned Cmd's Args field is constructed from the command name
   106	// followed by the elements of arg, so arg should not include the
   107	// command name itself. For example, Command("echo", "hello")
   108	func Command(name string, arg ...string) *Cmd {
   109		aname, err := LookPath(name)
   110		if err != nil {
   111			aname = name
   112		}
   113		return &Cmd{
   114			Path: aname,
   115			Args: append([]string{name}, arg...),
   116			err:  err,
   117		}
   118	}
   119	
   120	// interfaceEqual protects against panics from doing equality tests on
   121	// two interfaces with non-comparable underlying types
   122	func interfaceEqual(a, b interface{}) bool {
   123		defer func() {
   124			recover()
   125		}()
   126		return a == b
   127	}
   128	
   129	func (c *Cmd) envv() []string {
   130		if c.Env != nil {
   131			return c.Env
   132		}
   133		return os.Environ()
   134	}
   135	
   136	func (c *Cmd) argv() []string {
   137		if len(c.Args) > 0 {
   138			return c.Args
   139		}
   140		return []string{c.Path}
   141	}
   142	
   143	func (c *Cmd) stdin() (f *os.File, err error) {
   144		if c.Stdin == nil {
   145			f, err = os.Open(os.DevNull)
   146			c.closeAfterStart = append(c.closeAfterStart, f)
   147			return
   148		}
   149	
   150		if f, ok := c.Stdin.(*os.File); ok {
   151			return f, nil
   152		}
   153	
   154		pr, pw, err := os.Pipe()
   155		if err != nil {
   156			return
   157		}
   158	
   159		c.closeAfterStart = append(c.closeAfterStart, pr)
   160		c.closeAfterWait = append(c.closeAfterWait, pw)
   161		c.goroutine = append(c.goroutine, func() error {
   162			_, err := io.Copy(pw, c.Stdin)
   163			if err1 := pw.Close(); err == nil {
   164				err = err1
   165			}
   166			return err
   167		})
   168		return pr, nil
   169	}
   170	
   171	func (c *Cmd) stdout() (f *os.File, err error) {
   172		return c.writerDescriptor(c.Stdout)
   173	}
   174	
   175	func (c *Cmd) stderr() (f *os.File, err error) {
   176		if c.Stderr != nil && interfaceEqual(c.Stderr, c.Stdout) {
   177			return c.childFiles[1], nil
   178		}
   179		return c.writerDescriptor(c.Stderr)
   180	}
   181	
   182	func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) {
   183		if w == nil {
   184			f, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0)
   185			c.closeAfterStart = append(c.closeAfterStart, f)
   186			return
   187		}
   188	
   189		if f, ok := w.(*os.File); ok {
   190			return f, nil
   191		}
   192	
   193		pr, pw, err := os.Pipe()
   194		if err != nil {
   195			return
   196		}
   197	
   198		c.closeAfterStart = append(c.closeAfterStart, pw)
   199		c.closeAfterWait = append(c.closeAfterWait, pr)
   200		c.goroutine = append(c.goroutine, func() error {
   201			_, err := io.Copy(w, pr)
   202			return err
   203		})
   204		return pw, nil
   205	}
   206	
   207	// Run starts the specified command and waits for it to complete.
   208	//
   209	// The returned error is nil if the command runs, has no problems
   210	// copying stdin, stdout, and stderr, and exits with a zero exit
   211	// status.
   212	//
   213	// If the command fails to run or doesn't complete successfully, the
   214	// error is of type *ExitError. Other error types may be
   215	// returned for I/O problems.
   216	func (c *Cmd) Run() error {
   217		if err := c.Start(); err != nil {
   218			return err
   219		}
   220		return c.Wait()
   221	}
   222	
   223	// Start starts the specified command but does not wait for it to complete.
   224	func (c *Cmd) Start() error {
   225		if c.err != nil {
   226			return c.err
   227		}
   228		if c.Process != nil {
   229			return errors.New("exec: already started")
   230		}
   231	
   232		type F func(*Cmd) (*os.File, error)
   233		for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} {
   234			fd, err := setupFd(c)
   235			if err != nil {
   236				return err
   237			}
   238			c.childFiles = append(c.childFiles, fd)
   239		}
   240		c.childFiles = append(c.childFiles, c.ExtraFiles...)
   241	
   242		var err error
   243		c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{
   244			Dir:   c.Dir,
   245			Files: c.childFiles,
   246			Env:   c.envv(),
   247			Sys:   c.SysProcAttr,
   248		})
   249		if err != nil {
   250			return err
   251		}
   252	
   253		for _, fd := range c.closeAfterStart {
   254			fd.Close()
   255		}
   256	
   257		c.errch = make(chan error, len(c.goroutine))
   258		for _, fn := range c.goroutine {
   259			go func(fn func() error) {
   260				c.errch <- fn()
   261			}(fn)
   262		}
   263	
   264		return nil
   265	}
   266	
   267	// An ExitError reports an unsuccessful exit by a command.
   268	type ExitError struct {
   269		*os.ProcessState
   270	}
   271	
   272	func (e *ExitError) Error() string {
   273		return e.ProcessState.String()
   274	}
   275	
   276	// Wait waits for the command to exit.
   277	// It must have been started by Start.
   278	//
   279	// The returned error is nil if the command runs, has no problems
   280	// copying stdin, stdout, and stderr, and exits with a zero exit
   281	// status.
   282	//
   283	// If the command fails to run or doesn't complete successfully, the
   284	// error is of type *ExitError. Other error types may be
   285	// returned for I/O problems.
   286	func (c *Cmd) Wait() error {
   287		if c.Process == nil {
   288			return errors.New("exec: not started")
   289		}
   290		if c.finished {
   291			return errors.New("exec: Wait was already called")
   292		}
   293		c.finished = true
   294		state, err := c.Process.Wait()
   295		c.ProcessState = state
   296	
   297		var copyError error
   298		for _ = range c.goroutine {
   299			if err := <-c.errch; err != nil && copyError == nil {
   300				copyError = err
   301			}
   302		}
   303	
   304		for _, fd := range c.closeAfterWait {
   305			fd.Close()
   306		}
   307	
   308		if err != nil {
   309			return err
   310		} else if !state.Success() {
   311			return &ExitError{state}
   312		}
   313	
   314		return copyError
   315	}
   316	
   317	// Output runs the command and returns its standard output.
   318	func (c *Cmd) Output() ([]byte, error) {
   319		if c.Stdout != nil {
   320			return nil, errors.New("exec: Stdout already set")
   321		}
   322		var b bytes.Buffer
   323		c.Stdout = &b
   324		err := c.Run()
   325		return b.Bytes(), err
   326	}
   327	
   328	// CombinedOutput runs the command and returns its combined standard
   329	// output and standard error.
   330	func (c *Cmd) CombinedOutput() ([]byte, error) {
   331		if c.Stdout != nil {
   332			return nil, errors.New("exec: Stdout already set")
   333		}
   334		if c.Stderr != nil {
   335			return nil, errors.New("exec: Stderr already set")
   336		}
   337		var b bytes.Buffer
   338		c.Stdout = &b
   339		c.Stderr = &b
   340		err := c.Run()
   341		return b.Bytes(), err
   342	}
   343	
   344	// StdinPipe returns a pipe that will be connected to the command's
   345	// standard input when the command starts.
   346	func (c *Cmd) StdinPipe() (io.WriteCloser, error) {
   347		if c.Stdin != nil {
   348			return nil, errors.New("exec: Stdin already set")
   349		}
   350		if c.Process != nil {
   351			return nil, errors.New("exec: StdinPipe after process started")
   352		}
   353		pr, pw, err := os.Pipe()
   354		if err != nil {
   355			return nil, err
   356		}
   357		c.Stdin = pr
   358		c.closeAfterStart = append(c.closeAfterStart, pr)
   359		c.closeAfterWait = append(c.closeAfterWait, pw)
   360		return pw, nil
   361	}
   362	
   363	// StdoutPipe returns a pipe that will be connected to the command's
   364	// standard output when the command starts.
   365	// The pipe will be closed automatically after Wait sees the command exit.
   366	func (c *Cmd) StdoutPipe() (io.ReadCloser, error) {
   367		if c.Stdout != nil {
   368			return nil, errors.New("exec: Stdout already set")
   369		}
   370		if c.Process != nil {
   371			return nil, errors.New("exec: StdoutPipe after process started")
   372		}
   373		pr, pw, err := os.Pipe()
   374		if err != nil {
   375			return nil, err
   376		}
   377		c.Stdout = pw
   378		c.closeAfterStart = append(c.closeAfterStart, pw)
   379		c.closeAfterWait = append(c.closeAfterWait, pr)
   380		return pr, nil
   381	}
   382	
   383	// StderrPipe returns a pipe that will be connected to the command's
   384	// standard error when the command starts.
   385	// The pipe will be closed automatically after Wait sees the command exit.
   386	func (c *Cmd) StderrPipe() (io.ReadCloser, error) {
   387		if c.Stderr != nil {
   388			return nil, errors.New("exec: Stderr already set")
   389		}
   390		if c.Process != nil {
   391			return nil, errors.New("exec: StderrPipe after process started")
   392		}
   393		pr, pw, err := os.Pipe()
   394		if err != nil {
   395			return nil, err
   396		}
   397		c.Stderr = pw
   398		c.closeAfterStart = append(c.closeAfterStart, pw)
   399		c.closeAfterWait = append(c.closeAfterWait, pr)
   400		return pr, nil
   401	}