init.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // +build linux
  2. package nsinit
  3. import (
  4. "fmt"
  5. "github.com/dotcloud/docker/pkg/libcontainer"
  6. "github.com/dotcloud/docker/pkg/libcontainer/capabilities"
  7. "github.com/dotcloud/docker/pkg/libcontainer/network"
  8. "github.com/dotcloud/docker/pkg/system"
  9. "github.com/dotcloud/docker/pkg/user"
  10. "os"
  11. "os/exec"
  12. "path/filepath"
  13. "syscall"
  14. )
  15. // Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
  16. // and other options required for the new container.
  17. func Init(container *libcontainer.Container, uncleanRootfs, console string, syncPipe *SyncPipe, args []string) error {
  18. rootfs, err := resolveRootfs(uncleanRootfs)
  19. if err != nil {
  20. return err
  21. }
  22. // We always read this as it is a way to sync with the parent as well
  23. context, err := syncPipe.ReadFromParent()
  24. if err != nil {
  25. syncPipe.Close()
  26. return err
  27. }
  28. syncPipe.Close()
  29. if console != "" {
  30. // close pipes so that we can replace it with the pty
  31. closeStdPipes()
  32. slave, err := openTerminal(console, syscall.O_RDWR)
  33. if err != nil {
  34. return fmt.Errorf("open terminal %s", err)
  35. }
  36. if err := dupSlave(slave); err != nil {
  37. return fmt.Errorf("dup2 slave %s", err)
  38. }
  39. }
  40. if _, err := system.Setsid(); err != nil {
  41. return fmt.Errorf("setsid %s", err)
  42. }
  43. if console != "" {
  44. if err := system.Setctty(); err != nil {
  45. return fmt.Errorf("setctty %s", err)
  46. }
  47. }
  48. if err := system.ParentDeathSignal(); err != nil {
  49. return fmt.Errorf("parent deth signal %s", err)
  50. }
  51. if err := setupNewMountNamespace(rootfs, console, container.ReadonlyFs); err != nil {
  52. return fmt.Errorf("setup mount namespace %s", err)
  53. }
  54. if err := setupNetwork(container.Network, context); err != nil {
  55. return fmt.Errorf("setup networking %s", err)
  56. }
  57. if err := system.Sethostname(container.Hostname); err != nil {
  58. return fmt.Errorf("sethostname %s", err)
  59. }
  60. if err := capabilities.DropCapabilities(container); err != nil {
  61. return fmt.Errorf("drop capabilities %s", err)
  62. }
  63. if err := setupUser(container); err != nil {
  64. return fmt.Errorf("setup user %s", err)
  65. }
  66. if container.WorkingDir != "" {
  67. if err := system.Chdir(container.WorkingDir); err != nil {
  68. return fmt.Errorf("chdir to %s %s", container.WorkingDir, err)
  69. }
  70. }
  71. return execArgs(args, container.Env)
  72. }
  73. func execArgs(args []string, env []string) error {
  74. name, err := exec.LookPath(args[0])
  75. if err != nil {
  76. return err
  77. }
  78. if err := system.Exec(name, args[0:], env); err != nil {
  79. return fmt.Errorf("exec %s", err)
  80. }
  81. panic("unreachable")
  82. }
  83. func closeStdPipes() {
  84. os.Stdin.Close()
  85. os.Stdout.Close()
  86. os.Stderr.Close()
  87. }
  88. // resolveRootfs ensures that the current working directory is
  89. // not a symlink and returns the absolute path to the rootfs
  90. func resolveRootfs(uncleanRootfs string) (string, error) {
  91. rootfs, err := filepath.Abs(uncleanRootfs)
  92. if err != nil {
  93. return "", err
  94. }
  95. return filepath.EvalSymlinks(rootfs)
  96. }
  97. func setupUser(container *libcontainer.Container) error {
  98. if container.User != "" && container.User != "root" {
  99. uid, gid, suppGids, err := user.GetUserGroupSupplementary(container.User, syscall.Getuid(), syscall.Getgid())
  100. if err != nil {
  101. return err
  102. }
  103. if err := system.Setgroups(suppGids); err != nil {
  104. return err
  105. }
  106. if err := system.Setgid(gid); err != nil {
  107. return err
  108. }
  109. if err := system.Setuid(uid); err != nil {
  110. return err
  111. }
  112. } else {
  113. if err := system.Setgroups(nil); err != nil {
  114. return err
  115. }
  116. if err := system.Setresgid(0, 0, 0); err != nil {
  117. return err
  118. }
  119. if err := system.Setresuid(0, 0, 0); err != nil {
  120. return err
  121. }
  122. }
  123. return nil
  124. }
  125. // dupSlave dup2 the pty slave's fd into stdout and stdin and ensures that
  126. // the slave's fd is 0, or stdin
  127. func dupSlave(slave *os.File) error {
  128. if slave.Fd() != 0 {
  129. return fmt.Errorf("slave fd not 0 %d", slave.Fd())
  130. }
  131. if err := system.Dup2(slave.Fd(), 1); err != nil {
  132. return err
  133. }
  134. if err := system.Dup2(slave.Fd(), 2); err != nil {
  135. return err
  136. }
  137. return nil
  138. }
  139. // openTerminal is a clone of os.OpenFile without the O_CLOEXEC
  140. // used to open the pty slave inside the container namespace
  141. func openTerminal(name string, flag int) (*os.File, error) {
  142. r, e := syscall.Open(name, flag, 0)
  143. if e != nil {
  144. return nil, &os.PathError{"open", name, e}
  145. }
  146. return os.NewFile(uintptr(r), name), nil
  147. }
  148. // setupVethNetwork uses the Network config if it is not nil to initialize
  149. // the new veth interface inside the container for use by changing the name to eth0
  150. // setting the MTU and IP address along with the default gateway
  151. func setupNetwork(config *libcontainer.Network, context libcontainer.Context) error {
  152. if config != nil {
  153. strategy, err := network.GetStrategy(config.Type)
  154. if err != nil {
  155. return err
  156. }
  157. return strategy.Initialize(config, context)
  158. }
  159. return nil
  160. }