exec.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. package daemon
  2. import (
  3. "io"
  4. "strings"
  5. "time"
  6. "github.com/Sirupsen/logrus"
  7. "github.com/docker/docker/api/types"
  8. "github.com/docker/docker/api/types/strslice"
  9. "github.com/docker/docker/container"
  10. "github.com/docker/docker/daemon/exec"
  11. "github.com/docker/docker/daemon/execdriver"
  12. derr "github.com/docker/docker/errors"
  13. "github.com/docker/docker/pkg/pools"
  14. "github.com/docker/docker/pkg/promise"
  15. "github.com/docker/docker/pkg/term"
  16. )
  17. func (d *Daemon) registerExecCommand(container *container.Container, config *exec.Config) {
  18. // Storing execs in container in order to kill them gracefully whenever the container is stopped or removed.
  19. container.ExecCommands.Add(config.ID, config)
  20. // Storing execs in daemon for easy access via remote API.
  21. d.execCommands.Add(config.ID, config)
  22. }
  23. // ExecExists looks up the exec instance and returns a bool if it exists or not.
  24. // It will also return the error produced by `getConfig`
  25. func (d *Daemon) ExecExists(name string) (bool, error) {
  26. if _, err := d.getExecConfig(name); err != nil {
  27. return false, err
  28. }
  29. return true, nil
  30. }
  31. // getExecConfig looks up the exec instance by name. If the container associated
  32. // with the exec instance is stopped or paused, it will return an error.
  33. func (d *Daemon) getExecConfig(name string) (*exec.Config, error) {
  34. ec := d.execCommands.Get(name)
  35. // If the exec is found but its container is not in the daemon's list of
  36. // containers then it must have been deleted, in which case instead of
  37. // saying the container isn't running, we should return a 404 so that
  38. // the user sees the same error now that they will after the
  39. // 5 minute clean-up loop is run which erases old/dead execs.
  40. if ec != nil {
  41. if container := d.containers.Get(ec.ContainerID); container != nil {
  42. if !container.IsRunning() {
  43. return nil, derr.ErrorCodeContainerNotRunning.WithArgs(container.ID, container.State.String())
  44. }
  45. if container.IsPaused() {
  46. return nil, derr.ErrorCodeExecPaused.WithArgs(container.ID)
  47. }
  48. return ec, nil
  49. }
  50. }
  51. return nil, derr.ErrorCodeNoExecID.WithArgs(name)
  52. }
  53. func (d *Daemon) unregisterExecCommand(container *container.Container, execConfig *exec.Config) {
  54. container.ExecCommands.Delete(execConfig.ID)
  55. d.execCommands.Delete(execConfig.ID)
  56. }
  57. func (d *Daemon) getActiveContainer(name string) (*container.Container, error) {
  58. container, err := d.GetContainer(name)
  59. if err != nil {
  60. return nil, err
  61. }
  62. if !container.IsRunning() {
  63. return nil, derr.ErrorCodeNotRunning.WithArgs(name)
  64. }
  65. if container.IsPaused() {
  66. return nil, derr.ErrorCodeExecPaused.WithArgs(name)
  67. }
  68. return container, nil
  69. }
  70. // ContainerExecCreate sets up an exec in a running container.
  71. func (d *Daemon) ContainerExecCreate(config *types.ExecConfig) (string, error) {
  72. container, err := d.getActiveContainer(config.Container)
  73. if err != nil {
  74. return "", err
  75. }
  76. cmd := strslice.New(config.Cmd...)
  77. entrypoint, args := d.getEntrypointAndArgs(strslice.New(), cmd)
  78. keys := []byte{}
  79. if config.DetachKeys != "" {
  80. keys, err = term.ToBytes(config.DetachKeys)
  81. if err != nil {
  82. logrus.Warnf("Wrong escape keys provided (%s, error: %s) using default : ctrl-p ctrl-q", config.DetachKeys, err.Error())
  83. }
  84. }
  85. processConfig := &execdriver.ProcessConfig{
  86. CommonProcessConfig: execdriver.CommonProcessConfig{
  87. Tty: config.Tty,
  88. Entrypoint: entrypoint,
  89. Arguments: args,
  90. },
  91. }
  92. setPlatformSpecificExecProcessConfig(config, container, processConfig)
  93. execConfig := exec.NewConfig()
  94. execConfig.OpenStdin = config.AttachStdin
  95. execConfig.OpenStdout = config.AttachStdout
  96. execConfig.OpenStderr = config.AttachStderr
  97. execConfig.ProcessConfig = processConfig
  98. execConfig.ContainerID = container.ID
  99. execConfig.DetachKeys = keys
  100. d.registerExecCommand(container, execConfig)
  101. d.LogContainerEvent(container, "exec_create: "+execConfig.ProcessConfig.Entrypoint+" "+strings.Join(execConfig.ProcessConfig.Arguments, " "))
  102. return execConfig.ID, nil
  103. }
  104. // ContainerExecStart starts a previously set up exec instance. The
  105. // std streams are set up.
  106. func (d *Daemon) ContainerExecStart(name string, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) error {
  107. var (
  108. cStdin io.ReadCloser
  109. cStdout, cStderr io.Writer
  110. )
  111. ec, err := d.getExecConfig(name)
  112. if err != nil {
  113. return derr.ErrorCodeNoExecID.WithArgs(name)
  114. }
  115. ec.Lock()
  116. if ec.Running {
  117. ec.Unlock()
  118. return derr.ErrorCodeExecRunning.WithArgs(ec.ID)
  119. }
  120. ec.Running = true
  121. ec.Unlock()
  122. c := d.containers.Get(ec.ContainerID)
  123. logrus.Debugf("starting exec command %s in container %s", ec.ID, c.ID)
  124. d.LogContainerEvent(c, "exec_start: "+ec.ProcessConfig.Entrypoint+" "+strings.Join(ec.ProcessConfig.Arguments, " "))
  125. if ec.OpenStdin {
  126. r, w := io.Pipe()
  127. go func() {
  128. defer w.Close()
  129. defer logrus.Debugf("Closing buffered stdin pipe")
  130. pools.Copy(w, stdin)
  131. }()
  132. cStdin = r
  133. }
  134. if ec.OpenStdout {
  135. cStdout = stdout
  136. }
  137. if ec.OpenStderr {
  138. cStderr = stderr
  139. }
  140. if ec.OpenStdin {
  141. ec.NewInputPipes()
  142. } else {
  143. ec.NewNopInputPipe()
  144. }
  145. attachErr := container.AttachStreams(ec.StreamConfig, ec.OpenStdin, true, ec.ProcessConfig.Tty, cStdin, cStdout, cStderr, ec.DetachKeys)
  146. execErr := make(chan error)
  147. // Note, the ExecConfig data will be removed when the container
  148. // itself is deleted. This allows us to query it (for things like
  149. // the exitStatus) even after the cmd is done running.
  150. go func() {
  151. execErr <- d.containerExec(c, ec)
  152. }()
  153. select {
  154. case err := <-attachErr:
  155. if err != nil {
  156. return derr.ErrorCodeExecAttach.WithArgs(err)
  157. }
  158. return nil
  159. case err := <-execErr:
  160. if aErr := <-attachErr; aErr != nil && err == nil {
  161. return derr.ErrorCodeExecAttach.WithArgs(aErr)
  162. }
  163. if err == nil {
  164. return nil
  165. }
  166. // Maybe the container stopped while we were trying to exec
  167. if !c.IsRunning() {
  168. return derr.ErrorCodeExecContainerStopped
  169. }
  170. return derr.ErrorCodeExecCantRun.WithArgs(ec.ID, c.ID, err)
  171. }
  172. }
  173. // Exec calls the underlying exec driver to run
  174. func (d *Daemon) Exec(c *container.Container, execConfig *exec.Config, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (int, error) {
  175. hooks := execdriver.Hooks{
  176. Start: startCallback,
  177. }
  178. exitStatus, err := d.execDriver.Exec(c.Command, execConfig.ProcessConfig, pipes, hooks)
  179. // On err, make sure we don't leave ExitCode at zero
  180. if err != nil && exitStatus == 0 {
  181. exitStatus = 128
  182. }
  183. execConfig.ExitCode = exitStatus
  184. execConfig.Running = false
  185. return exitStatus, err
  186. }
  187. // execCommandGC runs a ticker to clean up the daemon references
  188. // of exec configs that are no longer part of the container.
  189. func (d *Daemon) execCommandGC() {
  190. for range time.Tick(5 * time.Minute) {
  191. var (
  192. cleaned int
  193. liveExecCommands = d.containerExecIds()
  194. )
  195. for id, config := range d.execCommands.Commands() {
  196. if config.CanRemove {
  197. cleaned++
  198. d.execCommands.Delete(id)
  199. } else {
  200. if _, exists := liveExecCommands[id]; !exists {
  201. config.CanRemove = true
  202. }
  203. }
  204. }
  205. if cleaned > 0 {
  206. logrus.Debugf("clean %d unused exec commands", cleaned)
  207. }
  208. }
  209. }
  210. // containerExecIds returns a list of all the current exec ids that are in use
  211. // and running inside a container.
  212. func (d *Daemon) containerExecIds() map[string]struct{} {
  213. ids := map[string]struct{}{}
  214. for _, c := range d.containers.List() {
  215. for _, id := range c.ExecCommands.List() {
  216. ids[id] = struct{}{}
  217. }
  218. }
  219. return ids
  220. }
  221. func (d *Daemon) containerExec(container *container.Container, ec *exec.Config) error {
  222. container.Lock()
  223. defer container.Unlock()
  224. callback := func(processConfig *execdriver.ProcessConfig, pid int, chOOM <-chan struct{}) error {
  225. if processConfig.Tty {
  226. // The callback is called after the process Start()
  227. // so we are in the parent process. In TTY mode, stdin/out/err is the PtySlave
  228. // which we close here.
  229. if c, ok := processConfig.Stdout.(io.Closer); ok {
  230. c.Close()
  231. }
  232. }
  233. ec.Close()
  234. return nil
  235. }
  236. // We use a callback here instead of a goroutine and an chan for
  237. // synchronization purposes
  238. cErr := promise.Go(func() error { return d.monitorExec(container, ec, callback) })
  239. return ec.Wait(cErr)
  240. }
  241. func (d *Daemon) monitorExec(container *container.Container, execConfig *exec.Config, callback execdriver.DriverCallback) error {
  242. pipes := execdriver.NewPipes(execConfig.Stdin(), execConfig.Stdout(), execConfig.Stderr(), execConfig.OpenStdin)
  243. exitCode, err := d.Exec(container, execConfig, pipes, callback)
  244. if err != nil {
  245. logrus.Errorf("Error running command in existing container %s: %s", container.ID, err)
  246. }
  247. logrus.Debugf("Exec task in container %s exited with code %d", container.ID, exitCode)
  248. if err := execConfig.CloseStreams(); err != nil {
  249. logrus.Errorf("%s: %s", container.ID, err)
  250. }
  251. if execConfig.ProcessConfig.Terminal != nil {
  252. if err := execConfig.ProcessConfig.Terminal.Close(); err != nil {
  253. logrus.Errorf("Error closing terminal while running in container %s: %s", container.ID, err)
  254. }
  255. }
  256. // remove the exec command from the container's store only and not the
  257. // daemon's store so that the exec command can be inspected.
  258. container.ExecCommands.Delete(execConfig.ID)
  259. return err
  260. }