go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/_motor/providers/ssh/command.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package ssh 5 6 import ( 7 "bytes" 8 "errors" 9 "time" 10 11 "github.com/rs/zerolog/log" 12 "go.mondoo.com/cnquery/motor/providers/os" 13 "golang.org/x/crypto/ssh" 14 ) 15 16 type Command struct { 17 os.Command 18 SSHProvider *Provider 19 } 20 21 func (c *Command) Exec(command string) (*os.Command, error) { 22 c.Command.Command = command 23 c.Command.Stats.Duration = time.Since(c.Command.Stats.Start) 24 25 stdoutBuffer := &bytes.Buffer{} 26 stderrBuffer := &bytes.Buffer{} 27 28 c.Command.Stdout = stdoutBuffer 29 c.Command.Stderr = stderrBuffer 30 31 if c.SSHProvider.SSHClient == nil { 32 return nil, errors.New("ssh session not established") 33 } 34 35 session, err := c.SSHProvider.SSHClient.NewSession() 36 if err != nil { 37 log.Debug().Msg("could not open new session, try to re-establish connection") 38 err = c.SSHProvider.Reconnect() 39 if err != nil { 40 return nil, err 41 } 42 43 session, err = c.SSHProvider.SSHClient.NewSession() 44 if err != nil { 45 return nil, err 46 } 47 } 48 defer session.Close() 49 50 // start ssh call 51 session.Stdout = stdoutBuffer 52 session.Stderr = stderrBuffer 53 err = session.Run(c.Command.Command) 54 c.Command.Stats.Duration = time.Since(c.Command.Stats.Start) 55 56 // command completed successfully, great :-) 57 if err == nil { 58 return &c.Command, nil 59 } 60 61 // if the program failed, we do not return err but its exit code 62 var e *ssh.ExitError 63 match := errors.As(err, &e) 64 if match { 65 c.Command.ExitStatus = e.ExitStatus() 66 return &c.Command, nil 67 } 68 69 // all other errors are real errors and not expected 70 return &c.Command, err 71 }