driver.go 12 KB

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