The Go Programming Language

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

release.r60.3. Except as noted, this content is licensed under a Creative Commons Attribution 3.0 License.