Source file
src/os/exec/exec.go
Documentation: os/exec
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package exec
22
23 import (
24 "bytes"
25 "context"
26 "errors"
27 "io"
28 "os"
29 "path/filepath"
30 "runtime"
31 "strconv"
32 "strings"
33 "sync"
34 "syscall"
35 )
36
37
38
39 type Error struct {
40
41 Name string
42
43 Err error
44 }
45
46 func (e *Error) Error() string {
47 return "exec: " + strconv.Quote(e.Name) + ": " + e.Err.Error()
48 }
49
50
51
52
53
54 type Cmd struct {
55
56
57
58
59
60 Path string
61
62
63
64
65
66 Args []string
67
68
69
70
71
72
73
74 Env []string
75
76
77
78
79 Dir string
80
81
82
83
84
85
86
87
88
89
90
91
92
93 Stdin io.Reader
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110 Stdout io.Writer
111 Stderr io.Writer
112
113
114
115
116
117
118 ExtraFiles []*os.File
119
120
121
122 SysProcAttr *syscall.SysProcAttr
123
124
125 Process *os.Process
126
127
128
129 ProcessState *os.ProcessState
130
131 ctx context.Context
132 lookPathErr error
133 finished bool
134 childFiles []*os.File
135 closeAfterStart []io.Closer
136 closeAfterWait []io.Closer
137 goroutine []func() error
138 errch chan error
139 waitDone chan struct{}
140 }
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155 func Command(name string, arg ...string) *Cmd {
156 cmd := &Cmd{
157 Path: name,
158 Args: append([]string{name}, arg...),
159 }
160 if filepath.Base(name) == name {
161 if lp, err := LookPath(name); err != nil {
162 cmd.lookPathErr = err
163 } else {
164 cmd.Path = lp
165 }
166 }
167 return cmd
168 }
169
170
171
172
173
174
175 func CommandContext(ctx context.Context, name string, arg ...string) *Cmd {
176 if ctx == nil {
177 panic("nil Context")
178 }
179 cmd := Command(name, arg...)
180 cmd.ctx = ctx
181 return cmd
182 }
183
184
185
186 func interfaceEqual(a, b interface{}) bool {
187 defer func() {
188 recover()
189 }()
190 return a == b
191 }
192
193 func (c *Cmd) envv() []string {
194 if c.Env != nil {
195 return c.Env
196 }
197 return os.Environ()
198 }
199
200 func (c *Cmd) argv() []string {
201 if len(c.Args) > 0 {
202 return c.Args
203 }
204 return []string{c.Path}
205 }
206
207
208
209
210 var skipStdinCopyError func(error) bool
211
212 func (c *Cmd) stdin() (f *os.File, err error) {
213 if c.Stdin == nil {
214 f, err = os.Open(os.DevNull)
215 if err != nil {
216 return
217 }
218 c.closeAfterStart = append(c.closeAfterStart, f)
219 return
220 }
221
222 if f, ok := c.Stdin.(*os.File); ok {
223 return f, nil
224 }
225
226 pr, pw, err := os.Pipe()
227 if err != nil {
228 return
229 }
230
231 c.closeAfterStart = append(c.closeAfterStart, pr)
232 c.closeAfterWait = append(c.closeAfterWait, pw)
233 c.goroutine = append(c.goroutine, func() error {
234 _, err := io.Copy(pw, c.Stdin)
235 if skip := skipStdinCopyError; skip != nil && skip(err) {
236 err = nil
237 }
238 if err1 := pw.Close(); err == nil {
239 err = err1
240 }
241 return err
242 })
243 return pr, nil
244 }
245
246 func (c *Cmd) stdout() (f *os.File, err error) {
247 return c.writerDescriptor(c.Stdout)
248 }
249
250 func (c *Cmd) stderr() (f *os.File, err error) {
251 if c.Stderr != nil && interfaceEqual(c.Stderr, c.Stdout) {
252 return c.childFiles[1], nil
253 }
254 return c.writerDescriptor(c.Stderr)
255 }
256
257 func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) {
258 if w == nil {
259 f, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0)
260 if err != nil {
261 return
262 }
263 c.closeAfterStart = append(c.closeAfterStart, f)
264 return
265 }
266
267 if f, ok := w.(*os.File); ok {
268 return f, nil
269 }
270
271 pr, pw, err := os.Pipe()
272 if err != nil {
273 return
274 }
275
276 c.closeAfterStart = append(c.closeAfterStart, pw)
277 c.closeAfterWait = append(c.closeAfterWait, pr)
278 c.goroutine = append(c.goroutine, func() error {
279 _, err := io.Copy(w, pr)
280 pr.Close()
281 return err
282 })
283 return pw, nil
284 }
285
286 func (c *Cmd) closeDescriptors(closers []io.Closer) {
287 for _, fd := range closers {
288 fd.Close()
289 }
290 }
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305 func (c *Cmd) Run() error {
306 if err := c.Start(); err != nil {
307 return err
308 }
309 return c.Wait()
310 }
311
312
313
314
315 func lookExtensions(path, dir string) (string, error) {
316 if filepath.Base(path) == path {
317 path = filepath.Join(".", path)
318 }
319 if dir == "" {
320 return LookPath(path)
321 }
322 if filepath.VolumeName(path) != "" {
323 return LookPath(path)
324 }
325 if len(path) > 1 && os.IsPathSeparator(path[0]) {
326 return LookPath(path)
327 }
328 dirandpath := filepath.Join(dir, path)
329
330 lp, err := LookPath(dirandpath)
331 if err != nil {
332 return "", err
333 }
334 ext := strings.TrimPrefix(lp, dirandpath)
335 return path + ext, nil
336 }
337
338
339
340
341
342 func (c *Cmd) Start() error {
343 if c.lookPathErr != nil {
344 c.closeDescriptors(c.closeAfterStart)
345 c.closeDescriptors(c.closeAfterWait)
346 return c.lookPathErr
347 }
348 if runtime.GOOS == "windows" {
349 lp, err := lookExtensions(c.Path, c.Dir)
350 if err != nil {
351 c.closeDescriptors(c.closeAfterStart)
352 c.closeDescriptors(c.closeAfterWait)
353 return err
354 }
355 c.Path = lp
356 }
357 if c.Process != nil {
358 return errors.New("exec: already started")
359 }
360 if c.ctx != nil {
361 select {
362 case <-c.ctx.Done():
363 c.closeDescriptors(c.closeAfterStart)
364 c.closeDescriptors(c.closeAfterWait)
365 return c.ctx.Err()
366 default:
367 }
368 }
369
370 type F func(*Cmd) (*os.File, error)
371 for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} {
372 fd, err := setupFd(c)
373 if err != nil {
374 c.closeDescriptors(c.closeAfterStart)
375 c.closeDescriptors(c.closeAfterWait)
376 return err
377 }
378 c.childFiles = append(c.childFiles, fd)
379 }
380 c.childFiles = append(c.childFiles, c.ExtraFiles...)
381
382 var err error
383 c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{
384 Dir: c.Dir,
385 Files: c.childFiles,
386 Env: dedupEnv(c.envv()),
387 Sys: c.SysProcAttr,
388 })
389 if err != nil {
390 c.closeDescriptors(c.closeAfterStart)
391 c.closeDescriptors(c.closeAfterWait)
392 return err
393 }
394
395 c.closeDescriptors(c.closeAfterStart)
396
397 c.errch = make(chan error, len(c.goroutine))
398 for _, fn := range c.goroutine {
399 go func(fn func() error) {
400 c.errch <- fn()
401 }(fn)
402 }
403
404 if c.ctx != nil {
405 c.waitDone = make(chan struct{})
406 go func() {
407 select {
408 case <-c.ctx.Done():
409 c.Process.Kill()
410 case <-c.waitDone:
411 }
412 }()
413 }
414
415 return nil
416 }
417
418
419 type ExitError struct {
420 *os.ProcessState
421
422
423
424
425
426
427
428
429
430
431
432 Stderr []byte
433 }
434
435 func (e *ExitError) Error() string {
436 return e.ProcessState.String()
437 }
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456 func (c *Cmd) Wait() error {
457 if c.Process == nil {
458 return errors.New("exec: not started")
459 }
460 if c.finished {
461 return errors.New("exec: Wait was already called")
462 }
463 c.finished = true
464
465 state, err := c.Process.Wait()
466 if c.waitDone != nil {
467 close(c.waitDone)
468 }
469 c.ProcessState = state
470
471 var copyError error
472 for range c.goroutine {
473 if err := <-c.errch; err != nil && copyError == nil {
474 copyError = err
475 }
476 }
477
478 c.closeDescriptors(c.closeAfterWait)
479
480 if err != nil {
481 return err
482 } else if !state.Success() {
483 return &ExitError{ProcessState: state}
484 }
485
486 return copyError
487 }
488
489
490
491
492 func (c *Cmd) Output() ([]byte, error) {
493 if c.Stdout != nil {
494 return nil, errors.New("exec: Stdout already set")
495 }
496 var stdout bytes.Buffer
497 c.Stdout = &stdout
498
499 captureErr := c.Stderr == nil
500 if captureErr {
501 c.Stderr = &prefixSuffixSaver{N: 32 << 10}
502 }
503
504 err := c.Run()
505 if err != nil && captureErr {
506 if ee, ok := err.(*ExitError); ok {
507 ee.Stderr = c.Stderr.(*prefixSuffixSaver).Bytes()
508 }
509 }
510 return stdout.Bytes(), err
511 }
512
513
514
515 func (c *Cmd) CombinedOutput() ([]byte, error) {
516 if c.Stdout != nil {
517 return nil, errors.New("exec: Stdout already set")
518 }
519 if c.Stderr != nil {
520 return nil, errors.New("exec: Stderr already set")
521 }
522 var b bytes.Buffer
523 c.Stdout = &b
524 c.Stderr = &b
525 err := c.Run()
526 return b.Bytes(), err
527 }
528
529
530
531
532
533
534
535 func (c *Cmd) StdinPipe() (io.WriteCloser, error) {
536 if c.Stdin != nil {
537 return nil, errors.New("exec: Stdin already set")
538 }
539 if c.Process != nil {
540 return nil, errors.New("exec: StdinPipe after process started")
541 }
542 pr, pw, err := os.Pipe()
543 if err != nil {
544 return nil, err
545 }
546 c.Stdin = pr
547 c.closeAfterStart = append(c.closeAfterStart, pr)
548 wc := &closeOnce{File: pw}
549 c.closeAfterWait = append(c.closeAfterWait, wc)
550 return wc, nil
551 }
552
553 type closeOnce struct {
554 *os.File
555
556 once sync.Once
557 err error
558 }
559
560 func (c *closeOnce) Close() error {
561 c.once.Do(c.close)
562 return c.err
563 }
564
565 func (c *closeOnce) close() {
566 c.err = c.File.Close()
567 }
568
569
570
571
572
573
574
575
576
577 func (c *Cmd) StdoutPipe() (io.ReadCloser, error) {
578 if c.Stdout != nil {
579 return nil, errors.New("exec: Stdout already set")
580 }
581 if c.Process != nil {
582 return nil, errors.New("exec: StdoutPipe after process started")
583 }
584 pr, pw, err := os.Pipe()
585 if err != nil {
586 return nil, err
587 }
588 c.Stdout = pw
589 c.closeAfterStart = append(c.closeAfterStart, pw)
590 c.closeAfterWait = append(c.closeAfterWait, pr)
591 return pr, nil
592 }
593
594
595
596
597
598
599
600
601
602 func (c *Cmd) StderrPipe() (io.ReadCloser, error) {
603 if c.Stderr != nil {
604 return nil, errors.New("exec: Stderr already set")
605 }
606 if c.Process != nil {
607 return nil, errors.New("exec: StderrPipe after process started")
608 }
609 pr, pw, err := os.Pipe()
610 if err != nil {
611 return nil, err
612 }
613 c.Stderr = pw
614 c.closeAfterStart = append(c.closeAfterStart, pw)
615 c.closeAfterWait = append(c.closeAfterWait, pr)
616 return pr, nil
617 }
618
619
620
621
622 type prefixSuffixSaver struct {
623 N int
624 prefix []byte
625 suffix []byte
626 suffixOff int
627 skipped int64
628
629
630
631
632
633
634 }
635
636 func (w *prefixSuffixSaver) Write(p []byte) (n int, err error) {
637 lenp := len(p)
638 p = w.fill(&w.prefix, p)
639
640
641 if overage := len(p) - w.N; overage > 0 {
642 p = p[overage:]
643 w.skipped += int64(overage)
644 }
645 p = w.fill(&w.suffix, p)
646
647
648 for len(p) > 0 {
649 n := copy(w.suffix[w.suffixOff:], p)
650 p = p[n:]
651 w.skipped += int64(n)
652 w.suffixOff += n
653 if w.suffixOff == w.N {
654 w.suffixOff = 0
655 }
656 }
657 return lenp, nil
658 }
659
660
661
662 func (w *prefixSuffixSaver) fill(dst *[]byte, p []byte) (pRemain []byte) {
663 if remain := w.N - len(*dst); remain > 0 {
664 add := minInt(len(p), remain)
665 *dst = append(*dst, p[:add]...)
666 p = p[add:]
667 }
668 return p
669 }
670
671 func (w *prefixSuffixSaver) Bytes() []byte {
672 if w.suffix == nil {
673 return w.prefix
674 }
675 if w.skipped == 0 {
676 return append(w.prefix, w.suffix...)
677 }
678 var buf bytes.Buffer
679 buf.Grow(len(w.prefix) + len(w.suffix) + 50)
680 buf.Write(w.prefix)
681 buf.WriteString("\n... omitting ")
682 buf.WriteString(strconv.FormatInt(w.skipped, 10))
683 buf.WriteString(" bytes ...\n")
684 buf.Write(w.suffix[w.suffixOff:])
685 buf.Write(w.suffix[:w.suffixOff])
686 return buf.Bytes()
687 }
688
689 func minInt(a, b int) int {
690 if a < b {
691 return a
692 }
693 return b
694 }
695
696
697
698
699 func dedupEnv(env []string) []string {
700 return dedupEnvCase(runtime.GOOS == "windows", env)
701 }
702
703
704
705 func dedupEnvCase(caseInsensitive bool, env []string) []string {
706 out := make([]string, 0, len(env))
707 saw := map[string]int{}
708 for _, kv := range env {
709 eq := strings.Index(kv, "=")
710 if eq < 0 {
711 out = append(out, kv)
712 continue
713 }
714 k := kv[:eq]
715 if caseInsensitive {
716 k = strings.ToLower(k)
717 }
718 if dupIdx, isDup := saw[k]; isDup {
719 out[dupIdx] = kv
720 continue
721 }
722 saw[k] = len(out)
723 out = append(out, kv)
724 }
725 return out
726 }
727
View as plain text