driver.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. package lxc
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io/ioutil"
  6. "log"
  7. "os"
  8. "os/exec"
  9. "path"
  10. "path/filepath"
  11. "runtime"
  12. "strconv"
  13. "strings"
  14. "syscall"
  15. "time"
  16. "github.com/docker/libcontainer/cgroups"
  17. "github.com/docker/libcontainer/label"
  18. "github.com/docker/libcontainer/mount/nodes"
  19. "github.com/dotcloud/docker/daemon/execdriver"
  20. "github.com/dotcloud/docker/utils"
  21. )
  22. const DriverName = "lxc"
  23. func init() {
  24. execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
  25. runtime.LockOSThread()
  26. if err := setupEnv(args); err != nil {
  27. return err
  28. }
  29. if err := setupHostname(args); err != nil {
  30. return err
  31. }
  32. if err := setupNetworking(args); err != nil {
  33. return err
  34. }
  35. if err := finalizeNamespace(args); err != nil {
  36. return err
  37. }
  38. path, err := exec.LookPath(args.Args[0])
  39. if err != nil {
  40. log.Printf("Unable to locate %v", args.Args[0])
  41. os.Exit(127)
  42. }
  43. if err := syscall.Exec(path, args.Args, os.Environ()); err != nil {
  44. return fmt.Errorf("dockerinit unable to execute %s - %s", path, err)
  45. }
  46. panic("Unreachable")
  47. })
  48. }
  49. type driver struct {
  50. root string // root path for the driver to use
  51. apparmor bool
  52. sharedRoot bool
  53. }
  54. func NewDriver(root string, apparmor bool) (*driver, error) {
  55. // setup unconfined symlink
  56. if err := linkLxcStart(root); err != nil {
  57. return nil, err
  58. }
  59. return &driver{
  60. apparmor: apparmor,
  61. root: root,
  62. sharedRoot: rootIsShared(),
  63. }, nil
  64. }
  65. func (d *driver) Name() string {
  66. version := d.version()
  67. return fmt.Sprintf("%s-%s", DriverName, version)
  68. }
  69. func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
  70. if err := execdriver.SetTerminal(c, pipes); err != nil {
  71. return -1, err
  72. }
  73. if err := d.generateEnvConfig(c); err != nil {
  74. return -1, err
  75. }
  76. configPath, err := d.generateLXCConfig(c)
  77. if err != nil {
  78. return -1, err
  79. }
  80. params := []string{
  81. "lxc-start",
  82. "-n", c.ID,
  83. "-f", configPath,
  84. "--",
  85. c.InitPath,
  86. "-driver",
  87. DriverName,
  88. }
  89. if c.Network.Interface != nil {
  90. params = append(params,
  91. "-g", c.Network.Interface.Gateway,
  92. "-i", fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen),
  93. )
  94. }
  95. params = append(params,
  96. "-mtu", strconv.Itoa(c.Network.Mtu),
  97. )
  98. if c.User != "" {
  99. params = append(params, "-u", c.User)
  100. }
  101. if c.Privileged {
  102. if d.apparmor {
  103. params[0] = path.Join(d.root, "lxc-start-unconfined")
  104. }
  105. params = append(params, "-privileged")
  106. }
  107. if c.WorkingDir != "" {
  108. params = append(params, "-w", c.WorkingDir)
  109. }
  110. params = append(params, "--", c.Entrypoint)
  111. params = append(params, c.Arguments...)
  112. if d.sharedRoot {
  113. // lxc-start really needs / to be non-shared, or all kinds of stuff break
  114. // when lxc-start unmount things and those unmounts propagate to the main
  115. // mount namespace.
  116. // What we really want is to clone into a new namespace and then
  117. // mount / MS_REC|MS_SLAVE, but since we can't really clone or fork
  118. // without exec in go we have to do this horrible shell hack...
  119. shellString :=
  120. "mount --make-rslave /; exec " +
  121. utils.ShellQuoteArguments(params)
  122. params = []string{
  123. "unshare", "-m", "--", "/bin/sh", "-c", shellString,
  124. }
  125. }
  126. var (
  127. name = params[0]
  128. arg = params[1:]
  129. )
  130. aname, err := exec.LookPath(name)
  131. if err != nil {
  132. aname = name
  133. }
  134. c.Path = aname
  135. c.Args = append([]string{name}, arg...)
  136. if err := nodes.CreateDeviceNodes(c.Rootfs, c.AutoCreatedDevices); err != nil {
  137. return -1, err
  138. }
  139. if err := c.Start(); err != nil {
  140. return -1, err
  141. }
  142. var (
  143. waitErr error
  144. waitLock = make(chan struct{})
  145. )
  146. go func() {
  147. if err := c.Wait(); err != nil {
  148. if _, ok := err.(*exec.ExitError); !ok { // Do not propagate the error if it's simply a status code != 0
  149. waitErr = err
  150. }
  151. }
  152. close(waitLock)
  153. }()
  154. // Poll lxc for RUNNING status
  155. pid, err := d.waitForStart(c, waitLock)
  156. if err != nil {
  157. if c.Process != nil {
  158. c.Process.Kill()
  159. c.Wait()
  160. }
  161. return -1, err
  162. }
  163. c.ContainerPid = pid
  164. if startCallback != nil {
  165. startCallback(c)
  166. }
  167. <-waitLock
  168. return getExitCode(c), waitErr
  169. }
  170. /// Return the exit code of the process
  171. // if the process has not exited -1 will be returned
  172. func getExitCode(c *execdriver.Command) int {
  173. if c.ProcessState == nil {
  174. return -1
  175. }
  176. return c.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
  177. }
  178. func (d *driver) Kill(c *execdriver.Command, sig int) error {
  179. return KillLxc(c.ID, sig)
  180. }
  181. func (d *driver) Pause(c *execdriver.Command) error {
  182. _, err := exec.LookPath("lxc-freeze")
  183. if err == nil {
  184. output, errExec := exec.Command("lxc-freeze", "-n", c.ID).CombinedOutput()
  185. if errExec != nil {
  186. return fmt.Errorf("Err: %s Output: %s", errExec, output)
  187. }
  188. }
  189. return err
  190. }
  191. func (d *driver) Unpause(c *execdriver.Command) error {
  192. _, err := exec.LookPath("lxc-unfreeze")
  193. if err == nil {
  194. output, errExec := exec.Command("lxc-unfreeze", "-n", c.ID).CombinedOutput()
  195. if errExec != nil {
  196. return fmt.Errorf("Err: %s Output: %s", errExec, output)
  197. }
  198. }
  199. return err
  200. }
  201. func (d *driver) Terminate(c *execdriver.Command) error {
  202. return KillLxc(c.ID, 9)
  203. }
  204. func (d *driver) version() string {
  205. var (
  206. version string
  207. output []byte
  208. err error
  209. )
  210. if _, errPath := exec.LookPath("lxc-version"); errPath == nil {
  211. output, err = exec.Command("lxc-version").CombinedOutput()
  212. } else {
  213. output, err = exec.Command("lxc-start", "--version").CombinedOutput()
  214. }
  215. if err == nil {
  216. version = strings.TrimSpace(string(output))
  217. if parts := strings.SplitN(version, ":", 2); len(parts) == 2 {
  218. version = strings.TrimSpace(parts[1])
  219. }
  220. }
  221. return version
  222. }
  223. func KillLxc(id string, sig int) error {
  224. var (
  225. err error
  226. output []byte
  227. )
  228. _, err = exec.LookPath("lxc-kill")
  229. if err == nil {
  230. output, err = exec.Command("lxc-kill", "-n", id, strconv.Itoa(sig)).CombinedOutput()
  231. } else {
  232. output, err = exec.Command("lxc-stop", "-k", "-n", id, strconv.Itoa(sig)).CombinedOutput()
  233. }
  234. if err != nil {
  235. return fmt.Errorf("Err: %s Output: %s", err, output)
  236. }
  237. return nil
  238. }
  239. // wait for the process to start and return the pid for the process
  240. func (d *driver) waitForStart(c *execdriver.Command, waitLock chan struct{}) (int, error) {
  241. var (
  242. err error
  243. output []byte
  244. )
  245. // We wait for the container to be fully running.
  246. // Timeout after 5 seconds. In case of broken pipe, just retry.
  247. // Note: The container can run and finish correctly before
  248. // the end of this loop
  249. for now := time.Now(); time.Since(now) < 5*time.Second; {
  250. select {
  251. case <-waitLock:
  252. // If the process dies while waiting for it, just return
  253. return -1, nil
  254. default:
  255. }
  256. output, err = d.getInfo(c.ID)
  257. if err == nil {
  258. info, err := parseLxcInfo(string(output))
  259. if err != nil {
  260. return -1, err
  261. }
  262. if info.Running {
  263. return info.Pid, nil
  264. }
  265. }
  266. time.Sleep(50 * time.Millisecond)
  267. }
  268. return -1, execdriver.ErrNotRunning
  269. }
  270. func (d *driver) getInfo(id string) ([]byte, error) {
  271. return exec.Command("lxc-info", "-n", id).CombinedOutput()
  272. }
  273. type info struct {
  274. ID string
  275. driver *driver
  276. }
  277. func (i *info) IsRunning() bool {
  278. var running bool
  279. output, err := i.driver.getInfo(i.ID)
  280. if err != nil {
  281. utils.Errorf("Error getting info for lxc container %s: %s (%s)", i.ID, err, output)
  282. return false
  283. }
  284. if strings.Contains(string(output), "RUNNING") {
  285. running = true
  286. }
  287. return running
  288. }
  289. func (d *driver) Info(id string) execdriver.Info {
  290. return &info{
  291. ID: id,
  292. driver: d,
  293. }
  294. }
  295. func (d *driver) GetPidsForContainer(id string) ([]int, error) {
  296. pids := []int{}
  297. // cpu is chosen because it is the only non optional subsystem in cgroups
  298. subsystem := "cpu"
  299. cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem)
  300. if err != nil {
  301. return pids, err
  302. }
  303. cgroupDir, err := cgroups.GetThisCgroupDir(subsystem)
  304. if err != nil {
  305. return pids, err
  306. }
  307. filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks")
  308. if _, err := os.Stat(filename); os.IsNotExist(err) {
  309. // With more recent lxc versions use, cgroup will be in lxc/
  310. filename = filepath.Join(cgroupRoot, cgroupDir, "lxc", id, "tasks")
  311. }
  312. output, err := ioutil.ReadFile(filename)
  313. if err != nil {
  314. return pids, err
  315. }
  316. for _, p := range strings.Split(string(output), "\n") {
  317. if len(p) == 0 {
  318. continue
  319. }
  320. pid, err := strconv.Atoi(p)
  321. if err != nil {
  322. return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
  323. }
  324. pids = append(pids, pid)
  325. }
  326. return pids, nil
  327. }
  328. func linkLxcStart(root string) error {
  329. sourcePath, err := exec.LookPath("lxc-start")
  330. if err != nil {
  331. return err
  332. }
  333. targetPath := path.Join(root, "lxc-start-unconfined")
  334. if _, err := os.Lstat(targetPath); err != nil && !os.IsNotExist(err) {
  335. return err
  336. } else if err == nil {
  337. if err := os.Remove(targetPath); err != nil {
  338. return err
  339. }
  340. }
  341. return os.Symlink(sourcePath, targetPath)
  342. }
  343. // TODO: This can be moved to the mountinfo reader in the mount pkg
  344. func rootIsShared() bool {
  345. if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil {
  346. for _, line := range strings.Split(string(data), "\n") {
  347. cols := strings.Split(line, " ")
  348. if len(cols) >= 6 && cols[4] == "/" {
  349. return strings.HasPrefix(cols[6], "shared")
  350. }
  351. }
  352. }
  353. // No idea, probably safe to assume so
  354. return true
  355. }
  356. func (d *driver) generateLXCConfig(c *execdriver.Command) (string, error) {
  357. var (
  358. process, mount string
  359. root = path.Join(d.root, "containers", c.ID, "config.lxc")
  360. labels = c.Config["label"]
  361. )
  362. fo, err := os.Create(root)
  363. if err != nil {
  364. return "", err
  365. }
  366. defer fo.Close()
  367. if len(labels) > 0 {
  368. process, mount, err = label.GenLabels(labels[0])
  369. if err != nil {
  370. return "", err
  371. }
  372. }
  373. if err := LxcTemplateCompiled.Execute(fo, struct {
  374. *execdriver.Command
  375. AppArmor bool
  376. ProcessLabel string
  377. MountLabel string
  378. }{
  379. Command: c,
  380. AppArmor: d.apparmor,
  381. ProcessLabel: process,
  382. MountLabel: mount,
  383. }); err != nil {
  384. return "", err
  385. }
  386. return root, nil
  387. }
  388. func (d *driver) generateEnvConfig(c *execdriver.Command) error {
  389. data, err := json.Marshal(c.Env)
  390. if err != nil {
  391. return err
  392. }
  393. p := path.Join(d.root, "containers", c.ID, "config.env")
  394. c.Mounts = append(c.Mounts, execdriver.Mount{p, "/.dockerenv", false, true})
  395. return ioutil.WriteFile(p, data, 0600)
  396. }