github.com/oyvindsk/docker@v1.5.0/daemon/attach.go (about)

     1  package daemon
     2  
     3  import (
     4  	"encoding/json"
     5  	"io"
     6  	"os"
     7  	"sync"
     8  	"time"
     9  
    10  	log "github.com/Sirupsen/logrus"
    11  	"github.com/docker/docker/engine"
    12  	"github.com/docker/docker/pkg/jsonlog"
    13  	"github.com/docker/docker/pkg/promise"
    14  	"github.com/docker/docker/utils"
    15  )
    16  
    17  func (daemon *Daemon) ContainerAttach(job *engine.Job) engine.Status {
    18  	if len(job.Args) != 1 {
    19  		return job.Errorf("Usage: %s CONTAINER\n", job.Name)
    20  	}
    21  
    22  	var (
    23  		name   = job.Args[0]
    24  		logs   = job.GetenvBool("logs")
    25  		stream = job.GetenvBool("stream")
    26  		stdin  = job.GetenvBool("stdin")
    27  		stdout = job.GetenvBool("stdout")
    28  		stderr = job.GetenvBool("stderr")
    29  	)
    30  
    31  	container := daemon.Get(name)
    32  	if container == nil {
    33  		return job.Errorf("No such container: %s", name)
    34  	}
    35  
    36  	//logs
    37  	if logs {
    38  		cLog, err := container.ReadLog("json")
    39  		if err != nil && os.IsNotExist(err) {
    40  			// Legacy logs
    41  			log.Debugf("Old logs format")
    42  			if stdout {
    43  				cLog, err := container.ReadLog("stdout")
    44  				if err != nil {
    45  					log.Errorf("Error reading logs (stdout): %s", err)
    46  				} else if _, err := io.Copy(job.Stdout, cLog); err != nil {
    47  					log.Errorf("Error streaming logs (stdout): %s", err)
    48  				}
    49  			}
    50  			if stderr {
    51  				cLog, err := container.ReadLog("stderr")
    52  				if err != nil {
    53  					log.Errorf("Error reading logs (stderr): %s", err)
    54  				} else if _, err := io.Copy(job.Stderr, cLog); err != nil {
    55  					log.Errorf("Error streaming logs (stderr): %s", err)
    56  				}
    57  			}
    58  		} else if err != nil {
    59  			log.Errorf("Error reading logs (json): %s", err)
    60  		} else {
    61  			dec := json.NewDecoder(cLog)
    62  			for {
    63  				l := &jsonlog.JSONLog{}
    64  
    65  				if err := dec.Decode(l); err == io.EOF {
    66  					break
    67  				} else if err != nil {
    68  					log.Errorf("Error streaming logs: %s", err)
    69  					break
    70  				}
    71  				if l.Stream == "stdout" && stdout {
    72  					io.WriteString(job.Stdout, l.Log)
    73  				}
    74  				if l.Stream == "stderr" && stderr {
    75  					io.WriteString(job.Stderr, l.Log)
    76  				}
    77  			}
    78  		}
    79  	}
    80  
    81  	//stream
    82  	if stream {
    83  		var (
    84  			cStdin           io.ReadCloser
    85  			cStdout, cStderr io.Writer
    86  		)
    87  
    88  		if stdin {
    89  			r, w := io.Pipe()
    90  			go func() {
    91  				defer w.Close()
    92  				defer log.Debugf("Closing buffered stdin pipe")
    93  				io.Copy(w, job.Stdin)
    94  			}()
    95  			cStdin = r
    96  		}
    97  		if stdout {
    98  			cStdout = job.Stdout
    99  		}
   100  		if stderr {
   101  			cStderr = job.Stderr
   102  		}
   103  
   104  		<-daemon.attach(&container.StreamConfig, container.Config.OpenStdin, container.Config.StdinOnce, container.Config.Tty, cStdin, cStdout, cStderr)
   105  		// If we are in stdinonce mode, wait for the process to end
   106  		// otherwise, simply return
   107  		if container.Config.StdinOnce && !container.Config.Tty {
   108  			container.WaitStop(-1 * time.Second)
   109  		}
   110  	}
   111  	return engine.StatusOK
   112  }
   113  
   114  func (daemon *Daemon) attach(streamConfig *StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) chan error {
   115  	var (
   116  		cStdout, cStderr io.ReadCloser
   117  		cStdin           io.WriteCloser
   118  		wg               sync.WaitGroup
   119  		errors           = make(chan error, 3)
   120  	)
   121  
   122  	if stdin != nil && openStdin {
   123  		cStdin = streamConfig.StdinPipe()
   124  		wg.Add(1)
   125  	}
   126  
   127  	if stdout != nil {
   128  		cStdout = streamConfig.StdoutPipe()
   129  		wg.Add(1)
   130  	}
   131  
   132  	if stderr != nil {
   133  		cStderr = streamConfig.StderrPipe()
   134  		wg.Add(1)
   135  	}
   136  
   137  	// Connect stdin of container to the http conn.
   138  	go func() {
   139  		if stdin == nil || !openStdin {
   140  			return
   141  		}
   142  		log.Debugf("attach: stdin: begin")
   143  		defer func() {
   144  			if stdinOnce && !tty {
   145  				cStdin.Close()
   146  			} else {
   147  				// No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr
   148  				if cStdout != nil {
   149  					cStdout.Close()
   150  				}
   151  				if cStderr != nil {
   152  					cStderr.Close()
   153  				}
   154  			}
   155  			wg.Done()
   156  			log.Debugf("attach: stdin: end")
   157  		}()
   158  
   159  		var err error
   160  		if tty {
   161  			_, err = utils.CopyEscapable(cStdin, stdin)
   162  		} else {
   163  			_, err = io.Copy(cStdin, stdin)
   164  
   165  		}
   166  		if err == io.ErrClosedPipe {
   167  			err = nil
   168  		}
   169  		if err != nil {
   170  			log.Errorf("attach: stdin: %s", err)
   171  			errors <- err
   172  			return
   173  		}
   174  	}()
   175  
   176  	attachStream := func(name string, stream io.Writer, streamPipe io.ReadCloser) {
   177  		if stream == nil {
   178  			return
   179  		}
   180  		defer func() {
   181  			// Make sure stdin gets closed
   182  			if stdin != nil {
   183  				stdin.Close()
   184  			}
   185  			streamPipe.Close()
   186  			wg.Done()
   187  			log.Debugf("attach: %s: end", name)
   188  		}()
   189  
   190  		log.Debugf("attach: %s: begin", name)
   191  		_, err := io.Copy(stream, streamPipe)
   192  		if err == io.ErrClosedPipe {
   193  			err = nil
   194  		}
   195  		if err != nil {
   196  			log.Errorf("attach: %s: %v", name, err)
   197  			errors <- err
   198  		}
   199  	}
   200  
   201  	go attachStream("stdout", stdout, cStdout)
   202  	go attachStream("stderr", stderr, cStderr)
   203  
   204  	return promise.Go(func() error {
   205  		wg.Wait()
   206  		close(errors)
   207  		for err := range errors {
   208  			if err != nil {
   209  				return err
   210  			}
   211  		}
   212  		return nil
   213  	})
   214  }