mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-06-10 16:40:22 +00:00
61 lines
1.6 KiB
Go
61 lines
1.6 KiB
Go
package commands
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"os/exec"
|
|
)
|
|
|
|
// RunOptions configures a single Run invocation.
|
|
type RunOptions struct {
|
|
// Stdin, if non-nil, is written to the process stdin.
|
|
Stdin []byte
|
|
// CombinedOutputCap limits the captured output (truncated at the end).
|
|
// 0 means unlimited. The agent uses ~1 MiB for cat-queue, smaller for
|
|
// status-style commands.
|
|
OutputCap int
|
|
}
|
|
|
|
// RunResult is what every shell-style command returns.
|
|
type RunResult struct {
|
|
Stdout string `json:"stdout,omitempty"`
|
|
Stderr string `json:"stderr,omitempty"`
|
|
ExitCode int `json:"exit_code"`
|
|
}
|
|
|
|
// Run executes argv[0] argv[1:] under ctx (the bus deadline). It does not
|
|
// translate exit codes to errors — callers inspect r.ExitCode themselves so
|
|
// they can map e.g. "queue id not found" exit codes to ErrNotFound.
|
|
func Run(ctx context.Context, opts RunOptions, argv ...string) (*RunResult, error) {
|
|
if len(argv) == 0 {
|
|
return nil, fmt.Errorf("commands.Run: empty argv")
|
|
}
|
|
cmd := exec.CommandContext(ctx, argv[0], argv[1:]...)
|
|
var stdout, stderr bytes.Buffer
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = &stderr
|
|
if opts.Stdin != nil {
|
|
cmd.Stdin = bytes.NewReader(opts.Stdin)
|
|
}
|
|
err := cmd.Run()
|
|
|
|
out := stdout.String()
|
|
errOut := stderr.String()
|
|
if opts.OutputCap > 0 {
|
|
if len(out) > opts.OutputCap {
|
|
out = out[:opts.OutputCap] + "\n…(truncated)"
|
|
}
|
|
if len(errOut) > opts.OutputCap {
|
|
errOut = errOut[:opts.OutputCap] + "\n…(truncated)"
|
|
}
|
|
}
|
|
|
|
exit := 0
|
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
|
exit = exitErr.ExitCode()
|
|
err = nil
|
|
}
|
|
return &RunResult{Stdout: out, Stderr: errOut, ExitCode: exit}, err
|
|
}
|