Documentation ¶
Overview ¶
Package pexec provides streaming, context-cancelable system command execution
Index ¶
- Constants
- Variables
- func ExecBlocking(stdin []byte, wantStdout Stdouts, wantStderr Stderrs, ctx context.Context, ...) (stdout, stderr *bytes.Buffer, err error)
- func ExecStream(stdin io.Reader, stdout io.WriteCloser, stderr io.WriteCloser, ...) (statusCode int, isCancel bool, err error)
- func ExecStreamFull(stdin io.Reader, stdout io.Writer, stderr io.Writer, env []string, ...) (statusCode int, isCancel bool, err error)
- func ExitError(err error) (hasStatusCode bool, statusCode int, signal unix.Signal, stderr []byte)
- func NewCmdContainer() (cmd *CmdContainer, startCallback StartCallback)
- type CmdContainer
- type ExitErrorData
- func (e *ExitErrorData) AddStderr(err error) (err2 error)
- func (e *ExitErrorData) Error() (exitErrorMessage string)
- func (e *ExitErrorData) ExitErrorString(includeStderr ...bool) (errS string)
- func (e *ExitErrorData) IsExitError() (isExitError bool)
- func (e *ExitErrorData) IsSignalKill() (isSignalKill bool)
- func (e *ExitErrorData) IsStatusCode1() (is1 bool)
- type StartCallback
- type Stderrs
- type Stdouts
Constants ¶
const ( // [ExitErrorData.ExitErrorString] should include standard error output ExitErrorIncludeStderr = true // status code 1, which in POSIX means a general error StatusCode1 = 1 )
const (
// the status code of a process terminated by signal
TerminatedBySignal = -1
)
Variables ¶
var ( // [ExecStreamFull] env: use the environment of the parent process DefaulEnv []string // [ExecStreamFull] startCallback: no startCallback NoStartCallback StartCallback // [ExecStreamFull] stdin: no stdin NoStdin io.Reader // [ExecStreamFull] stdout: no stdout NoStdout io.Writer // [ExecStreamFull] stderr: no stderr NoStderr io.Writer // [ExecStreamFull] extraFiles: no extraFiles NoExtraFiles []*os.File )
var ErrArgsListEmpty = errors.New("args list empty")
ErrArgsListEmpty is returned when args doe not contain a command
var ExecBlockingClosedStdin = []byte{}
ExecBlocking the system command receives a closed standard input
var ExecBlockingEmptyStdin = []byte{}
ExecBlocking the system command receives an open but empty standard input
var NoExecBlockingStdin []byte
ExecBlocking the system command receives a closed standard input
Functions ¶
func ExecBlocking ¶ added in v0.4.87
func ExecBlocking(stdin []byte, wantStdout Stdouts, wantStderr Stderrs, ctx context.Context, args ...string) (stdout, stderr *bytes.Buffer, err error)
ExecBlocking provides a simple way to quietly execute a system command
- blocking, control over standard i/o, ability to cancel
- stdin: a byte sequence used as input to the system command
- stdin ExecBlockingClosedStdin nil: the command receives a closed standard input
- stdin ExecBlockingEmptyStdin: the system command receives an open but empty standard input
- wantStdout WantStdout: the command’s standard poutput is returned in stdout
- wantStdout NoExecBlockingStdout: the command’s standard output is discarded
- wantStderr WantStderr: the command’s standard error output is returned in stderr
- wantStderr StderrIsError: if the command outputs to standard error, the output is returned as an error
- wantStderr NoExecBlockingStderr: the command’s standard error output is discarded. standard error output may still be provided in err
- ctx: termianes the commad usinng SIGKILL on context cancel. Cannot be nil
- args: args[0] is non-empty command. args[1…] holds command-line arguments
func ExecStream ¶
func ExecStream( stdin io.Reader, stdout io.WriteCloser, stderr io.WriteCloser, ctx context.Context, args ...string, ) (statusCode int, isCancel bool, err error)
ExecStream executes a system command using the exec.Cmd type with flexible streaming
- blocking, flexible streaming, ability to cancel
- — like ExecStreamFull but with fewer arguments
- — single-statement version of exec.Cmd with flexible streaming
- args: the command and its arguments. args[0] empty is error. If args[0] does not contain path, the command is resolved using the parent process’ env.PATH
- stdin: an io.Reader producing the new process’ standard input.
- — stdin be os.Stdin if the parent process does not use its standard input
- — stdin can be [pio.EofReader] which is a standard input that is open but does not provide any input
- — If stdin is nil, stdin is /dev/null meaning the sub-process’ standard input is closed which for some commands cause immediate process termination
- —ExecStreamFull does not close a non-nil stdin but its use has ceased upon return
- stdout: an io.Writer receiving the process’ standard output
- — stdout can be os.Stdout causing the sub-process’ output to appear on the terminal
- — If stdout is nil, the sub-process’ output is discarded
- — ExecStreamFull does not close stdout but its use has ceased upon return
- stderr: an io.Writer receiving the process’ standard error
- — stderr can be os.Stderr causing the sub-process’ output to appear on the terminal
- — If stderr is nil, the new process’ error output is discarded.
- ctx: a context that can be used to terminate the process using SIGKILL. ctx nil is panic
- statusCode: the process’ exit code:
- — 0 on successful process termination
- — TerminatedBySignal -1 on process terminated by signal such as ^C SIGINT or SIGTERM
- — otherwise, a command-specific exit value
- isCancel: true if context cancel terminated the process with SIGKILL or a stream-copying error occurred
- err: any occurring error
- upon return from ExecStream there are 5 outcomes:
- — successful exit: statusCode == 0, err == nil
- — failure exit: statusCode != 0, isCancel == false. statusCode is the command-specific value provided by sub-process exit. err is the value returned by exec.Cmd.Wait upon process exit. If statusCode is pexec.TerminatedBySignal -1, the process was terminated by signal. The signal value can be obtained from err using pexec.ExitError
- — context cancel: isCancel == true, err == nil. statusCode is pexec.TerminatedBySignal -1
- — stream copying error: isCancel == true, err != nil statusCode is pexec.TerminatedBySignal -1
- — other error condition: err != nil, statusCode == 0
- for stdout and stderr pio has usable types:
- — pio.NewWriteCloserToString
- — pio.NewWriteCloserToChan
- — pio.NewWriteCloserToChanLine
- — pio.NewReadWriteCloserSlice
- parl.EchoModerator can be used with ExecStream:
- — if system commands slow down or lock-up, too many (dozens) invoking goroutines may cause increased memory consumption, thrashing or exhaust of file handles, ie. an uncontrollable host state
- — EchoModerator notifies of slow or hung commands and limits parallelism
func ExecStreamFull ¶ added in v0.4.38
func ExecStreamFull( stdin io.Reader, stdout io.Writer, stderr io.Writer, env []string, ctx context.Context, startCallback StartCallback, extraFiles []*os.File, args ...string, ) (statusCode int, isCancel bool, err error)
ExecStreamFull executes a system command using the exec.Cmd type and flexible streaming
- ExecStreamFull makes streaming exec.Cmd easy to use
- args: the command and its arguments. args[0] empty is error. If args[0] does not contain path, the command is resolved using the parent process’ env.PATH
- stdin: an io.Reader producing the new process’ standard input.
- — stdin be os.Stdin if the parent process does not use its standard input
- — stdin can be [pio.EofReader] which is a standard input that is open but does not provide any input
- — If stdin is nil, stdin is /dev/null meaning the sub-process’ standard input is closed which for some commands cause immediate process termination
- —ExecStreamFull does not close a non-nil stdin but its use has ceased upon return
- stdout: an io.Writer receiving the process’ standard output
- — stdout can be os.Stdout causing the sub-process’ output to appear on the terminal
- — If stdout is nil, the sub-process’ output is discarded
- — ExecStreamFull does not close stdout but its use has ceased upon return
- stderr: an io.Writer receiving the process’ standard error
- — stderr can be os.Stderr causing the sub-process’ output to appear on the terminal
- — If stderr is nil, the new process’ error output is discarded. However, on process error exit, stderr output is available in the returned error value
- — ExecStreamFull does not close stderr but its use has ceased upon return
- env: an environment for the process. If env is nil, the new process uses the current process’ environment
- ctx: a context that can be used to terminate the process using SIGKILL. ctx nil is panic
- startCallback: an optional callback invoked immediately after process start exec.Cmd.Start
- extraFiles: an optional list of streams for the process’ file descriptors 3…
- statusCode: the process’ exit code:
- — 0 on successful process termination
- — TerminatedBySignal -1 on process terminated by signal such as ^C SIGINT or SIGTERM
- — otherwise, a command-specific exit value
- isCancel: true if context cancel terminated the process with SIGKILL or a stream-copying error occurred
- err: any occurring error
- upon return from ExecStreamFull there are 5 outcomes:
- — successful exit: statusCode == 0, err == nil
- — failure exit: statusCode != 0, isCancel == false. statusCode is the command-specific value provided by sub-process exit. err is the value returned by exec.Cmd.Wait upon process exit. If statusCode is pexec.TerminatedBySignal -1, the process was terminated by signal. The signal value can be obtained from err using pexec.ExitError
- — context cancel: isCancel == true, err == nil. statusCode is pexec.TerminatedBySignal -1
- — stream copying error: isCancel == true, err != nil statusCode is pexec.TerminatedBySignal -1
- — other error condition: err != nil, statusCode == 0
- ExecStreamFull blocks during command execution until:
- — the started sub-process terminates
- — ctx context is canceled causing the process to be terminated by signal unix.SIGKILL
- — the process’ context is canceled due to an error ocurring in stream-copying threads
- ExecStreamFull may fail prior to exec.Cmd.Wait command-execution:
- — args does not contain a valid command
- — an error occured during stream-copying thread creation
- — exec.Cmd.Start returned an error occuring prior to process start
- Up to 3 stream-copying threads are used to copy data when stdin, stdout or stderr are non-nil and not os.Stdin, os.Stdout or os.Stderr respectively
- for stdout and stderr, the [pio] package has usable types:
- — [pio.NewWriteCloserToString]
- — [pio.NewWriteCloserToChan]
- — [pio.NewWriteCloserToChanLine]
- — [pio.NewReadWriteCloserSlice]
- parl.EchoModerator can be used with ExecStreamFull:
- — if system commands slow down or lock-up, too many (dozens) invoking goroutines may cause increased memory consumption, thrashing or exhaust of file handles, ie. an uncontrollable host state
- — EchoModerator notifies of slow or hung commands and limits parallelism
func ExitError ¶ added in v0.4.40
ExitError returns information on why the exec.Cmd.Start process terminated
- if hasStatusCode is true, the process terminated with a status code
- if hasStatusCode is false, exec.Cmd.Start failed prior to launch
- if StatusCode has value -1 or pexec.TerminatedBySignal, the process terminated due to the signal signal. Common signals are:
- — unix.SIGINT from ^C
- — unix.SIGKILL from context termination
- — unix.SIGTERM from operating-system process-termination
func NewCmdContainer ¶ added in v0.4.179
func NewCmdContainer() (cmd *CmdContainer, startCallback StartCallback)
NewCmdContainer returns a thread-safe container for the exec.Cmd value
- startCallback: callback for ExecStreamFull
- CmdContainer.Cmd returns the exec.Cmd value
- CmdContainer.Ch awaits the exec.Cmd value
- — does not trigger if pexec.ExecStreamFull fails prior to exec.Cmd.Start
- CmdContainer.Err returns any error from exec.Cmd.Start
Types ¶
type CmdContainer ¶ added in v0.4.179
type CmdContainer struct {
// contains filtered or unexported fields
}
CmdContainer is a thread-safe container for the exec.Cmd value
func (*CmdContainer) Ch ¶ added in v0.4.179
func (c *CmdContainer) Ch() (ch parl.AwaitableCh)
Ch awaits process start when the Cmd value is available
- does not trigger if ExecStreamFull fails prior to exec.Cmd.Start
func (*CmdContainer) Cmd ¶ added in v0.4.179
func (c *CmdContainer) Cmd() (execCmd *exec.Cmd)
Cmd returns the exec.Cmd value, available after process start
func (*CmdContainer) Err ¶ added in v0.4.179
func (c *CmdContainer) Err() (err error)
Cmd returns any error from exec.Cmd.Start
type ExitErrorData ¶ added in v0.4.106
type ExitErrorData struct { // the original error Err error // Err interpreted as ExitError, possibly nil ExitErr *exec.ExitError // status code in ExitError, possibly 0 StatusCode int // signal in ExitError, possibly 0 Signal unix.Signal // stderr in ExitError or from argument, possibly nil Stderr []byte }
ExitErrorData provides additional detail on pexec.ExitError values
func NewExitErrorData ¶ added in v0.4.106
func NewExitErrorData(err error, stderr ...[]byte) (exitErrorData *ExitErrorData)
NewExitErrorData returns parse-once data on a possible ExitError
- if ExitErr field is nil or IsExitError method returns false, err does not contain an ExitError
- the returned value is an error implementation
func (*ExitErrorData) AddStderr ¶ added in v0.4.111
func (e *ExitErrorData) AddStderr(err error) (err2 error)
AddStderr adds standard error output at the end of the error message for err. Also ensures stack trace.
- ExitError has standard error if the Output method was used
- NewExitErrorData can also have been provided stderr
func (*ExitErrorData) Error ¶ added in v0.4.106
func (e *ExitErrorData) Error() (exitErrorMessage string)
the Error method returns the message from any ExitError, otherwise empty string
- Error also makes ExitErrorData implementing the error interface
func (*ExitErrorData) ExitErrorString ¶ added in v0.4.106
func (e *ExitErrorData) ExitErrorString(includeStderr ...bool) (errS string)
ExitErrorString returns the ExitError error message and data from Err and stderr, not an error value
- for non-signal: “status code: 1 ‘read error’”
- for signal: “signal: "abort trap" ‘signal: abort trap’”
- the error message for err: “message: ‘failure’”
- stderr if non-empty from ExitErr or stderr argument and includeStderr is ExitErrorIncludeStderr:
- “stderr: ‘I/O error’”
- returned value is never empty
func (*ExitErrorData) IsExitError ¶ added in v0.4.106
func (e *ExitErrorData) IsExitError() (isExitError bool)
IsExitError returns true if an pexec.ExitError is present
- false if Err was nil or some other type of error
func (*ExitErrorData) IsSignalKill ¶ added in v0.4.106
func (e *ExitErrorData) IsSignalKill() (isSignalKill bool)
IsSignalKill returns true if the err error chain contains an ExitError with signal kill
- signal kill is the response to a command’s context being canceled. This should be checked together with context.Context.Err
- SIGKILL can also be sent to the process by the operating system trying to reclaim memory or by other processes
func (*ExitErrorData) IsStatusCode1 ¶ added in v0.4.106
func (e *ExitErrorData) IsStatusCode1() (is1 bool)
IsStatusCode1 returns if the err error chain contains an ExitError that indicates status code 1
- Status code 1 indicates an unspecified failure of a process
- Success has no ExitError and status code 0
- Terminated by signal is status code -1
- Input syntax error is status code 2
type StartCallback ¶ added in v0.4.104
type StartCallback interface { // StartResult is invoked by [ExecStreamFull] unless it fails // prior to Start // - StartResult receives the command-description and // process data along with any error occurring during Start // - if err is nil, the command sub-process did start // - StartResult must be thread-safe StartResult(execCmd *exec.Cmd, err error) }
ExecStreamFull startCallback: the signature of startCallback
type Stderrs ¶ added in v0.4.87
type Stderrs uint8
Stderrs are the possible values for ExecBlocking stderr
- NoExecBlockingStderr WantStderr StderrIsError
type Stdouts ¶ added in v0.4.87
type Stdouts bool
Stdouts are the possible values for ExecBlocking stdout
- NoExecBlockingStdout WantStdout