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