driver.go 19 KB

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