github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/container/stream/streams.go (about) 1 package stream // import "github.com/Prakhar-Agarwal-byte/moby/container/stream" 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "strings" 8 "sync" 9 10 "github.com/containerd/containerd/cio" 11 "github.com/containerd/log" 12 "github.com/Prakhar-Agarwal-byte/moby/pkg/broadcaster" 13 "github.com/Prakhar-Agarwal-byte/moby/pkg/ioutils" 14 "github.com/Prakhar-Agarwal-byte/moby/pkg/pools" 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 wg sync.WaitGroup 28 stdout *broadcaster.Unbuffered 29 stderr *broadcaster.Unbuffered 30 stdin io.ReadCloser 31 stdinPipe io.WriteCloser 32 dio *cio.DirectIO 33 } 34 35 // NewConfig creates a stream config and initializes 36 // the standard err and standard out to new unbuffered broadcasters. 37 func NewConfig() *Config { 38 return &Config{ 39 stderr: new(broadcaster.Unbuffered), 40 stdout: new(broadcaster.Unbuffered), 41 } 42 } 43 44 // Stdout returns the standard output in the configuration. 45 func (c *Config) Stdout() *broadcaster.Unbuffered { 46 return c.stdout 47 } 48 49 // Stderr returns the standard error in the configuration. 50 func (c *Config) Stderr() *broadcaster.Unbuffered { 51 return c.stderr 52 } 53 54 // Stdin returns the standard input in the configuration. 55 func (c *Config) Stdin() io.ReadCloser { 56 return c.stdin 57 } 58 59 // StdinPipe returns an input writer pipe as an io.WriteCloser. 60 func (c *Config) StdinPipe() io.WriteCloser { 61 return c.stdinPipe 62 } 63 64 // StdoutPipe creates a new io.ReadCloser with an empty bytes pipe. 65 // It adds this new out pipe to the Stdout broadcaster. 66 // This will block stdout if unconsumed. 67 func (c *Config) StdoutPipe() io.ReadCloser { 68 bytesPipe := ioutils.NewBytesPipe() 69 c.stdout.Add(bytesPipe) 70 return bytesPipe 71 } 72 73 // StderrPipe creates a new io.ReadCloser with an empty bytes pipe. 74 // It adds this new err pipe to the Stderr broadcaster. 75 // This will block stderr if unconsumed. 76 func (c *Config) StderrPipe() io.ReadCloser { 77 bytesPipe := ioutils.NewBytesPipe() 78 c.stderr.Add(bytesPipe) 79 return bytesPipe 80 } 81 82 // NewInputPipes creates new pipes for both standard inputs, Stdin and StdinPipe. 83 func (c *Config) NewInputPipes() { 84 c.stdin, c.stdinPipe = io.Pipe() 85 } 86 87 // NewNopInputPipe creates a new input pipe that will silently drop all messages in the input. 88 func (c *Config) NewNopInputPipe() { 89 c.stdinPipe = ioutils.NopWriteCloser(io.Discard) 90 } 91 92 // CloseStreams ensures that the configured streams are properly closed. 93 func (c *Config) CloseStreams() error { 94 var errors []string 95 96 if c.stdin != nil { 97 if err := c.stdin.Close(); err != nil { 98 errors = append(errors, fmt.Sprintf("error close stdin: %s", err)) 99 } 100 } 101 102 if err := c.stdout.Clean(); err != nil { 103 errors = append(errors, fmt.Sprintf("error close stdout: %s", err)) 104 } 105 106 if err := c.stderr.Clean(); err != nil { 107 errors = append(errors, fmt.Sprintf("error close stderr: %s", err)) 108 } 109 110 if len(errors) > 0 { 111 return fmt.Errorf(strings.Join(errors, "\n")) 112 } 113 114 return nil 115 } 116 117 // CopyToPipe connects streamconfig with a libcontainerd.IOPipe 118 func (c *Config) CopyToPipe(iop *cio.DirectIO) { 119 ctx := context.TODO() 120 121 c.dio = iop 122 copyFunc := func(w io.Writer, r io.ReadCloser) { 123 c.wg.Add(1) 124 go func() { 125 if _, err := pools.Copy(w, r); err != nil { 126 log.G(ctx).Errorf("stream copy error: %v", err) 127 } 128 r.Close() 129 c.wg.Done() 130 }() 131 } 132 133 if iop.Stdout != nil { 134 copyFunc(c.Stdout(), iop.Stdout) 135 } 136 if iop.Stderr != nil { 137 copyFunc(c.Stderr(), iop.Stderr) 138 } 139 140 if stdin := c.Stdin(); stdin != nil { 141 if iop.Stdin != nil { 142 go func() { 143 pools.Copy(iop.Stdin, stdin) 144 if err := iop.Stdin.Close(); err != nil { 145 log.G(ctx).Warnf("failed to close stdin: %v", err) 146 } 147 }() 148 } 149 } 150 } 151 152 // Wait for the stream to close 153 // Wait supports timeouts via the context to unblock and forcefully 154 // close the io streams 155 func (c *Config) Wait(ctx context.Context) { 156 done := make(chan struct{}, 1) 157 go func() { 158 c.wg.Wait() 159 close(done) 160 }() 161 select { 162 case <-done: 163 case <-ctx.Done(): 164 if c.dio != nil { 165 c.dio.Cancel() 166 c.dio.Wait() 167 c.dio.Close() 168 } 169 } 170 }