1 // Helper type to run and log commands
3 // SPDX-License-Identifier: GPL-3.0-or-later
4 // Copyright (C) 2021-2024 Simon Ruderich
14 "ruderich.org/simon/safcm/remote/log"
22 func NewCmd(runner Runner, logger *log.Logger) *Cmd {
29 // Run runs a command and return stdout and stderr.
31 // Use this only for commands running on our behalf where we need to parse the
32 // output. Use CombinedOutput() for user commands because these should show
33 // the complete output and have stdout and stderr in the proper order and not
35 func (c *Cmd) Run(module string, args ...string) ([]byte, []byte, error) {
36 var stdout, stderr bytes.Buffer
37 cmd := exec.Command(args[0], args[1:]...)
40 quoted := QuoteForDebug(cmd)
41 c.logger.Debugf("%s: running %s", module, quoted)
42 err := c.Runner.Run(cmd)
44 c.logger.Debug2f("%s: command stdout:\n%s",
45 module, stdout.Bytes())
48 c.logger.Debug2f("%s: command stderr:\n%s",
49 module, stderr.Bytes())
52 return nil, nil, fmt.Errorf(
53 "%s failed: %v; stdout: %q, stderr: %q",
54 quoted, err, stdout.String(), stderr.String())
56 return stdout.Bytes(), stderr.Bytes(), nil
59 // CombinedOutputCmd runs the command and returns its combined stdout and
61 func (c *Cmd) CombinedOutputCmd(module string, cmd *exec.Cmd) ([]byte, error) {
62 quoted := QuoteForDebug(cmd)
63 c.logger.Debugf("%s: running %s", module, quoted)
64 out, err := c.Runner.CombinedOutput(cmd)
66 c.logger.Debug2f("%s: command output:\n%s", module, out)
69 return nil, fmt.Errorf("%s failed: %v; output: %q",
75 func QuoteForDebug(cmd *exec.Cmd) string {
76 // TODO: proper shell escaping, remove quotes when not necessary
78 for _, x := range append([]string{cmd.Path}, cmd.Args[1:]...) {
79 quoted = append(quoted, fmt.Sprintf("%q", x))
81 return strings.Join(quoted, " ")