driver.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. package lxc
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/dotcloud/docker/execdriver"
  6. "os/exec"
  7. "strconv"
  8. "strings"
  9. "syscall"
  10. "time"
  11. )
  12. const (
  13. startPath = "lxc-start"
  14. )
  15. var (
  16. ErrNotRunning = errors.New("Process could not be started")
  17. ErrWaitTimeoutReached = errors.New("Wait timeout reached")
  18. )
  19. func init() {
  20. // Register driver
  21. }
  22. type driver struct {
  23. root string // root path for the driver to use
  24. containers map[string]*execdriver.Process
  25. }
  26. func NewDriver(root string) (execdriver.Driver, error) {
  27. // setup unconfined symlink
  28. return &driver{
  29. root: root,
  30. containers: make(map[string]*execdriver.Process),
  31. }, nil
  32. }
  33. func (d *driver) Start(c *execdriver.Process) error {
  34. params := []string{
  35. startPath,
  36. "-n", c.Name,
  37. "-f", c.ConfigPath,
  38. "--",
  39. c.InitPath,
  40. }
  41. if c.Network != nil {
  42. params = append(params,
  43. "-g", c.Network.Gateway,
  44. "-i", fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
  45. "-mtu", strconv.Itoa(c.Network.Mtu),
  46. )
  47. }
  48. if c.User != "" {
  49. params = append(params, "-u", c.User)
  50. }
  51. if c.Privileged {
  52. params = append(params, "-privileged")
  53. }
  54. if c.WorkingDir != "" {
  55. params = append(params, "-w", c.WorkingDir)
  56. }
  57. params = append(params, "--", c.Entrypoint)
  58. params = append(params, c.Arguments...)
  59. var (
  60. name = params[0]
  61. arg = params[1:]
  62. )
  63. aname, err := exec.LookPath(name)
  64. if err != nil {
  65. aname = name
  66. }
  67. c.Path = aname
  68. c.Args = append([]string{name}, arg...)
  69. c.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
  70. c.SysProcAttr.Setctty = true
  71. if err := c.Start(); err != nil {
  72. return err
  73. }
  74. // Poll for running
  75. if err := d.waitForStart(c); err != nil {
  76. return err
  77. }
  78. return nil
  79. }
  80. func (d *driver) Stop(c *execdriver.Process) error {
  81. if err := d.kill(c, 15); err != nil {
  82. if err := d.kill(c, 9); err != nil {
  83. return err
  84. }
  85. }
  86. if err := d.wait(c, 10*time.Second); err != nil {
  87. if err := d.kill(c, 9); err != nil {
  88. return err
  89. }
  90. }
  91. return nil
  92. }
  93. func (d *driver) Kill(c *execdriver.Process, sig int) error {
  94. return d.kill(c, sig)
  95. }
  96. func (d *driver) Wait(c *execdriver.Process, duration time.Duration) error {
  97. return d.wait(c, duration)
  98. }
  99. // If seconds < 0 then wait forever
  100. func (d *driver) wait(c *execdriver.Process, duration time.Duration) error {
  101. var (
  102. killer bool
  103. done = d.waitCmd(c)
  104. )
  105. begin:
  106. if duration > 0 {
  107. select {
  108. case err := <-done:
  109. if err != nil && err == execdriver.ErrCommandIsNil {
  110. done = d.waitLxc(c, &killer)
  111. goto begin
  112. }
  113. return err
  114. case <-time.After(duration):
  115. killer = true
  116. return ErrWaitTimeoutReached
  117. }
  118. } else {
  119. if err := <-done; err != nil {
  120. if err == execdriver.ErrCommandIsNil {
  121. done = d.waitLxc(c, &killer)
  122. goto begin
  123. }
  124. return err
  125. }
  126. }
  127. return nil
  128. }
  129. func (d *driver) kill(c *execdriver.Process, sig int) error {
  130. return exec.Command("lxc-kill", "-n", c.Name, strconv.Itoa(sig)).Run()
  131. }
  132. func (d *driver) waitForStart(c *execdriver.Process) error {
  133. // We wait for the container to be fully running.
  134. // Timeout after 5 seconds. In case of broken pipe, just retry.
  135. // Note: The container can run and finish correctly before
  136. // the end of this loop
  137. for now := time.Now(); time.Since(now) < 5*time.Second; {
  138. // If the container dies while waiting for it, just return
  139. /*
  140. if !c.State.IsRunning() {
  141. return nil
  142. }
  143. */
  144. output, err := exec.Command("lxc-info", "-s", "-n", c.Name).CombinedOutput()
  145. if err != nil {
  146. output, err = exec.Command("lxc-info", "-s", "-n", c.Name).CombinedOutput()
  147. if err != nil {
  148. return err
  149. }
  150. }
  151. if strings.Contains(string(output), "RUNNING") {
  152. return nil
  153. }
  154. time.Sleep(50 * time.Millisecond)
  155. }
  156. return ErrNotRunning
  157. }
  158. func (d *driver) waitCmd(c *execdriver.Process) <-chan error {
  159. done := make(chan error)
  160. go func() {
  161. done <- c.Wait()
  162. }()
  163. return done
  164. }
  165. func (d *driver) waitLxc(c *execdriver.Process, kill *bool) <-chan error {
  166. done := make(chan error)
  167. go func() {
  168. for *kill {
  169. output, err := exec.Command("lxc-info", "-n", c.Name).CombinedOutput()
  170. if err != nil {
  171. done <- err
  172. return
  173. }
  174. if !strings.Contains(string(output), "RUNNING") {
  175. done <- err
  176. return
  177. }
  178. time.Sleep(500 * time.Millisecond)
  179. }
  180. }()
  181. return done
  182. }