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  }