github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/api/client/attach.go (about)

     1  package client
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net/http/httputil"
     7  
     8  	"golang.org/x/net/context"
     9  
    10  	"github.com/Sirupsen/logrus"
    11  	Cli "github.com/docker/docker/cli"
    12  	flag "github.com/docker/docker/pkg/mflag"
    13  	"github.com/docker/docker/pkg/signal"
    14  	"github.com/docker/engine-api/types"
    15  )
    16  
    17  // CmdAttach attaches to a running container.
    18  //
    19  // Usage: docker attach [OPTIONS] CONTAINER
    20  func (cli *DockerCli) CmdAttach(args ...string) error {
    21  	cmd := Cli.Subcmd("attach", []string{"CONTAINER"}, Cli.DockerCommands["attach"].Description, true)
    22  	noStdin := cmd.Bool([]string{"-no-stdin"}, false, "Do not attach STDIN")
    23  	proxy := cmd.Bool([]string{"-sig-proxy"}, true, "Proxy all received signals to the process")
    24  	detachKeys := cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container")
    25  
    26  	cmd.Require(flag.Exact, 1)
    27  
    28  	cmd.ParseFlags(args, true)
    29  
    30  	c, err := cli.client.ContainerInspect(context.Background(), cmd.Arg(0))
    31  	if err != nil {
    32  		return err
    33  	}
    34  
    35  	if !c.State.Running {
    36  		return fmt.Errorf("You cannot attach to a stopped container, start it first")
    37  	}
    38  
    39  	if c.State.Paused {
    40  		return fmt.Errorf("You cannot attach to a paused container, unpause it first")
    41  	}
    42  
    43  	if err := cli.CheckTtyInput(!*noStdin, c.Config.Tty); err != nil {
    44  		return err
    45  	}
    46  
    47  	if *detachKeys != "" {
    48  		cli.configFile.DetachKeys = *detachKeys
    49  	}
    50  
    51  	options := types.ContainerAttachOptions{
    52  		ContainerID: cmd.Arg(0),
    53  		Stream:      true,
    54  		Stdin:       !*noStdin && c.Config.OpenStdin,
    55  		Stdout:      true,
    56  		Stderr:      true,
    57  		DetachKeys:  cli.configFile.DetachKeys,
    58  	}
    59  
    60  	var in io.ReadCloser
    61  	if options.Stdin {
    62  		in = cli.in
    63  	}
    64  
    65  	if *proxy && !c.Config.Tty {
    66  		sigc := cli.forwardAllSignals(options.ContainerID)
    67  		defer signal.StopCatch(sigc)
    68  	}
    69  
    70  	resp, errAttach := cli.client.ContainerAttach(context.Background(), options)
    71  	if errAttach != nil && errAttach != httputil.ErrPersistEOF {
    72  		// ContainerAttach returns an ErrPersistEOF (connection closed)
    73  		// means server met an error and put it in Hijacked connection
    74  		// keep the error and read detailed error message from hijacked connection later
    75  		return errAttach
    76  	}
    77  	defer resp.Close()
    78  
    79  	if c.Config.Tty && cli.isTerminalOut {
    80  		height, width := cli.getTtySize()
    81  		// To handle the case where a user repeatedly attaches/detaches without resizing their
    82  		// terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially
    83  		// resize it, then go back to normal. Without this, every attach after the first will
    84  		// require the user to manually resize or hit enter.
    85  		cli.resizeTtyTo(cmd.Arg(0), height+1, width+1, false)
    86  
    87  		// After the above resizing occurs, the call to monitorTtySize below will handle resetting back
    88  		// to the actual size.
    89  		if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil {
    90  			logrus.Debugf("Error monitoring TTY size: %s", err)
    91  		}
    92  	}
    93  	if err := cli.holdHijackedConnection(context.Background(), c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
    94  		return err
    95  	}
    96  
    97  	if errAttach != nil {
    98  		return errAttach
    99  	}
   100  
   101  	_, status, err := getExitCode(cli, options.ContainerID)
   102  	if err != nil {
   103  		return err
   104  	}
   105  	if status != 0 {
   106  		return Cli.StatusError{StatusCode: status}
   107  	}
   108  
   109  	return nil
   110  }