github.com/uriddle/docker@v0.0.0-20210926094723-4072e6aeb013/daemon/attach.go (about)

     1  package daemon
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net/http"
     7  	"time"
     8  
     9  	"github.com/Sirupsen/logrus"
    10  	"github.com/docker/docker/container"
    11  	"github.com/docker/docker/daemon/logger"
    12  	derr "github.com/docker/docker/errors"
    13  	"github.com/docker/docker/pkg/stdcopy"
    14  )
    15  
    16  // ContainerAttachWithLogsConfig holds the streams to use when connecting to a container to view logs.
    17  type ContainerAttachWithLogsConfig struct {
    18  	Hijacker   http.Hijacker
    19  	Upgrade    bool
    20  	UseStdin   bool
    21  	UseStdout  bool
    22  	UseStderr  bool
    23  	Logs       bool
    24  	Stream     bool
    25  	DetachKeys []byte
    26  }
    27  
    28  // ContainerAttachWithLogs attaches to logs according to the config passed in. See ContainerAttachWithLogsConfig.
    29  func (daemon *Daemon) ContainerAttachWithLogs(prefixOrName string, c *ContainerAttachWithLogsConfig) error {
    30  	if c.Hijacker == nil {
    31  		return derr.ErrorCodeNoHijackConnection.WithArgs(prefixOrName)
    32  	}
    33  	container, err := daemon.GetContainer(prefixOrName)
    34  	if err != nil {
    35  		return derr.ErrorCodeNoSuchContainer.WithArgs(prefixOrName)
    36  	}
    37  	if container.IsPaused() {
    38  		return derr.ErrorCodePausedContainer.WithArgs(prefixOrName)
    39  	}
    40  
    41  	conn, _, err := c.Hijacker.Hijack()
    42  	if err != nil {
    43  		return err
    44  	}
    45  	defer conn.Close()
    46  	// Flush the options to make sure the client sets the raw mode
    47  	conn.Write([]byte{})
    48  	inStream := conn.(io.ReadCloser)
    49  	outStream := conn.(io.Writer)
    50  
    51  	if c.Upgrade {
    52  		fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
    53  	} else {
    54  		fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
    55  	}
    56  
    57  	var errStream io.Writer
    58  
    59  	if !container.Config.Tty {
    60  		errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
    61  		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
    62  	} else {
    63  		errStream = outStream
    64  	}
    65  
    66  	var stdin io.ReadCloser
    67  	var stdout, stderr io.Writer
    68  
    69  	if c.UseStdin {
    70  		stdin = inStream
    71  	}
    72  	if c.UseStdout {
    73  		stdout = outStream
    74  	}
    75  	if c.UseStderr {
    76  		stderr = errStream
    77  	}
    78  
    79  	if err := daemon.attachWithLogs(container, stdin, stdout, stderr, c.Logs, c.Stream, c.DetachKeys); err != nil {
    80  		fmt.Fprintf(outStream, "Error attaching: %s\n", err)
    81  	}
    82  	return nil
    83  }
    84  
    85  // ContainerWsAttachWithLogsConfig attach with websockets, since all
    86  // stream data is delegated to the websocket to handle there.
    87  type ContainerWsAttachWithLogsConfig struct {
    88  	InStream             io.ReadCloser
    89  	OutStream, ErrStream io.Writer
    90  	Logs, Stream         bool
    91  	DetachKeys           []byte
    92  }
    93  
    94  // ContainerWsAttachWithLogs websocket connection
    95  func (daemon *Daemon) ContainerWsAttachWithLogs(prefixOrName string, c *ContainerWsAttachWithLogsConfig) error {
    96  	container, err := daemon.GetContainer(prefixOrName)
    97  	if err != nil {
    98  		return err
    99  	}
   100  	return daemon.attachWithLogs(container, c.InStream, c.OutStream, c.ErrStream, c.Logs, c.Stream, c.DetachKeys)
   101  }
   102  
   103  func (daemon *Daemon) attachWithLogs(container *container.Container, stdin io.ReadCloser, stdout, stderr io.Writer, logs, stream bool, keys []byte) error {
   104  	if logs {
   105  		logDriver, err := daemon.getLogger(container)
   106  		if err != nil {
   107  			return err
   108  		}
   109  		cLog, ok := logDriver.(logger.LogReader)
   110  		if !ok {
   111  			return logger.ErrReadLogsNotSupported
   112  		}
   113  		logs := cLog.ReadLogs(logger.ReadConfig{Tail: -1})
   114  
   115  	LogLoop:
   116  		for {
   117  			select {
   118  			case msg, ok := <-logs.Msg:
   119  				if !ok {
   120  					break LogLoop
   121  				}
   122  				if msg.Source == "stdout" && stdout != nil {
   123  					stdout.Write(msg.Line)
   124  				}
   125  				if msg.Source == "stderr" && stderr != nil {
   126  					stderr.Write(msg.Line)
   127  				}
   128  			case err := <-logs.Err:
   129  				logrus.Errorf("Error streaming logs: %v", err)
   130  				break LogLoop
   131  			}
   132  		}
   133  	}
   134  
   135  	daemon.LogContainerEvent(container, "attach")
   136  
   137  	//stream
   138  	if stream {
   139  		var stdinPipe io.ReadCloser
   140  		if stdin != nil {
   141  			r, w := io.Pipe()
   142  			go func() {
   143  				defer w.Close()
   144  				defer logrus.Debugf("Closing buffered stdin pipe")
   145  				io.Copy(w, stdin)
   146  			}()
   147  			stdinPipe = r
   148  		}
   149  		<-container.Attach(stdinPipe, stdout, stderr, keys)
   150  		// If we are in stdinonce mode, wait for the process to end
   151  		// otherwise, simply return
   152  		if container.Config.StdinOnce && !container.Config.Tty {
   153  			container.WaitStop(-1 * time.Second)
   154  		}
   155  	}
   156  	return nil
   157  }