container_linux.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. package libcontainerd
  2. import (
  3. "encoding/json"
  4. "io"
  5. "io/ioutil"
  6. "os"
  7. "path/filepath"
  8. "syscall"
  9. "github.com/Sirupsen/logrus"
  10. containerd "github.com/docker/containerd/api/grpc/types"
  11. "github.com/docker/docker/pkg/ioutils"
  12. "github.com/opencontainers/runtime-spec/specs-go"
  13. "golang.org/x/net/context"
  14. )
  15. type container struct {
  16. containerCommon
  17. // Platform specific fields are below here.
  18. pauseMonitor
  19. oom bool
  20. runtime string
  21. runtimeArgs []string
  22. }
  23. type runtime struct {
  24. path string
  25. args []string
  26. }
  27. // WithRuntime sets the runtime to be used for the created container
  28. func WithRuntime(path string, args []string) CreateOption {
  29. return runtime{path, args}
  30. }
  31. func (rt runtime) Apply(p interface{}) error {
  32. if pr, ok := p.(*container); ok {
  33. pr.runtime = rt.path
  34. pr.runtimeArgs = rt.args
  35. }
  36. return nil
  37. }
  38. func (ctr *container) clean() error {
  39. if os.Getenv("LIBCONTAINERD_NOCLEAN") == "1" {
  40. return nil
  41. }
  42. if _, err := os.Lstat(ctr.dir); err != nil {
  43. if os.IsNotExist(err) {
  44. return nil
  45. }
  46. return err
  47. }
  48. if err := os.RemoveAll(ctr.dir); err != nil {
  49. return err
  50. }
  51. return nil
  52. }
  53. // cleanProcess removes the fifos used by an additional process.
  54. // Caller needs to lock container ID before calling this method.
  55. func (ctr *container) cleanProcess(id string) {
  56. if p, ok := ctr.processes[id]; ok {
  57. for _, i := range []int{syscall.Stdin, syscall.Stdout, syscall.Stderr} {
  58. if err := os.Remove(p.fifo(i)); err != nil {
  59. logrus.Warnf("libcontainerd: failed to remove %v for process %v: %v", p.fifo(i), id, err)
  60. }
  61. }
  62. }
  63. delete(ctr.processes, id)
  64. }
  65. func (ctr *container) spec() (*specs.Spec, error) {
  66. var spec specs.Spec
  67. dt, err := ioutil.ReadFile(filepath.Join(ctr.dir, configFilename))
  68. if err != nil {
  69. return nil, err
  70. }
  71. if err := json.Unmarshal(dt, &spec); err != nil {
  72. return nil, err
  73. }
  74. return &spec, nil
  75. }
  76. func (ctr *container) start(checkpoint string, checkpointDir string) error {
  77. spec, err := ctr.spec()
  78. if err != nil {
  79. return nil
  80. }
  81. createChan := make(chan struct{})
  82. iopipe, err := ctr.openFifos(spec.Process.Terminal)
  83. if err != nil {
  84. return err
  85. }
  86. // we need to delay stdin closure after container start or else "stdin close"
  87. // event will be rejected by containerd.
  88. // stdin closure happens in AttachStreams
  89. stdin := iopipe.Stdin
  90. iopipe.Stdin = ioutils.NewWriteCloserWrapper(stdin, func() error {
  91. go func() {
  92. <-createChan
  93. stdin.Close()
  94. }()
  95. return nil
  96. })
  97. r := &containerd.CreateContainerRequest{
  98. Id: ctr.containerID,
  99. BundlePath: ctr.dir,
  100. Stdin: ctr.fifo(syscall.Stdin),
  101. Stdout: ctr.fifo(syscall.Stdout),
  102. Stderr: ctr.fifo(syscall.Stderr),
  103. Checkpoint: checkpoint,
  104. CheckpointDir: checkpointDir,
  105. // check to see if we are running in ramdisk to disable pivot root
  106. NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "",
  107. Runtime: ctr.runtime,
  108. RuntimeArgs: ctr.runtimeArgs,
  109. }
  110. ctr.client.appendContainer(ctr)
  111. if err := ctr.client.backend.AttachStreams(ctr.containerID, *iopipe); err != nil {
  112. close(createChan)
  113. ctr.closeFifos(iopipe)
  114. return err
  115. }
  116. resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r)
  117. if err != nil {
  118. close(createChan)
  119. ctr.closeFifos(iopipe)
  120. return err
  121. }
  122. ctr.systemPid = systemPid(resp.Container)
  123. close(createChan)
  124. return ctr.client.backend.StateChanged(ctr.containerID, StateInfo{
  125. CommonStateInfo: CommonStateInfo{
  126. State: StateStart,
  127. Pid: ctr.systemPid,
  128. }})
  129. }
  130. func (ctr *container) newProcess(friendlyName string) *process {
  131. return &process{
  132. dir: ctr.dir,
  133. processCommon: processCommon{
  134. containerID: ctr.containerID,
  135. friendlyName: friendlyName,
  136. client: ctr.client,
  137. },
  138. }
  139. }
  140. func (ctr *container) handleEvent(e *containerd.Event) error {
  141. ctr.client.lock(ctr.containerID)
  142. defer ctr.client.unlock(ctr.containerID)
  143. switch e.Type {
  144. case StateExit, StatePause, StateResume, StateOOM:
  145. st := StateInfo{
  146. CommonStateInfo: CommonStateInfo{
  147. State: e.Type,
  148. ExitCode: e.Status,
  149. },
  150. OOMKilled: e.Type == StateExit && ctr.oom,
  151. }
  152. if e.Type == StateOOM {
  153. ctr.oom = true
  154. }
  155. if e.Type == StateExit && e.Pid != InitFriendlyName {
  156. st.ProcessID = e.Pid
  157. st.State = StateExitProcess
  158. }
  159. // Remove process from list if we have exited
  160. switch st.State {
  161. case StateExit:
  162. ctr.clean()
  163. ctr.client.deleteContainer(e.Id)
  164. case StateExitProcess:
  165. ctr.cleanProcess(st.ProcessID)
  166. }
  167. ctr.client.q.append(e.Id, func() {
  168. if err := ctr.client.backend.StateChanged(e.Id, st); err != nil {
  169. logrus.Errorf("libcontainerd: backend.StateChanged(): %v", err)
  170. }
  171. if e.Type == StatePause || e.Type == StateResume {
  172. ctr.pauseMonitor.handle(e.Type)
  173. }
  174. if e.Type == StateExit {
  175. if en := ctr.client.getExitNotifier(e.Id); en != nil {
  176. en.close()
  177. }
  178. }
  179. })
  180. default:
  181. logrus.Debugf("libcontainerd: event unhandled: %+v", e)
  182. }
  183. return nil
  184. }
  185. // discardFifos attempts to fully read the container fifos to unblock processes
  186. // that may be blocked on the writer side.
  187. func (ctr *container) discardFifos() {
  188. for _, i := range []int{syscall.Stdout, syscall.Stderr} {
  189. f := ctr.fifo(i)
  190. c := make(chan struct{})
  191. go func() {
  192. r := openReaderFromFifo(f)
  193. close(c) // this channel is used to not close the writer too early, before readonly open has been called.
  194. io.Copy(ioutil.Discard, r)
  195. }()
  196. <-c
  197. closeReaderFifo(f) // avoid blocking permanently on open if there is no writer side
  198. }
  199. }