driver.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. // +build linux
  2. package lxc
  3. import (
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "os"
  10. "os/exec"
  11. "path"
  12. "path/filepath"
  13. "runtime"
  14. "strconv"
  15. "strings"
  16. "sync"
  17. "syscall"
  18. "time"
  19. "github.com/Sirupsen/logrus"
  20. "github.com/docker/docker/context"
  21. "github.com/docker/docker/daemon/execdriver"
  22. "github.com/docker/docker/pkg/stringutils"
  23. sysinfo "github.com/docker/docker/pkg/system"
  24. "github.com/docker/docker/pkg/term"
  25. "github.com/docker/docker/pkg/version"
  26. "github.com/kr/pty"
  27. "github.com/opencontainers/runc/libcontainer"
  28. "github.com/opencontainers/runc/libcontainer/cgroups"
  29. "github.com/opencontainers/runc/libcontainer/configs"
  30. "github.com/opencontainers/runc/libcontainer/system"
  31. "github.com/opencontainers/runc/libcontainer/user"
  32. "github.com/vishvananda/netns"
  33. )
  34. // DriverName for lxc driver
  35. const DriverName = "lxc"
  36. // ErrExec defines unsupported error message
  37. var ErrExec = errors.New("Unsupported: Exec is not supported by the lxc driver")
  38. // Driver contains all information for lxc driver,
  39. // it implements execdriver.Driver
  40. type Driver struct {
  41. root string // root path for the driver to use
  42. libPath string
  43. initPath string
  44. apparmor bool
  45. sharedRoot bool
  46. activeContainers map[string]*activeContainer
  47. machineMemory int64
  48. sync.Mutex
  49. }
  50. type activeContainer struct {
  51. container *configs.Config
  52. cmd *exec.Cmd
  53. }
  54. // NewDriver returns a new lxc driver, called from NewDriver of execdriver
  55. func NewDriver(root, libPath, initPath string, apparmor bool) (*Driver, error) {
  56. if err := os.MkdirAll(root, 0700); err != nil {
  57. return nil, err
  58. }
  59. // setup unconfined symlink
  60. if err := linkLxcStart(root); err != nil {
  61. return nil, err
  62. }
  63. meminfo, err := sysinfo.ReadMemInfo()
  64. if err != nil {
  65. return nil, err
  66. }
  67. return &Driver{
  68. apparmor: apparmor,
  69. root: root,
  70. libPath: libPath,
  71. initPath: initPath,
  72. sharedRoot: rootIsShared(),
  73. activeContainers: make(map[string]*activeContainer),
  74. machineMemory: meminfo.MemTotal,
  75. }, nil
  76. }
  77. // Name implements the exec driver Driver interface.
  78. func (d *Driver) Name() string {
  79. version := d.version()
  80. return fmt.Sprintf("%s-%s", DriverName, version)
  81. }
  82. func setupNetNs(nsPath string) (*os.Process, error) {
  83. runtime.LockOSThread()
  84. defer runtime.UnlockOSThread()
  85. origns, err := netns.Get()
  86. if err != nil {
  87. return nil, err
  88. }
  89. defer origns.Close()
  90. f, err := os.OpenFile(nsPath, os.O_RDONLY, 0)
  91. if err != nil {
  92. return nil, fmt.Errorf("failed to get network namespace %q: %v", nsPath, err)
  93. }
  94. defer f.Close()
  95. nsFD := f.Fd()
  96. if err := netns.Set(netns.NsHandle(nsFD)); err != nil {
  97. return nil, fmt.Errorf("failed to set network namespace %q: %v", nsPath, err)
  98. }
  99. defer netns.Set(origns)
  100. cmd := exec.Command("/bin/sh", "-c", "while true; do sleep 1; done")
  101. if err := cmd.Start(); err != nil {
  102. return nil, fmt.Errorf("failed to start netns process: %v", err)
  103. }
  104. return cmd.Process, nil
  105. }
  106. func killNetNsProc(proc *os.Process) {
  107. proc.Kill()
  108. proc.Wait()
  109. }
  110. // Run implements the exec driver Driver interface,
  111. // it calls 'exec.Cmd' to launch lxc commands to run a container.
  112. func (d *Driver) Run(ctx context.Context, c *execdriver.Command, pipes *execdriver.Pipes, hooks execdriver.Hooks) (execdriver.ExitStatus, error) {
  113. var (
  114. term execdriver.Terminal
  115. err error
  116. dataPath = d.containerDir(c.ID)
  117. )
  118. if c.Network == nil || (c.Network.NamespacePath == "" && c.Network.ContainerID == "") {
  119. return execdriver.ExitStatus{ExitCode: -1}, fmt.Errorf("empty namespace path for non-container network")
  120. }
  121. container, err := d.createContainer(c)
  122. if err != nil {
  123. return execdriver.ExitStatus{ExitCode: -1}, err
  124. }
  125. if c.ProcessConfig.Tty {
  126. term, err = NewTtyConsole(&c.ProcessConfig, pipes)
  127. } else {
  128. term, err = execdriver.NewStdConsole(&c.ProcessConfig, pipes)
  129. }
  130. if err != nil {
  131. return execdriver.ExitStatus{ExitCode: -1}, err
  132. }
  133. c.ProcessConfig.Terminal = term
  134. d.Lock()
  135. d.activeContainers[c.ID] = &activeContainer{
  136. container: container,
  137. cmd: &c.ProcessConfig.Cmd,
  138. }
  139. d.Unlock()
  140. c.Mounts = append(c.Mounts, execdriver.Mount{
  141. Source: d.initPath,
  142. Destination: c.InitPath,
  143. Writable: false,
  144. Private: true,
  145. })
  146. if err := d.generateEnvConfig(c); err != nil {
  147. return execdriver.ExitStatus{ExitCode: -1}, err
  148. }
  149. configPath, err := d.generateLXCConfig(c)
  150. if err != nil {
  151. return execdriver.ExitStatus{ExitCode: -1}, err
  152. }
  153. params := []string{
  154. "lxc-start",
  155. "-n", c.ID,
  156. "-f", configPath,
  157. "-q",
  158. }
  159. // From lxc>=1.1 the default behavior is to daemonize containers after start
  160. lxcVersion := version.Version(d.version())
  161. if lxcVersion.GreaterThanOrEqualTo(version.Version("1.1")) {
  162. params = append(params, "-F")
  163. }
  164. proc := &os.Process{}
  165. if c.Network.ContainerID != "" {
  166. params = append(params,
  167. "--share-net", c.Network.ContainerID,
  168. )
  169. } else {
  170. proc, err = setupNetNs(c.Network.NamespacePath)
  171. if err != nil {
  172. return execdriver.ExitStatus{ExitCode: -1}, err
  173. }
  174. pidStr := fmt.Sprintf("%d", proc.Pid)
  175. params = append(params,
  176. "--share-net", pidStr)
  177. }
  178. if c.Ipc != nil {
  179. if c.Ipc.ContainerID != "" {
  180. params = append(params,
  181. "--share-ipc", c.Ipc.ContainerID,
  182. )
  183. } else if c.Ipc.HostIpc {
  184. params = append(params,
  185. "--share-ipc", "1",
  186. )
  187. }
  188. }
  189. params = append(params,
  190. "--",
  191. c.InitPath,
  192. )
  193. if c.ProcessConfig.User != "" {
  194. params = append(params, "-u", c.ProcessConfig.User)
  195. }
  196. if c.ProcessConfig.Privileged {
  197. if d.apparmor {
  198. params[0] = path.Join(d.root, "lxc-start-unconfined")
  199. }
  200. params = append(params, "-privileged")
  201. }
  202. if c.WorkingDir != "" {
  203. params = append(params, "-w", c.WorkingDir)
  204. }
  205. params = append(params, "--", c.ProcessConfig.Entrypoint)
  206. params = append(params, c.ProcessConfig.Arguments...)
  207. if d.sharedRoot {
  208. // lxc-start really needs / to be non-shared, or all kinds of stuff break
  209. // when lxc-start unmount things and those unmounts propagate to the main
  210. // mount namespace.
  211. // What we really want is to clone into a new namespace and then
  212. // mount / MS_REC|MS_SLAVE, but since we can't really clone or fork
  213. // without exec in go we have to do this horrible shell hack...
  214. shellString :=
  215. "mount --make-rslave /; exec " +
  216. stringutils.ShellQuoteArguments(params)
  217. params = []string{
  218. "unshare", "-m", "--", "/bin/sh", "-c", shellString,
  219. }
  220. }
  221. logrus.Debugf("lxc params %s", params)
  222. var (
  223. name = params[0]
  224. arg = params[1:]
  225. )
  226. aname, err := exec.LookPath(name)
  227. if err != nil {
  228. aname = name
  229. }
  230. c.ProcessConfig.Path = aname
  231. c.ProcessConfig.Args = append([]string{name}, arg...)
  232. if err := createDeviceNodes(c.Rootfs, c.AutoCreatedDevices); err != nil {
  233. killNetNsProc(proc)
  234. return execdriver.ExitStatus{ExitCode: -1}, err
  235. }
  236. if err := c.ProcessConfig.Start(); err != nil {
  237. killNetNsProc(proc)
  238. return execdriver.ExitStatus{ExitCode: -1}, err
  239. }
  240. var (
  241. waitErr error
  242. waitLock = make(chan struct{})
  243. )
  244. go func() {
  245. if err := c.ProcessConfig.Wait(); err != nil {
  246. if _, ok := err.(*exec.ExitError); !ok { // Do not propagate the error if it's simply a status code != 0
  247. waitErr = err
  248. }
  249. }
  250. close(waitLock)
  251. }()
  252. terminate := func(terr error) (execdriver.ExitStatus, error) {
  253. if c.ProcessConfig.Process != nil {
  254. c.ProcessConfig.Process.Kill()
  255. c.ProcessConfig.Wait()
  256. }
  257. return execdriver.ExitStatus{ExitCode: -1}, terr
  258. }
  259. // Poll lxc for RUNNING status
  260. pid, err := d.waitForStart(c, waitLock)
  261. if err != nil {
  262. killNetNsProc(proc)
  263. return terminate(err)
  264. }
  265. killNetNsProc(proc)
  266. cgroupPaths, err := cgroupPaths(c.ID)
  267. if err != nil {
  268. return terminate(err)
  269. }
  270. state := &libcontainer.State{
  271. InitProcessPid: pid,
  272. CgroupPaths: cgroupPaths,
  273. }
  274. f, err := os.Create(filepath.Join(dataPath, "state.json"))
  275. if err != nil {
  276. return terminate(err)
  277. }
  278. defer f.Close()
  279. if err := json.NewEncoder(f).Encode(state); err != nil {
  280. return terminate(err)
  281. }
  282. c.ContainerPid = pid
  283. oomKill := false
  284. oomKillNotification, err := notifyOnOOM(cgroupPaths)
  285. if hooks.Start != nil {
  286. logrus.Debugf("Invoking startCallback")
  287. hooks.Start(ctx, &c.ProcessConfig, pid, oomKillNotification)
  288. }
  289. <-waitLock
  290. exitCode := getExitCode(c)
  291. if err == nil {
  292. _, oomKill = <-oomKillNotification
  293. logrus.Debugf("oomKill error: %v, waitErr: %v", oomKill, waitErr)
  294. } else {
  295. logrus.Warnf("Your kernel does not support OOM notifications: %s", err)
  296. }
  297. // check oom error
  298. if oomKill {
  299. exitCode = 137
  300. }
  301. return execdriver.ExitStatus{ExitCode: exitCode, OOMKilled: oomKill}, waitErr
  302. }
  303. // copy from libcontainer
  304. func notifyOnOOM(paths map[string]string) (<-chan struct{}, error) {
  305. dir := paths["memory"]
  306. if dir == "" {
  307. return nil, fmt.Errorf("There is no path for %q in state", "memory")
  308. }
  309. oomControl, err := os.Open(filepath.Join(dir, "memory.oom_control"))
  310. if err != nil {
  311. return nil, err
  312. }
  313. fd, _, syserr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0)
  314. if syserr != 0 {
  315. oomControl.Close()
  316. return nil, syserr
  317. }
  318. eventfd := os.NewFile(fd, "eventfd")
  319. eventControlPath := filepath.Join(dir, "cgroup.event_control")
  320. data := fmt.Sprintf("%d %d", eventfd.Fd(), oomControl.Fd())
  321. if err := ioutil.WriteFile(eventControlPath, []byte(data), 0700); err != nil {
  322. eventfd.Close()
  323. oomControl.Close()
  324. return nil, err
  325. }
  326. ch := make(chan struct{})
  327. go func() {
  328. defer func() {
  329. close(ch)
  330. eventfd.Close()
  331. oomControl.Close()
  332. }()
  333. buf := make([]byte, 8)
  334. for {
  335. if _, err := eventfd.Read(buf); err != nil {
  336. return
  337. }
  338. // When a cgroup is destroyed, an event is sent to eventfd.
  339. // So if the control path is gone, return instead of notifying.
  340. if _, err := os.Lstat(eventControlPath); os.IsNotExist(err) {
  341. return
  342. }
  343. ch <- struct{}{}
  344. }
  345. }()
  346. return ch, nil
  347. }
  348. // createContainer populates and configures the container type with the
  349. // data provided by the execdriver.Command
  350. func (d *Driver) createContainer(c *execdriver.Command) (*configs.Config, error) {
  351. container := execdriver.InitContainer(c)
  352. if err := execdriver.SetupCgroups(container, c); err != nil {
  353. return nil, err
  354. }
  355. return container, nil
  356. }
  357. // Return an map of susbystem -> absolute container cgroup path
  358. func cgroupPaths(containerID string) (map[string]string, error) {
  359. subsystems, err := cgroups.GetAllSubsystems()
  360. if err != nil {
  361. return nil, err
  362. }
  363. logrus.Debugf("subsystems: %s", subsystems)
  364. paths := make(map[string]string)
  365. for _, subsystem := range subsystems {
  366. cgroupRoot, cgroupDir, err := findCgroupRootAndDir(subsystem)
  367. logrus.Debugf("cgroup path %s %s", cgroupRoot, cgroupDir)
  368. if err != nil {
  369. //unsupported subystem
  370. continue
  371. }
  372. path := filepath.Join(cgroupRoot, cgroupDir, "lxc", containerID)
  373. paths[subsystem] = path
  374. }
  375. return paths, nil
  376. }
  377. // this is copy from old libcontainer nodes.go
  378. func createDeviceNodes(rootfs string, nodesToCreate []*configs.Device) error {
  379. oldMask := syscall.Umask(0000)
  380. defer syscall.Umask(oldMask)
  381. for _, node := range nodesToCreate {
  382. if err := createDeviceNode(rootfs, node); err != nil {
  383. return err
  384. }
  385. }
  386. return nil
  387. }
  388. // Creates the device node in the rootfs of the container.
  389. func createDeviceNode(rootfs string, node *configs.Device) error {
  390. var (
  391. dest = filepath.Join(rootfs, node.Path)
  392. parent = filepath.Dir(dest)
  393. )
  394. if err := os.MkdirAll(parent, 0755); err != nil {
  395. return err
  396. }
  397. fileMode := node.FileMode
  398. switch node.Type {
  399. case 'c':
  400. fileMode |= syscall.S_IFCHR
  401. case 'b':
  402. fileMode |= syscall.S_IFBLK
  403. default:
  404. return fmt.Errorf("%c is not a valid device type for device %s", node.Type, node.Path)
  405. }
  406. if err := syscall.Mknod(dest, uint32(fileMode), node.Mkdev()); err != nil && !os.IsExist(err) {
  407. return fmt.Errorf("mknod %s %s", node.Path, err)
  408. }
  409. if err := syscall.Chown(dest, int(node.Uid), int(node.Gid)); err != nil {
  410. return fmt.Errorf("chown %s to %d:%d", node.Path, node.Uid, node.Gid)
  411. }
  412. return nil
  413. }
  414. // setupUser changes the groups, gid, and uid for the user inside the container
  415. // copy from libcontainer, cause not it's private
  416. func setupUser(userSpec string) error {
  417. // Set up defaults.
  418. defaultExecUser := user.ExecUser{
  419. Uid: syscall.Getuid(),
  420. Gid: syscall.Getgid(),
  421. Home: "/",
  422. }
  423. passwdPath, err := user.GetPasswdPath()
  424. if err != nil {
  425. return err
  426. }
  427. groupPath, err := user.GetGroupPath()
  428. if err != nil {
  429. return err
  430. }
  431. execUser, err := user.GetExecUserPath(userSpec, &defaultExecUser, passwdPath, groupPath)
  432. if err != nil {
  433. return err
  434. }
  435. if err := syscall.Setgroups(execUser.Sgids); err != nil {
  436. return err
  437. }
  438. if err := system.Setgid(execUser.Gid); err != nil {
  439. return err
  440. }
  441. if err := system.Setuid(execUser.Uid); err != nil {
  442. return err
  443. }
  444. // if we didn't get HOME already, set it based on the user's HOME
  445. if envHome := os.Getenv("HOME"); envHome == "" {
  446. if err := os.Setenv("HOME", execUser.Home); err != nil {
  447. return err
  448. }
  449. }
  450. return nil
  451. }
  452. // getExitCode returns the exit code of the process.
  453. // If the process has not exited -1 will be returned.
  454. func getExitCode(c *execdriver.Command) int {
  455. if c.ProcessConfig.ProcessState == nil {
  456. return -1
  457. }
  458. return c.ProcessConfig.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
  459. }
  460. // Kill implements the exec driver Driver interface.
  461. func (d *Driver) Kill(c *execdriver.Command, sig int) error {
  462. if sig == 9 || c.ProcessConfig.Process == nil {
  463. return killLxc(c.ID, sig)
  464. }
  465. return c.ProcessConfig.Process.Signal(syscall.Signal(sig))
  466. }
  467. // Pause implements the exec driver Driver interface,
  468. // it executes lxc-freeze to pause a container.
  469. func (d *Driver) Pause(c *execdriver.Command) error {
  470. _, err := exec.LookPath("lxc-freeze")
  471. if err == nil {
  472. output, errExec := exec.Command("lxc-freeze", "-n", c.ID).CombinedOutput()
  473. if errExec != nil {
  474. return fmt.Errorf("Err: %s Output: %s", errExec, output)
  475. }
  476. }
  477. return err
  478. }
  479. // Unpause implements the exec driver Driver interface,
  480. // it executes lxc-unfreeze to unpause a container.
  481. func (d *Driver) Unpause(c *execdriver.Command) error {
  482. _, err := exec.LookPath("lxc-unfreeze")
  483. if err == nil {
  484. output, errExec := exec.Command("lxc-unfreeze", "-n", c.ID).CombinedOutput()
  485. if errExec != nil {
  486. return fmt.Errorf("Err: %s Output: %s", errExec, output)
  487. }
  488. }
  489. return err
  490. }
  491. // Terminate implements the exec driver Driver interface.
  492. func (d *Driver) Terminate(c *execdriver.Command) error {
  493. return killLxc(c.ID, 9)
  494. }
  495. func (d *Driver) version() string {
  496. var (
  497. version string
  498. output []byte
  499. err error
  500. )
  501. if _, errPath := exec.LookPath("lxc-version"); errPath == nil {
  502. output, err = exec.Command("lxc-version").CombinedOutput()
  503. } else {
  504. output, err = exec.Command("lxc-start", "--version").CombinedOutput()
  505. }
  506. if err == nil {
  507. version = strings.TrimSpace(string(output))
  508. if parts := strings.SplitN(version, ":", 2); len(parts) == 2 {
  509. version = strings.TrimSpace(parts[1])
  510. }
  511. }
  512. return version
  513. }
  514. func killLxc(id string, sig int) error {
  515. var (
  516. err error
  517. output []byte
  518. )
  519. _, err = exec.LookPath("lxc-kill")
  520. if err == nil {
  521. output, err = exec.Command("lxc-kill", "-n", id, strconv.Itoa(sig)).CombinedOutput()
  522. } else {
  523. // lxc-stop does not take arbitrary signals like lxc-kill does
  524. output, err = exec.Command("lxc-stop", "-k", "-n", id).CombinedOutput()
  525. }
  526. if err != nil {
  527. return fmt.Errorf("Err: %s Output: %s", err, output)
  528. }
  529. return nil
  530. }
  531. // wait for the process to start and return the pid for the process
  532. func (d *Driver) waitForStart(c *execdriver.Command, waitLock chan struct{}) (int, error) {
  533. var (
  534. err error
  535. output []byte
  536. )
  537. // We wait for the container to be fully running.
  538. // Timeout after 5 seconds. In case of broken pipe, just retry.
  539. // Note: The container can run and finish correctly before
  540. // the end of this loop
  541. for now := time.Now(); time.Since(now) < 5*time.Second; {
  542. select {
  543. case <-waitLock:
  544. // If the process dies while waiting for it, just return
  545. return -1, nil
  546. default:
  547. }
  548. output, err = d.getInfo(c.ID)
  549. if err == nil {
  550. info, err := parseLxcInfo(string(output))
  551. if err != nil {
  552. return -1, err
  553. }
  554. if info.Running {
  555. return info.Pid, nil
  556. }
  557. }
  558. time.Sleep(50 * time.Millisecond)
  559. }
  560. return -1, execdriver.ErrNotRunning
  561. }
  562. func (d *Driver) getInfo(id string) ([]byte, error) {
  563. return exec.Command("lxc-info", "-n", id).CombinedOutput()
  564. }
  565. type info struct {
  566. ID string
  567. driver *Driver
  568. }
  569. func (i *info) IsRunning() bool {
  570. var running bool
  571. output, err := i.driver.getInfo(i.ID)
  572. if err != nil {
  573. logrus.Errorf("Error getting info for lxc container %s: %s (%s)", i.ID, err, output)
  574. return false
  575. }
  576. if strings.Contains(string(output), "RUNNING") {
  577. running = true
  578. }
  579. return running
  580. }
  581. // Info implements the exec driver Driver interface.
  582. func (d *Driver) Info(id string) execdriver.Info {
  583. return &info{
  584. ID: id,
  585. driver: d,
  586. }
  587. }
  588. func findCgroupRootAndDir(subsystem string) (string, string, error) {
  589. cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem)
  590. if err != nil {
  591. return "", "", err
  592. }
  593. cgroupDir, err := cgroups.GetThisCgroupDir(subsystem)
  594. if err != nil {
  595. return "", "", err
  596. }
  597. return cgroupRoot, cgroupDir, nil
  598. }
  599. // GetPidsForContainer implements the exec driver Driver interface.
  600. func (d *Driver) GetPidsForContainer(id string) ([]int, error) {
  601. pids := []int{}
  602. // cpu is chosen because it is the only non optional subsystem in cgroups
  603. subsystem := "cpu"
  604. cgroupRoot, cgroupDir, err := findCgroupRootAndDir(subsystem)
  605. if err != nil {
  606. return pids, err
  607. }
  608. filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks")
  609. if _, err := os.Stat(filename); os.IsNotExist(err) {
  610. // With more recent lxc versions use, cgroup will be in lxc/
  611. filename = filepath.Join(cgroupRoot, cgroupDir, "lxc", id, "tasks")
  612. }
  613. output, err := ioutil.ReadFile(filename)
  614. if err != nil {
  615. return pids, err
  616. }
  617. for _, p := range strings.Split(string(output), "\n") {
  618. if len(p) == 0 {
  619. continue
  620. }
  621. pid, err := strconv.Atoi(p)
  622. if err != nil {
  623. return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
  624. }
  625. pids = append(pids, pid)
  626. }
  627. return pids, nil
  628. }
  629. func linkLxcStart(root string) error {
  630. sourcePath, err := exec.LookPath("lxc-start")
  631. if err != nil {
  632. return err
  633. }
  634. targetPath := path.Join(root, "lxc-start-unconfined")
  635. if _, err := os.Lstat(targetPath); err != nil && !os.IsNotExist(err) {
  636. return err
  637. } else if err == nil {
  638. if err := os.Remove(targetPath); err != nil {
  639. return err
  640. }
  641. }
  642. return os.Symlink(sourcePath, targetPath)
  643. }
  644. // TODO: This can be moved to the mountinfo reader in the mount pkg
  645. func rootIsShared() bool {
  646. if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil {
  647. for _, line := range strings.Split(string(data), "\n") {
  648. cols := strings.Split(line, " ")
  649. if len(cols) >= 6 && cols[4] == "/" {
  650. return strings.HasPrefix(cols[6], "shared")
  651. }
  652. }
  653. }
  654. // No idea, probably safe to assume so
  655. return true
  656. }
  657. func (d *Driver) containerDir(containerID string) string {
  658. return path.Join(d.libPath, "containers", containerID)
  659. }
  660. func (d *Driver) generateLXCConfig(c *execdriver.Command) (string, error) {
  661. root := path.Join(d.containerDir(c.ID), "config.lxc")
  662. fo, err := os.Create(root)
  663. if err != nil {
  664. return "", err
  665. }
  666. defer fo.Close()
  667. if err := lxcTemplateCompiled.Execute(fo, struct {
  668. *execdriver.Command
  669. AppArmor bool
  670. }{
  671. Command: c,
  672. AppArmor: d.apparmor,
  673. }); err != nil {
  674. return "", err
  675. }
  676. return root, nil
  677. }
  678. func (d *Driver) generateEnvConfig(c *execdriver.Command) error {
  679. data, err := json.Marshal(c.ProcessConfig.Env)
  680. if err != nil {
  681. return err
  682. }
  683. p := path.Join(d.libPath, "containers", c.ID, "config.env")
  684. c.Mounts = append(c.Mounts, execdriver.Mount{
  685. Source: p,
  686. Destination: "/.dockerenv",
  687. Writable: false,
  688. Private: true,
  689. })
  690. return ioutil.WriteFile(p, data, 0600)
  691. }
  692. // Clean implements the exec driver Driver interface,
  693. // it's not implemented by lxc.
  694. func (d *Driver) Clean(id string) error {
  695. return nil
  696. }
  697. // TtyConsole implements the exec driver Terminal interface,
  698. // it stores the master and slave ends of the container's pty.
  699. type TtyConsole struct {
  700. MasterPty *os.File
  701. SlavePty *os.File
  702. }
  703. // NewTtyConsole returns a new TtyConsole struct.
  704. // Wired up to the provided process config and stdin/stdout/stderr pipes.
  705. func NewTtyConsole(processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes) (*TtyConsole, error) {
  706. // lxc is special in that we cannot create the master outside of the container without
  707. // opening the slave because we have nothing to provide to the cmd. We have to open both then do
  708. // the crazy setup on command right now instead of passing the console path to lxc and telling it
  709. // to open up that console. we save a couple of openfiles in the native driver because we can do
  710. // this.
  711. ptyMaster, ptySlave, err := pty.Open()
  712. if err != nil {
  713. return nil, err
  714. }
  715. tty := &TtyConsole{
  716. MasterPty: ptyMaster,
  717. SlavePty: ptySlave,
  718. }
  719. if err := tty.AttachPipes(&processConfig.Cmd, pipes); err != nil {
  720. tty.Close()
  721. return nil, err
  722. }
  723. processConfig.Console = tty.SlavePty.Name()
  724. return tty, nil
  725. }
  726. // Resize implements Resize method of Terminal interface
  727. func (t *TtyConsole) Resize(h, w int) error {
  728. return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
  729. }
  730. // AttachPipes attaches given pipes to exec.Cmd
  731. func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error {
  732. command.Stdout = t.SlavePty
  733. command.Stderr = t.SlavePty
  734. go func() {
  735. if wb, ok := pipes.Stdout.(interface {
  736. CloseWriters() error
  737. }); ok {
  738. defer wb.CloseWriters()
  739. }
  740. io.Copy(pipes.Stdout, t.MasterPty)
  741. }()
  742. if pipes.Stdin != nil {
  743. command.Stdin = t.SlavePty
  744. command.SysProcAttr.Setctty = true
  745. go func() {
  746. io.Copy(t.MasterPty, pipes.Stdin)
  747. pipes.Stdin.Close()
  748. }()
  749. }
  750. return nil
  751. }
  752. // Close implements Close method of Terminal interface
  753. func (t *TtyConsole) Close() error {
  754. t.SlavePty.Close()
  755. return t.MasterPty.Close()
  756. }
  757. // Exec implements the exec driver Driver interface,
  758. // it is not implemented by lxc.
  759. func (d *Driver) Exec(ctx context.Context, c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, hooks execdriver.Hooks) (int, error) {
  760. return -1, ErrExec
  761. }
  762. // Stats implements the exec driver Driver interface.
  763. // Lxc doesn't implement it's own Stats, it does some trick by implementing
  764. // execdriver.Stats to get stats info by libcontainer APIs.
  765. func (d *Driver) Stats(id string) (*execdriver.ResourceStats, error) {
  766. if _, ok := d.activeContainers[id]; !ok {
  767. return nil, fmt.Errorf("%s is not a key in active containers", id)
  768. }
  769. return execdriver.Stats(d.containerDir(id), d.activeContainers[id].container.Cgroups.Memory, d.machineMemory)
  770. }
  771. // SupportsHooks implements the execdriver Driver interface.
  772. // The LXC execdriver does not support the hook mechanism, which is currently unique to runC/libcontainer.
  773. func (d *Driver) SupportsHooks() bool {
  774. return false
  775. }