direct_process.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. package runtime
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "os"
  7. "path"
  8. "path/filepath"
  9. "sync"
  10. "syscall"
  11. "github.com/docker/containerd/specs"
  12. "github.com/docker/containerd/subreaper"
  13. "github.com/docker/containerd/subreaper/exec"
  14. "github.com/docker/docker/pkg/term"
  15. "github.com/opencontainers/runc/libcontainer"
  16. )
  17. type directProcess struct {
  18. *process
  19. sync.WaitGroup
  20. io stdio
  21. console libcontainer.Console
  22. consolePath string
  23. exec bool
  24. checkpoint string
  25. specs *specs.Spec
  26. }
  27. func newDirectProcess(config *processConfig) (*directProcess, error) {
  28. lp, err := newProcess(config)
  29. if err != nil {
  30. return nil, err
  31. }
  32. return &directProcess{
  33. specs: config.spec,
  34. process: lp,
  35. exec: config.exec,
  36. checkpoint: config.checkpoint,
  37. }, nil
  38. }
  39. func (d *directProcess) CloseStdin() error {
  40. if d.io.stdin != nil {
  41. return d.io.stdin.Close()
  42. }
  43. return nil
  44. }
  45. func (d *directProcess) Resize(w, h int) error {
  46. if d.console == nil {
  47. return nil
  48. }
  49. ws := term.Winsize{
  50. Width: uint16(w),
  51. Height: uint16(h),
  52. }
  53. return term.SetWinsize(d.console.Fd(), &ws)
  54. }
  55. func (d *directProcess) openIO() (*os.File, *os.File, *os.File, error) {
  56. uid, gid, err := getRootIDs(d.specs)
  57. if err != nil {
  58. return nil, nil, nil, err
  59. }
  60. if d.spec.Terminal {
  61. console, err := libcontainer.NewConsole(uid, gid)
  62. if err != nil {
  63. return nil, nil, nil, err
  64. }
  65. d.console = console
  66. d.consolePath = console.Path()
  67. stdin, err := os.OpenFile(d.stdio.Stdin, syscall.O_RDONLY, 0)
  68. if err != nil {
  69. return nil, nil, nil, err
  70. }
  71. go io.Copy(console, stdin)
  72. stdout, err := os.OpenFile(d.stdio.Stdout, syscall.O_RDWR, 0)
  73. if err != nil {
  74. return nil, nil, nil, err
  75. }
  76. d.Add(1)
  77. go func() {
  78. io.Copy(stdout, console)
  79. console.Close()
  80. d.Done()
  81. }()
  82. d.io.stdin = stdin
  83. d.io.stdout = stdout
  84. d.io.stderr = stdout
  85. return nil, nil, nil, nil
  86. }
  87. stdin, err := os.OpenFile(d.stdio.Stdin, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
  88. if err != nil {
  89. return nil, nil, nil, err
  90. }
  91. stdout, err := os.OpenFile(d.stdio.Stdout, syscall.O_RDWR, 0)
  92. if err != nil {
  93. return nil, nil, nil, err
  94. }
  95. stderr, err := os.OpenFile(d.stdio.Stderr, syscall.O_RDWR, 0)
  96. if err != nil {
  97. return nil, nil, nil, err
  98. }
  99. d.io.stdin = stdin
  100. d.io.stdout = stdout
  101. d.io.stderr = stderr
  102. return stdin, stdout, stderr, nil
  103. }
  104. func (d *directProcess) loadCheckpoint(bundle string) (*Checkpoint, error) {
  105. if d.checkpoint == "" {
  106. return nil, nil
  107. }
  108. f, err := os.Open(filepath.Join(bundle, "checkpoints", d.checkpoint, "config.json"))
  109. if err != nil {
  110. return nil, err
  111. }
  112. defer f.Close()
  113. var cpt Checkpoint
  114. if err := json.NewDecoder(f).Decode(&cpt); err != nil {
  115. return nil, err
  116. }
  117. return &cpt, nil
  118. }
  119. func (d *directProcess) Start() error {
  120. cwd, err := filepath.Abs(d.root)
  121. if err != nil {
  122. return err
  123. }
  124. stdin, stdout, stderr, err := d.openIO()
  125. if err != nil {
  126. return nil
  127. }
  128. checkpoint, err := d.loadCheckpoint(d.container.bundle)
  129. if err != nil {
  130. return err
  131. }
  132. logPath := filepath.Join(cwd, "log.json")
  133. args := append([]string{
  134. "--log", logPath,
  135. "--log-format", "json",
  136. }, d.container.runtimeArgs...)
  137. if d.exec {
  138. args = append(args, "exec",
  139. "--process", filepath.Join(cwd, "process.json"),
  140. "--console", d.consolePath,
  141. )
  142. } else if checkpoint != nil {
  143. args = append(args, "restore",
  144. "--image-path", filepath.Join(d.container.bundle, "checkpoints", checkpoint.Name),
  145. )
  146. add := func(flags ...string) {
  147. args = append(args, flags...)
  148. }
  149. if checkpoint.Shell {
  150. add("--shell-job")
  151. }
  152. if checkpoint.Tcp {
  153. add("--tcp-established")
  154. }
  155. if checkpoint.UnixSockets {
  156. add("--ext-unix-sk")
  157. }
  158. if d.container.noPivotRoot {
  159. add("--no-pivot")
  160. }
  161. } else {
  162. args = append(args, "start",
  163. "--bundle", d.container.bundle,
  164. "--console", d.consolePath,
  165. )
  166. if d.container.noPivotRoot {
  167. args = append(args, "--no-pivot")
  168. }
  169. }
  170. args = append(args,
  171. "-d",
  172. "--pid-file", filepath.Join(cwd, "pid"),
  173. d.container.id,
  174. )
  175. cmd := exec.Command(d.container.runtime, args...)
  176. cmd.Dir = d.container.bundle
  177. cmd.Stdin = stdin
  178. cmd.Stdout = stdout
  179. cmd.Stderr = stderr
  180. // set the parent death signal to SIGKILL so that if containerd dies the container
  181. // process also dies
  182. cmd.SysProcAttr = &syscall.SysProcAttr{
  183. Pdeathsig: syscall.SIGKILL,
  184. }
  185. exitSubscription := subreaper.Subscribe()
  186. err = d.startCmd(cmd)
  187. if err != nil {
  188. subreaper.Unsubscribe(exitSubscription)
  189. d.delete()
  190. return err
  191. }
  192. go d.watch(cmd, exitSubscription)
  193. return nil
  194. }
  195. func (d *directProcess) watch(cmd *exec.Cmd, exitSubscription *subreaper.Subscription) {
  196. defer subreaper.Unsubscribe(exitSubscription)
  197. defer d.delete()
  198. f, err := os.OpenFile(path.Join(d.root, ExitFile), syscall.O_WRONLY, 0)
  199. if err == nil {
  200. defer f.Close()
  201. }
  202. exitCode := 0
  203. if err = cmd.Wait(); err != nil {
  204. if exitError, ok := err.(exec.ExitCodeError); ok {
  205. exitCode = exitError.Code
  206. }
  207. }
  208. if exitCode == 0 {
  209. pid, err := d.getPidFromFile()
  210. if err != nil {
  211. return
  212. }
  213. exitSubscription.SetPid(pid)
  214. exitCode = exitSubscription.Wait()
  215. }
  216. writeInt(path.Join(d.root, ExitStatusFile), exitCode)
  217. }
  218. func (d *directProcess) delete() {
  219. if d.console != nil {
  220. d.console.Close()
  221. }
  222. d.io.Close()
  223. d.Wait()
  224. if !d.exec {
  225. exec.Command(d.container.runtime, append(d.container.runtimeArgs, "delete", d.container.id)...).Run()
  226. }
  227. }
  228. func writeInt(path string, i int) error {
  229. f, err := os.Create(path)
  230. if err != nil {
  231. return err
  232. }
  233. defer f.Close()
  234. _, err = fmt.Fprintf(f, "%d", i)
  235. return err
  236. }
  237. type stdio struct {
  238. stdin *os.File
  239. stdout *os.File
  240. stderr *os.File
  241. }
  242. func (s stdio) Close() error {
  243. err := s.stdin.Close()
  244. if oerr := s.stdout.Close(); err == nil {
  245. err = oerr
  246. }
  247. if oerr := s.stderr.Close(); err == nil {
  248. err = oerr
  249. }
  250. return err
  251. }