go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/connection/container/docker_engine/command.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package docker_engine 5 6 import ( 7 "bufio" 8 "bytes" 9 "context" 10 "encoding/binary" 11 "io" 12 "time" 13 14 docker "github.com/docker/docker/api/types" 15 "github.com/docker/docker/client" 16 "go.mondoo.com/cnquery/providers/os/connection/shared" 17 ) 18 19 type Command struct { 20 shared.Command 21 Container string 22 Client *client.Client 23 } 24 25 func (c *Command) Exec(command string) (*shared.Command, error) { 26 c.Command.Command = command 27 c.Command.Stats.Start = time.Now() 28 29 ctx := context.Background() 30 res, err := c.Client.ContainerExecCreate(ctx, c.Container, docker.ExecConfig{ 31 Cmd: []string{"/bin/sh", "-c", c.Command.Command}, 32 Detach: true, 33 Tty: false, 34 AttachStdin: false, 35 AttachStderr: true, 36 AttachStdout: true, 37 }) 38 if err != nil { 39 return nil, err 40 } 41 42 resp, err := c.Client.ContainerExecAttach(ctx, res.ID, docker.ExecStartCheck{ 43 Detach: false, 44 Tty: false, 45 }) 46 if err != nil { 47 return nil, err 48 } 49 50 // TODO: transformHijack breaks for long stdout, but not if we read stdout/stderr in upfront 51 content, err := io.ReadAll(resp.Reader) 52 if err != nil { 53 return nil, err 54 } 55 56 var stdoutBuffer bytes.Buffer 57 var stderrBuffer bytes.Buffer 58 59 // create buffered stream 60 c.Command.Stdout = &stdoutBuffer 61 c.Command.Stderr = &stderrBuffer 62 63 stdOutWriter := bufio.NewWriter(&stdoutBuffer) 64 stdErrWriter := bufio.NewWriter(&stderrBuffer) 65 66 // extract stdout, stderr 67 c.transformHijack(bytes.NewReader(content), stdOutWriter, stdErrWriter) 68 69 defer stdOutWriter.Flush() 70 defer stdErrWriter.Flush() 71 72 c.Command.Stats.Duration = time.Since(c.Command.Stats.Start) 73 74 info, err := c.Client.ContainerExecInspect(ctx, res.ID) 75 if err != nil { 76 return nil, err 77 } 78 c.Command.ExitStatus = info.ExitCode 79 80 return &c.Command, nil 81 } 82 83 const ( 84 STDIN byte = 0 85 STDOUT byte = 1 86 STDERR byte = 2 87 ) 88 89 // Format is defined in https://docs.docker.com/engine/api/v1.33/#operation/ContainerAttach 90 func (c *Command) transformHijack(docker io.Reader, stdout io.Writer, stderr io.Writer) { 91 header := make([]byte, 8) 92 for { 93 // read header 94 _, err := docker.Read(header) 95 96 // end reached 97 if err == io.EOF { 98 break 99 } 100 101 size := binary.BigEndian.Uint32(header[4:8]) 102 content := make([]byte, size) 103 _, err = docker.Read(content) 104 105 if header[0] == STDIN || header[0] == STDOUT { 106 stdout.Write(content) 107 } else if header[0] == STDERR { 108 stderr.Write(content) 109 } 110 111 // end reached 112 if err == io.EOF { 113 break 114 } 115 } 116 }