driver.go 8.6 KB

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