v.io/jiri@v0.0.0-20160715023856-abfb8b131290/runutil/sequence.go (about)

     1  // Copyright 2015 The Vanadium 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 runutil
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"runtime"
    16  	"sync"
    17  	"syscall"
    18  	"time"
    19  
    20  	"v.io/x/lib/cmdline"
    21  	"v.io/x/lib/envvar"
    22  )
    23  
    24  // Sequence provides for convenient chaining of multiple calls to its
    25  // methods to avoid repeated tests for error returns. The usage is:
    26  //
    27  // err := s.Run("echo", "a").Run("echo", "b").Done()
    28  //
    29  // The first method to encounter an error short circuits any following
    30  // methods and the result of that first error is returned by the
    31  // Done method or any of the other 'terminating methods' (see below).
    32  // Sequence is not thread safe. It also good practice to use a new
    33  // instance of a Sequence in defer's.
    34  //
    35  // Unless directed to specific stdout and stderr io.Writers using Capture(),
    36  // the stdout and stderr output from the command is discarded, unless an error
    37  // is encountered, in which case the output from the command that failed (both
    38  // stdout and stderr) is written to the stderr io.Writer specified via
    39  // NewSequence. In addition, in verbose mode, command execution logging
    40  // is written to the stdout and stderr io.Writers configured via NewSequence,
    41  // and never to the stdout specified via Capture if the command succeeded.
    42  //
    43  // Modifier methods are provided that influence the behaviour of the
    44  // next invocation of the Run method to set timeouts (Timed), to
    45  // capture output (Capture), input (Read) and to set the environment (Env).
    46  // For example, the following will result in a timeout error.
    47  //
    48  // err := s.Timeout(time.Second).Run("sleep","10").Done()
    49  // err := s.Timeout(time.Second).Last("sleep","10")
    50  //
    51  // A sequence of commands must be terminated with a call to a 'terminating'
    52  // method. The simplest are the Done or Last methods used in the examples above,
    53  // but there are other methods which typically return results in addition to
    54  // error, such as ReadFile(filename string) ([]byte, error). Here the usage
    55  // would be:
    56  //
    57  // o.Stdout, _ = os.Create("foo")
    58  // data, err := s.Capture(o, nil).Run("echo","b").ReadFile("foo")
    59  // // data == "b"
    60  //
    61  // Note that terminating functions, even those that take an action, may
    62  // return an error generated by a previous method.
    63  //
    64  // In addition to Run which will always run a command as a subprocess,
    65  // the Call method will invoke a function. Note that Capture and Timeout
    66  // do not affect such calls.
    67  //
    68  // Errors returned by Sequence augment those returned by the underlying
    69  // packages with details of the exact call that generated those errors.
    70  // This means that it is not possible to test directly for errors from
    71  // those packages. The GetOriginalError function can be used to obtain
    72  // the error from the underlying package, or the IsTimeout, IsNotExists etc
    73  // functions can be used on the wrapped error. The ExitCode method
    74  // is also provided to convert to the exit codes expected by the
    75  // v.io/x/lib/cmdline package which is often used in conjunction with
    76  // Sequence.
    77  type Sequence struct {
    78  	// NOTE: we use a struct as the return value of all
    79  	// Sequence methods to ensure that code of the form:
    80  	// if err := s.WriteFile(); err != nil {...}
    81  	// does not compile.
    82  	*sequence
    83  }
    84  
    85  type sequence struct {
    86  	r                            *executor
    87  	err                          error
    88  	caller                       string
    89  	stdout, stderr               io.Writer
    90  	stdin                        io.Reader
    91  	reading                      bool
    92  	env                          map[string]string
    93  	opts                         *opts
    94  	defaultStdin                 io.Reader
    95  	defaultStdout, defaultStderr io.Writer
    96  	dirs                         []string
    97  	verbosity                    *bool
    98  	cmdDir                       string
    99  	timeout                      time.Duration
   100  	serializedWriterLock         sync.Mutex
   101  }
   102  
   103  // NewSequence creates an instance of Sequence with default values for its
   104  // environment, stdin, stderr, stdout and other supported options.
   105  // If the environment parameter is nil or empty then the current value of
   106  // os.Environ() will be used instead.
   107  func NewSequence(env map[string]string, stdin io.Reader, stdout, stderr io.Writer, color, verbose bool) Sequence {
   108  	if len(env) == 0 {
   109  		env = envvar.SliceToMap(os.Environ())
   110  	}
   111  	s := Sequence{
   112  		&sequence{
   113  			r:            newExecutor(env, stdin, stdout, stderr, color, verbose),
   114  			defaultStdin: stdin,
   115  		},
   116  	}
   117  	s.defaultStdout, s.defaultStderr = s.serializeWriter(stdout), s.serializeWriter(stderr)
   118  	return s
   119  }
   120  
   121  // RunOpts returns the value of verbose that was used to
   122  // create this sequence.
   123  func (s Sequence) RunOpts() (verbose bool) {
   124  	opts := s.getOpts()
   125  	return opts.verbose
   126  }
   127  
   128  // Capture arranges for the next call to Run or Last to write its stdout and
   129  // stderr output to the supplied io.Writers. This will be cleared and not used
   130  // for any calls to Run or Last beyond the next one. Specifying nil for
   131  // a writer will result in using the the corresponding io.Writer supplied
   132  // to NewSequence. ioutil.Discard should be used to discard output.
   133  func (s Sequence) Capture(stdout, stderr io.Writer) Sequence {
   134  	if s.err != nil {
   135  		return s
   136  	}
   137  	s.stdout, s.stderr = stdout, stderr
   138  	return s
   139  }
   140  
   141  // Read arranges for the next call to Run or Last to read from the supplied
   142  // io.Reader. This will be cleared and not used for any calls to Run or Last
   143  // beyond the next one. Specifying nil will result in reading from os.DevNull.
   144  func (s Sequence) Read(stdin io.Reader) Sequence {
   145  	if s.err != nil {
   146  		return s
   147  	}
   148  	s.reading = true
   149  	s.stdin = stdin
   150  	return s
   151  }
   152  
   153  // SetEnv arranges for the next call to Run, Call, Start or Last to use the supplied
   154  // environment variables. These will be cleared and not used for any calls
   155  // to Run, Call or Last beyond the next one.
   156  func (s Sequence) SetEnv(env map[string]string) Sequence {
   157  	if s.err != nil {
   158  		return s
   159  	}
   160  	s.env = env
   161  	return s
   162  }
   163  
   164  // Env arranges for the next call to Run, Call, Start or Last to use
   165  // the result of merging the supplied environment variables with those
   166  // specified when the sequence was created or with those set by the most
   167  // recent call to the Env method. These will be cleared and not used
   168  // for any calls to Run, Call or Last beyond the next one.
   169  func (s Sequence) Env(env map[string]string) Sequence {
   170  	if s.err != nil {
   171  		return s
   172  	}
   173  	if s.env != nil {
   174  		s.env = envvar.MergeMaps(s.env, env)
   175  	} else {
   176  		e := s.getOpts().env
   177  		s.env = envvar.MergeMaps(e, env)
   178  	}
   179  	return s
   180  }
   181  
   182  // Verbosity arranges for the next call to Run, Call, Start or Last to use the
   183  // specified verbosity. This will be cleared and not used for any calls
   184  // to Run, Call or Last beyond the next one.
   185  func (s Sequence) Verbose(verbosity bool) Sequence {
   186  	if s.err != nil {
   187  		return s
   188  	}
   189  	s.verbosity = &verbosity
   190  	return s
   191  }
   192  
   193  // Dir sets the working directory for the next subprocess that is created
   194  // via Run, Call, Start or Last to the supplied parameter. This is the only
   195  // way to safely set the working directory of a command when multiple threads
   196  // are used.
   197  func (s Sequence) Dir(dir string) Sequence {
   198  	if s.err != nil {
   199  		return s
   200  	}
   201  	s.cmdDir = dir
   202  	return s
   203  }
   204  
   205  // internal getOpts that doesn't override stdin, stdout, stderr
   206  func (s Sequence) getOpts() opts {
   207  	var opts opts
   208  	if s.opts != nil {
   209  		opts = *s.opts
   210  	} else {
   211  		opts = s.r.opts
   212  	}
   213  	return opts
   214  }
   215  
   216  // Timeout arranges for the next call to Run, Start or Last to be subject to the
   217  // specified timeout. The timeout will be cleared and not used any calls to Run
   218  // or Last beyond the next one. It has no effect for calls to Call.
   219  func (s Sequence) Timeout(timeout time.Duration) Sequence {
   220  	if s.err != nil {
   221  		return s
   222  	}
   223  	s.timeout = timeout
   224  	return s
   225  }
   226  
   227  func (s Sequence) setOpts(opts opts) {
   228  	s.opts = &opts
   229  }
   230  
   231  type wrappedError struct {
   232  	oe, we error
   233  }
   234  
   235  func (ie *wrappedError) Error() string {
   236  	return ie.we.Error()
   237  }
   238  
   239  // Error returns the error, if any, stored in the Sequence.
   240  func (s Sequence) Error() error {
   241  	if s.err != nil && len(s.caller) > 0 {
   242  		return &wrappedError{oe: s.err, we: fmt.Errorf("%s: %v", s.caller, s.err)}
   243  	}
   244  	return s.err
   245  }
   246  
   247  // TranslateExitCode translates errors from the "os/exec" package that
   248  // contain exit codes into cmdline.ErrExitCode errors.
   249  func TranslateExitCode(err error) error {
   250  	return translateExitCode(GetOriginalError(err))
   251  }
   252  
   253  func translateExitCode(err error) error {
   254  	if exit, ok := err.(*exec.ExitError); ok {
   255  		if wait, ok := exit.Sys().(syscall.WaitStatus); ok {
   256  			if status := wait.ExitStatus(); wait.Exited() && status != 0 {
   257  				return cmdline.ErrExitCode(status)
   258  			}
   259  		}
   260  	}
   261  	return err
   262  }
   263  
   264  // GetOriginalError gets the original error wrapped in the supplied err.
   265  // If the given err has not been wrapped by Sequence, then the supplied error
   266  // is returned.
   267  func GetOriginalError(err error) error {
   268  	if we, ok := err.(*wrappedError); ok {
   269  		return we.oe
   270  	}
   271  	return err
   272  }
   273  
   274  // IsExist returns a boolean indicating whether the error is known
   275  // to report that a file or directory already exists.
   276  func IsExist(err error) bool {
   277  	if we, ok := err.(*wrappedError); ok {
   278  		return os.IsExist(we.oe)
   279  	}
   280  	return os.IsExist(err)
   281  }
   282  
   283  // IsNotExist returns a boolean indicating whether the error is known
   284  // to report that a file or directory does not exist.
   285  func IsNotExist(err error) bool {
   286  	if we, ok := err.(*wrappedError); ok {
   287  		return os.IsNotExist(we.oe)
   288  	}
   289  	return os.IsNotExist(err)
   290  }
   291  
   292  // IsPermission returns a boolean indicating whether the error is known
   293  // to report that permission is denied.
   294  func IsPermission(err error) bool {
   295  	if we, ok := err.(*wrappedError); ok {
   296  		return os.IsPermission(we.oe)
   297  	}
   298  	return os.IsPermission(err)
   299  }
   300  
   301  // IsTimeout returns a boolean indicating whether the error is a result of
   302  // a timeout.
   303  func IsTimeout(err error) bool {
   304  	if we, ok := err.(*wrappedError); ok {
   305  		return we.oe == commandTimedOutErr
   306  	}
   307  	return err == commandTimedOutErr
   308  }
   309  
   310  func fmtError(depth int, err error, detail string) string {
   311  	_, file, line, _ := runtime.Caller(depth + 1)
   312  	return fmt.Sprintf("%s:%d: %s", filepath.Base(file), line, detail)
   313  }
   314  
   315  func (s Sequence) setError(err error, detail string) {
   316  	if err == nil || s.err != nil {
   317  		return
   318  	}
   319  	s.err = err
   320  	s.caller = fmtError(2, err, detail)
   321  }
   322  
   323  // reset all state except s.err
   324  func (s Sequence) reset() {
   325  	s.stdin, s.stdout, s.stderr, s.env = nil, nil, nil, nil
   326  	s.opts, s.verbosity = nil, nil
   327  	s.cmdDir = ""
   328  	s.reading = false
   329  	s.timeout = 0
   330  }
   331  
   332  func cleanup(p1, p2 *io.PipeWriter, stdinCh, stderrCh chan error) error {
   333  	p1.Close()
   334  	p2.Close()
   335  	if stdinCh != nil {
   336  		if err := <-stdinCh; err != nil {
   337  			return err
   338  		}
   339  	}
   340  	if stderrCh != nil {
   341  		if err := <-stderrCh; err != nil {
   342  			return err
   343  		}
   344  	}
   345  	return nil
   346  }
   347  
   348  func useIfNotNil(a, b io.Writer) io.Writer {
   349  	if a != nil {
   350  		return a
   351  	}
   352  	return b
   353  }
   354  
   355  func writeOutput(logdir bool, from string, to io.Writer) {
   356  	if fi, err := os.Open(from); err == nil {
   357  		io.Copy(to, fi)
   358  		fi.Close()
   359  	}
   360  	if !logdir {
   361  		return
   362  	}
   363  	if wd, err := os.Getwd(); err == nil {
   364  		fmt.Fprintf(to, "Current Directory: %v\n", wd)
   365  	}
   366  }
   367  
   368  type sharedLockWriter struct {
   369  	mu *sync.Mutex
   370  	f  io.Writer
   371  }
   372  
   373  func (lw *sharedLockWriter) Write(d []byte) (int, error) {
   374  	lw.mu.Lock()
   375  	defer lw.mu.Unlock()
   376  	return lw.f.Write(d)
   377  }
   378  
   379  func (s Sequence) serializeWriter(a io.Writer) io.Writer {
   380  	if a != nil {
   381  		return &sharedLockWriter{&s.serializedWriterLock, a}
   382  	}
   383  	return nil
   384  }
   385  
   386  // isWriterTTY returns whether the io.Writer w is an os.File pointing at a tty.
   387  func isWriterTTY(w io.Writer) (isTTY bool) {
   388  	_, isOsFile := w.(*os.File)
   389  	if isOsFile {
   390  		stat, _ := os.Stdin.Stat()
   391  		isTTY = (stat.Mode() & (os.ModeDevice | os.ModeCharDevice)) == (os.ModeDevice | os.ModeCharDevice)
   392  	}
   393  	return isTTY
   394  }
   395  
   396  func (s Sequence) initAndDefer(h *Handle) func() {
   397  	if s.stdout == nil && s.stderr == nil {
   398  		fout, err := ioutil.TempFile("", "seq")
   399  		if err != nil {
   400  			return func() {}
   401  		}
   402  		opts := s.getOpts()
   403  		opts.stdout, opts.stderr = s.serializeWriter(fout), s.serializeWriter(fout)
   404  		if len(s.env) > 0 {
   405  			opts.env = s.env
   406  		}
   407  		if s.reading {
   408  			opts.stdin = s.stdin
   409  		}
   410  		if s.verbosity != nil {
   411  			opts.verbose = *s.verbosity
   412  		}
   413  		opts.dir = s.cmdDir
   414  		s.setOpts(opts)
   415  		if h != nil {
   416  			return func() {
   417  				h.stderr = useIfNotNil(s.defaultStderr, os.Stderr)
   418  				h.filename = fout.Name()
   419  				h.doneErr = nil
   420  				fout.Close()
   421  			}
   422  		}
   423  		return func() {
   424  			filename := fout.Name()
   425  			fout.Close()
   426  			defer func() { os.Remove(filename); s.opts = nil }()
   427  			if s.err != nil {
   428  				writeOutput(true, filename, useIfNotNil(s.defaultStderr, os.Stderr))
   429  			}
   430  			if opts.verbose && s.defaultStderr != s.defaultStdout {
   431  				writeOutput(false, filename, useIfNotNil(s.defaultStdout, os.Stdout))
   432  			}
   433  		}
   434  	}
   435  	opts := s.getOpts()
   436  	rStdout, wStdout := io.Pipe()
   437  	rStderr, wStderr := io.Pipe()
   438  	// If the requested stdout/stderr is a raw os.File pointing at a tty,
   439  	// use that directly.  This allows the invocation of tools
   440  	// like editors, which expect stdout/stderr to be ttys.
   441  	if isWriterTTY(s.stdout) {
   442  		opts.stdout = s.stdout
   443  	} else {
   444  		opts.stdout = wStdout
   445  	}
   446  	if isWriterTTY(s.stderr) {
   447  		opts.stderr = s.stderr
   448  	} else {
   449  		opts.stderr = wStderr
   450  	}
   451  	if len(s.env) > 0 {
   452  		opts.env = s.env
   453  	}
   454  	if s.reading {
   455  		opts.stdin = s.stdin
   456  	}
   457  	var stdinCh, stderrCh chan error
   458  	stdout, stderr := s.serializeWriter(s.stdout), s.serializeWriter(s.stderr)
   459  	if stdout != nil {
   460  		stdinCh = make(chan error)
   461  		go copy(stdout, rStdout, stdinCh)
   462  	} else {
   463  		opts.stdout = s.defaultStdout
   464  	}
   465  	if stderr != nil {
   466  		stderrCh = make(chan error)
   467  		go copy(stderr, rStderr, stderrCh)
   468  	} else {
   469  		opts.stderr = s.defaultStderr
   470  	}
   471  	if s.verbosity != nil {
   472  		opts.verbose = *s.verbosity
   473  	}
   474  	opts.dir = s.cmdDir
   475  	s.setOpts(opts)
   476  	if h != nil {
   477  		return func() {
   478  			h.filename = ""
   479  			h.doneErr = cleanup(wStdout, wStderr, stdinCh, stderrCh)
   480  		}
   481  	}
   482  	return func() {
   483  		if err := cleanup(wStdout, wStderr, stdinCh, stderrCh); err != nil && s.err == nil {
   484  			// If we haven't already encountered an error and we fail to
   485  			// cleanup then record the error from the cleanup
   486  			s.err = err
   487  		}
   488  		// reset does not affect s.err
   489  		s.reset()
   490  	}
   491  }
   492  
   493  func fmtStringArgs(args ...string) string {
   494  	if len(args) == 0 {
   495  		return ""
   496  	}
   497  	var out bytes.Buffer
   498  	for _, a := range args {
   499  		fmt.Fprintf(&out, ", %q", a)
   500  	}
   501  	return out.String()
   502  }
   503  
   504  // Run runs the given command as a subprocess.
   505  func (s Sequence) Run(path string, args ...string) Sequence {
   506  	if s.err != nil {
   507  		return s
   508  	}
   509  	defer s.initAndDefer(nil)()
   510  	s.setError(s.r.run(s.timeout, s.getOpts(), path, args...), fmt.Sprintf("Run(%q%s)", path, fmtStringArgs(args...)))
   511  	return s
   512  }
   513  
   514  // Last runs the given command as a subprocess and returns an error
   515  // immediately terminating the sequence, it is equivalent to
   516  // calling s.Run(path, args...).Done().
   517  func (s Sequence) Last(path string, args ...string) error {
   518  	if s.err != nil {
   519  		return s.Done()
   520  	}
   521  	defer s.Done()
   522  	defer s.initAndDefer(nil)()
   523  	s.setError(s.r.run(s.timeout, s.getOpts(), path, args...), fmt.Sprintf("Last(%q%s)", path, fmtStringArgs(args...)))
   524  	return s.Error()
   525  }
   526  
   527  // Call runs the given function. Note that Capture and Timeout have no
   528  // effect on invocations of Call, but Opts can control logging.
   529  func (s Sequence) Call(fn func() error, format string, args ...interface{}) Sequence {
   530  	if s.err != nil {
   531  		return s
   532  	}
   533  	defer s.initAndDefer(nil)()
   534  	s.setError(s.r.function(s.getOpts(), fn, format, args...), fmt.Sprintf(format, args...))
   535  	return s
   536  }
   537  
   538  // Handle represents a command running in the background.
   539  type Handle struct {
   540  	stdout, stderr io.Writer
   541  	doneErr        error
   542  	filename       string
   543  	deferFn        func()
   544  	cmd            *exec.Cmd
   545  }
   546  
   547  // Kill terminates the currently running background process.
   548  func (h *Handle) Kill() error {
   549  	return h.cmd.Process.Kill()
   550  }
   551  
   552  // Pid returns the pid of the running process.
   553  func (h *Handle) Pid() int {
   554  	return h.cmd.Process.Pid
   555  }
   556  
   557  func (h *Handle) Signal(sig os.Signal) error {
   558  	return h.cmd.Process.Signal(sig)
   559  }
   560  
   561  // Wait waits for the currently running background process to terminate.
   562  func (h *Handle) Wait() error {
   563  	err := h.cmd.Wait()
   564  	h.deferFn()
   565  	if len(h.filename) > 0 {
   566  		if err != nil {
   567  			writeOutput(true, h.filename, h.stderr)
   568  		}
   569  		os.Remove(h.filename)
   570  		return err
   571  	}
   572  	return h.doneErr
   573  }
   574  
   575  // Start runs the given command as a subprocess in background and returns
   576  // a handle that can be used to kill and/or wait for that background process.
   577  // Start is a terminating function.
   578  func (s Sequence) Start(path string, args ...string) (*Handle, error) {
   579  	if s.err != nil {
   580  		return nil, s.Done()
   581  	}
   582  	h := &Handle{}
   583  	h.deferFn = s.initAndDefer(h)
   584  	cmd, err := s.r.start(s.timeout, s.getOpts(), path, args...)
   585  	h.cmd = cmd
   586  	s.setError(err, fmt.Sprintf("Start(%q%s)", path, fmtStringArgs(args...)))
   587  	return h, s.Error()
   588  }
   589  
   590  // Output logs the given list of lines using the currently in effect verbosity
   591  // as specified by Opts, or the default otherwise.
   592  func (s Sequence) Output(output []string) Sequence {
   593  	if s.err != nil {
   594  		return s
   595  	}
   596  	opts := s.getOpts()
   597  	if s.verbosity != nil {
   598  		opts.verbose = *s.verbosity
   599  	}
   600  	s.r.output(opts, output)
   601  	return s
   602  }
   603  
   604  // Fprintf calls fmt.Fprintf.
   605  func (s Sequence) Fprintf(f io.Writer, format string, args ...interface{}) Sequence {
   606  	if s.err != nil {
   607  		return s
   608  	}
   609  	fmt.Fprintf(f, format, args...)
   610  	return s
   611  }
   612  
   613  // Done returns the error stored in the Sequence and pops back to the first
   614  // entry in the directory stack if Pushd has been called. Done is a terminating
   615  // function. There is no need to ensure that Done is called before returning
   616  // from a function that uses a sequence unless it is necessary to pop the
   617  // stack.
   618  func (s Sequence) Done() error {
   619  	rerr := s.Error()
   620  	s.err = nil
   621  	s.caller = ""
   622  	s.reset()
   623  	if len(s.dirs) > 0 {
   624  		cwd := s.dirs[0]
   625  		s.dirs = nil
   626  		err := s.r.call(func() error {
   627  			return os.Chdir(cwd)
   628  		}, fmt.Sprintf("sequence done popd %q", cwd))
   629  		if err != nil {
   630  			detail := "Done: Chdir(" + cwd + ")"
   631  			if rerr == nil {
   632  				s.setError(err, detail)
   633  			} else {
   634  				// In the unlikely event that Chdir fails in addition to an
   635  				// earlier error, we append an appropriate error message.
   636  				s.err = fmt.Errorf("%v\n%v", rerr, fmtError(1, err, detail))
   637  			}
   638  			return s.Error()
   639  		}
   640  	}
   641  	return rerr
   642  }
   643  
   644  // Pushd pushes the current directory onto a stack and changes directory
   645  // to the specified one. Calling any terminating function will pop back
   646  // to the first element in the stack on completion of that function.
   647  func (s Sequence) Pushd(dir string) Sequence {
   648  	cwd, err := os.Getwd()
   649  	if err != nil {
   650  		s.setError(err, "Pushd("+dir+"): os.Getwd")
   651  		return s
   652  	}
   653  	s.dirs = append(s.dirs, cwd)
   654  	err = s.r.call(func() error {
   655  		return os.Chdir(dir)
   656  	}, fmt.Sprintf("pushd %q", dir))
   657  	s.setError(err, "Pushd("+dir+")")
   658  	return s
   659  }
   660  
   661  // Popd popds the last directory from the directory stack and chdir's to it.
   662  // Calling any termination function will pop back to the first element in
   663  // the stack on completion of that function.
   664  func (s Sequence) Popd() Sequence {
   665  	if s.err != nil {
   666  		return s
   667  	}
   668  	if len(s.dirs) == 0 {
   669  		s.setError(fmt.Errorf("directory stack is empty"), "Popd()")
   670  		return s
   671  	}
   672  	last := s.dirs[len(s.dirs)-1]
   673  	s.dirs = s.dirs[:len(s.dirs)-1]
   674  	err := s.r.call(func() error {
   675  		return os.Chdir(last)
   676  	}, fmt.Sprintf("popd %q", last))
   677  	s.setError(err, "Popd() -> "+last)
   678  	return s
   679  }
   680  
   681  // Chdir is a wrapper around os.Chdir that handles options such as
   682  // "verbose".
   683  func (s Sequence) Chdir(dir string) Sequence {
   684  	if s.err != nil {
   685  		return s
   686  	}
   687  	err := s.r.call(func() error {
   688  		return os.Chdir(dir)
   689  	}, fmt.Sprintf("cd %q", dir))
   690  	s.setError(err, "Chdir("+dir+")")
   691  	return s
   692  
   693  }
   694  
   695  // Chmod is a wrapper around os.Chmod that handles options such as
   696  // "verbose".
   697  func (s Sequence) Chmod(dir string, mode os.FileMode) Sequence {
   698  	if s.err != nil {
   699  		return s
   700  	}
   701  	err := s.r.call(func() error { return os.Chmod(dir, mode) }, fmt.Sprintf("chmod %v %q", mode, dir))
   702  	s.setError(err, fmt.Sprintf("Chmod(%s, %s)", dir, mode))
   703  	return s
   704  
   705  }
   706  
   707  // MkdirAll is a wrapper around os.MkdirAll that handles options such
   708  // as "verbose".
   709  func (s Sequence) MkdirAll(dir string, mode os.FileMode) Sequence {
   710  	if s.err != nil {
   711  		return s
   712  	}
   713  	err := s.r.call(func() error { return os.MkdirAll(dir, mode) }, fmt.Sprintf("mkdir -p %q", dir))
   714  	s.setError(err, fmt.Sprintf("MkdirAll(%s, %s)", dir, mode))
   715  	return s
   716  }
   717  
   718  // RemoveAll is a wrapper around os.RemoveAll that handles options
   719  // such as "verbose".
   720  func (s Sequence) RemoveAll(dir string) Sequence {
   721  	if s.err != nil {
   722  		return s
   723  	}
   724  	err := s.r.call(func() error { return os.RemoveAll(dir) }, fmt.Sprintf("rm -rf %q", dir))
   725  	s.setError(err, fmt.Sprintf("RemoveAll(%s)", dir))
   726  	return s
   727  }
   728  
   729  // Remove is a wrapper around os.Remove that handles options
   730  // such as "verbose".
   731  func (s Sequence) Remove(file string) Sequence {
   732  	if s.err != nil {
   733  		return s
   734  	}
   735  	err := s.r.call(func() error { return os.Remove(file) }, fmt.Sprintf("rm %q", file))
   736  	s.setError(err, fmt.Sprintf("Remove(%s)", file))
   737  	return s
   738  }
   739  
   740  // Rename is a wrapper around os.Rename that handles options such as
   741  // "verbose".
   742  func (s Sequence) Rename(src, dst string) Sequence {
   743  	if s.err != nil {
   744  		return s
   745  	}
   746  	err := s.r.call(func() error {
   747  		if err := os.Rename(src, dst); err != nil {
   748  			// Check if the rename operation failed
   749  			// because the source and destination are
   750  			// located on different mount points.
   751  			linkErr, ok := err.(*os.LinkError)
   752  			if !ok {
   753  				return err
   754  			}
   755  			errno, ok := linkErr.Err.(syscall.Errno)
   756  			if !ok || errno != syscall.EXDEV {
   757  				return err
   758  			}
   759  			// Fall back to a non-atomic rename.
   760  			cmd := exec.Command("mv", src, dst)
   761  			return cmd.Run()
   762  		}
   763  		return nil
   764  	}, fmt.Sprintf("mv %q %q", src, dst))
   765  	s.setError(err, fmt.Sprintf("Rename(%s, %s)", src, dst))
   766  	return s
   767  
   768  }
   769  
   770  // Symlink is a wrapper around os.Symlink that handles options such as
   771  // "verbose".
   772  func (s Sequence) Symlink(src, dst string) Sequence {
   773  	if s.err != nil {
   774  		return s
   775  	}
   776  	err := s.r.call(func() error { return os.Symlink(src, dst) }, fmt.Sprintf("ln -s %q %q", src, dst))
   777  	s.setError(err, fmt.Sprintf("Symlink(%s, %s)", src, dst))
   778  	return s
   779  }
   780  
   781  // Open is a wrapper around os.Open that handles options such as
   782  // "verbose". Open is a terminating function.
   783  func (s Sequence) Open(name string) (f *os.File, err error) {
   784  	if s.err != nil {
   785  		return nil, s.Done()
   786  	}
   787  	s.r.call(func() error {
   788  		f, err = os.Open(name)
   789  		return err
   790  	}, fmt.Sprintf("open %q", name))
   791  	s.setError(err, fmt.Sprintf("Open(%s)", name))
   792  	err = s.Done()
   793  	return
   794  }
   795  
   796  // OpenFile is a wrapper around os.OpenFile that handles options such as
   797  // "verbose". OpenFile is a terminating function.
   798  func (s Sequence) OpenFile(name string, flag int, perm os.FileMode) (f *os.File, err error) {
   799  	if s.err != nil {
   800  		return nil, s.Done()
   801  	}
   802  	s.r.call(func() error {
   803  		f, err = os.OpenFile(name, flag, perm)
   804  		return err
   805  	}, fmt.Sprintf("open file %q", name))
   806  	s.setError(err, fmt.Sprintf("OpenFile(%s, 0x%x, %s)", name, flag, perm))
   807  	err = s.Done()
   808  	return
   809  }
   810  
   811  // Create is a wrapper around os.Create that handles options such as "verbose"
   812  //. Create is a terminating function.
   813  func (s Sequence) Create(name string) (f *os.File, err error) {
   814  	if s.err != nil {
   815  		return nil, s.Done()
   816  	}
   817  	s.r.call(func() error {
   818  		f, err = os.Create(name)
   819  		return err
   820  	}, fmt.Sprintf("create %q", name))
   821  	s.setError(err, fmt.Sprintf("Create(%s)", name))
   822  	err = s.Done()
   823  	return
   824  }
   825  
   826  // ReadDir is a wrapper around ioutil.ReadDir that handles options
   827  // such as "verbose". ReadDir is a terminating function.
   828  func (s Sequence) ReadDir(dirname string) (fi []os.FileInfo, err error) {
   829  	if s.err != nil {
   830  		return nil, s.Done()
   831  	}
   832  	s.r.call(func() error {
   833  		fi, err = ioutil.ReadDir(dirname)
   834  		return err
   835  	}, fmt.Sprintf("ls %q", dirname))
   836  	s.setError(err, fmt.Sprintf("ReadDir(%s)", dirname))
   837  	err = s.Done()
   838  	return
   839  }
   840  
   841  // ReadFile is a wrapper around ioutil.ReadFile that handles options
   842  // such as "verbose". ReadFile is a terminating function.
   843  func (s Sequence) ReadFile(filename string) (bytes []byte, err error) {
   844  
   845  	if s.err != nil {
   846  		return nil, s.Done()
   847  	}
   848  	s.r.call(func() error {
   849  		bytes, err = ioutil.ReadFile(filename)
   850  		return err
   851  	}, fmt.Sprintf("read %q", filename))
   852  	s.setError(err, fmt.Sprintf("ReadFile(%s)", filename))
   853  	err = s.Done()
   854  	return
   855  }
   856  
   857  // WriteFile is a wrapper around ioutil.WriteFile that handles options
   858  // such as "verbose".
   859  func (s Sequence) WriteFile(filename string, data []byte, perm os.FileMode) Sequence {
   860  	if s.err != nil {
   861  		return s
   862  	}
   863  	err := s.r.call(func() error {
   864  		return ioutil.WriteFile(filename, data, perm)
   865  	}, fmt.Sprintf("write %q", filename))
   866  	s.setError(err, fmt.Sprintf("WriteFile(%s, %.10s,  %s)", filename, data, perm))
   867  	return s
   868  }
   869  
   870  // Copy is a wrapper around io.Copy that handles options such as "verbose".
   871  // Copy is a terminating function.
   872  func (s Sequence) Copy(dst io.Writer, src io.Reader) (n int64, err error) {
   873  	if s.err != nil {
   874  		return 0, s.Done()
   875  	}
   876  	s.r.call(func() error {
   877  		n, err = io.Copy(dst, src)
   878  		return err
   879  	}, "io.copy")
   880  	s.setError(err, fmt.Sprintf("Copy(%s, %s)", dst, src))
   881  	err = s.Done()
   882  	return
   883  }
   884  
   885  // Stat is a wrapper around os.Stat that handles options such as
   886  // "verbose". Stat is a terminating function.
   887  func (s Sequence) Stat(name string) (fi os.FileInfo, err error) {
   888  	if s.err != nil {
   889  		return nil, s.Done()
   890  	}
   891  	s.r.call(func() error {
   892  		fi, err = os.Stat(name)
   893  		return err
   894  	}, fmt.Sprintf("stat %q", name))
   895  	s.setError(err, fmt.Sprintf("Stat(%s)", name))
   896  	err = s.Done()
   897  	return
   898  }
   899  
   900  // Lstat is a wrapper around os.Lstat that handles options such as
   901  // "verbose". Lstat is a terminating function.
   902  func (s Sequence) Lstat(name string) (fi os.FileInfo, err error) {
   903  	if s.err != nil {
   904  		return nil, s.Done()
   905  	}
   906  	s.r.call(func() error {
   907  		fi, err = os.Lstat(name)
   908  		return err
   909  	}, fmt.Sprintf("lstat %q", name))
   910  	s.setError(err, fmt.Sprintf("Lstat(%s)", name))
   911  	err = s.Done()
   912  	return
   913  }
   914  
   915  // Readlink is a wrapper around os.Readlink that handles options such as
   916  // "verbose". Lstat is a terminating function.
   917  func (s Sequence) Readlink(name string) (link string, err error) {
   918  	if s.err != nil {
   919  		return "", s.Done()
   920  	}
   921  	s.r.call(func() error {
   922  		link, err = os.Readlink(name)
   923  		return err
   924  	}, fmt.Sprintf("readlink %q", name))
   925  	s.setError(err, fmt.Sprintf("Readlink(%s)", name))
   926  	err = s.Done()
   927  	return
   928  }
   929  
   930  // TempDir is a wrapper around ioutil.TempDir that handles options
   931  // such as "verbose". TempDir is a terminating function.
   932  func (s Sequence) TempDir(dir, prefix string) (tmpDir string, err error) {
   933  	if s.err != nil {
   934  		return "", s.Done()
   935  	}
   936  	if dir == "" {
   937  		dir = os.Getenv("TMPDIR")
   938  	}
   939  	tmpDir = filepath.Join(dir, prefix+"XXXXXX")
   940  	s.r.call(func() error {
   941  		tmpDir, err = ioutil.TempDir(dir, prefix)
   942  		return err
   943  	}, fmt.Sprintf("mkdir -p %q", tmpDir))
   944  	s.setError(err, fmt.Sprintf("TempDir(%s,%s)", dir, prefix))
   945  	err = s.Done()
   946  	return
   947  }
   948  
   949  // TempFile is a wrapper around ioutil.TempFile that handles options
   950  // such as "verbose".
   951  func (s Sequence) TempFile(dir, prefix string) (f *os.File, err error) {
   952  	if s.err != nil {
   953  		return nil, s.Done()
   954  	}
   955  	if dir == "" {
   956  		dir = os.Getenv("TMPDIR")
   957  	}
   958  	s.r.call(func() error {
   959  		f, err = ioutil.TempFile(dir, prefix)
   960  		return err
   961  	}, fmt.Sprintf("tempFile %q %q", dir, prefix))
   962  	s.setError(err, fmt.Sprintf("TempFile(%s,%s)", dir, prefix))
   963  	err = s.Done()
   964  	return
   965  }
   966  
   967  // IsDir is a wrapper around os.Stat with appropriate logging that
   968  // returns true of dirname exists and is a directory.
   969  // IsDir is a terminating function.
   970  func (s Sequence) IsDir(dirname string) (bool, error) {
   971  	if s.err != nil {
   972  		return false, s.Done()
   973  	}
   974  	var fileInfo os.FileInfo
   975  	var err error
   976  	err = s.r.call(func() error {
   977  		fileInfo, err = os.Stat(dirname)
   978  		return err
   979  	}, fmt.Sprintf("isdir %q", dirname))
   980  	if IsNotExist(err) {
   981  		return false, nil
   982  	}
   983  	if err != nil {
   984  		return false, err
   985  	}
   986  	s.setError(err, fmt.Sprintf("IsDir(%s)", dirname))
   987  	return fileInfo.IsDir(), s.Done()
   988  }
   989  
   990  // IsFile is a wrapper around os.Stat with appropriate logging that
   991  // returns true if file exists and is not a directory.
   992  // IsFile is a terminating function.
   993  func (s Sequence) IsFile(file string) (bool, error) {
   994  	if s.err != nil {
   995  		return false, s.Done()
   996  	}
   997  	var fileInfo os.FileInfo
   998  	var err error
   999  	err = s.r.call(func() error {
  1000  		fileInfo, err = os.Stat(file)
  1001  		return err
  1002  	}, fmt.Sprintf("isfile %q", file))
  1003  	if IsNotExist(err) {
  1004  		return false, nil
  1005  	}
  1006  	if err != nil {
  1007  		return false, err
  1008  	}
  1009  	s.setError(err, fmt.Sprintf("IsFile(%s)", file))
  1010  	return !fileInfo.IsDir(), s.Done()
  1011  }
  1012  
  1013  // AssertDirExists asserts that the specified directory exists with appropriate
  1014  // logging.
  1015  func (s Sequence) AssertDirExists(dirname string) Sequence {
  1016  	if s.err != nil {
  1017  		return s
  1018  	}
  1019  	isdir, err := s.IsDir(dirname)
  1020  	if !isdir && err == nil {
  1021  		err = os.ErrNotExist
  1022  	}
  1023  	s.setError(err, fmt.Sprintf("AssertDirExists(%s)", dirname))
  1024  	return s
  1025  }
  1026  
  1027  // AssertFileExists asserts that the specified file exists with appropriate
  1028  // logging.
  1029  func (s Sequence) AssertFileExists(file string) Sequence {
  1030  	if s.err != nil {
  1031  		return s
  1032  	}
  1033  	isfile, err := s.IsFile(file)
  1034  	if !isfile && err == nil {
  1035  		err = os.ErrNotExist
  1036  	}
  1037  	s.setError(err, fmt.Sprintf("AssertFileExists(%s)", file))
  1038  	return s
  1039  }
  1040  
  1041  func copy(to io.Writer, from io.Reader, ch chan error) {
  1042  	_, err := io.Copy(to, from)
  1043  	ch <- err
  1044  }