driver.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. package native
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "github.com/dotcloud/docker/execdriver"
  6. "github.com/dotcloud/docker/pkg/cgroups"
  7. "github.com/dotcloud/docker/pkg/libcontainer"
  8. "github.com/dotcloud/docker/pkg/libcontainer/apparmor"
  9. "github.com/dotcloud/docker/pkg/libcontainer/nsinit"
  10. "github.com/dotcloud/docker/pkg/system"
  11. "io"
  12. "io/ioutil"
  13. "log"
  14. "os"
  15. "os/exec"
  16. "path/filepath"
  17. "strconv"
  18. "strings"
  19. "syscall"
  20. )
  21. const (
  22. DriverName = "native"
  23. Version = "0.1"
  24. )
  25. func init() {
  26. execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
  27. var (
  28. container *libcontainer.Container
  29. ns = nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{args.Root}, createLogger(""))
  30. )
  31. f, err := os.Open(filepath.Join(args.Root, "container.json"))
  32. if err != nil {
  33. return err
  34. }
  35. if err := json.NewDecoder(f).Decode(&container); err != nil {
  36. f.Close()
  37. return err
  38. }
  39. f.Close()
  40. cwd, err := os.Getwd()
  41. if err != nil {
  42. return err
  43. }
  44. syncPipe, err := nsinit.NewSyncPipeFromFd(0, uintptr(args.Pipe))
  45. if err != nil {
  46. return err
  47. }
  48. if err := ns.Init(container, cwd, args.Console, syncPipe, args.Args); err != nil {
  49. return err
  50. }
  51. return nil
  52. })
  53. }
  54. type driver struct {
  55. root string
  56. initPath string
  57. }
  58. func NewDriver(root, initPath string) (*driver, error) {
  59. if err := os.MkdirAll(root, 0700); err != nil {
  60. return nil, err
  61. }
  62. if err := apparmor.InstallDefaultProfile(); err != nil {
  63. return nil, err
  64. }
  65. return &driver{
  66. root: root,
  67. initPath: initPath,
  68. }, nil
  69. }
  70. func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
  71. if err := d.validateCommand(c); err != nil {
  72. return -1, err
  73. }
  74. var (
  75. term nsinit.Terminal
  76. container = createContainer(c)
  77. factory = &dockerCommandFactory{c: c, driver: d}
  78. stateWriter = &dockerStateWriter{
  79. callback: startCallback,
  80. c: c,
  81. dsw: &nsinit.DefaultStateWriter{filepath.Join(d.root, c.ID)},
  82. }
  83. ns = nsinit.NewNsInit(factory, stateWriter, createLogger(os.Getenv("DEBUG")))
  84. args = append([]string{c.Entrypoint}, c.Arguments...)
  85. )
  86. if err := d.createContainerRoot(c.ID); err != nil {
  87. return -1, err
  88. }
  89. defer d.removeContainerRoot(c.ID)
  90. if c.Tty {
  91. term = &dockerTtyTerm{
  92. pipes: pipes,
  93. }
  94. } else {
  95. term = &dockerStdTerm{
  96. pipes: pipes,
  97. }
  98. }
  99. c.Terminal = term
  100. if err := d.writeContainerFile(container, c.ID); err != nil {
  101. return -1, err
  102. }
  103. return ns.Exec(container, term, args)
  104. }
  105. func (d *driver) Kill(p *execdriver.Command, sig int) error {
  106. err := syscall.Kill(p.Process.Pid, syscall.Signal(sig))
  107. d.removeContainerRoot(p.ID)
  108. return err
  109. }
  110. func (d *driver) Info(id string) execdriver.Info {
  111. return &info{
  112. ID: id,
  113. driver: d,
  114. }
  115. }
  116. func (d *driver) Name() string {
  117. return fmt.Sprintf("%s-%s", DriverName, Version)
  118. }
  119. // TODO: this can be improved with our driver
  120. // there has to be a better way to do this
  121. func (d *driver) GetPidsForContainer(id string) ([]int, error) {
  122. pids := []int{}
  123. subsystem := "devices"
  124. cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem)
  125. if err != nil {
  126. return pids, err
  127. }
  128. cgroupDir, err := cgroups.GetThisCgroupDir(subsystem)
  129. if err != nil {
  130. return pids, err
  131. }
  132. filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks")
  133. if _, err := os.Stat(filename); os.IsNotExist(err) {
  134. filename = filepath.Join(cgroupRoot, cgroupDir, "docker", id, "tasks")
  135. }
  136. output, err := ioutil.ReadFile(filename)
  137. if err != nil {
  138. return pids, err
  139. }
  140. for _, p := range strings.Split(string(output), "\n") {
  141. if len(p) == 0 {
  142. continue
  143. }
  144. pid, err := strconv.Atoi(p)
  145. if err != nil {
  146. return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
  147. }
  148. pids = append(pids, pid)
  149. }
  150. return pids, nil
  151. }
  152. func (d *driver) writeContainerFile(container *libcontainer.Container, id string) error {
  153. data, err := json.Marshal(container)
  154. if err != nil {
  155. return err
  156. }
  157. return ioutil.WriteFile(filepath.Join(d.root, id, "container.json"), data, 0655)
  158. }
  159. func (d *driver) createContainerRoot(id string) error {
  160. return os.MkdirAll(filepath.Join(d.root, id), 0655)
  161. }
  162. func (d *driver) removeContainerRoot(id string) error {
  163. return os.RemoveAll(filepath.Join(d.root, id))
  164. }
  165. func (d *driver) validateCommand(c *execdriver.Command) error {
  166. // we need to check the Config of the command to make sure that we
  167. // do not have any of the lxc-conf variables
  168. for _, conf := range c.Config {
  169. if strings.Contains(conf, "lxc") {
  170. return fmt.Errorf("%s is not supported by the native driver", conf)
  171. }
  172. }
  173. return nil
  174. }
  175. func getEnv(key string, env []string) string {
  176. for _, pair := range env {
  177. parts := strings.Split(pair, "=")
  178. if parts[0] == key {
  179. return parts[1]
  180. }
  181. }
  182. return ""
  183. }
  184. type dockerCommandFactory struct {
  185. c *execdriver.Command
  186. driver *driver
  187. }
  188. // createCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
  189. // defined on the container's configuration and use the current binary as the init with the
  190. // args provided
  191. func (d *dockerCommandFactory) Create(container *libcontainer.Container, console string, syncFile *os.File, args []string) *exec.Cmd {
  192. // we need to join the rootfs because nsinit will setup the rootfs and chroot
  193. initPath := filepath.Join(d.c.Rootfs, d.c.InitPath)
  194. d.c.Path = d.driver.initPath
  195. d.c.Args = append([]string{
  196. initPath,
  197. "-driver", DriverName,
  198. "-console", console,
  199. "-pipe", "3",
  200. "-root", filepath.Join(d.driver.root, d.c.ID),
  201. "--",
  202. }, args...)
  203. // set this to nil so that when we set the clone flags anything else is reset
  204. d.c.SysProcAttr = nil
  205. system.SetCloneFlags(&d.c.Cmd, uintptr(nsinit.GetNamespaceFlags(container.Namespaces)))
  206. d.c.ExtraFiles = []*os.File{syncFile}
  207. d.c.Env = container.Env
  208. d.c.Dir = d.c.Rootfs
  209. return &d.c.Cmd
  210. }
  211. type dockerStateWriter struct {
  212. dsw nsinit.StateWriter
  213. c *execdriver.Command
  214. callback execdriver.StartCallback
  215. }
  216. func (d *dockerStateWriter) WritePid(pid int) error {
  217. d.c.ContainerPid = pid
  218. err := d.dsw.WritePid(pid)
  219. if d.callback != nil {
  220. d.callback(d.c)
  221. }
  222. return err
  223. }
  224. func (d *dockerStateWriter) DeletePid() error {
  225. return d.dsw.DeletePid()
  226. }
  227. func createLogger(debug string) *log.Logger {
  228. var w io.Writer
  229. // if we are in debug mode set the logger to stderr
  230. if debug != "" {
  231. w = os.Stderr
  232. } else {
  233. w = ioutil.Discard
  234. }
  235. return log.New(w, "[libcontainer] ", log.LstdFlags)
  236. }