attach.go 2.8 KB

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