github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/runconfig/streams.go (about) 1 package runconfig 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "strings" 8 "sync" 9 10 "github.com/docker/docker/pkg/broadcaster" 11 "github.com/docker/docker/pkg/ioutils" 12 ) 13 14 // StreamConfig holds information about I/O streams managed together. 15 // 16 // streamConfig.StdinPipe returns a WriteCloser which can be used to feed data 17 // to the standard input of the streamConfig's active process. 18 // streamConfig.StdoutPipe and streamConfig.StderrPipe each return a ReadCloser 19 // which can be used to retrieve the standard output (and error) generated 20 // by the container's active process. The output (and error) are actually 21 // copied and delivered to all StdoutPipe and StderrPipe consumers, using 22 // a kind of "broadcaster". 23 type StreamConfig struct { 24 sync.WaitGroup 25 stdout *broadcaster.Unbuffered 26 stderr *broadcaster.Unbuffered 27 stdin io.ReadCloser 28 stdinPipe io.WriteCloser 29 } 30 31 // NewStreamConfig creates a stream config and initializes 32 // the standard err and standard out to new unbuffered broadcasters. 33 func NewStreamConfig() *StreamConfig { 34 return &StreamConfig{ 35 stderr: new(broadcaster.Unbuffered), 36 stdout: new(broadcaster.Unbuffered), 37 } 38 } 39 40 // Stdout returns the standard output in the configuration. 41 func (streamConfig *StreamConfig) Stdout() *broadcaster.Unbuffered { 42 return streamConfig.stdout 43 } 44 45 // Stderr returns the standard error in the configuration. 46 func (streamConfig *StreamConfig) Stderr() *broadcaster.Unbuffered { 47 return streamConfig.stderr 48 } 49 50 // Stdin returns the standard input in the configuration. 51 func (streamConfig *StreamConfig) Stdin() io.ReadCloser { 52 return streamConfig.stdin 53 } 54 55 // StdinPipe returns an input writer pipe as an io.WriteCloser. 56 func (streamConfig *StreamConfig) StdinPipe() io.WriteCloser { 57 return streamConfig.stdinPipe 58 } 59 60 // StdoutPipe creates a new io.ReadCloser with an empty bytes pipe. 61 // It adds this new out pipe to the Stdout broadcaster. 62 func (streamConfig *StreamConfig) StdoutPipe() io.ReadCloser { 63 bytesPipe := ioutils.NewBytesPipe() 64 streamConfig.stdout.Add(bytesPipe) 65 return bytesPipe 66 } 67 68 // StderrPipe creates a new io.ReadCloser with an empty bytes pipe. 69 // It adds this new err pipe to the Stderr broadcaster. 70 func (streamConfig *StreamConfig) StderrPipe() io.ReadCloser { 71 bytesPipe := ioutils.NewBytesPipe() 72 streamConfig.stderr.Add(bytesPipe) 73 return bytesPipe 74 } 75 76 // NewInputPipes creates new pipes for both standard inputs, Stdin and StdinPipe. 77 func (streamConfig *StreamConfig) NewInputPipes() { 78 streamConfig.stdin, streamConfig.stdinPipe = io.Pipe() 79 } 80 81 // NewNopInputPipe creates a new input pipe that will silently drop all messages in the input. 82 func (streamConfig *StreamConfig) NewNopInputPipe() { 83 streamConfig.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) 84 } 85 86 // CloseStreams ensures that the configured streams are properly closed. 87 func (streamConfig *StreamConfig) CloseStreams() error { 88 var errors []string 89 90 if streamConfig.stdin != nil { 91 if err := streamConfig.stdin.Close(); err != nil { 92 errors = append(errors, fmt.Sprintf("error close stdin: %s", err)) 93 } 94 } 95 96 if err := streamConfig.stdout.Clean(); err != nil { 97 errors = append(errors, fmt.Sprintf("error close stdout: %s", err)) 98 } 99 100 if err := streamConfig.stderr.Clean(); err != nil { 101 errors = append(errors, fmt.Sprintf("error close stderr: %s", err)) 102 } 103 104 if len(errors) > 0 { 105 return fmt.Errorf(strings.Join(errors, "\n")) 106 } 107 108 return nil 109 }