123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- package lxc
- import (
- "errors"
- "fmt"
- "github.com/dotcloud/docker/execdriver"
- "os/exec"
- "strconv"
- "strings"
- "syscall"
- "time"
- )
- const (
- startPath = "lxc-start"
- )
- var (
- ErrNotRunning = errors.New("Process could not be started")
- ErrWaitTimeoutReached = errors.New("Wait timeout reached")
- )
- func init() {
- // Register driver
- }
- type driver struct {
- root string // root path for the driver to use
- containers map[string]*execdriver.Process
- }
- func NewDriver(root string) (execdriver.Driver, error) {
- // setup unconfined symlink
- return &driver{
- root: root,
- containers: make(map[string]*execdriver.Process),
- }, nil
- }
- func (d *driver) Start(c *execdriver.Process) error {
- params := []string{
- startPath,
- "-n", c.Name,
- "-f", c.ConfigPath,
- "--",
- c.InitPath,
- }
- if c.Network != nil {
- params = append(params,
- "-g", c.Network.Gateway,
- "-i", fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
- "-mtu", strconv.Itoa(c.Network.Mtu),
- )
- }
- if c.User != "" {
- params = append(params, "-u", c.User)
- }
- if c.Privileged {
- params = append(params, "-privileged")
- }
- if c.WorkingDir != "" {
- params = append(params, "-w", c.WorkingDir)
- }
- params = append(params, "--", c.Entrypoint)
- params = append(params, c.Arguments...)
- var (
- name = params[0]
- arg = params[1:]
- )
- aname, err := exec.LookPath(name)
- if err != nil {
- aname = name
- }
- c.Path = aname
- c.Args = append([]string{name}, arg...)
- c.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
- c.SysProcAttr.Setctty = true
- if err := c.Start(); err != nil {
- return err
- }
- // Poll for running
- if err := d.waitForStart(c); err != nil {
- return err
- }
- return nil
- }
- func (d *driver) Stop(c *execdriver.Process) error {
- if err := d.kill(c, 15); err != nil {
- if err := d.kill(c, 9); err != nil {
- return err
- }
- }
- if err := d.wait(c, 10*time.Second); err != nil {
- if err := d.kill(c, 9); err != nil {
- return err
- }
- }
- return nil
- }
- func (d *driver) Kill(c *execdriver.Process, sig int) error {
- return d.kill(c, sig)
- }
- func (d *driver) Wait(c *execdriver.Process, duration time.Duration) error {
- return d.wait(c, duration)
- }
- // If seconds < 0 then wait forever
- func (d *driver) wait(c *execdriver.Process, duration time.Duration) error {
- var (
- killer bool
- done = d.waitCmd(c)
- )
- begin:
- if duration > 0 {
- select {
- case err := <-done:
- if err != nil && err == execdriver.ErrCommandIsNil {
- done = d.waitLxc(c, &killer)
- goto begin
- }
- return err
- case <-time.After(duration):
- killer = true
- return ErrWaitTimeoutReached
- }
- } else {
- if err := <-done; err != nil {
- if err == execdriver.ErrCommandIsNil {
- done = d.waitLxc(c, &killer)
- goto begin
- }
- return err
- }
- }
- return nil
- }
- func (d *driver) kill(c *execdriver.Process, sig int) error {
- return exec.Command("lxc-kill", "-n", c.Name, strconv.Itoa(sig)).Run()
- }
- func (d *driver) waitForStart(c *execdriver.Process) error {
- // We wait for the container to be fully running.
- // Timeout after 5 seconds. In case of broken pipe, just retry.
- // Note: The container can run and finish correctly before
- // the end of this loop
- for now := time.Now(); time.Since(now) < 5*time.Second; {
- // If the container dies while waiting for it, just return
- /*
- if !c.State.IsRunning() {
- return nil
- }
- */
- output, err := exec.Command("lxc-info", "-s", "-n", c.Name).CombinedOutput()
- if err != nil {
- output, err = exec.Command("lxc-info", "-s", "-n", c.Name).CombinedOutput()
- if err != nil {
- return err
- }
- }
- if strings.Contains(string(output), "RUNNING") {
- return nil
- }
- time.Sleep(50 * time.Millisecond)
- }
- return ErrNotRunning
- }
- func (d *driver) waitCmd(c *execdriver.Process) <-chan error {
- done := make(chan error)
- go func() {
- done <- c.Wait()
- }()
- return done
- }
- func (d *driver) waitLxc(c *execdriver.Process, kill *bool) <-chan error {
- done := make(chan error)
- go func() {
- for *kill {
- output, err := exec.Command("lxc-info", "-n", c.Name).CombinedOutput()
- if err != nil {
- done <- err
- return
- }
- if !strings.Contains(string(output), "RUNNING") {
- done <- err
- return
- }
- time.Sleep(500 * time.Millisecond)
- }
- }()
- return done
- }
|