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