| // Copyright 2009 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // Package exec runs external commands. It wraps os.StartProcess to make it | |
| // easier to remap stdin and stdout, connect I/O with pipes, and do other | |
| // adjustments. | |
| // | |
| // Unlike the "system" library call from C and other languages, the | |
| // os/exec package intentionally does not invoke the system shell and | |
| // does not expand any glob patterns or handle other expansions, | |
| // pipelines, or redirections typically done by shells. The package | |
| // behaves more like C's "exec" family of functions. To expand glob | |
| // patterns, either call the shell directly, taking care to escape any | |
| // dangerous input, or use the path/filepath package's Glob function. | |
| // To expand environment variables, use package os's ExpandEnv. | |
| // | |
| // Note that the examples in this package assume a Unix system. | |
| // They may not run on Windows, and they do not run in the Go Playground | |
| // used by golang.org and godoc.org. | |
| // | |
| // # Executables in the current directory | |
| // | |
| // The functions Command and LookPath look for a program | |
| // in the directories listed in the current path, following the | |
| // conventions of the host operating system. | |
| // Operating systems have for decades included the current | |
| // directory in this search, sometimes implicitly and sometimes | |
| // configured explicitly that way by default. | |
| // Modern practice is that including the current directory | |
| // is usually unexpected and often leads to security problems. | |
| // | |
| // To avoid those security problems, as of Go 1.19, this package will not resolve a program | |
| // using an implicit or explicit path entry relative to the current directory. | |
| // That is, if you run exec.LookPath("go"), it will not successfully return | |
| // ./go on Unix nor .\go.exe on Windows, no matter how the path is configured. | |
| // Instead, if the usual path algorithms would result in that answer, | |
| // these functions return an error err satisfying errors.Is(err, ErrDot). | |
| // | |
| // For example, consider these two program snippets: | |
| // | |
| // path, err := exec.LookPath("prog") | |
| // if err != nil { | |
| // log.Fatal(err) | |
| // } | |
| // use(path) | |
| // | |
| // and | |
| // | |
| // cmd := exec.Command("prog") | |
| // if err := cmd.Run(); err != nil { | |
| // log.Fatal(err) | |
| // } | |
| // | |
| // These will not find and run ./prog or .\prog.exe, | |
| // no matter how the current path is configured. | |
| // | |
| // Code that always wants to run a program from the current directory | |
| // can be rewritten to say "./prog" instead of "prog". | |
| // | |
| // Code that insists on including results from relative path entries | |
| // can instead override the error using an errors.Is check: | |
| // | |
| // path, err := exec.LookPath("prog") | |
| // if errors.Is(err, exec.ErrDot) { | |
| // err = nil | |
| // } | |
| // if err != nil { | |
| // log.Fatal(err) | |
| // } | |
| // use(path) | |
| // | |
| // and | |
| // | |
| // cmd := exec.Command("prog") | |
| // if errors.Is(cmd.Err, exec.ErrDot) { | |
| // cmd.Err = nil | |
| // } | |
| // if err := cmd.Run(); err != nil { | |
| // log.Fatal(err) | |
| // } | |
| // | |
| // Before adding such overrides, make sure you understand the | |
| // security implications of doing so. | |
| // See https://go.dev/blog/path-security for more information. | |
| package exec | |
| import ( | |
| "bytes" | |
| "context" | |
| "errors" | |
| "internal/syscall/execenv" | |
| "io" | |
| "os" | |
| "path/filepath" | |
| "runtime" | |
| "strconv" | |
| "strings" | |
| "sync" | |
| "syscall" | |
| ) | |
| // Error is returned by LookPath when it fails to classify a file as an | |
| // executable. | |
| type Error struct { | |
| // Name is the file name for which the error occurred. | |
| Name string | |
| // Err is the underlying error. | |
| Err error | |
| } | |
| func (e *Error) Error() string { | |
| return "exec: " + strconv.Quote(e.Name) + ": " + e.Err.Error() | |
| } | |
| func (e *Error) Unwrap() error { return e.Err } | |
| // wrappedError wraps an error without relying on fmt.Errorf. | |
| type wrappedError struct { | |
| prefix string | |
| err error | |
| } | |
| func (w wrappedError) Error() string { | |
| return w.prefix + ": " + w.err.Error() | |
| } | |
| func (w wrappedError) Unwrap() error { | |
| return w.err | |
| } | |
| // Cmd represents an external command being prepared or run. | |
| // | |
| // A Cmd cannot be reused after calling its Run, Output or CombinedOutput | |
| // methods. | |
| type Cmd struct { | |
| // Path is the path of the command to run. | |
| // | |
| // This is the only field that must be set to a non-zero | |
| // value. If Path is relative, it is evaluated relative | |
| // to Dir. | |
| Path string | |
| // Args holds command line arguments, including the command as Args[0]. | |
| // If the Args field is empty or nil, Run uses {Path}. | |
| // | |
| // In typical use, both Path and Args are set by calling Command. | |
| Args []string | |
| // Env specifies the environment of the process. | |
| // Each entry is of the form "key=value". | |
| // If Env is nil, the new process uses the current process's | |
| // environment. | |
| // If Env contains duplicate environment keys, only the last | |
| // value in the slice for each duplicate key is used. | |
| // As a special case on Windows, SYSTEMROOT is always added if | |
| // missing and not explicitly set to the empty string. | |
| Env []string | |
| // Dir specifies the working directory of the command. | |
| // If Dir is the empty string, Run runs the command in the | |
| // calling process's current directory. | |
| Dir string | |
| // Stdin specifies the process's standard input. | |
| // | |
| // If Stdin is nil, the process reads from the null device (os.DevNull). | |
| // | |
| // If Stdin is an *os.File, the process's standard input is connected | |
| // directly to that file. | |
| // | |
| // Otherwise, during the execution of the command a separate | |
| // goroutine reads from Stdin and delivers that data to the command | |
| // over a pipe. In this case, Wait does not complete until the goroutine | |
| // stops copying, either because it has reached the end of Stdin | |
| // (EOF or a read error) or because writing to the pipe returned an error. | |
| Stdin io.Reader | |
| // Stdout and Stderr specify the process's standard output and error. | |
| // | |
| // If either is nil, Run connects the corresponding file descriptor | |
| // to the null device (os.DevNull). | |
| // | |
| // If either is an *os.File, the corresponding output from the process | |
| // is connected directly to that file. | |
| // | |
| // Otherwise, during the execution of the command a separate goroutine | |
| // reads from the process over a pipe and delivers that data to the | |
| // corresponding Writer. In this case, Wait does not complete until the | |
| // goroutine reaches EOF or encounters an error. | |
| // | |
| // If Stdout and Stderr are the same writer, and have a type that can | |
| // be compared with ==, at most one goroutine at a time will call Write. | |
| Stdout io.Writer | |
| Stderr io.Writer | |
| // ExtraFiles specifies additional open files to be inherited by the | |
| // new process. It does not include standard input, standard output, or | |
| // standard error. If non-nil, entry i becomes file descriptor 3+i. | |
| // | |
| // ExtraFiles is not supported on Windows. | |
| ExtraFiles []*os.File | |
| // SysProcAttr holds optional, operating system-specific attributes. | |
| // Run passes it to os.StartProcess as the os.ProcAttr's Sys field. | |
| SysProcAttr *syscall.SysProcAttr | |
| // Process is the underlying process, once started. | |
| Process *os.Process | |
| // ProcessState contains information about an exited process, | |
| // available after a call to Wait or Run. | |
| ProcessState *os.ProcessState | |
| ctx context.Context // nil means none | |
| Err error // LookPath error, if any. | |
| childFiles []*os.File | |
| closeAfterStart []io.Closer | |
| closeAfterWait []io.Closer | |
| goroutine []func() error | |
| goroutineErrs <-chan error // one receive per goroutine | |
| ctxErr <-chan error // if non nil, receives the error from watchCtx exactly once | |
| // For a security release long ago, we created x/sys/execabs, | |
| // which manipulated the unexported lookPathErr error field | |
| // in this struct. For Go 1.19 we exported the field as Err error, | |
| // above, but we have to keep lookPathErr around for use by | |
| // old programs building against new toolchains. | |
| // The String and Start methods look for an error in lookPathErr | |
| // in preference to Err, to preserve the errors that execabs sets. | |
| // | |
| // In general we don't guarantee misuse of reflect like this, | |
| // but the misuse of reflect was by us, the best of various bad | |
| // options to fix the security problem, and people depend on | |
| // those old copies of execabs continuing to work. | |
| // The result is that we have to leave this variable around for the | |
| // rest of time, a compatibility scar. | |
| // | |
| // See https://go.dev/blog/path-security | |
| // and https://go.dev/issue/43724 for more context. | |
| lookPathErr error | |
| } | |
| // Command returns the Cmd struct to execute the named program with | |
| // the given arguments. | |
| // | |
| // It sets only the Path and Args in the returned structure. | |
| // | |
| // If name contains no path separators, Command uses LookPath to | |
| // resolve name to a complete path if possible. Otherwise it uses name | |
| // directly as Path. | |
| // | |
| // The returned Cmd's Args field is constructed from the command name | |
| // followed by the elements of arg, so arg should not include the | |
| // command name itself. For example, Command("echo", "hello"). | |
| // Args[0] is always name, not the possibly resolved Path. | |
| // | |
| // On Windows, processes receive the whole command line as a single string | |
| // and do their own parsing. Command combines and quotes Args into a command | |
| // line string with an algorithm compatible with applications using | |
| // CommandLineToArgvW (which is the most common way). Notable exceptions are | |
| // msiexec.exe and cmd.exe (and thus, all batch files), which have a different | |
| // unquoting algorithm. In these or other similar cases, you can do the | |
| // quoting yourself and provide the full command line in SysProcAttr.CmdLine, | |
| // leaving Args empty. | |
| func Command(name string, arg ...string) *Cmd { | |
| cmd := &Cmd{ | |
| Path: name, | |
| Args: append([]string{name}, arg...), | |
| } | |
| if filepath.Base(name) == name { | |
| lp, err := LookPath(name) | |
| if lp != "" { | |
| // Update cmd.Path even if err is non-nil. | |
| // If err is ErrDot (especially on Windows), lp may include a resolved | |
| // extension (like .exe or .bat) that should be preserved. | |
| cmd.Path = lp | |
| } | |
| if err != nil { | |
| cmd.Err = err | |
| } | |
| } | |
| return cmd | |
| } | |
| // CommandContext is like Command but includes a context. | |
| // | |
| // The provided context is used to kill the process (by calling | |
| // os.Process.Kill) if the context becomes done before the command | |
| // completes on its own. | |
| func CommandContext(ctx context.Context, name string, arg ...string) *Cmd { | |
| if ctx == nil { | |
| panic("nil Context") | |
| } | |
| cmd := Command(name, arg...) | |
| cmd.ctx = ctx | |
| return cmd | |
| } | |
| // String returns a human-readable description of c. | |
| // It is intended only for debugging. | |
| // In particular, it is not suitable for use as input to a shell. | |
| // The output of String may vary across Go releases. | |
| func (c *Cmd) String() string { | |
| if c.Err != nil || c.lookPathErr != nil { | |
| // failed to resolve path; report the original requested path (plus args) | |
| return strings.Join(c.Args, " ") | |
| } | |
| // report the exact executable path (plus args) | |
| b := new(strings.Builder) | |
| b.WriteString(c.Path) | |
| for _, a := range c.Args[1:] { | |
| b.WriteByte(' ') | |
| b.WriteString(a) | |
| } | |
| return b.String() | |
| } | |
| // interfaceEqual protects against panics from doing equality tests on | |
| // two interfaces with non-comparable underlying types. | |
| func interfaceEqual(a, b any) bool { | |
| defer func() { | |
| recover() | |
| }() | |
| return a == b | |
| } | |
| func (c *Cmd) argv() []string { | |
| if len(c.Args) > 0 { | |
| return c.Args | |
| } | |
| return []string{c.Path} | |
| } | |
| func (c *Cmd) stdin() (f *os.File, err error) { | |
| if c.Stdin == nil { | |
| f, err = os.Open(os.DevNull) | |
| if err != nil { | |
| return | |
| } | |
| c.closeAfterStart = append(c.closeAfterStart, f) | |
| return | |
| } | |
| if f, ok := c.Stdin.(*os.File); ok { | |
| return f, nil | |
| } | |
| pr, pw, err := os.Pipe() | |
| if err != nil { | |
| return | |
| } | |
| c.closeAfterStart = append(c.closeAfterStart, pr) | |
| c.closeAfterWait = append(c.closeAfterWait, pw) | |
| c.goroutine = append(c.goroutine, func() error { | |
| _, err := io.Copy(pw, c.Stdin) | |
| if skipStdinCopyError(err) { | |
| err = nil | |
| } | |
| if err1 := pw.Close(); err == nil { | |
| err = err1 | |
| } | |
| return err | |
| }) | |
| return pr, nil | |
| } | |
| func (c *Cmd) stdout() (f *os.File, err error) { | |
| return c.writerDescriptor(c.Stdout) | |
| } | |
| func (c *Cmd) stderr() (f *os.File, err error) { | |
| if c.Stderr != nil && interfaceEqual(c.Stderr, c.Stdout) { | |
| return c.childFiles[1], nil | |
| } | |
| return c.writerDescriptor(c.Stderr) | |
| } | |
| func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) { | |
| if w == nil { | |
| f, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0) | |
| if err != nil { | |
| return | |
| } | |
| c.closeAfterStart = append(c.closeAfterStart, f) | |
| return | |
| } | |
| if f, ok := w.(*os.File); ok { | |
| return f, nil | |
| } | |
| pr, pw, err := os.Pipe() | |
| if err != nil { | |
| return | |
| } | |
| c.closeAfterStart = append(c.closeAfterStart, pw) | |
| c.closeAfterWait = append(c.closeAfterWait, pr) | |
| c.goroutine = append(c.goroutine, func() error { | |
| _, err := io.Copy(w, pr) | |
| pr.Close() // in case io.Copy stopped due to write error | |
| return err | |
| }) | |
| return pw, nil | |
| } | |
| func (c *Cmd) closeDescriptors(closers []io.Closer) { | |
| for _, fd := range closers { | |
| fd.Close() | |
| } | |
| } | |
| // Run starts the specified command and waits for it to complete. | |
| // | |
| // The returned error is nil if the command runs, has no problems | |
| // copying stdin, stdout, and stderr, and exits with a zero exit | |
| // status. | |
| // | |
| // If the command starts but does not complete successfully, the error is of | |
| // type *ExitError. Other error types may be returned for other situations. | |
| // | |
| // If the calling goroutine has locked the operating system thread | |
| // with runtime.LockOSThread and modified any inheritable OS-level | |
| // thread state (for example, Linux or Plan 9 name spaces), the new | |
| // process will inherit the caller's thread state. | |
| func (c *Cmd) Run() error { | |
| if err := c.Start(); err != nil { | |
| return err | |
| } | |
| return c.Wait() | |
| } | |
| // lookExtensions finds windows executable by its dir and path. | |
| // It uses LookPath to try appropriate extensions. | |
| // lookExtensions does not search PATH, instead it converts `prog` into `.\prog`. | |
| func lookExtensions(path, dir string) (string, error) { | |
| if filepath.Base(path) == path { | |
| path = "." + string(filepath.Separator) + path | |
| } | |
| if dir == "" { | |
| return LookPath(path) | |
| } | |
| if filepath.VolumeName(path) != "" { | |
| return LookPath(path) | |
| } | |
| if len(path) > 1 && os.IsPathSeparator(path[0]) { | |
| return LookPath(path) | |
| } | |
| dirandpath := filepath.Join(dir, path) | |
| // We assume that LookPath will only add file extension. | |
| lp, err := LookPath(dirandpath) | |
| if err != nil { | |
| return "", err | |
| } | |
| ext := strings.TrimPrefix(lp, dirandpath) | |
| return path + ext, nil | |
| } | |
| // Start starts the specified command but does not wait for it to complete. | |
| // | |
| // If Start returns successfully, the c.Process field will be set. | |
| // | |
| // After a successful call to Start the Wait method must be called in | |
| // order to release associated system resources. | |
| func (c *Cmd) Start() error { | |
| if c.Path == "" && c.Err == nil && c.lookPathErr == nil { | |
| c.Err = errors.New("exec: no command") | |
| } | |
| if c.Err != nil || c.lookPathErr != nil { | |
| c.closeDescriptors(c.closeAfterStart) | |
| c.closeDescriptors(c.closeAfterWait) | |
| if c.lookPathErr != nil { | |
| return c.lookPathErr | |
| } | |
| return c.Err | |
| } | |
| if runtime.GOOS == "windows" { | |
| lp, err := lookExtensions(c.Path, c.Dir) | |
| if err != nil { | |
| c.closeDescriptors(c.closeAfterStart) | |
| c.closeDescriptors(c.closeAfterWait) | |
| return err | |
| } | |
| c.Path = lp | |
| } | |
| if c.Process != nil { | |
| return errors.New("exec: already started") | |
| } | |
| if c.ctx != nil { | |
| select { | |
| case <-c.ctx.Done(): | |
| c.closeDescriptors(c.closeAfterStart) | |
| c.closeDescriptors(c.closeAfterWait) | |
| return c.ctx.Err() | |
| default: | |
| } | |
| } | |
| c.childFiles = make([]*os.File, 0, 3+len(c.ExtraFiles)) | |
| type F func(*Cmd) (*os.File, error) | |
| for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} { | |
| fd, err := setupFd(c) | |
| if err != nil { | |
| c.closeDescriptors(c.closeAfterStart) | |
| c.closeDescriptors(c.closeAfterWait) | |
| return err | |
| } | |
| c.childFiles = append(c.childFiles, fd) | |
| } | |
| c.childFiles = append(c.childFiles, c.ExtraFiles...) | |
| env, err := c.environ() | |
| if err != nil { | |
| return err | |
| } | |
| c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{ | |
| Dir: c.Dir, | |
| Files: c.childFiles, | |
| Env: env, | |
| Sys: c.SysProcAttr, | |
| }) | |
| if err != nil { | |
| c.closeDescriptors(c.closeAfterStart) | |
| c.closeDescriptors(c.closeAfterWait) | |
| return err | |
| } | |
| c.closeDescriptors(c.closeAfterStart) | |
| // Don't allocate the goroutineErrs channel unless there are goroutines to fire. | |
| if len(c.goroutine) > 0 { | |
| errc := make(chan error, len(c.goroutine)) | |
| c.goroutineErrs = errc | |
| for _, fn := range c.goroutine { | |
| go func(fn func() error) { | |
| errc <- fn() | |
| }(fn) | |
| } | |
| } | |
| c.ctxErr = c.watchCtx() | |
| return nil | |
| } | |
| // An ExitError reports an unsuccessful exit by a command. | |
| type ExitError struct { | |
| *os.ProcessState | |
| // Stderr holds a subset of the standard error output from the | |
| // Cmd.Output method if standard error was not otherwise being | |
| // collected. | |
| // | |
| // If the error output is long, Stderr may contain only a prefix | |
| // and suffix of the output, with the middle replaced with | |
| // text about the number of omitted bytes. | |
| // | |
| // Stderr is provided for debugging, for inclusion in error messages. | |
| // Users with other needs should redirect Cmd.Stderr as needed. | |
| Stderr []byte | |
| } | |
| func (e *ExitError) Error() string { | |
| return e.ProcessState.String() | |
| } | |
| // Wait waits for the command to exit and waits for any copying to | |
| // stdin or copying from stdout or stderr to complete. | |
| // | |
| // The command must have been started by Start. | |
| // | |
| // The returned error is nil if the command runs, has no problems | |
| // copying stdin, stdout, and stderr, and exits with a zero exit | |
| // status. | |
| // | |
| // If the command fails to run or doesn't complete successfully, the | |
| // error is of type *ExitError. Other error types may be | |
| // returned for I/O problems. | |
| // | |
| // If any of c.Stdin, c.Stdout or c.Stderr are not an *os.File, Wait also waits | |
| // for the respective I/O loop copying to or from the process to complete. | |
| // | |
| // Wait releases any resources associated with the Cmd. | |
| func (c *Cmd) Wait() error { | |
| if c.Process == nil { | |
| return errors.New("exec: not started") | |
| } | |
| if c.ProcessState != nil { | |
| return errors.New("exec: Wait was already called") | |
| } | |
| state, err := c.Process.Wait() | |
| if err == nil && !state.Success() { | |
| err = &ExitError{ProcessState: state} | |
| } | |
| c.ProcessState = state | |
| // Wait for the pipe-copying goroutines to complete. | |
| var copyError error | |
| for range c.goroutine { | |
| if err := <-c.goroutineErrs; err != nil && copyError == nil { | |
| copyError = err | |
| } | |
| } | |
| c.goroutine = nil // Allow the goroutines' closures to be GC'd. | |
| if c.ctxErr != nil { | |
| interruptErr := <-c.ctxErr | |
| // If c.Process.Wait returned an error, prefer that. | |
| // Otherwise, report any error from the interrupt goroutine. | |
| if interruptErr != nil && err == nil { | |
| err = interruptErr | |
| } | |
| } | |
| // Report errors from the copying goroutines only if the program otherwise | |
| // exited normally on its own. Otherwise, the copying error may be due to the | |
| // abnormal termination. | |
| if err == nil { | |
| err = copyError | |
| } | |
| c.closeDescriptors(c.closeAfterWait) | |
| c.closeAfterWait = nil | |
| return err | |
| } | |
| // watchCtx conditionally starts a goroutine that waits until either c.ctx is | |
| // done or c.Process.Wait has completed (called from Wait). | |
| // If c.ctx is done first, the goroutine terminates c.Process. | |
| // | |
| // If a goroutine was started, watchCtx returns a channel on which its result | |
| // must be received. | |
| func (c *Cmd) watchCtx() <-chan error { | |
| if c.ctx == nil { | |
| return nil | |
| } | |
| errc := make(chan error) | |
| go func() { | |
| select { | |
| case errc <- nil: | |
| return | |
| case <-c.ctx.Done(): | |
| } | |
| var err error | |
| if killErr := c.Process.Kill(); killErr == nil { | |
| // We appear to have successfully delivered a kill signal, so any | |
| // program behavior from this point may be due to ctx. | |
| err = c.ctx.Err() | |
| } else if !errors.Is(killErr, os.ErrProcessDone) { | |
| err = wrappedError{ | |
| prefix: "exec: error sending signal to Cmd", | |
| err: killErr, | |
| } | |
| } | |
| errc <- err | |
| }() | |
| return errc | |
| } | |
| // Output runs the command and returns its standard output. | |
| // Any returned error will usually be of type *ExitError. | |
| // If c.Stderr was nil, Output populates ExitError.Stderr. | |
| func (c *Cmd) Output() ([]byte, error) { | |
| if c.Stdout != nil { | |
| return nil, errors.New("exec: Stdout already set") | |
| } | |
| var stdout bytes.Buffer | |
| c.Stdout = &stdout | |
| captureErr := c.Stderr == nil | |
| if captureErr { | |
| c.Stderr = &prefixSuffixSaver{N: 32 << 10} | |
| } | |
| err := c.Run() | |
| if err != nil && captureErr { | |
| if ee, ok := err.(*ExitError); ok { | |
| ee.Stderr = c.Stderr.(*prefixSuffixSaver).Bytes() | |
| } | |
| } | |
| return stdout.Bytes(), err | |
| } | |
| // CombinedOutput runs the command and returns its combined standard | |
| // output and standard error. | |
| func (c *Cmd) CombinedOutput() ([]byte, error) { | |
| if c.Stdout != nil { | |
| return nil, errors.New("exec: Stdout already set") | |
| } | |
| if c.Stderr != nil { | |
| return nil, errors.New("exec: Stderr already set") | |
| } | |
| var b bytes.Buffer | |
| c.Stdout = &b | |
| c.Stderr = &b | |
| err := c.Run() | |
| return b.Bytes(), err | |
| } | |
| // StdinPipe returns a pipe that will be connected to the command's | |
| // standard input when the command starts. | |
| // The pipe will be closed automatically after Wait sees the command exit. | |
| // A caller need only call Close to force the pipe to close sooner. | |
| // For example, if the command being run will not exit until standard input | |
| // is closed, the caller must close the pipe. | |
| func (c *Cmd) StdinPipe() (io.WriteCloser, error) { | |
| if c.Stdin != nil { | |
| return nil, errors.New("exec: Stdin already set") | |
| } | |
| if c.Process != nil { | |
| return nil, errors.New("exec: StdinPipe after process started") | |
| } | |
| pr, pw, err := os.Pipe() | |
| if err != nil { | |
| return nil, err | |
| } | |
| c.Stdin = pr | |
| c.closeAfterStart = append(c.closeAfterStart, pr) | |
| wc := &closeOnce{File: pw} | |
| c.closeAfterWait = append(c.closeAfterWait, wc) | |
| return wc, nil | |
| } | |
| type closeOnce struct { | |
| *os.File | |
| once sync.Once | |
| err error | |
| } | |
| func (c *closeOnce) Close() error { | |
| c.once.Do(c.close) | |
| return c.err | |
| } | |
| func (c *closeOnce) close() { | |
| c.err = c.File.Close() | |
| } | |
| // StdoutPipe returns a pipe that will be connected to the command's | |
| // standard output when the command starts. | |
| // | |
| // Wait will close the pipe after seeing the command exit, so most callers | |
| // need not close the pipe themselves. It is thus incorrect to call Wait | |
| // before all reads from the pipe have completed. | |
| // For the same reason, it is incorrect to call Run when using StdoutPipe. | |
| // See the example for idiomatic usage. | |
| func (c *Cmd) StdoutPipe() (io.ReadCloser, error) { | |
| if c.Stdout != nil { | |
| return nil, errors.New("exec: Stdout already set") | |
| } | |
| if c.Process != nil { | |
| return nil, errors.New("exec: StdoutPipe after process started") | |
| } | |
| pr, pw, err := os.Pipe() | |
| if err != nil { | |
| return nil, err | |
| } | |
| c.Stdout = pw | |
| c.closeAfterStart = append(c.closeAfterStart, pw) | |
| c.closeAfterWait = append(c.closeAfterWait, pr) | |
| return pr, nil | |
| } | |
| // StderrPipe returns a pipe that will be connected to the command's | |
| // standard error when the command starts. | |
| // | |
| // Wait will close the pipe after seeing the command exit, so most callers | |
| // need not close the pipe themselves. It is thus incorrect to call Wait | |
| // before all reads from the pipe have completed. | |
| // For the same reason, it is incorrect to use Run when using StderrPipe. | |
| // See the StdoutPipe example for idiomatic usage. | |
| func (c *Cmd) StderrPipe() (io.ReadCloser, error) { | |
| if c.Stderr != nil { | |
| return nil, errors.New("exec: Stderr already set") | |
| } | |
| if c.Process != nil { | |
| return nil, errors.New("exec: StderrPipe after process started") | |
| } | |
| pr, pw, err := os.Pipe() | |
| if err != nil { | |
| return nil, err | |
| } | |
| c.Stderr = pw | |
| c.closeAfterStart = append(c.closeAfterStart, pw) | |
| c.closeAfterWait = append(c.closeAfterWait, pr) | |
| return pr, nil | |
| } | |
| // prefixSuffixSaver is an io.Writer which retains the first N bytes | |
| // and the last N bytes written to it. The Bytes() methods reconstructs | |
| // it with a pretty error message. | |
| type prefixSuffixSaver struct { | |
| N int // max size of prefix or suffix | |
| prefix []byte | |
| suffix []byte // ring buffer once len(suffix) == N | |
| suffixOff int // offset to write into suffix | |
| skipped int64 | |
| // TODO(bradfitz): we could keep one large []byte and use part of it for | |
| // the prefix, reserve space for the '... Omitting N bytes ...' message, | |
| // then the ring buffer suffix, and just rearrange the ring buffer | |
| // suffix when Bytes() is called, but it doesn't seem worth it for | |
| // now just for error messages. It's only ~64KB anyway. | |
| } | |
| func (w *prefixSuffixSaver) Write(p []byte) (n int, err error) { | |
| lenp := len(p) | |
| p = w.fill(&w.prefix, p) | |
| // Only keep the last w.N bytes of suffix data. | |
| if overage := len(p) - w.N; overage > 0 { | |
| p = p[overage:] | |
| w.skipped += int64(overage) | |
| } | |
| p = w.fill(&w.suffix, p) | |
| // w.suffix is full now if p is non-empty. Overwrite it in a circle. | |
| for len(p) > 0 { // 0, 1, or 2 iterations. | |
| n := copy(w.suffix[w.suffixOff:], p) | |
| p = p[n:] | |
| w.skipped += int64(n) | |
| w.suffixOff += n | |
| if w.suffixOff == w.N { | |
| w.suffixOff = 0 | |
| } | |
| } | |
| return lenp, nil | |
| } | |
| // fill appends up to len(p) bytes of p to *dst, such that *dst does not | |
| // grow larger than w.N. It returns the un-appended suffix of p. | |
| func (w *prefixSuffixSaver) fill(dst *[]byte, p []byte) (pRemain []byte) { | |
| if remain := w.N - len(*dst); remain > 0 { | |
| add := minInt(len(p), remain) | |
| *dst = append(*dst, p[:add]...) | |
| p = p[add:] | |
| } | |
| return p | |
| } | |
| func (w *prefixSuffixSaver) Bytes() []byte { | |
| if w.suffix == nil { | |
| return w.prefix | |
| } | |
| if w.skipped == 0 { | |
| return append(w.prefix, w.suffix...) | |
| } | |
| var buf bytes.Buffer | |
| buf.Grow(len(w.prefix) + len(w.suffix) + 50) | |
| buf.Write(w.prefix) | |
| buf.WriteString("\n... omitting ") | |
| buf.WriteString(strconv.FormatInt(w.skipped, 10)) | |
| buf.WriteString(" bytes ...\n") | |
| buf.Write(w.suffix[w.suffixOff:]) | |
| buf.Write(w.suffix[:w.suffixOff]) | |
| return buf.Bytes() | |
| } | |
| func minInt(a, b int) int { | |
| if a < b { | |
| return a | |
| } | |
| return b | |
| } | |
| // environ returns a best-effort copy of the environment in which the command | |
| // would be run as it is currently configured. If an error occurs in computing | |
| // the environment, it is returned alongside the best-effort copy. | |
| func (c *Cmd) environ() ([]string, error) { | |
| var err error | |
| env := c.Env | |
| if env == nil { | |
| env, err = execenv.Default(c.SysProcAttr) | |
| if err != nil { | |
| env = os.Environ() | |
| // Note that the non-nil err is preserved despite env being overridden. | |
| } | |
| if c.Dir != "" { | |
| switch runtime.GOOS { | |
| case "windows", "plan9": | |
| // Windows and Plan 9 do not use the PWD variable, so we don't need to | |
| // keep it accurate. | |
| default: | |
| // On POSIX platforms, PWD represents βan absolute pathname of the | |
| // current working directory.β Since we are changing the working | |
| // directory for the command, we should also update PWD to reflect that. | |
| // | |
| // Unfortunately, we didn't always do that, so (as proposed in | |
| // https://go.dev/issue/50599) to avoid unintended collateral damage we | |
| // only implicitly update PWD when Env is nil. That way, we're much | |
| // less likely to override an intentional change to the variable. | |
| if pwd, absErr := filepath.Abs(c.Dir); absErr == nil { | |
| env = append(env, "PWD="+pwd) | |
| } else if err == nil { | |
| err = absErr | |
| } | |
| } | |
| } | |
| } | |
| return addCriticalEnv(dedupEnv(env)), err | |
| } | |
| // Environ returns a copy of the environment in which the command would be run | |
| // as it is currently configured. | |
| func (c *Cmd) Environ() []string { | |
| // Intentionally ignore errors: environ returns a best-effort environment no matter what. | |
| env, _ := c.environ() | |
| return env | |
| } | |
| // dedupEnv returns a copy of env with any duplicates removed, in favor of | |
| // later values. | |
| // Items not of the normal environment "key=value" form are preserved unchanged. | |
| func dedupEnv(env []string) []string { | |
| return dedupEnvCase(runtime.GOOS == "windows", env) | |
| } | |
| // dedupEnvCase is dedupEnv with a case option for testing. | |
| // If caseInsensitive is true, the case of keys is ignored. | |
| func dedupEnvCase(caseInsensitive bool, env []string) []string { | |
| // Construct the output in reverse order, to preserve the | |
| // last occurrence of each key. | |
| out := make([]string, 0, len(env)) | |
| saw := make(map[string]bool, len(env)) | |
| for n := len(env); n > 0; n-- { | |
| kv := env[n-1] | |
| i := strings.Index(kv, "=") | |
| if i == 0 { | |
| // We observe in practice keys with a single leading "=" on Windows. | |
| // TODO(#49886): Should we consume only the first leading "=" as part | |
| // of the key, or parse through arbitrarily many of them until a non-"="? | |
| i = strings.Index(kv[1:], "=") + 1 | |
| } | |
| if i < 0 { | |
| if kv != "" { | |
| // The entry is not of the form "key=value" (as it is required to be). | |
| // Leave it as-is for now. | |
| // TODO(#52436): should we strip or reject these bogus entries? | |
| out = append(out, kv) | |
| } | |
| continue | |
| } | |
| k := kv[:i] | |
| if caseInsensitive { | |
| k = strings.ToLower(k) | |
| } | |
| if saw[k] { | |
| continue | |
| } | |
| saw[k] = true | |
| out = append(out, kv) | |
| } | |
| // Now reverse the slice to restore the original order. | |
| for i := 0; i < len(out)/2; i++ { | |
| j := len(out) - i - 1 | |
| out[i], out[j] = out[j], out[i] | |
| } | |
| return out | |
| } | |
| // addCriticalEnv adds any critical environment variables that are required | |
| // (or at least almost always required) on the operating system. | |
| // Currently this is only used for Windows. | |
| func addCriticalEnv(env []string) []string { | |
| if runtime.GOOS != "windows" { | |
| return env | |
| } | |
| for _, kv := range env { | |
| k, _, ok := strings.Cut(kv, "=") | |
| if !ok { | |
| continue | |
| } | |
| if strings.EqualFold(k, "SYSTEMROOT") { | |
| // We already have it. | |
| return env | |
| } | |
| } | |
| return append(env, "SYSTEMROOT="+os.Getenv("SYSTEMROOT")) | |
| } | |
| // ErrDot indicates that a path lookup resolved to an executable | |
| // in the current directory due to β.β being in the path, either | |
| // implicitly or explicitly. See the package documentation for details. | |
| // | |
| // Note that functions in this package do not return ErrDot directly. | |
| // Code should use errors.Is(err, ErrDot), not err == ErrDot, | |
| // to test whether a returned error err is due to this condition. | |
| var ErrDot = errors.New("cannot run executable found relative to current directory") |