driver.go 9.6 KB

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