github.com/git-lfs/git-lfs@v2.5.2+incompatible/subprocess/subprocess.go (about)

     1  // Package subprocess provides helper functions for forking new processes
     2  // NOTE: Subject to change, do not rely on this package from outside git-lfs source
     3  package subprocess
     4  
     5  import (
     6  	"bufio"
     7  	"bytes"
     8  	"fmt"
     9  	"os"
    10  	"os/exec"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/rubyist/tracerx"
    15  )
    16  
    17  // BufferedExec starts up a command and creates a stdin pipe and a buffered
    18  // stdout & stderr pipes, wrapped in a BufferedCmd. The stdout buffer will be
    19  // of stdoutBufSize bytes.
    20  func BufferedExec(name string, args ...string) (*BufferedCmd, error) {
    21  	cmd := ExecCommand(name, args...)
    22  	stdout, err := cmd.StdoutPipe()
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  	stderr, err := cmd.StderrPipe()
    27  	if err != nil {
    28  		return nil, err
    29  	}
    30  
    31  	stdin, err := cmd.StdinPipe()
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  
    36  	if err := cmd.Start(); err != nil {
    37  		return nil, err
    38  	}
    39  
    40  	return &BufferedCmd{
    41  		cmd,
    42  		stdin,
    43  		bufio.NewReaderSize(stdout, stdoutBufSize),
    44  		bufio.NewReaderSize(stderr, stdoutBufSize),
    45  	}, nil
    46  }
    47  
    48  // SimpleExec is a small wrapper around os/exec.Command.
    49  func SimpleExec(name string, args ...string) (string, error) {
    50  	Trace(name, args...)
    51  	return Output(ExecCommand(name, args...))
    52  }
    53  
    54  func Output(cmd *Cmd) (string, error) {
    55  	//start copied from Go 1.6 exec.go
    56  	captureErr := cmd.Stderr == nil
    57  	if captureErr {
    58  		cmd.Stderr = &prefixSuffixSaver{N: 32 << 10}
    59  	}
    60  	//end copied from Go 1.6 exec.go
    61  
    62  	out, err := cmd.Output()
    63  
    64  	if exitError, ok := err.(*exec.ExitError); ok {
    65  		// TODO for min Go 1.6+, replace with ExitError.Stderr
    66  		errorOutput := strings.TrimSpace(string(cmd.Stderr.(*prefixSuffixSaver).Bytes()))
    67  		if errorOutput == "" {
    68  			// some commands might write nothing to stderr but something to stdout in error-conditions, in which case, we'll use that
    69  			// in the error string
    70  			errorOutput = strings.TrimSpace(string(out))
    71  		}
    72  
    73  		ran := cmd.Path
    74  		if len(cmd.Args) > 1 {
    75  			ran = fmt.Sprintf("%s %s", cmd.Path, quotedArgs(cmd.Args[1:]))
    76  		}
    77  		formattedErr := fmt.Errorf("Error running %s: '%s' '%s'", ran, errorOutput, strings.TrimSpace(exitError.Error()))
    78  
    79  		// return "" as output in error case, for callers that don't care about errors but rely on "" returned, in-case stdout != ""
    80  		return "", formattedErr
    81  	}
    82  
    83  	return strings.Trim(string(out), " \n"), err
    84  }
    85  
    86  func Trace(name string, args ...string) {
    87  	tracerx.Printf("exec: %s %s", name, quotedArgs(args))
    88  }
    89  
    90  func quotedArgs(args []string) string {
    91  	if len(args) == 0 {
    92  		return ""
    93  	}
    94  
    95  	quoted := make([]string, len(args))
    96  	for i, arg := range args {
    97  		quoted[i] = fmt.Sprintf("'%s'", arg)
    98  	}
    99  	return strings.Join(quoted, " ")
   100  }
   101  
   102  // An env for an exec.Command without GIT_TRACE
   103  var env []string
   104  var traceEnv = "GIT_TRACE="
   105  
   106  func init() {
   107  	realEnv := os.Environ()
   108  	env = make([]string, 0, len(realEnv))
   109  
   110  	for _, kv := range realEnv {
   111  		if strings.HasPrefix(kv, traceEnv) {
   112  			continue
   113  		}
   114  		env = append(env, kv)
   115  	}
   116  }
   117  
   118  // remaining code in file copied from Go 1.6 (c4fa25f4fc8f4419d0b0707bcdae9199a745face) exec.go and can be removed if moving to Go 1.6 minimum.
   119  // go 1.6 adds ExitError.Stderr with nice prefix/suffix trimming, which could replace cmd.Stderr above
   120  
   121  //start copied from Go 1.6 exec.go
   122  // prefixSuffixSaver is an io.Writer which retains the first N bytes
   123  // and the last N bytes written to it. The Bytes() methods reconstructs
   124  // it with a pretty error message.
   125  type prefixSuffixSaver struct {
   126  	N         int // max size of prefix or suffix
   127  	prefix    []byte
   128  	suffix    []byte // ring buffer once len(suffix) == N
   129  	suffixOff int    // offset to write into suffix
   130  	skipped   int64
   131  
   132  	// TODO(bradfitz): we could keep one large []byte and use part of it for
   133  	// the prefix, reserve space for the '... Omitting N bytes ...' message,
   134  	// then the ring buffer suffix, and just rearrange the ring buffer
   135  	// suffix when Bytes() is called, but it doesn't seem worth it for
   136  	// now just for error messages. It's only ~64KB anyway.
   137  }
   138  
   139  func (w *prefixSuffixSaver) Write(p []byte) (n int, err error) {
   140  	lenp := len(p)
   141  	p = w.fill(&w.prefix, p)
   142  
   143  	// Only keep the last w.N bytes of suffix data.
   144  	if overage := len(p) - w.N; overage > 0 {
   145  		p = p[overage:]
   146  		w.skipped += int64(overage)
   147  	}
   148  	p = w.fill(&w.suffix, p)
   149  
   150  	// w.suffix is full now if p is non-empty. Overwrite it in a circle.
   151  	for len(p) > 0 { // 0, 1, or 2 iterations.
   152  		n := copy(w.suffix[w.suffixOff:], p)
   153  		p = p[n:]
   154  		w.skipped += int64(n)
   155  		w.suffixOff += n
   156  		if w.suffixOff == w.N {
   157  			w.suffixOff = 0
   158  		}
   159  	}
   160  	return lenp, nil
   161  }
   162  
   163  // fill appends up to len(p) bytes of p to *dst, such that *dst does not
   164  // grow larger than w.N. It returns the un-appended suffix of p.
   165  func (w *prefixSuffixSaver) fill(dst *[]byte, p []byte) (pRemain []byte) {
   166  	if remain := w.N - len(*dst); remain > 0 {
   167  		add := minInt(len(p), remain)
   168  		*dst = append(*dst, p[:add]...)
   169  		p = p[add:]
   170  	}
   171  	return p
   172  }
   173  
   174  func (w *prefixSuffixSaver) Bytes() []byte {
   175  	if w.suffix == nil {
   176  		return w.prefix
   177  	}
   178  	if w.skipped == 0 {
   179  		return append(w.prefix, w.suffix...)
   180  	}
   181  	var buf bytes.Buffer
   182  	buf.Grow(len(w.prefix) + len(w.suffix) + 50)
   183  	buf.Write(w.prefix)
   184  	buf.WriteString("\n... omitting ")
   185  	buf.WriteString(strconv.FormatInt(w.skipped, 10))
   186  	buf.WriteString(" bytes ...\n")
   187  	buf.Write(w.suffix[w.suffixOff:])
   188  	buf.Write(w.suffix[:w.suffixOff])
   189  	return buf.Bytes()
   190  }
   191  
   192  func minInt(a, b int) int {
   193  	if a < b {
   194  		return a
   195  	}
   196  	return b
   197  }
   198  
   199  //end copied from Go 1.6 exec.go