github.com/endophage/docker@v1.4.2-0.20161027011718-242853499895/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/Sirupsen/logrus" 11 "github.com/docker/docker/libcontainerd" 12 "github.com/docker/docker/pkg/broadcaster" 13 "github.com/docker/docker/pkg/ioutils" 14 "github.com/docker/docker/pkg/pools" 15 ) 16 17 // StreamConfig holds information about I/O streams managed together. 18 // 19 // streamConfig.StdinPipe returns a WriteCloser which can be used to feed data 20 // to the standard input of the streamConfig's active process. 21 // streamConfig.StdoutPipe and streamConfig.StderrPipe each return a ReadCloser 22 // which can be used to retrieve the standard output (and error) generated 23 // by the container's active process. The output (and error) are actually 24 // copied and delivered to all StdoutPipe and StderrPipe consumers, using 25 // a kind of "broadcaster". 26 type StreamConfig struct { 27 sync.WaitGroup 28 stdout *broadcaster.Unbuffered 29 stderr *broadcaster.Unbuffered 30 stdin io.ReadCloser 31 stdinPipe io.WriteCloser 32 } 33 34 // NewStreamConfig creates a stream config and initializes 35 // the standard err and standard out to new unbuffered broadcasters. 36 func NewStreamConfig() *StreamConfig { 37 return &StreamConfig{ 38 stderr: new(broadcaster.Unbuffered), 39 stdout: new(broadcaster.Unbuffered), 40 } 41 } 42 43 // Stdout returns the standard output in the configuration. 44 func (streamConfig *StreamConfig) Stdout() *broadcaster.Unbuffered { 45 return streamConfig.stdout 46 } 47 48 // Stderr returns the standard error in the configuration. 49 func (streamConfig *StreamConfig) Stderr() *broadcaster.Unbuffered { 50 return streamConfig.stderr 51 } 52 53 // Stdin returns the standard input in the configuration. 54 func (streamConfig *StreamConfig) Stdin() io.ReadCloser { 55 return streamConfig.stdin 56 } 57 58 // StdinPipe returns an input writer pipe as an io.WriteCloser. 59 func (streamConfig *StreamConfig) StdinPipe() io.WriteCloser { 60 return streamConfig.stdinPipe 61 } 62 63 // StdoutPipe creates a new io.ReadCloser with an empty bytes pipe. 64 // It adds this new out pipe to the Stdout broadcaster. 65 func (streamConfig *StreamConfig) StdoutPipe() io.ReadCloser { 66 bytesPipe := ioutils.NewBytesPipe() 67 streamConfig.stdout.Add(bytesPipe) 68 return bytesPipe 69 } 70 71 // StderrPipe creates a new io.ReadCloser with an empty bytes pipe. 72 // It adds this new err pipe to the Stderr broadcaster. 73 func (streamConfig *StreamConfig) StderrPipe() io.ReadCloser { 74 bytesPipe := ioutils.NewBytesPipe() 75 streamConfig.stderr.Add(bytesPipe) 76 return bytesPipe 77 } 78 79 // NewInputPipes creates new pipes for both standard inputs, Stdin and StdinPipe. 80 func (streamConfig *StreamConfig) NewInputPipes() { 81 streamConfig.stdin, streamConfig.stdinPipe = io.Pipe() 82 } 83 84 // NewNopInputPipe creates a new input pipe that will silently drop all messages in the input. 85 func (streamConfig *StreamConfig) NewNopInputPipe() { 86 streamConfig.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) 87 } 88 89 // CloseStreams ensures that the configured streams are properly closed. 90 func (streamConfig *StreamConfig) CloseStreams() error { 91 var errors []string 92 93 if streamConfig.stdin != nil { 94 if err := streamConfig.stdin.Close(); err != nil { 95 errors = append(errors, fmt.Sprintf("error close stdin: %s", err)) 96 } 97 } 98 99 if err := streamConfig.stdout.Clean(); err != nil { 100 errors = append(errors, fmt.Sprintf("error close stdout: %s", err)) 101 } 102 103 if err := streamConfig.stderr.Clean(); err != nil { 104 errors = append(errors, fmt.Sprintf("error close stderr: %s", err)) 105 } 106 107 if len(errors) > 0 { 108 return fmt.Errorf(strings.Join(errors, "\n")) 109 } 110 111 return nil 112 } 113 114 // CopyToPipe connects streamconfig with a libcontainerd.IOPipe 115 func (streamConfig *StreamConfig) CopyToPipe(iop libcontainerd.IOPipe) { 116 copyFunc := func(w io.Writer, r io.Reader) { 117 streamConfig.Add(1) 118 go func() { 119 if _, err := pools.Copy(w, r); err != nil { 120 logrus.Errorf("stream copy error: %+v", err) 121 } 122 streamConfig.Done() 123 }() 124 } 125 126 if iop.Stdout != nil { 127 copyFunc(streamConfig.Stdout(), iop.Stdout) 128 } 129 if iop.Stderr != nil { 130 copyFunc(streamConfig.Stderr(), iop.Stderr) 131 } 132 133 if stdin := streamConfig.Stdin(); stdin != nil { 134 if iop.Stdin != nil { 135 go func() { 136 pools.Copy(iop.Stdin, stdin) 137 if err := iop.Stdin.Close(); err != nil { 138 logrus.Error("failed to clise stdin: %+v", err) 139 } 140 }() 141 } 142 } 143 }