utils.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package client
  2. import (
  3. "fmt"
  4. "io"
  5. "io/ioutil"
  6. "os"
  7. gosignal "os/signal"
  8. "path/filepath"
  9. "runtime"
  10. "time"
  11. "golang.org/x/net/context"
  12. "github.com/Sirupsen/logrus"
  13. "github.com/docker/docker/pkg/signal"
  14. "github.com/docker/docker/pkg/term"
  15. "github.com/docker/engine-api/client"
  16. "github.com/docker/engine-api/types"
  17. )
  18. func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) {
  19. height, width := cli.GetTtySize()
  20. cli.ResizeTtyTo(ctx, id, height, width, isExec)
  21. }
  22. // ResizeTtyTo resizes tty to specific height and width
  23. // TODO: this can be unexported again once all container related commands move to package container
  24. func (cli *DockerCli) ResizeTtyTo(ctx context.Context, id string, height, width int, isExec bool) {
  25. if height == 0 && width == 0 {
  26. return
  27. }
  28. options := types.ResizeOptions{
  29. Height: height,
  30. Width: width,
  31. }
  32. var err error
  33. if isExec {
  34. err = cli.client.ContainerExecResize(ctx, id, options)
  35. } else {
  36. err = cli.client.ContainerResize(ctx, id, options)
  37. }
  38. if err != nil {
  39. logrus.Debugf("Error resize: %s", err)
  40. }
  41. }
  42. // getExecExitCode perform an inspect on the exec command. It returns
  43. // the running state and the exit code.
  44. func (cli *DockerCli) getExecExitCode(ctx context.Context, execID string) (bool, int, error) {
  45. resp, err := cli.client.ContainerExecInspect(ctx, execID)
  46. if err != nil {
  47. // If we can't connect, then the daemon probably died.
  48. if err != client.ErrConnectionFailed {
  49. return false, -1, err
  50. }
  51. return false, -1, nil
  52. }
  53. return resp.Running, resp.ExitCode, nil
  54. }
  55. // MonitorTtySize updates the container tty size when the terminal tty changes size
  56. func (cli *DockerCli) MonitorTtySize(ctx context.Context, id string, isExec bool) error {
  57. cli.resizeTty(ctx, id, isExec)
  58. if runtime.GOOS == "windows" {
  59. go func() {
  60. prevH, prevW := cli.GetTtySize()
  61. for {
  62. time.Sleep(time.Millisecond * 250)
  63. h, w := cli.GetTtySize()
  64. if prevW != w || prevH != h {
  65. cli.resizeTty(ctx, id, isExec)
  66. }
  67. prevH = h
  68. prevW = w
  69. }
  70. }()
  71. } else {
  72. sigchan := make(chan os.Signal, 1)
  73. gosignal.Notify(sigchan, signal.SIGWINCH)
  74. go func() {
  75. for range sigchan {
  76. cli.resizeTty(ctx, id, isExec)
  77. }
  78. }()
  79. }
  80. return nil
  81. }
  82. // GetTtySize returns the height and width in characters of the tty
  83. func (cli *DockerCli) GetTtySize() (int, int) {
  84. if !cli.isTerminalOut {
  85. return 0, 0
  86. }
  87. ws, err := term.GetWinsize(cli.outFd)
  88. if err != nil {
  89. logrus.Debugf("Error getting size: %s", err)
  90. if ws == nil {
  91. return 0, 0
  92. }
  93. }
  94. return int(ws.Height), int(ws.Width)
  95. }
  96. // CopyToFile writes the content of the reader to the specified file
  97. func CopyToFile(outfile string, r io.Reader) error {
  98. tmpFile, err := ioutil.TempFile(filepath.Dir(outfile), ".docker_temp_")
  99. if err != nil {
  100. return err
  101. }
  102. tmpPath := tmpFile.Name()
  103. _, err = io.Copy(tmpFile, r)
  104. tmpFile.Close()
  105. if err != nil {
  106. os.Remove(tmpPath)
  107. return err
  108. }
  109. if err = os.Rename(tmpPath, outfile); err != nil {
  110. os.Remove(tmpPath)
  111. return err
  112. }
  113. return nil
  114. }
  115. // ForwardAllSignals forwards signals to the contianer
  116. // TODO: this can be unexported again once all container commands are under
  117. // api/client/container
  118. func (cli *DockerCli) ForwardAllSignals(ctx context.Context, cid string) chan os.Signal {
  119. sigc := make(chan os.Signal, 128)
  120. signal.CatchAll(sigc)
  121. go func() {
  122. for s := range sigc {
  123. if s == signal.SIGCHLD || s == signal.SIGPIPE {
  124. continue
  125. }
  126. var sig string
  127. for sigStr, sigN := range signal.SignalMap {
  128. if sigN == s {
  129. sig = sigStr
  130. break
  131. }
  132. }
  133. if sig == "" {
  134. fmt.Fprintf(cli.err, "Unsupported signal: %v. Discarding.\n", s)
  135. continue
  136. }
  137. if err := cli.client.ContainerKill(ctx, cid, sig); err != nil {
  138. logrus.Debugf("Error sending signal: %s", err)
  139. }
  140. }
  141. }()
  142. return sigc
  143. }