utils.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. // +build linux
  2. package runc
  3. import (
  4. "errors"
  5. "fmt"
  6. "os"
  7. "path/filepath"
  8. "syscall"
  9. "github.com/Sirupsen/logrus"
  10. "github.com/codegangsta/cli"
  11. "github.com/opencontainers/runc/libcontainer"
  12. "github.com/opencontainers/runc/libcontainer/cgroups/systemd"
  13. "github.com/opencontainers/runc/libcontainer/specconv"
  14. "github.com/opencontainers/runtime-spec/specs-go"
  15. )
  16. var errEmptyID = errors.New("container id cannot be empty")
  17. var container libcontainer.Container
  18. // loadFactory returns the configured factory instance for execing containers.
  19. func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
  20. root := context.GlobalString("root")
  21. abs, err := filepath.Abs(root)
  22. if err != nil {
  23. return nil, err
  24. }
  25. cgroupManager := libcontainer.Cgroupfs
  26. if context.GlobalBool("systemd-cgroup") {
  27. if systemd.UseSystemd() {
  28. cgroupManager = libcontainer.SystemdCgroups
  29. } else {
  30. return nil, fmt.Errorf("systemd cgroup flag passed, but systemd support for managing cgroups is not available.")
  31. }
  32. }
  33. return libcontainer.New(abs, cgroupManager, func(l *libcontainer.LinuxFactory) error {
  34. l.CriuPath = context.GlobalString("criu")
  35. return nil
  36. })
  37. }
  38. // getContainer returns the specified container instance by loading it from state
  39. // with the default factory.
  40. func getContainer(context *cli.Context) (libcontainer.Container, error) {
  41. id := context.Args().First()
  42. if id == "" {
  43. return nil, errEmptyID
  44. }
  45. factory, err := loadFactory(context)
  46. if err != nil {
  47. return nil, err
  48. }
  49. return factory.Load(id)
  50. }
  51. // fatal prints the error's details if it is a libcontainer specific error type
  52. // then exits the program with an exit status of 1.
  53. func fatal(err error) {
  54. // make sure the error is written to the logger
  55. logrus.Error(err)
  56. fmt.Fprintln(os.Stderr, err)
  57. os.Exit(1)
  58. }
  59. func fatalf(t string, v ...interface{}) {
  60. fatal(fmt.Errorf(t, v...))
  61. }
  62. func getDefaultImagePath(context *cli.Context) string {
  63. cwd, err := os.Getwd()
  64. if err != nil {
  65. panic(err)
  66. }
  67. return filepath.Join(cwd, "checkpoint")
  68. }
  69. // newProcess returns a new libcontainer Process with the arguments from the
  70. // spec and stdio from the current process.
  71. func newProcess(p specs.Process) (*libcontainer.Process, error) {
  72. lp := &libcontainer.Process{
  73. Args: p.Args,
  74. Env: p.Env,
  75. // TODO: fix libcontainer's API to better support uid/gid in a typesafe way.
  76. User: fmt.Sprintf("%d:%d", p.User.UID, p.User.GID),
  77. Cwd: p.Cwd,
  78. Capabilities: p.Capabilities,
  79. Label: p.SelinuxLabel,
  80. NoNewPrivileges: &p.NoNewPrivileges,
  81. AppArmorProfile: p.ApparmorProfile,
  82. }
  83. for _, rlimit := range p.Rlimits {
  84. rl, err := createLibContainerRlimit(rlimit)
  85. if err != nil {
  86. return nil, err
  87. }
  88. lp.Rlimits = append(lp.Rlimits, rl)
  89. }
  90. return lp, nil
  91. }
  92. func dupStdio(process *libcontainer.Process, rootuid int) error {
  93. process.Stdin = os.Stdin
  94. process.Stdout = os.Stdout
  95. process.Stderr = os.Stderr
  96. for _, fd := range []uintptr{
  97. os.Stdin.Fd(),
  98. os.Stdout.Fd(),
  99. os.Stderr.Fd(),
  100. } {
  101. if err := syscall.Fchown(int(fd), rootuid, rootuid); err != nil {
  102. return err
  103. }
  104. }
  105. return nil
  106. }
  107. // If systemd is supporting sd_notify protocol, this function will add support
  108. // for sd_notify protocol from within the container.
  109. func setupSdNotify(spec *specs.Spec, notifySocket string) {
  110. spec.Mounts = append(spec.Mounts, specs.Mount{Destination: notifySocket, Type: "bind", Source: notifySocket, Options: []string{"bind"}})
  111. spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", notifySocket))
  112. }
  113. func destroy(container libcontainer.Container) {
  114. if err := container.Destroy(); err != nil {
  115. logrus.Error(err)
  116. }
  117. }
  118. // setupIO sets the proper IO on the process depending on the configuration
  119. // If there is a nil error then there must be a non nil tty returned
  120. func setupIO(process *libcontainer.Process, rootuid int, console string, createTTY, detach bool) (*tty, error) {
  121. // detach and createTty will not work unless a console path is passed
  122. // so error out here before changing any terminal settings
  123. if createTTY && detach && console == "" {
  124. return nil, fmt.Errorf("cannot allocate tty if runc will detach")
  125. }
  126. if createTTY {
  127. return createTty(process, rootuid, console)
  128. }
  129. if detach {
  130. if err := dupStdio(process, rootuid); err != nil {
  131. return nil, err
  132. }
  133. return &tty{}, nil
  134. }
  135. return createStdioPipes(process, rootuid)
  136. }
  137. // createPidFile creates a file with the processes pid inside it atomically
  138. // it creates a temp file with the paths filename + '.' infront of it
  139. // then renames the file
  140. func createPidFile(path string, process *libcontainer.Process) error {
  141. pid, err := process.Pid()
  142. if err != nil {
  143. return err
  144. }
  145. var (
  146. tmpDir = filepath.Dir(path)
  147. tmpName = filepath.Join(tmpDir, fmt.Sprintf(".%s", filepath.Base(path)))
  148. )
  149. f, err := os.OpenFile(tmpName, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666)
  150. if err != nil {
  151. return err
  152. }
  153. _, err = fmt.Fprintf(f, "%d", pid)
  154. f.Close()
  155. if err != nil {
  156. return err
  157. }
  158. return os.Rename(tmpName, path)
  159. }
  160. func createContainer(context *cli.Context, id string, spec *specs.Spec) (libcontainer.Container, error) {
  161. config, err := specconv.CreateLibcontainerConfig(&specconv.CreateOpts{
  162. CgroupName: id,
  163. UseSystemdCgroup: context.GlobalBool("systemd-cgroup"),
  164. NoPivotRoot: context.Bool("no-pivot"),
  165. Spec: spec,
  166. })
  167. if err != nil {
  168. return nil, err
  169. }
  170. if _, err := os.Stat(config.Rootfs); err != nil {
  171. if os.IsNotExist(err) {
  172. return nil, fmt.Errorf("rootfs (%q) does not exist", config.Rootfs)
  173. }
  174. return nil, err
  175. }
  176. factory, err := loadFactory(context)
  177. if err != nil {
  178. return nil, err
  179. }
  180. return factory.Create(id, config)
  181. }
  182. type runner struct {
  183. enableSubreaper bool
  184. shouldDestroy bool
  185. detach bool
  186. listenFDs []*os.File
  187. pidFile string
  188. console string
  189. container libcontainer.Container
  190. }
  191. func (r *runner) run(config *specs.Process) (int, error) {
  192. process, err := newProcess(*config)
  193. if err != nil {
  194. r.destroy()
  195. return -1, err
  196. }
  197. if len(r.listenFDs) > 0 {
  198. process.Env = append(process.Env, fmt.Sprintf("LISTEN_FDS=%d", len(r.listenFDs)), "LISTEN_PID=1")
  199. process.ExtraFiles = append(process.ExtraFiles, r.listenFDs...)
  200. }
  201. rootuid, err := r.container.Config().HostUID()
  202. if err != nil {
  203. r.destroy()
  204. return -1, err
  205. }
  206. tty, err := setupIO(process, rootuid, r.console, config.Terminal, r.detach)
  207. if err != nil {
  208. r.destroy()
  209. return -1, err
  210. }
  211. handler := newSignalHandler(tty, r.enableSubreaper)
  212. if err := r.container.Start(process); err != nil {
  213. r.destroy()
  214. tty.Close()
  215. return -1, err
  216. }
  217. if err := tty.ClosePostStart(); err != nil {
  218. r.terminate(process)
  219. r.destroy()
  220. tty.Close()
  221. return -1, err
  222. }
  223. if r.pidFile != "" {
  224. if err := createPidFile(r.pidFile, process); err != nil {
  225. r.terminate(process)
  226. r.destroy()
  227. tty.Close()
  228. return -1, err
  229. }
  230. }
  231. if r.detach {
  232. tty.Close()
  233. return 0, nil
  234. }
  235. status, err := handler.forward(process)
  236. if err != nil {
  237. r.terminate(process)
  238. }
  239. r.destroy()
  240. tty.Close()
  241. return status, err
  242. }
  243. func (r *runner) destroy() {
  244. if r.shouldDestroy {
  245. destroy(r.container)
  246. }
  247. }
  248. func (r *runner) terminate(p *libcontainer.Process) {
  249. p.Signal(syscall.SIGKILL)
  250. p.Wait()
  251. }
  252. func validateProcessSpec(spec *specs.Process) error {
  253. if spec.Cwd == "" {
  254. return fmt.Errorf("Cwd property must not be empty")
  255. }
  256. if !filepath.IsAbs(spec.Cwd) {
  257. return fmt.Errorf("Cwd must be an absolute path")
  258. }
  259. if len(spec.Args) == 0 {
  260. return fmt.Errorf("args must not be empty")
  261. }
  262. return nil
  263. }