restore.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // +build linux
  2. package runc
  3. import (
  4. "os"
  5. "syscall"
  6. "github.com/Sirupsen/logrus"
  7. "github.com/codegangsta/cli"
  8. "github.com/opencontainers/runc/libcontainer"
  9. "github.com/opencontainers/runc/libcontainer/configs"
  10. "github.com/opencontainers/runc/libcontainer/specconv"
  11. "github.com/opencontainers/runtime-spec/specs-go"
  12. )
  13. var restoreCommand = cli.Command{
  14. Name: "restore",
  15. Usage: "restore a container from a previous checkpoint",
  16. ArgsUsage: `<container-id>
  17. Where "<container-id>" is the name for the instance of the container to be
  18. restored.`,
  19. Description: `Restores the saved state of the container instance that was previously saved
  20. using the runc checkpoint command.`,
  21. Flags: []cli.Flag{
  22. cli.StringFlag{
  23. Name: "image-path",
  24. Value: "",
  25. Usage: "path to criu image files for restoring",
  26. },
  27. cli.StringFlag{
  28. Name: "work-path",
  29. Value: "",
  30. Usage: "path for saving work files and logs",
  31. },
  32. cli.BoolFlag{
  33. Name: "tcp-established",
  34. Usage: "allow open tcp connections",
  35. },
  36. cli.BoolFlag{
  37. Name: "ext-unix-sk",
  38. Usage: "allow external unix sockets",
  39. },
  40. cli.BoolFlag{
  41. Name: "shell-job",
  42. Usage: "allow shell jobs",
  43. },
  44. cli.BoolFlag{
  45. Name: "file-locks",
  46. Usage: "handle file locks, for safety",
  47. },
  48. cli.StringFlag{
  49. Name: "manage-cgroups-mode",
  50. Value: "",
  51. Usage: "cgroups mode: 'soft' (default), 'full' and 'strict'.",
  52. },
  53. cli.StringFlag{
  54. Name: "bundle, b",
  55. Value: "",
  56. Usage: "path to the root of the bundle directory",
  57. },
  58. cli.BoolFlag{
  59. Name: "detach,d",
  60. Usage: "detach from the container's process",
  61. },
  62. cli.StringFlag{
  63. Name: "pid-file",
  64. Value: "",
  65. Usage: "specify the file to write the process id to",
  66. },
  67. cli.BoolFlag{
  68. Name: "no-subreaper",
  69. Usage: "disable the use of the subreaper used to reap reparented processes",
  70. },
  71. cli.BoolFlag{
  72. Name: "no-pivot",
  73. Usage: "do not use pivot root to jail process inside rootfs. This should be used whenever the rootfs is on top of a ramdisk",
  74. },
  75. },
  76. Action: func(context *cli.Context) {
  77. imagePath := context.String("image-path")
  78. id := context.Args().First()
  79. if id == "" {
  80. fatal(errEmptyID)
  81. }
  82. if imagePath == "" {
  83. imagePath = getDefaultImagePath(context)
  84. }
  85. bundle := context.String("bundle")
  86. if bundle != "" {
  87. if err := os.Chdir(bundle); err != nil {
  88. fatal(err)
  89. }
  90. }
  91. spec, err := loadSpec(specConfig)
  92. if err != nil {
  93. fatal(err)
  94. }
  95. config, err := specconv.CreateLibcontainerConfig(&specconv.CreateOpts{
  96. CgroupName: id,
  97. UseSystemdCgroup: context.GlobalBool("systemd-cgroup"),
  98. NoPivotRoot: context.Bool("no-pivot"),
  99. Spec: spec,
  100. })
  101. if err != nil {
  102. fatal(err)
  103. }
  104. status, err := restoreContainer(context, spec, config, imagePath)
  105. if err != nil {
  106. fatal(err)
  107. }
  108. os.Exit(status)
  109. },
  110. }
  111. func restoreContainer(context *cli.Context, spec *specs.Spec, config *configs.Config, imagePath string) (code int, err error) {
  112. var (
  113. rootuid = 0
  114. id = context.Args().First()
  115. )
  116. factory, err := loadFactory(context)
  117. if err != nil {
  118. return -1, err
  119. }
  120. container, err := factory.Load(id)
  121. if err != nil {
  122. container, err = factory.Create(id, config)
  123. if err != nil {
  124. return -1, err
  125. }
  126. }
  127. options := criuOptions(context)
  128. status, err := container.Status()
  129. if err != nil {
  130. logrus.Error(err)
  131. }
  132. if status == libcontainer.Running {
  133. fatalf("Container with id %s already running", id)
  134. }
  135. setManageCgroupsMode(context, options)
  136. // ensure that the container is always removed if we were the process
  137. // that created it.
  138. detach := context.Bool("detach")
  139. if !detach {
  140. defer destroy(container)
  141. }
  142. process := &libcontainer.Process{}
  143. tty, err := setupIO(process, rootuid, "", false, detach)
  144. if err != nil {
  145. return -1, err
  146. }
  147. defer tty.Close()
  148. handler := newSignalHandler(tty, !context.Bool("no-subreaper"))
  149. if err := container.Restore(process, options); err != nil {
  150. return -1, err
  151. }
  152. if err := tty.ClosePostStart(); err != nil {
  153. return -1, err
  154. }
  155. if pidFile := context.String("pid-file"); pidFile != "" {
  156. if err := createPidFile(pidFile, process); err != nil {
  157. process.Signal(syscall.SIGKILL)
  158. process.Wait()
  159. return -1, err
  160. }
  161. }
  162. if detach {
  163. return 0, nil
  164. }
  165. return handler.forward(process)
  166. }
  167. func criuOptions(context *cli.Context) *libcontainer.CriuOpts {
  168. imagePath := getCheckpointImagePath(context)
  169. if err := os.MkdirAll(imagePath, 0655); err != nil {
  170. fatal(err)
  171. }
  172. return &libcontainer.CriuOpts{
  173. ImagesDirectory: imagePath,
  174. WorkDirectory: context.String("work-path"),
  175. LeaveRunning: context.Bool("leave-running"),
  176. TcpEstablished: context.Bool("tcp-established"),
  177. ExternalUnixConnections: context.Bool("ext-unix-sk"),
  178. ShellJob: context.Bool("shell-job"),
  179. FileLocks: context.Bool("file-locks"),
  180. }
  181. }