go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/_motor/providers/os/cmd/command.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package cmd 5 6 import ( 7 "bytes" 8 "os/exec" 9 "strings" 10 "syscall" 11 "time" 12 13 "go.mondoo.com/cnquery/motor/providers/os" 14 ) 15 16 type CommandRunner struct { 17 os.Command 18 cmdExecutor *exec.Cmd 19 Shell []string 20 } 21 22 func (c *CommandRunner) Exec(usercmd string, args []string) (*os.Command, error) { 23 c.Command.Stats.Start = time.Now() 24 25 var cmd string 26 cmdArgs := []string{} 27 28 if len(c.Shell) > 0 { 29 shellCommand, shellArgs := c.Shell[0], c.Shell[1:] 30 cmd = shellCommand 31 cmdArgs = append(cmdArgs, shellArgs...) 32 cmdArgs = append(cmdArgs, usercmd) 33 } else { 34 cmd = usercmd 35 } 36 cmdArgs = append(cmdArgs, args...) 37 38 // this only stores the user command, not the shell 39 c.Command.Command = usercmd + " " + strings.Join(args, " ") 40 c.cmdExecutor = exec.Command(cmd, cmdArgs...) 41 42 var stdoutBuffer bytes.Buffer 43 var stderrBuffer bytes.Buffer 44 45 // create buffered stream 46 c.Command.Stdout = &stdoutBuffer 47 c.Command.Stderr = &stderrBuffer 48 49 c.cmdExecutor.Stdout = c.Command.Stdout 50 c.cmdExecutor.Stderr = c.Command.Stderr 51 52 err := c.cmdExecutor.Run() 53 c.Command.Stats.Duration = time.Since(c.Command.Stats.Start) 54 55 // command completed successfully, great :-) 56 if err == nil { 57 return &c.Command, nil 58 } 59 60 // if the program failed, we do not return err but its exit code 61 if exiterr, ok := err.(*exec.ExitError); ok { 62 if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { 63 c.Command.ExitStatus = status.ExitStatus() 64 } 65 return &c.Command, nil 66 } 67 68 // all other errors are real errors and not expected 69 return &c.Command, err 70 }