github.com/saracen/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