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  }