driver.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. package namespaces
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "github.com/dotcloud/docker/execdriver"
  7. "github.com/dotcloud/docker/execdriver/lxc"
  8. "github.com/dotcloud/docker/pkg/cgroups"
  9. "github.com/dotcloud/docker/pkg/libcontainer"
  10. "github.com/dotcloud/docker/pkg/libcontainer/nsinit"
  11. "io"
  12. "io/ioutil"
  13. "os"
  14. "os/exec"
  15. "path/filepath"
  16. "strconv"
  17. "strings"
  18. "syscall"
  19. )
  20. const (
  21. DriverName = "namespaces"
  22. Version = "0.1"
  23. )
  24. var (
  25. ErrNotSupported = errors.New("not supported")
  26. )
  27. func init() {
  28. execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
  29. var container *libcontainer.Container
  30. f, err := os.Open("container.json")
  31. if err != nil {
  32. return err
  33. }
  34. if err := json.NewDecoder(f).Decode(&container); err != nil {
  35. f.Close()
  36. return err
  37. }
  38. f.Close()
  39. cwd, err := os.Getwd()
  40. if err != nil {
  41. return err
  42. }
  43. syncPipe, err := nsinit.NewSyncPipeFromFd(0, uintptr(args.Pipe))
  44. if err != nil {
  45. return err
  46. }
  47. if err := nsinit.Init(container, cwd, args.Console, syncPipe, args.Args); err != nil {
  48. return err
  49. }
  50. return nil
  51. })
  52. }
  53. type driver struct {
  54. root string
  55. }
  56. type info struct {
  57. ID string
  58. driver *driver
  59. }
  60. func (i *info) IsRunning() bool {
  61. p := filepath.Join(i.driver.root, "containers", i.ID, "root", ".nspid")
  62. if _, err := os.Stat(p); err == nil {
  63. return true
  64. }
  65. return false
  66. }
  67. func NewDriver(root string) (*driver, error) {
  68. return &driver{
  69. root: root,
  70. }, nil
  71. }
  72. func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
  73. var (
  74. term nsinit.Terminal
  75. container = createContainer(c)
  76. factory = &dockerCommandFactory{c}
  77. stateWriter = &dockerStateWriter{
  78. callback: startCallback,
  79. c: c,
  80. dsw: &nsinit.DefaultStateWriter{c.Rootfs},
  81. }
  82. )
  83. if c.Tty {
  84. term = &dockerTtyTerm{
  85. pipes: pipes,
  86. }
  87. } else {
  88. term = &dockerStdTerm{
  89. pipes: pipes,
  90. }
  91. }
  92. c.Terminal = term
  93. if err := writeContainerFile(container, c.Rootfs); err != nil {
  94. return -1, err
  95. }
  96. args := append([]string{c.Entrypoint}, c.Arguments...)
  97. return nsinit.Exec(container, factory, stateWriter, term, "", args)
  98. }
  99. func (d *driver) Kill(p *execdriver.Command, sig int) error {
  100. return syscall.Kill(p.Process.Pid, syscall.Signal(sig))
  101. }
  102. func (d *driver) Restore(c *execdriver.Command) error {
  103. var (
  104. nspid int
  105. p = filepath.Join(d.root, "containers", c.ID, "root", ".nspid")
  106. )
  107. f, err := os.Open(p)
  108. if err != nil {
  109. return err
  110. }
  111. defer f.Close()
  112. if _, err := fmt.Fscanf(f, "%d", &nspid); err != nil {
  113. return err
  114. }
  115. proc, err := os.FindProcess(nspid)
  116. if err != nil {
  117. return err
  118. }
  119. _, err = proc.Wait()
  120. return err
  121. }
  122. func (d *driver) Info(id string) execdriver.Info {
  123. return &info{
  124. ID: id,
  125. driver: d,
  126. }
  127. }
  128. func (d *driver) Name() string {
  129. return fmt.Sprintf("%s-%s", DriverName, Version)
  130. }
  131. func (d *driver) GetPidsForContainer(id string) ([]int, error) {
  132. pids := []int{}
  133. subsystem := "devices"
  134. cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem)
  135. if err != nil {
  136. return pids, err
  137. }
  138. cgroupDir, err := cgroups.GetThisCgroupDir(subsystem)
  139. if err != nil {
  140. return pids, err
  141. }
  142. filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks")
  143. if _, err := os.Stat(filename); os.IsNotExist(err) {
  144. filename = filepath.Join(cgroupRoot, cgroupDir, "docker", id, "tasks")
  145. }
  146. output, err := ioutil.ReadFile(filename)
  147. if err != nil {
  148. return pids, err
  149. }
  150. for _, p := range strings.Split(string(output), "\n") {
  151. if len(p) == 0 {
  152. continue
  153. }
  154. pid, err := strconv.Atoi(p)
  155. if err != nil {
  156. return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
  157. }
  158. pids = append(pids, pid)
  159. }
  160. return pids, nil
  161. }
  162. func writeContainerFile(container *libcontainer.Container, rootfs string) error {
  163. data, err := json.Marshal(container)
  164. if err != nil {
  165. return err
  166. }
  167. return ioutil.WriteFile(filepath.Join(rootfs, "container.json"), data, 0755)
  168. }
  169. func getEnv(key string, env []string) string {
  170. for _, pair := range env {
  171. parts := strings.Split(pair, "=")
  172. if parts[0] == key {
  173. return parts[1]
  174. }
  175. }
  176. return ""
  177. }
  178. type dockerCommandFactory struct {
  179. c *execdriver.Command
  180. }
  181. // createCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
  182. // defined on the container's configuration and use the current binary as the init with the
  183. // args provided
  184. func (d *dockerCommandFactory) Create(container *libcontainer.Container,
  185. console, logFile string, syncFd uintptr, args []string) *exec.Cmd {
  186. c := d.c
  187. // we need to join the rootfs because nsinit will setup the rootfs and chroot
  188. initPath := filepath.Join(c.Rootfs, c.InitPath)
  189. c.Path = initPath
  190. c.Args = append([]string{
  191. initPath,
  192. "-driver", DriverName,
  193. "-console", console,
  194. "-pipe", fmt.Sprint(syncFd),
  195. "-log", logFile,
  196. }, args...)
  197. c.SysProcAttr = &syscall.SysProcAttr{
  198. Cloneflags: uintptr(nsinit.GetNamespaceFlags(container.Namespaces)),
  199. }
  200. c.Env = container.Env
  201. c.Dir = c.Rootfs
  202. return &c.Cmd
  203. }
  204. type dockerStateWriter struct {
  205. dsw nsinit.StateWriter
  206. c *execdriver.Command
  207. callback execdriver.StartCallback
  208. }
  209. func (d *dockerStateWriter) WritePid(pid int) error {
  210. err := d.dsw.WritePid(pid)
  211. if d.callback != nil {
  212. d.callback(d.c)
  213. }
  214. return err
  215. }
  216. func (d *dockerStateWriter) DeletePid() error {
  217. return d.dsw.DeletePid()
  218. }
  219. func createContainer(c *execdriver.Command) *libcontainer.Container {
  220. container := getDefaultTemplate()
  221. container.Hostname = getEnv("HOSTNAME", c.Env)
  222. container.Tty = c.Tty
  223. container.User = c.User
  224. container.WorkingDir = c.WorkingDir
  225. container.Env = c.Env
  226. container.Env = append(container.Env, "container=docker")
  227. if c.Network != nil {
  228. container.Network = &libcontainer.Network{
  229. Mtu: c.Network.Mtu,
  230. Address: fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
  231. Gateway: c.Network.Gateway,
  232. Type: "veth",
  233. Context: libcontainer.Context{
  234. "prefix": "dock",
  235. "bridge": c.Network.Bridge,
  236. },
  237. }
  238. }
  239. container.Cgroups.Name = c.ID
  240. if c.Privileged {
  241. container.Capabilities = nil
  242. container.Cgroups.DeviceAccess = true
  243. }
  244. if c.Resources != nil {
  245. container.Cgroups.CpuShares = c.Resources.CpuShares
  246. container.Cgroups.Memory = c.Resources.Memory
  247. container.Cgroups.MemorySwap = c.Resources.MemorySwap
  248. }
  249. return container
  250. }
  251. type dockerStdTerm struct {
  252. lxc.StdConsole
  253. pipes *execdriver.Pipes
  254. }
  255. func (d *dockerStdTerm) Attach(cmd *exec.Cmd) error {
  256. return d.AttachPipes(cmd, d.pipes)
  257. }
  258. func (d *dockerStdTerm) SetMaster(master *os.File) {
  259. // do nothing
  260. }
  261. type dockerTtyTerm struct {
  262. lxc.TtyConsole
  263. pipes *execdriver.Pipes
  264. }
  265. func (t *dockerTtyTerm) Attach(cmd *exec.Cmd) error {
  266. go io.Copy(t.pipes.Stdout, t.MasterPty)
  267. if t.pipes.Stdin != nil {
  268. go io.Copy(t.MasterPty, t.pipes.Stdin)
  269. }
  270. return nil
  271. }
  272. func (t *dockerTtyTerm) SetMaster(master *os.File) {
  273. t.MasterPty = master
  274. }