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. 8package exec 9 10import ( 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 executed 20// and the reason it failed. 21type Error struct { 22 Name string 23 Err error 24} 25 26func (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. 31type 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") 108func 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 122func interfaceEqual(a, b interface{}) bool { 123 defer func() { 124 recover() 125 }() 126 return a == b 127} 128 129func (c *Cmd) envv() []string { 130 if c.Env != nil { 131 return c.Env 132 } 133 return os.Environ() 134} 135 136func (c *Cmd) argv() []string { 137 if len(c.Args) > 0 { 138 return c.Args 139 } 140 return []string{c.Path} 141} 142 143func (c *Cmd) stdin() (f *os.File, err error) { 144 if c.Stdin == nil { 145 f, err = os.Open(os.DevNull) 146 if err != nil { 147 return 148 } 149 c.closeAfterStart = append(c.closeAfterStart, f) 150 return 151 } 152 153 if f, ok := c.Stdin.(*os.File); ok { 154 return f, nil 155 } 156 157 pr, pw, err := os.Pipe() 158 if err != nil { 159 return 160 } 161 162 c.closeAfterStart = append(c.closeAfterStart, pr) 163 c.closeAfterWait = append(c.closeAfterWait, pw) 164 c.goroutine = append(c.goroutine, func() error { 165 _, err := io.Copy(pw, c.Stdin) 166 if err1 := pw.Close(); err == nil { 167 err = err1 168 } 169 return err 170 }) 171 return pr, nil 172} 173 174func (c *Cmd) stdout() (f *os.File, err error) { 175 return c.writerDescriptor(c.Stdout) 176} 177 178func (c *Cmd) stderr() (f *os.File, err error) { 179 if c.Stderr != nil && interfaceEqual(c.Stderr, c.Stdout) { 180 return c.childFiles[1], nil 181 } 182 return c.writerDescriptor(c.Stderr) 183} 184 185func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) { 186 if w == nil { 187 f, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0) 188 if err != nil { 189 return 190 } 191 c.closeAfterStart = append(c.closeAfterStart, f) 192 return 193 } 194 195 if f, ok := w.(*os.File); ok { 196 return f, nil 197 } 198 199 pr, pw, err := os.Pipe() 200 if err != nil { 201 return 202 } 203 204 c.closeAfterStart = append(c.closeAfterStart, pw) 205 c.closeAfterWait = append(c.closeAfterWait, pr) 206 c.goroutine = append(c.goroutine, func() error { 207 _, err := io.Copy(w, pr) 208 return err 209 }) 210 return pw, nil 211} 212 213func (c *Cmd) closeDescriptors(closers []io.Closer) { 214 for _, fd := range closers { 215 fd.Close() 216 } 217} 218 219// Run starts the specified command and waits for it to complete. 220// 221// The returned error is nil if the command runs, has no problems 222// copying stdin, stdout, and stderr, and exits with a zero exit 223// status. 224// 225// If the command fails to run or doesn't complete successfully, the 226// error is of type *ExitError. Other error types may be 227// returned for I/O problems. 228func (c *Cmd) Run() error { 229 if err := c.Start(); err != nil { 230 return err 231 } 232 return c.Wait() 233} 234 235// Start starts the specified command but does not wait for it to complete. 236func (c *Cmd) Start() error { 237 if c.err != nil { 238 c.closeDescriptors(c.closeAfterStart) 239 c.closeDescriptors(c.closeAfterWait) 240 return c.err 241 } 242 if c.Process != nil { 243 return errors.New("exec: already started") 244 } 245 246 type F func(*Cmd) (*os.File, error) 247 for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} { 248 fd, err := setupFd(c) 249 if err != nil { 250 c.closeDescriptors(c.closeAfterStart) 251 c.closeDescriptors(c.closeAfterWait) 252 return err 253 } 254 c.childFiles = append(c.childFiles, fd) 255 } 256 c.childFiles = append(c.childFiles, c.ExtraFiles...) 257 258 var err error 259 c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{ 260 Dir: c.Dir, 261 Files: c.childFiles, 262 Env: c.envv(), 263 Sys: c.SysProcAttr, 264 }) 265 if err != nil { 266 c.closeDescriptors(c.closeAfterStart) 267 c.closeDescriptors(c.closeAfterWait) 268 return err 269 } 270 271 c.closeDescriptors(c.closeAfterStart) 272 273 c.errch = make(chan error, len(c.goroutine)) 274 for _, fn := range c.goroutine { 275 go func(fn func() error) { 276 c.errch <- fn() 277 }(fn) 278 } 279 280 return nil 281} 282 283// An ExitError reports an unsuccessful exit by a command. 284type ExitError struct { 285 *os.ProcessState 286} 287 288func (e *ExitError) Error() string { 289 return e.ProcessState.String() 290} 291 292// Wait waits for the command to exit. 293// It must have been started by Start. 294// 295// The returned error is nil if the command runs, has no problems 296// copying stdin, stdout, and stderr, and exits with a zero exit 297// status. 298// 299// If the command fails to run or doesn't complete successfully, the 300// error is of type *ExitError. Other error types may be 301// returned for I/O problems. 302func (c *Cmd) Wait() error { 303 if c.Process == nil { 304 return errors.New("exec: not started") 305 } 306 if c.finished { 307 return errors.New("exec: Wait was already called") 308 } 309 c.finished = true 310 state, err := c.Process.Wait() 311 c.ProcessState = state 312 313 var copyError error 314 for _ = range c.goroutine { 315 if err := <-c.errch; err != nil && copyError == nil { 316 copyError = err 317 } 318 } 319 320 c.closeDescriptors(c.closeAfterWait) 321 322 if err != nil { 323 return err 324 } else if !state.Success() { 325 return &ExitError{state} 326 } 327 328 return copyError 329} 330 331// Output runs the command and returns its standard output. 332func (c *Cmd) Output() ([]byte, error) { 333 if c.Stdout != nil { 334 return nil, errors.New("exec: Stdout already set") 335 } 336 var b bytes.Buffer 337 c.Stdout = &b 338 err := c.Run() 339 return b.Bytes(), err 340} 341 342// CombinedOutput runs the command and returns its combined standard 343// output and standard error. 344func (c *Cmd) CombinedOutput() ([]byte, error) { 345 if c.Stdout != nil { 346 return nil, errors.New("exec: Stdout already set") 347 } 348 if c.Stderr != nil { 349 return nil, errors.New("exec: Stderr already set") 350 } 351 var b bytes.Buffer 352 c.Stdout = &b 353 c.Stderr = &b 354 err := c.Run() 355 return b.Bytes(), err 356} 357 358// StdinPipe returns a pipe that will be connected to the command's 359// standard input when the command starts. 360func (c *Cmd) StdinPipe() (io.WriteCloser, error) { 361 if c.Stdin != nil { 362 return nil, errors.New("exec: Stdin already set") 363 } 364 if c.Process != nil { 365 return nil, errors.New("exec: StdinPipe after process started") 366 } 367 pr, pw, err := os.Pipe() 368 if err != nil { 369 return nil, err 370 } 371 c.Stdin = pr 372 c.closeAfterStart = append(c.closeAfterStart, pr) 373 c.closeAfterWait = append(c.closeAfterWait, pw) 374 return pw, nil 375} 376 377// StdoutPipe returns a pipe that will be connected to the command's 378// standard output when the command starts. 379// The pipe will be closed automatically after Wait sees the command exit. 380func (c *Cmd) StdoutPipe() (io.ReadCloser, error) { 381 if c.Stdout != nil { 382 return nil, errors.New("exec: Stdout already set") 383 } 384 if c.Process != nil { 385 return nil, errors.New("exec: StdoutPipe after process started") 386 } 387 pr, pw, err := os.Pipe() 388 if err != nil { 389 return nil, err 390 } 391 c.Stdout = pw 392 c.closeAfterStart = append(c.closeAfterStart, pw) 393 c.closeAfterWait = append(c.closeAfterWait, pr) 394 return pr, nil 395} 396 397// StderrPipe returns a pipe that will be connected to the command's 398// standard error when the command starts. 399// The pipe will be closed automatically after Wait sees the command exit. 400func (c *Cmd) StderrPipe() (io.ReadCloser, error) { 401 if c.Stderr != nil { 402 return nil, errors.New("exec: Stderr already set") 403 } 404 if c.Process != nil { 405 return nil, errors.New("exec: StderrPipe after process started") 406 } 407 pr, pw, err := os.Pipe() 408 if err != nil { 409 return nil, err 410 } 411 c.Stderr = pw 412 c.closeAfterStart = append(c.closeAfterStart, pw) 413 c.closeAfterWait = append(c.closeAfterWait, pr) 414 return pr, nil 415} 416