1 // Helper type to run and log commands
3 // Copyright (C) 2021 Simon Ruderich
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
26 "ruderich.org/simon/safcm/remote/log"
34 func NewCmd(runner Runner, logger *log.Logger) *Cmd {
41 // Run runs a command and return stdout and stderr.
43 // Use this only for commands running on our behalf where we need to parse the
44 // output. Use CombinedOutput() for user commands because these should show
45 // the complete output and have stdout and stderr in the proper order and not
47 func (c *Cmd) Run(module string, args ...string) ([]byte, []byte, error) {
48 var stdout, stderr bytes.Buffer
49 cmd := exec.Command(args[0], args[1:]...)
52 quoted := QuoteForDebug(cmd)
53 c.logger.Debugf("%s: running %s", module, quoted)
54 err := c.Runner.Run(cmd)
56 c.logger.Debug2f("%s: command stdout:\n%s",
57 module, stdout.Bytes())
60 c.logger.Debug2f("%s: command stderr:\n%s",
61 module, stderr.Bytes())
64 return nil, nil, fmt.Errorf(
65 "%s failed: %v; stdout: %q, stderr: %q",
66 quoted, err, stdout.String(), stderr.String())
68 return stdout.Bytes(), stderr.Bytes(), nil
71 // CombinedOutputCmd runs the command and returns its combined stdout and
73 func (c *Cmd) CombinedOutputCmd(module string, cmd *exec.Cmd) ([]byte, error) {
74 quoted := QuoteForDebug(cmd)
75 c.logger.Debugf("%s: running %s", module, quoted)
76 out, err := c.Runner.CombinedOutput(cmd)
78 c.logger.Debug2f("%s: command output:\n%s", module, out)
81 return nil, fmt.Errorf("%s failed: %v; output: %q",
87 func QuoteForDebug(cmd *exec.Cmd) string {
88 // TODO: proper shell escaping, remove quotes when not necessary
90 for _, x := range append([]string{cmd.Path}, cmd.Args[1:]...) {
91 quoted = append(quoted, fmt.Sprintf("%q", x))
93 return strings.Join(quoted, " ")