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