Browse code

Move execdrivers into runtime top level pkg Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby authored on 2014/03/15 06:07:32
Showing 38 changed files
1 1
deleted file mode 100644
... ...
@@ -1,2 +0,0 @@
1
-Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
2
-Guillaume J. Charmes <guillaume@docker.com> (@creack)
3 1
deleted file mode 100644
... ...
@@ -1,135 +0,0 @@
1
-package execdriver
2
-
3
-import (
4
-	"errors"
5
-	"io"
6
-	"os"
7
-	"os/exec"
8
-)
9
-
10
-var (
11
-	ErrNotRunning              = errors.New("Process could not be started")
12
-	ErrWaitTimeoutReached      = errors.New("Wait timeout reached")
13
-	ErrDriverAlreadyRegistered = errors.New("A driver already registered this docker init function")
14
-	ErrDriverNotFound          = errors.New("The requested docker init has not been found")
15
-)
16
-
17
-var dockerInitFcts map[string]InitFunc
18
-
19
-type (
20
-	StartCallback func(*Command)
21
-	InitFunc      func(i *InitArgs) error
22
-)
23
-
24
-func RegisterInitFunc(name string, fct InitFunc) error {
25
-	if dockerInitFcts == nil {
26
-		dockerInitFcts = make(map[string]InitFunc)
27
-	}
28
-	if _, ok := dockerInitFcts[name]; ok {
29
-		return ErrDriverAlreadyRegistered
30
-	}
31
-	dockerInitFcts[name] = fct
32
-	return nil
33
-}
34
-
35
-func GetInitFunc(name string) (InitFunc, error) {
36
-	fct, ok := dockerInitFcts[name]
37
-	if !ok {
38
-		return nil, ErrDriverNotFound
39
-	}
40
-	return fct, nil
41
-}
42
-
43
-// Args provided to the init function for a driver
44
-type InitArgs struct {
45
-	User       string
46
-	Gateway    string
47
-	Ip         string
48
-	WorkDir    string
49
-	Privileged bool
50
-	Env        []string
51
-	Args       []string
52
-	Mtu        int
53
-	Driver     string
54
-	Console    string
55
-	Pipe       int
56
-	Root       string
57
-}
58
-
59
-// Driver specific information based on
60
-// processes registered with the driver
61
-type Info interface {
62
-	IsRunning() bool
63
-}
64
-
65
-// Terminal in an interface for drivers to implement
66
-// if they want to support Close and Resize calls from
67
-// the core
68
-type Terminal interface {
69
-	io.Closer
70
-	Resize(height, width int) error
71
-}
72
-
73
-type TtyTerminal interface {
74
-	Master() *os.File
75
-}
76
-
77
-type Driver interface {
78
-	Run(c *Command, pipes *Pipes, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code
79
-	Kill(c *Command, sig int) error
80
-	Name() string                                 // Driver name
81
-	Info(id string) Info                          // "temporary" hack (until we move state from core to plugins)
82
-	GetPidsForContainer(id string) ([]int, error) // Returns a list of pids for the given container.
83
-}
84
-
85
-// Network settings of the container
86
-type Network struct {
87
-	Gateway     string `json:"gateway"`
88
-	IPAddress   string `json:"ip"`
89
-	Bridge      string `json:"bridge"`
90
-	IPPrefixLen int    `json:"ip_prefix_len"`
91
-	Mtu         int    `json:"mtu"`
92
-}
93
-
94
-type Resources struct {
95
-	Memory     int64 `json:"memory"`
96
-	MemorySwap int64 `json:"memory_swap"`
97
-	CpuShares  int64 `json:"cpu_shares"`
98
-}
99
-
100
-type Mount struct {
101
-	Source      string `json:"source"`
102
-	Destination string `json:"destination"`
103
-	Writable    bool   `json:"writable"`
104
-	Private     bool   `json:"private"`
105
-}
106
-
107
-// Process wrapps an os/exec.Cmd to add more metadata
108
-type Command struct {
109
-	exec.Cmd `json:"-"`
110
-
111
-	ID         string     `json:"id"`
112
-	Privileged bool       `json:"privileged"`
113
-	User       string     `json:"user"`
114
-	Rootfs     string     `json:"rootfs"`   // root fs of the container
115
-	InitPath   string     `json:"initpath"` // dockerinit
116
-	Entrypoint string     `json:"entrypoint"`
117
-	Arguments  []string   `json:"arguments"`
118
-	WorkingDir string     `json:"working_dir"`
119
-	ConfigPath string     `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver
120
-	Tty        bool       `json:"tty"`
121
-	Network    *Network   `json:"network"` // if network is nil then networking is disabled
122
-	Config     []string   `json:"config"`  //  generic values that specific drivers can consume
123
-	Resources  *Resources `json:"resources"`
124
-	Mounts     []Mount    `json:"mounts"`
125
-
126
-	Terminal     Terminal `json:"-"`             // standard or tty terminal
127
-	Console      string   `json:"-"`             // dev/console path
128
-	ContainerPid int      `json:"container_pid"` // the pid for the process inside a container
129
-}
130
-
131
-// Return the pid of the process
132
-// If the process is nil -1 will be returned
133
-func (c *Command) Pid() int {
134
-	return c.ContainerPid
135
-}
136 1
deleted file mode 100644
... ...
@@ -1,23 +0,0 @@
1
-package execdrivers
2
-
3
-import (
4
-	"fmt"
5
-	"github.com/dotcloud/docker/execdriver"
6
-	"github.com/dotcloud/docker/execdriver/lxc"
7
-	"github.com/dotcloud/docker/execdriver/native"
8
-	"github.com/dotcloud/docker/pkg/sysinfo"
9
-	"path"
10
-)
11
-
12
-func NewDriver(name, root, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) {
13
-	switch name {
14
-	case "lxc":
15
-		// we want to five the lxc driver the full docker root because it needs
16
-		// to access and write config and template files in /var/lib/docker/containers/*
17
-		// to be backwards compatible
18
-		return lxc.NewDriver(root, sysInfo.AppArmor)
19
-	case "native":
20
-		return native.NewDriver(path.Join(root, "execdriver", "native"), initPath)
21
-	}
22
-	return nil, fmt.Errorf("unknown exec driver %s", name)
23
-}
24 1
deleted file mode 100644
... ...
@@ -1,396 +0,0 @@
1
-package lxc
2
-
3
-import (
4
-	"fmt"
5
-	"github.com/dotcloud/docker/execdriver"
6
-	"github.com/dotcloud/docker/pkg/cgroups"
7
-	"github.com/dotcloud/docker/utils"
8
-	"io/ioutil"
9
-	"log"
10
-	"os"
11
-	"os/exec"
12
-	"path"
13
-	"path/filepath"
14
-	"strconv"
15
-	"strings"
16
-	"syscall"
17
-	"time"
18
-)
19
-
20
-const DriverName = "lxc"
21
-
22
-func init() {
23
-	execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
24
-		if err := setupEnv(args); err != nil {
25
-			return err
26
-		}
27
-
28
-		if err := setupHostname(args); err != nil {
29
-			return err
30
-		}
31
-
32
-		if err := setupNetworking(args); err != nil {
33
-			return err
34
-		}
35
-
36
-		if err := setupCapabilities(args); err != nil {
37
-			return err
38
-		}
39
-
40
-		if err := setupWorkingDirectory(args); err != nil {
41
-			return err
42
-		}
43
-
44
-		if err := changeUser(args); err != nil {
45
-			return err
46
-		}
47
-
48
-		path, err := exec.LookPath(args.Args[0])
49
-		if err != nil {
50
-			log.Printf("Unable to locate %v", args.Args[0])
51
-			os.Exit(127)
52
-		}
53
-		if err := syscall.Exec(path, args.Args, os.Environ()); err != nil {
54
-			return fmt.Errorf("dockerinit unable to execute %s - %s", path, err)
55
-		}
56
-		panic("Unreachable")
57
-	})
58
-}
59
-
60
-type driver struct {
61
-	root       string // root path for the driver to use
62
-	apparmor   bool
63
-	sharedRoot bool
64
-}
65
-
66
-func NewDriver(root string, apparmor bool) (*driver, error) {
67
-	// setup unconfined symlink
68
-	if err := linkLxcStart(root); err != nil {
69
-		return nil, err
70
-	}
71
-	return &driver{
72
-		apparmor:   apparmor,
73
-		root:       root,
74
-		sharedRoot: rootIsShared(),
75
-	}, nil
76
-}
77
-
78
-func (d *driver) Name() string {
79
-	version := d.version()
80
-	return fmt.Sprintf("%s-%s", DriverName, version)
81
-}
82
-
83
-func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
84
-	if err := execdriver.SetTerminal(c, pipes); err != nil {
85
-		return -1, err
86
-	}
87
-	configPath, err := d.generateLXCConfig(c)
88
-	if err != nil {
89
-		return -1, err
90
-	}
91
-	params := []string{
92
-		"lxc-start",
93
-		"-n", c.ID,
94
-		"-f", configPath,
95
-		"--",
96
-		c.InitPath,
97
-		"-driver",
98
-		DriverName,
99
-	}
100
-
101
-	if c.Network != nil {
102
-		params = append(params,
103
-			"-g", c.Network.Gateway,
104
-			"-i", fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
105
-			"-mtu", strconv.Itoa(c.Network.Mtu),
106
-		)
107
-	}
108
-
109
-	if c.User != "" {
110
-		params = append(params, "-u", c.User)
111
-	}
112
-
113
-	if c.Privileged {
114
-		if d.apparmor {
115
-			params[0] = path.Join(d.root, "lxc-start-unconfined")
116
-
117
-		}
118
-		params = append(params, "-privileged")
119
-	}
120
-
121
-	if c.WorkingDir != "" {
122
-		params = append(params, "-w", c.WorkingDir)
123
-	}
124
-
125
-	params = append(params, "--", c.Entrypoint)
126
-	params = append(params, c.Arguments...)
127
-
128
-	if d.sharedRoot {
129
-		// lxc-start really needs / to be non-shared, or all kinds of stuff break
130
-		// when lxc-start unmount things and those unmounts propagate to the main
131
-		// mount namespace.
132
-		// What we really want is to clone into a new namespace and then
133
-		// mount / MS_REC|MS_SLAVE, but since we can't really clone or fork
134
-		// without exec in go we have to do this horrible shell hack...
135
-		shellString :=
136
-			"mount --make-rslave /; exec " +
137
-				utils.ShellQuoteArguments(params)
138
-
139
-		params = []string{
140
-			"unshare", "-m", "--", "/bin/sh", "-c", shellString,
141
-		}
142
-	}
143
-
144
-	var (
145
-		name = params[0]
146
-		arg  = params[1:]
147
-	)
148
-	aname, err := exec.LookPath(name)
149
-	if err != nil {
150
-		aname = name
151
-	}
152
-	c.Path = aname
153
-	c.Args = append([]string{name}, arg...)
154
-
155
-	if err := c.Start(); err != nil {
156
-		return -1, err
157
-	}
158
-
159
-	var (
160
-		waitErr  error
161
-		waitLock = make(chan struct{})
162
-	)
163
-	go func() {
164
-		if err := c.Wait(); err != nil {
165
-			if _, ok := err.(*exec.ExitError); !ok { // Do not propagate the error if it's simply a status code != 0
166
-				waitErr = err
167
-			}
168
-		}
169
-		close(waitLock)
170
-	}()
171
-
172
-	// Poll lxc for RUNNING status
173
-	pid, err := d.waitForStart(c, waitLock)
174
-	if err != nil {
175
-		if c.Process != nil {
176
-			c.Process.Kill()
177
-		}
178
-		return -1, err
179
-	}
180
-	c.ContainerPid = pid
181
-
182
-	if startCallback != nil {
183
-		startCallback(c)
184
-	}
185
-
186
-	<-waitLock
187
-
188
-	return getExitCode(c), waitErr
189
-}
190
-
191
-/// Return the exit code of the process
192
-// if the process has not exited -1 will be returned
193
-func getExitCode(c *execdriver.Command) int {
194
-	if c.ProcessState == nil {
195
-		return -1
196
-	}
197
-	return c.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
198
-}
199
-
200
-func (d *driver) Kill(c *execdriver.Command, sig int) error {
201
-	return KillLxc(c.ID, sig)
202
-}
203
-
204
-func (d *driver) version() string {
205
-	var (
206
-		version string
207
-		output  []byte
208
-		err     error
209
-	)
210
-	if _, errPath := exec.LookPath("lxc-version"); errPath == nil {
211
-		output, err = exec.Command("lxc-version").CombinedOutput()
212
-	} else {
213
-		output, err = exec.Command("lxc-start", "--version").CombinedOutput()
214
-	}
215
-	if err == nil {
216
-		version = strings.TrimSpace(string(output))
217
-		if parts := strings.SplitN(version, ":", 2); len(parts) == 2 {
218
-			version = strings.TrimSpace(parts[1])
219
-		}
220
-	}
221
-	return version
222
-}
223
-
224
-func KillLxc(id string, sig int) error {
225
-	var (
226
-		err    error
227
-		output []byte
228
-	)
229
-	_, err = exec.LookPath("lxc-kill")
230
-	if err == nil {
231
-		output, err = exec.Command("lxc-kill", "-n", id, strconv.Itoa(sig)).CombinedOutput()
232
-	} else {
233
-		output, err = exec.Command("lxc-stop", "-k", "-n", id, strconv.Itoa(sig)).CombinedOutput()
234
-	}
235
-	if err != nil {
236
-		return fmt.Errorf("Err: %s Output: %s", err, output)
237
-	}
238
-	return nil
239
-}
240
-
241
-// wait for the process to start and return the pid for the process
242
-func (d *driver) waitForStart(c *execdriver.Command, waitLock chan struct{}) (int, error) {
243
-	var (
244
-		err    error
245
-		output []byte
246
-	)
247
-	// We wait for the container to be fully running.
248
-	// Timeout after 5 seconds. In case of broken pipe, just retry.
249
-	// Note: The container can run and finish correctly before
250
-	// the end of this loop
251
-	for now := time.Now(); time.Since(now) < 5*time.Second; {
252
-		select {
253
-		case <-waitLock:
254
-			// If the process dies while waiting for it, just return
255
-			return -1, nil
256
-		default:
257
-		}
258
-
259
-		output, err = d.getInfo(c.ID)
260
-		if err != nil {
261
-			output, err = d.getInfo(c.ID)
262
-			if err != nil {
263
-				return -1, err
264
-			}
265
-		}
266
-		info, err := parseLxcInfo(string(output))
267
-		if err != nil {
268
-			return -1, err
269
-		}
270
-		if info.Running {
271
-			return info.Pid, nil
272
-		}
273
-		time.Sleep(50 * time.Millisecond)
274
-	}
275
-	return -1, execdriver.ErrNotRunning
276
-}
277
-
278
-func (d *driver) getInfo(id string) ([]byte, error) {
279
-	return exec.Command("lxc-info", "-n", id).CombinedOutput()
280
-}
281
-
282
-type info struct {
283
-	ID     string
284
-	driver *driver
285
-}
286
-
287
-func (i *info) IsRunning() bool {
288
-	var running bool
289
-
290
-	output, err := i.driver.getInfo(i.ID)
291
-	if err != nil {
292
-		utils.Errorf("Error getting info for lxc container %s: %s (%s)", i.ID, err, output)
293
-		return false
294
-	}
295
-	if strings.Contains(string(output), "RUNNING") {
296
-		running = true
297
-	}
298
-	return running
299
-}
300
-
301
-func (d *driver) Info(id string) execdriver.Info {
302
-	return &info{
303
-		ID:     id,
304
-		driver: d,
305
-	}
306
-}
307
-
308
-func (d *driver) GetPidsForContainer(id string) ([]int, error) {
309
-	pids := []int{}
310
-
311
-	// cpu is chosen because it is the only non optional subsystem in cgroups
312
-	subsystem := "cpu"
313
-	cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem)
314
-	if err != nil {
315
-		return pids, err
316
-	}
317
-
318
-	cgroupDir, err := cgroups.GetThisCgroupDir(subsystem)
319
-	if err != nil {
320
-		return pids, err
321
-	}
322
-
323
-	filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks")
324
-	if _, err := os.Stat(filename); os.IsNotExist(err) {
325
-		// With more recent lxc versions use, cgroup will be in lxc/
326
-		filename = filepath.Join(cgroupRoot, cgroupDir, "lxc", id, "tasks")
327
-	}
328
-
329
-	output, err := ioutil.ReadFile(filename)
330
-	if err != nil {
331
-		return pids, err
332
-	}
333
-	for _, p := range strings.Split(string(output), "\n") {
334
-		if len(p) == 0 {
335
-			continue
336
-		}
337
-		pid, err := strconv.Atoi(p)
338
-		if err != nil {
339
-			return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
340
-		}
341
-		pids = append(pids, pid)
342
-	}
343
-	return pids, nil
344
-}
345
-
346
-func linkLxcStart(root string) error {
347
-	sourcePath, err := exec.LookPath("lxc-start")
348
-	if err != nil {
349
-		return err
350
-	}
351
-	targetPath := path.Join(root, "lxc-start-unconfined")
352
-
353
-	if _, err := os.Lstat(targetPath); err != nil && !os.IsNotExist(err) {
354
-		return err
355
-	} else if err == nil {
356
-		if err := os.Remove(targetPath); err != nil {
357
-			return err
358
-		}
359
-	}
360
-	return os.Symlink(sourcePath, targetPath)
361
-}
362
-
363
-// TODO: This can be moved to the mountinfo reader in the mount pkg
364
-func rootIsShared() bool {
365
-	if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil {
366
-		for _, line := range strings.Split(string(data), "\n") {
367
-			cols := strings.Split(line, " ")
368
-			if len(cols) >= 6 && cols[4] == "/" {
369
-				return strings.HasPrefix(cols[6], "shared")
370
-			}
371
-		}
372
-	}
373
-
374
-	// No idea, probably safe to assume so
375
-	return true
376
-}
377
-
378
-func (d *driver) generateLXCConfig(c *execdriver.Command) (string, error) {
379
-	root := path.Join(d.root, "containers", c.ID, "config.lxc")
380
-	fo, err := os.Create(root)
381
-	if err != nil {
382
-		return "", err
383
-	}
384
-	defer fo.Close()
385
-
386
-	if err := LxcTemplateCompiled.Execute(fo, struct {
387
-		*execdriver.Command
388
-		AppArmor bool
389
-	}{
390
-		Command:  c,
391
-		AppArmor: d.apparmor,
392
-	}); err != nil {
393
-		return "", err
394
-	}
395
-	return root, nil
396
-}
397 1
deleted file mode 100644
... ...
@@ -1,50 +0,0 @@
1
-package lxc
2
-
3
-import (
4
-	"bufio"
5
-	"errors"
6
-	"strconv"
7
-	"strings"
8
-)
9
-
10
-var (
11
-	ErrCannotParse = errors.New("cannot parse raw input")
12
-)
13
-
14
-type lxcInfo struct {
15
-	Running bool
16
-	Pid     int
17
-}
18
-
19
-func parseLxcInfo(raw string) (*lxcInfo, error) {
20
-	if raw == "" {
21
-		return nil, ErrCannotParse
22
-	}
23
-	var (
24
-		err  error
25
-		s    = bufio.NewScanner(strings.NewReader(raw))
26
-		info = &lxcInfo{}
27
-	)
28
-	for s.Scan() {
29
-		text := s.Text()
30
-
31
-		if s.Err() != nil {
32
-			return nil, s.Err()
33
-		}
34
-
35
-		parts := strings.Split(text, ":")
36
-		if len(parts) < 2 {
37
-			continue
38
-		}
39
-		switch strings.ToLower(strings.TrimSpace(parts[0])) {
40
-		case "state":
41
-			info.Running = strings.TrimSpace(parts[1]) == "RUNNING"
42
-		case "pid":
43
-			info.Pid, err = strconv.Atoi(strings.TrimSpace(parts[1]))
44
-			if err != nil {
45
-				return nil, err
46
-			}
47
-		}
48
-	}
49
-	return info, nil
50
-}
51 1
deleted file mode 100644
... ...
@@ -1,36 +0,0 @@
1
-package lxc
2
-
3
-import (
4
-	"testing"
5
-)
6
-
7
-func TestParseRunningInfo(t *testing.T) {
8
-	raw := `
9
-    state: RUNNING
10
-    pid:    50`
11
-
12
-	info, err := parseLxcInfo(raw)
13
-	if err != nil {
14
-		t.Fatal(err)
15
-	}
16
-	if !info.Running {
17
-		t.Fatal("info should return a running state")
18
-	}
19
-	if info.Pid != 50 {
20
-		t.Fatalf("info should have pid 50 got %d", info.Pid)
21
-	}
22
-}
23
-
24
-func TestEmptyInfo(t *testing.T) {
25
-	_, err := parseLxcInfo("")
26
-	if err == nil {
27
-		t.Fatal("error should not be nil")
28
-	}
29
-}
30
-
31
-func TestBadInfo(t *testing.T) {
32
-	_, err := parseLxcInfo("state")
33
-	if err != nil {
34
-		t.Fatal(err)
35
-	}
36
-}
37 1
deleted file mode 100644
... ...
@@ -1,176 +0,0 @@
1
-package lxc
2
-
3
-import (
4
-	"encoding/json"
5
-	"fmt"
6
-	"github.com/dotcloud/docker/execdriver"
7
-	"github.com/dotcloud/docker/pkg/netlink"
8
-	"github.com/dotcloud/docker/pkg/user"
9
-	"github.com/syndtr/gocapability/capability"
10
-	"io/ioutil"
11
-	"net"
12
-	"os"
13
-	"strings"
14
-	"syscall"
15
-)
16
-
17
-// Clear environment pollution introduced by lxc-start
18
-func setupEnv(args *execdriver.InitArgs) error {
19
-	// Get env
20
-	var env []string
21
-	content, err := ioutil.ReadFile(".dockerenv")
22
-	if err != nil {
23
-		return fmt.Errorf("Unable to load environment variables: %v", err)
24
-	}
25
-	if err := json.Unmarshal(content, &env); err != nil {
26
-		return fmt.Errorf("Unable to unmarshal environment variables: %v", err)
27
-	}
28
-	// Propagate the plugin-specific container env variable
29
-	env = append(env, "container="+os.Getenv("container"))
30
-
31
-	args.Env = env
32
-
33
-	os.Clearenv()
34
-	for _, kv := range args.Env {
35
-		parts := strings.SplitN(kv, "=", 2)
36
-		if len(parts) == 1 {
37
-			parts = append(parts, "")
38
-		}
39
-		os.Setenv(parts[0], parts[1])
40
-	}
41
-
42
-	return nil
43
-}
44
-
45
-func setupHostname(args *execdriver.InitArgs) error {
46
-	hostname := getEnv(args, "HOSTNAME")
47
-	if hostname == "" {
48
-		return nil
49
-	}
50
-	return setHostname(hostname)
51
-}
52
-
53
-// Setup networking
54
-func setupNetworking(args *execdriver.InitArgs) error {
55
-	if args.Ip != "" {
56
-		// eth0
57
-		iface, err := net.InterfaceByName("eth0")
58
-		if err != nil {
59
-			return fmt.Errorf("Unable to set up networking: %v", err)
60
-		}
61
-		ip, ipNet, err := net.ParseCIDR(args.Ip)
62
-		if err != nil {
63
-			return fmt.Errorf("Unable to set up networking: %v", err)
64
-		}
65
-		if err := netlink.NetworkLinkAddIp(iface, ip, ipNet); err != nil {
66
-			return fmt.Errorf("Unable to set up networking: %v", err)
67
-		}
68
-		if err := netlink.NetworkSetMTU(iface, args.Mtu); err != nil {
69
-			return fmt.Errorf("Unable to set MTU: %v", err)
70
-		}
71
-		if err := netlink.NetworkLinkUp(iface); err != nil {
72
-			return fmt.Errorf("Unable to set up networking: %v", err)
73
-		}
74
-
75
-		// loopback
76
-		iface, err = net.InterfaceByName("lo")
77
-		if err != nil {
78
-			return fmt.Errorf("Unable to set up networking: %v", err)
79
-		}
80
-		if err := netlink.NetworkLinkUp(iface); err != nil {
81
-			return fmt.Errorf("Unable to set up networking: %v", err)
82
-		}
83
-	}
84
-	if args.Gateway != "" {
85
-		gw := net.ParseIP(args.Gateway)
86
-		if gw == nil {
87
-			return fmt.Errorf("Unable to set up networking, %s is not a valid gateway IP", args.Gateway)
88
-		}
89
-
90
-		if err := netlink.AddDefaultGw(gw); err != nil {
91
-			return fmt.Errorf("Unable to set up networking: %v", err)
92
-		}
93
-	}
94
-
95
-	return nil
96
-}
97
-
98
-// Setup working directory
99
-func setupWorkingDirectory(args *execdriver.InitArgs) error {
100
-	if args.WorkDir == "" {
101
-		return nil
102
-	}
103
-	if err := syscall.Chdir(args.WorkDir); err != nil {
104
-		return fmt.Errorf("Unable to change dir to %v: %v", args.WorkDir, err)
105
-	}
106
-	return nil
107
-}
108
-
109
-// Takes care of dropping privileges to the desired user
110
-func changeUser(args *execdriver.InitArgs) error {
111
-	uid, gid, suppGids, err := user.GetUserGroupSupplementary(
112
-		args.User,
113
-		syscall.Getuid(), syscall.Getgid(),
114
-	)
115
-	if err != nil {
116
-		return err
117
-	}
118
-
119
-	if err := syscall.Setgroups(suppGids); err != nil {
120
-		return fmt.Errorf("Setgroups failed: %v", err)
121
-	}
122
-	if err := syscall.Setgid(gid); err != nil {
123
-		return fmt.Errorf("Setgid failed: %v", err)
124
-	}
125
-	if err := syscall.Setuid(uid); err != nil {
126
-		return fmt.Errorf("Setuid failed: %v", err)
127
-	}
128
-
129
-	return nil
130
-}
131
-
132
-func setupCapabilities(args *execdriver.InitArgs) error {
133
-	if args.Privileged {
134
-		return nil
135
-	}
136
-
137
-	drop := []capability.Cap{
138
-		capability.CAP_SETPCAP,
139
-		capability.CAP_SYS_MODULE,
140
-		capability.CAP_SYS_RAWIO,
141
-		capability.CAP_SYS_PACCT,
142
-		capability.CAP_SYS_ADMIN,
143
-		capability.CAP_SYS_NICE,
144
-		capability.CAP_SYS_RESOURCE,
145
-		capability.CAP_SYS_TIME,
146
-		capability.CAP_SYS_TTY_CONFIG,
147
-		capability.CAP_MKNOD,
148
-		capability.CAP_AUDIT_WRITE,
149
-		capability.CAP_AUDIT_CONTROL,
150
-		capability.CAP_MAC_OVERRIDE,
151
-		capability.CAP_MAC_ADMIN,
152
-		capability.CAP_NET_ADMIN,
153
-	}
154
-
155
-	c, err := capability.NewPid(os.Getpid())
156
-	if err != nil {
157
-		return err
158
-	}
159
-
160
-	c.Unset(capability.CAPS|capability.BOUNDS, drop...)
161
-
162
-	if err := c.Apply(capability.CAPS | capability.BOUNDS); err != nil {
163
-		return err
164
-	}
165
-	return nil
166
-}
167
-
168
-func getEnv(args *execdriver.InitArgs, key string) string {
169
-	for _, kv := range args.Env {
170
-		parts := strings.SplitN(kv, "=", 2)
171
-		if parts[0] == key && len(parts) == 2 {
172
-			return parts[1]
173
-		}
174
-	}
175
-	return ""
176
-}
177 1
deleted file mode 100644
... ...
@@ -1,11 +0,0 @@
1
-// +build amd64
2
-
3
-package lxc
4
-
5
-import (
6
-	"syscall"
7
-)
8
-
9
-func setHostname(hostname string) error {
10
-	return syscall.Sethostname([]byte(hostname))
11
-}
12 1
deleted file mode 100644
... ...
@@ -1,7 +0,0 @@
1
-// +build !linux !amd64
2
-
3
-package lxc
4
-
5
-func setHostname(hostname string) error {
6
-	panic("Not supported on darwin")
7
-}
8 1
deleted file mode 100644
... ...
@@ -1,155 +0,0 @@
1
-package lxc
2
-
3
-import (
4
-	"github.com/dotcloud/docker/execdriver"
5
-	"strings"
6
-	"text/template"
7
-)
8
-
9
-const LxcTemplate = `
10
-{{if .Network}}
11
-# network configuration
12
-lxc.network.type = veth
13
-lxc.network.link = {{.Network.Bridge}}
14
-lxc.network.name = eth0
15
-lxc.network.mtu = {{.Network.Mtu}}
16
-{{else}}
17
-# network is disabled (-n=false)
18
-lxc.network.type = empty
19
-lxc.network.flags = up
20
-{{end}}
21
-
22
-# root filesystem
23
-{{$ROOTFS := .Rootfs}}
24
-lxc.rootfs = {{$ROOTFS}}
25
-
26
-# use a dedicated pts for the container (and limit the number of pseudo terminal
27
-# available)
28
-lxc.pts = 1024
29
-
30
-# disable the main console
31
-lxc.console = none
32
-
33
-# no controlling tty at all
34
-lxc.tty = 1
35
-
36
-{{if .Privileged}}
37
-lxc.cgroup.devices.allow = a
38
-{{else}}
39
-# no implicit access to devices
40
-lxc.cgroup.devices.deny = a
41
-
42
-# /dev/null and zero
43
-lxc.cgroup.devices.allow = c 1:3 rwm
44
-lxc.cgroup.devices.allow = c 1:5 rwm
45
-
46
-# consoles
47
-lxc.cgroup.devices.allow = c 5:1 rwm
48
-lxc.cgroup.devices.allow = c 5:0 rwm
49
-lxc.cgroup.devices.allow = c 4:0 rwm
50
-lxc.cgroup.devices.allow = c 4:1 rwm
51
-
52
-# /dev/urandom,/dev/random
53
-lxc.cgroup.devices.allow = c 1:9 rwm
54
-lxc.cgroup.devices.allow = c 1:8 rwm
55
-
56
-# /dev/pts/ - pts namespaces are "coming soon"
57
-lxc.cgroup.devices.allow = c 136:* rwm
58
-lxc.cgroup.devices.allow = c 5:2 rwm
59
-
60
-# tuntap
61
-lxc.cgroup.devices.allow = c 10:200 rwm
62
-
63
-# fuse
64
-#lxc.cgroup.devices.allow = c 10:229 rwm
65
-
66
-# rtc
67
-#lxc.cgroup.devices.allow = c 254:0 rwm
68
-{{end}}
69
-
70
-# standard mount point
71
-# Use mnt.putold as per https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/986385
72
-lxc.pivotdir = lxc_putold
73
-
74
-# NOTICE: These mounts must be applied within the namespace
75
-
76
-#  WARNING: procfs is a known attack vector and should probably be disabled
77
-#           if your userspace allows it. eg. see http://blog.zx2c4.com/749
78
-lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
79
-
80
-# WARNING: sysfs is a known attack vector and should probably be disabled
81
-# if your userspace allows it. eg. see http://bit.ly/T9CkqJ
82
-lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
83
-
84
-{{if .Tty}}
85
-lxc.mount.entry = {{.Console}} {{escapeFstabSpaces $ROOTFS}}/dev/console none bind,rw 0 0
86
-{{end}}
87
-
88
-lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0
89
-lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0
90
-
91
-{{range $value := .Mounts}}
92
-{{if $value.Writable}}
93
-lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $value.Destination}} none bind,rw 0 0
94
-{{else}}
95
-lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $value.Destination}} none bind,ro 0 0
96
-{{end}}
97
-{{end}}
98
-
99
-{{if .Privileged}}
100
-{{if .AppArmor}}
101
-lxc.aa_profile = unconfined
102
-{{else}}
103
-#lxc.aa_profile = unconfined
104
-{{end}}
105
-{{end}}
106
-
107
-# limits
108
-{{if .Resources}}
109
-{{if .Resources.Memory}}
110
-lxc.cgroup.memory.limit_in_bytes = {{.Resources.Memory}}
111
-lxc.cgroup.memory.soft_limit_in_bytes = {{.Resources.Memory}}
112
-{{with $memSwap := getMemorySwap .Resources}}
113
-lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}}
114
-{{end}}
115
-{{end}}
116
-{{if .Resources.CpuShares}}
117
-lxc.cgroup.cpu.shares = {{.Resources.CpuShares}}
118
-{{end}}
119
-{{end}}
120
-
121
-{{if .Config}}
122
-{{range $value := .Config}}
123
-{{$value}}
124
-{{end}}
125
-{{end}}
126
-`
127
-
128
-var LxcTemplateCompiled *template.Template
129
-
130
-// Escape spaces in strings according to the fstab documentation, which is the
131
-// format for "lxc.mount.entry" lines in lxc.conf. See also "man 5 fstab".
132
-func escapeFstabSpaces(field string) string {
133
-	return strings.Replace(field, " ", "\\040", -1)
134
-}
135
-
136
-func getMemorySwap(v *execdriver.Resources) int64 {
137
-	// By default, MemorySwap is set to twice the size of RAM.
138
-	// If you want to omit MemorySwap, set it to `-1'.
139
-	if v.MemorySwap < 0 {
140
-		return 0
141
-	}
142
-	return v.Memory * 2
143
-}
144
-
145
-func init() {
146
-	var err error
147
-	funcMap := template.FuncMap{
148
-		"getMemorySwap":     getMemorySwap,
149
-		"escapeFstabSpaces": escapeFstabSpaces,
150
-	}
151
-	LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate)
152
-	if err != nil {
153
-		panic(err)
154
-	}
155
-}
156 1
deleted file mode 100644
... ...
@@ -1,125 +0,0 @@
1
-package lxc
2
-
3
-import (
4
-	"bufio"
5
-	"fmt"
6
-	"github.com/dotcloud/docker/execdriver"
7
-	"io/ioutil"
8
-	"math/rand"
9
-	"os"
10
-	"path"
11
-	"strings"
12
-	"testing"
13
-	"time"
14
-)
15
-
16
-func TestLXCConfig(t *testing.T) {
17
-	root, err := ioutil.TempDir("", "TestLXCConfig")
18
-	if err != nil {
19
-		t.Fatal(err)
20
-	}
21
-	defer os.RemoveAll(root)
22
-
23
-	os.MkdirAll(path.Join(root, "containers", "1"), 0777)
24
-
25
-	// Memory is allocated randomly for testing
26
-	rand.Seed(time.Now().UTC().UnixNano())
27
-	var (
28
-		memMin = 33554432
29
-		memMax = 536870912
30
-		mem    = memMin + rand.Intn(memMax-memMin)
31
-		cpuMin = 100
32
-		cpuMax = 10000
33
-		cpu    = cpuMin + rand.Intn(cpuMax-cpuMin)
34
-	)
35
-
36
-	driver, err := NewDriver(root, false)
37
-	if err != nil {
38
-		t.Fatal(err)
39
-	}
40
-	command := &execdriver.Command{
41
-		ID: "1",
42
-		Resources: &execdriver.Resources{
43
-			Memory:    int64(mem),
44
-			CpuShares: int64(cpu),
45
-		},
46
-	}
47
-	p, err := driver.generateLXCConfig(command)
48
-	if err != nil {
49
-		t.Fatal(err)
50
-	}
51
-	grepFile(t, p,
52
-		fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
53
-
54
-	grepFile(t, p,
55
-		fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2))
56
-}
57
-
58
-func TestCustomLxcConfig(t *testing.T) {
59
-	root, err := ioutil.TempDir("", "TestCustomLxcConfig")
60
-	if err != nil {
61
-		t.Fatal(err)
62
-	}
63
-	defer os.RemoveAll(root)
64
-
65
-	os.MkdirAll(path.Join(root, "containers", "1"), 0777)
66
-
67
-	driver, err := NewDriver(root, false)
68
-	if err != nil {
69
-		t.Fatal(err)
70
-	}
71
-	command := &execdriver.Command{
72
-		ID:         "1",
73
-		Privileged: false,
74
-		Config: []string{
75
-			"lxc.utsname = docker",
76
-			"lxc.cgroup.cpuset.cpus = 0,1",
77
-		},
78
-	}
79
-
80
-	p, err := driver.generateLXCConfig(command)
81
-	if err != nil {
82
-		t.Fatal(err)
83
-	}
84
-
85
-	grepFile(t, p, "lxc.utsname = docker")
86
-	grepFile(t, p, "lxc.cgroup.cpuset.cpus = 0,1")
87
-}
88
-
89
-func grepFile(t *testing.T, path string, pattern string) {
90
-	f, err := os.Open(path)
91
-	if err != nil {
92
-		t.Fatal(err)
93
-	}
94
-	defer f.Close()
95
-	r := bufio.NewReader(f)
96
-	var (
97
-		line string
98
-	)
99
-	err = nil
100
-	for err == nil {
101
-		line, err = r.ReadString('\n')
102
-		if strings.Contains(line, pattern) == true {
103
-			return
104
-		}
105
-	}
106
-	t.Fatalf("grepFile: pattern \"%s\" not found in \"%s\"", pattern, path)
107
-}
108
-
109
-func TestEscapeFstabSpaces(t *testing.T) {
110
-	var testInputs = map[string]string{
111
-		" ":                      "\\040",
112
-		"":                       "",
113
-		"/double  space":         "/double\\040\\040space",
114
-		"/some long test string": "/some\\040long\\040test\\040string",
115
-		"/var/lib/docker":        "/var/lib/docker",
116
-		" leading":               "\\040leading",
117
-		"trailing ":              "trailing\\040",
118
-	}
119
-	for in, exp := range testInputs {
120
-		if out := escapeFstabSpaces(in); exp != out {
121
-			t.Logf("Expected %s got %s", exp, out)
122
-			t.Fail()
123
-		}
124
-	}
125
-}
126 1
deleted file mode 100644
... ...
@@ -1,94 +0,0 @@
1
-package native
2
-
3
-import (
4
-	"fmt"
5
-	"github.com/dotcloud/docker/execdriver"
6
-	"github.com/dotcloud/docker/pkg/cgroups"
7
-	"github.com/dotcloud/docker/pkg/libcontainer"
8
-	"os"
9
-)
10
-
11
-// createContainer populates and configures the container type with the
12
-// data provided by the execdriver.Command
13
-func createContainer(c *execdriver.Command) *libcontainer.Container {
14
-	container := getDefaultTemplate()
15
-
16
-	container.Hostname = getEnv("HOSTNAME", c.Env)
17
-	container.Tty = c.Tty
18
-	container.User = c.User
19
-	container.WorkingDir = c.WorkingDir
20
-	container.Env = c.Env
21
-
22
-	if c.Network != nil {
23
-		container.Networks = []*libcontainer.Network{
24
-			{
25
-				Mtu:     c.Network.Mtu,
26
-				Address: fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
27
-				Gateway: c.Network.Gateway,
28
-				Type:    "veth",
29
-				Context: libcontainer.Context{
30
-					"prefix": "veth",
31
-					"bridge": c.Network.Bridge,
32
-				},
33
-			},
34
-		}
35
-	}
36
-
37
-	container.Cgroups.Name = c.ID
38
-	if c.Privileged {
39
-		container.CapabilitiesMask = nil
40
-		container.Cgroups.DeviceAccess = true
41
-		container.Context["apparmor_profile"] = "unconfined"
42
-	}
43
-	if c.Resources != nil {
44
-		container.Cgroups.CpuShares = c.Resources.CpuShares
45
-		container.Cgroups.Memory = c.Resources.Memory
46
-		container.Cgroups.MemorySwap = c.Resources.MemorySwap
47
-	}
48
-	// check to see if we are running in ramdisk to disable pivot root
49
-	container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
50
-
51
-	for _, m := range c.Mounts {
52
-		container.Mounts = append(container.Mounts, libcontainer.Mount{m.Source, m.Destination, m.Writable, m.Private})
53
-	}
54
-
55
-	return container
56
-}
57
-
58
-// getDefaultTemplate returns the docker default for
59
-// the libcontainer configuration file
60
-func getDefaultTemplate() *libcontainer.Container {
61
-	return &libcontainer.Container{
62
-		CapabilitiesMask: libcontainer.Capabilities{
63
-			libcontainer.GetCapability("SETPCAP"),
64
-			libcontainer.GetCapability("SYS_MODULE"),
65
-			libcontainer.GetCapability("SYS_RAWIO"),
66
-			libcontainer.GetCapability("SYS_PACCT"),
67
-			libcontainer.GetCapability("SYS_ADMIN"),
68
-			libcontainer.GetCapability("SYS_NICE"),
69
-			libcontainer.GetCapability("SYS_RESOURCE"),
70
-			libcontainer.GetCapability("SYS_TIME"),
71
-			libcontainer.GetCapability("SYS_TTY_CONFIG"),
72
-			libcontainer.GetCapability("MKNOD"),
73
-			libcontainer.GetCapability("AUDIT_WRITE"),
74
-			libcontainer.GetCapability("AUDIT_CONTROL"),
75
-			libcontainer.GetCapability("MAC_OVERRIDE"),
76
-			libcontainer.GetCapability("MAC_ADMIN"),
77
-			libcontainer.GetCapability("NET_ADMIN"),
78
-		},
79
-		Namespaces: libcontainer.Namespaces{
80
-			libcontainer.GetNamespace("NEWNS"),
81
-			libcontainer.GetNamespace("NEWUTS"),
82
-			libcontainer.GetNamespace("NEWIPC"),
83
-			libcontainer.GetNamespace("NEWPID"),
84
-			libcontainer.GetNamespace("NEWNET"),
85
-		},
86
-		Cgroups: &cgroups.Cgroup{
87
-			Parent:       "docker",
88
-			DeviceAccess: false,
89
-		},
90
-		Context: libcontainer.Context{
91
-			"apparmor_profile": "docker-default",
92
-		},
93
-	}
94
-}
95 1
deleted file mode 100644
... ...
@@ -1,266 +0,0 @@
1
-package native
2
-
3
-import (
4
-	"encoding/json"
5
-	"fmt"
6
-	"github.com/dotcloud/docker/execdriver"
7
-	"github.com/dotcloud/docker/pkg/cgroups"
8
-	"github.com/dotcloud/docker/pkg/libcontainer"
9
-	"github.com/dotcloud/docker/pkg/libcontainer/apparmor"
10
-	"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
11
-	"github.com/dotcloud/docker/pkg/system"
12
-	"io"
13
-	"io/ioutil"
14
-	"log"
15
-	"os"
16
-	"os/exec"
17
-	"path/filepath"
18
-	"strconv"
19
-	"strings"
20
-	"syscall"
21
-)
22
-
23
-const (
24
-	DriverName = "native"
25
-	Version    = "0.1"
26
-)
27
-
28
-func init() {
29
-	execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
30
-		var (
31
-			container *libcontainer.Container
32
-			ns        = nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{args.Root}, createLogger(""))
33
-		)
34
-		f, err := os.Open(filepath.Join(args.Root, "container.json"))
35
-		if err != nil {
36
-			return err
37
-		}
38
-		if err := json.NewDecoder(f).Decode(&container); err != nil {
39
-			f.Close()
40
-			return err
41
-		}
42
-		f.Close()
43
-
44
-		cwd, err := os.Getwd()
45
-		if err != nil {
46
-			return err
47
-		}
48
-		syncPipe, err := nsinit.NewSyncPipeFromFd(0, uintptr(args.Pipe))
49
-		if err != nil {
50
-			return err
51
-		}
52
-		if err := ns.Init(container, cwd, args.Console, syncPipe, args.Args); err != nil {
53
-			return err
54
-		}
55
-		return nil
56
-	})
57
-}
58
-
59
-type driver struct {
60
-	root     string
61
-	initPath string
62
-}
63
-
64
-func NewDriver(root, initPath string) (*driver, error) {
65
-	if err := os.MkdirAll(root, 0700); err != nil {
66
-		return nil, err
67
-	}
68
-	if err := apparmor.InstallDefaultProfile(); err != nil {
69
-		return nil, err
70
-	}
71
-	return &driver{
72
-		root:     root,
73
-		initPath: initPath,
74
-	}, nil
75
-}
76
-
77
-func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
78
-	if err := d.validateCommand(c); err != nil {
79
-		return -1, err
80
-	}
81
-	var (
82
-		term        nsinit.Terminal
83
-		container   = createContainer(c)
84
-		factory     = &dockerCommandFactory{c: c, driver: d}
85
-		stateWriter = &dockerStateWriter{
86
-			callback: startCallback,
87
-			c:        c,
88
-			dsw:      &nsinit.DefaultStateWriter{filepath.Join(d.root, c.ID)},
89
-		}
90
-		ns   = nsinit.NewNsInit(factory, stateWriter, createLogger(os.Getenv("DEBUG")))
91
-		args = append([]string{c.Entrypoint}, c.Arguments...)
92
-	)
93
-	if err := d.createContainerRoot(c.ID); err != nil {
94
-		return -1, err
95
-	}
96
-	defer d.removeContainerRoot(c.ID)
97
-
98
-	if c.Tty {
99
-		term = &dockerTtyTerm{
100
-			pipes: pipes,
101
-		}
102
-	} else {
103
-		term = &dockerStdTerm{
104
-			pipes: pipes,
105
-		}
106
-	}
107
-	c.Terminal = term
108
-	if err := d.writeContainerFile(container, c.ID); err != nil {
109
-		return -1, err
110
-	}
111
-	return ns.Exec(container, term, args)
112
-}
113
-
114
-func (d *driver) Kill(p *execdriver.Command, sig int) error {
115
-	err := syscall.Kill(p.Process.Pid, syscall.Signal(sig))
116
-	d.removeContainerRoot(p.ID)
117
-	return err
118
-}
119
-
120
-func (d *driver) Info(id string) execdriver.Info {
121
-	return &info{
122
-		ID:     id,
123
-		driver: d,
124
-	}
125
-}
126
-
127
-func (d *driver) Name() string {
128
-	return fmt.Sprintf("%s-%s", DriverName, Version)
129
-}
130
-
131
-// TODO: this can be improved with our driver
132
-// there has to be a better way to do this
133
-func (d *driver) GetPidsForContainer(id string) ([]int, error) {
134
-	pids := []int{}
135
-
136
-	subsystem := "devices"
137
-	cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem)
138
-	if err != nil {
139
-		return pids, err
140
-	}
141
-	cgroupDir, err := cgroups.GetThisCgroupDir(subsystem)
142
-	if err != nil {
143
-		return pids, err
144
-	}
145
-
146
-	filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks")
147
-	if _, err := os.Stat(filename); os.IsNotExist(err) {
148
-		filename = filepath.Join(cgroupRoot, cgroupDir, "docker", id, "tasks")
149
-	}
150
-
151
-	output, err := ioutil.ReadFile(filename)
152
-	if err != nil {
153
-		return pids, err
154
-	}
155
-	for _, p := range strings.Split(string(output), "\n") {
156
-		if len(p) == 0 {
157
-			continue
158
-		}
159
-		pid, err := strconv.Atoi(p)
160
-		if err != nil {
161
-			return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
162
-		}
163
-		pids = append(pids, pid)
164
-	}
165
-	return pids, nil
166
-}
167
-
168
-func (d *driver) writeContainerFile(container *libcontainer.Container, id string) error {
169
-	data, err := json.Marshal(container)
170
-	if err != nil {
171
-		return err
172
-	}
173
-	return ioutil.WriteFile(filepath.Join(d.root, id, "container.json"), data, 0655)
174
-}
175
-
176
-func (d *driver) createContainerRoot(id string) error {
177
-	return os.MkdirAll(filepath.Join(d.root, id), 0655)
178
-}
179
-
180
-func (d *driver) removeContainerRoot(id string) error {
181
-	return os.RemoveAll(filepath.Join(d.root, id))
182
-}
183
-
184
-func (d *driver) validateCommand(c *execdriver.Command) error {
185
-	// we need to check the Config of the command to make sure that we
186
-	// do not have any of the lxc-conf variables
187
-	for _, conf := range c.Config {
188
-		if strings.Contains(conf, "lxc") {
189
-			return fmt.Errorf("%s is not supported by the native driver", conf)
190
-		}
191
-	}
192
-	return nil
193
-}
194
-
195
-func getEnv(key string, env []string) string {
196
-	for _, pair := range env {
197
-		parts := strings.Split(pair, "=")
198
-		if parts[0] == key {
199
-			return parts[1]
200
-		}
201
-	}
202
-	return ""
203
-}
204
-
205
-type dockerCommandFactory struct {
206
-	c      *execdriver.Command
207
-	driver *driver
208
-}
209
-
210
-// createCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
211
-// defined on the container's configuration and use the current binary as the init with the
212
-// args provided
213
-func (d *dockerCommandFactory) Create(container *libcontainer.Container, console string, syncFile *os.File, args []string) *exec.Cmd {
214
-	// we need to join the rootfs because nsinit will setup the rootfs and chroot
215
-	initPath := filepath.Join(d.c.Rootfs, d.c.InitPath)
216
-
217
-	d.c.Path = d.driver.initPath
218
-	d.c.Args = append([]string{
219
-		initPath,
220
-		"-driver", DriverName,
221
-		"-console", console,
222
-		"-pipe", "3",
223
-		"-root", filepath.Join(d.driver.root, d.c.ID),
224
-		"--",
225
-	}, args...)
226
-
227
-	// set this to nil so that when we set the clone flags anything else is reset
228
-	d.c.SysProcAttr = nil
229
-	system.SetCloneFlags(&d.c.Cmd, uintptr(nsinit.GetNamespaceFlags(container.Namespaces)))
230
-	d.c.ExtraFiles = []*os.File{syncFile}
231
-
232
-	d.c.Env = container.Env
233
-	d.c.Dir = d.c.Rootfs
234
-
235
-	return &d.c.Cmd
236
-}
237
-
238
-type dockerStateWriter struct {
239
-	dsw      nsinit.StateWriter
240
-	c        *execdriver.Command
241
-	callback execdriver.StartCallback
242
-}
243
-
244
-func (d *dockerStateWriter) WritePid(pid int) error {
245
-	d.c.ContainerPid = pid
246
-	err := d.dsw.WritePid(pid)
247
-	if d.callback != nil {
248
-		d.callback(d.c)
249
-	}
250
-	return err
251
-}
252
-
253
-func (d *dockerStateWriter) DeletePid() error {
254
-	return d.dsw.DeletePid()
255
-}
256
-
257
-func createLogger(debug string) *log.Logger {
258
-	var w io.Writer
259
-	// if we are in debug mode set the logger to stderr
260
-	if debug != "" {
261
-		w = os.Stderr
262
-	} else {
263
-		w = ioutil.Discard
264
-	}
265
-	return log.New(w, "[libcontainer] ", log.LstdFlags)
266
-}
267 1
deleted file mode 100644
... ...
@@ -1,21 +0,0 @@
1
-package native
2
-
3
-import (
4
-	"os"
5
-	"path/filepath"
6
-)
7
-
8
-type info struct {
9
-	ID     string
10
-	driver *driver
11
-}
12
-
13
-// IsRunning is determined by looking for the
14
-// pid file for a container.  If the file exists then the
15
-// container is currently running
16
-func (i *info) IsRunning() bool {
17
-	if _, err := os.Stat(filepath.Join(i.driver.root, i.ID, "pid")); err == nil {
18
-		return true
19
-	}
20
-	return false
21
-}
22 1
deleted file mode 100644
... ...
@@ -1,42 +0,0 @@
1
-/*
2
-   These types are wrappers around the libcontainer Terminal interface so that
3
-   we can resuse the docker implementations where possible.
4
-*/
5
-package native
6
-
7
-import (
8
-	"github.com/dotcloud/docker/execdriver"
9
-	"io"
10
-	"os"
11
-	"os/exec"
12
-)
13
-
14
-type dockerStdTerm struct {
15
-	execdriver.StdConsole
16
-	pipes *execdriver.Pipes
17
-}
18
-
19
-func (d *dockerStdTerm) Attach(cmd *exec.Cmd) error {
20
-	return d.AttachPipes(cmd, d.pipes)
21
-}
22
-
23
-func (d *dockerStdTerm) SetMaster(master *os.File) {
24
-	// do nothing
25
-}
26
-
27
-type dockerTtyTerm struct {
28
-	execdriver.TtyConsole
29
-	pipes *execdriver.Pipes
30
-}
31
-
32
-func (t *dockerTtyTerm) Attach(cmd *exec.Cmd) error {
33
-	go io.Copy(t.pipes.Stdout, t.MasterPty)
34
-	if t.pipes.Stdin != nil {
35
-		go io.Copy(t.MasterPty, t.pipes.Stdin)
36
-	}
37
-	return nil
38
-}
39
-
40
-func (t *dockerTtyTerm) SetMaster(master *os.File) {
41
-	t.MasterPty = master
42
-}
43 1
deleted file mode 100644
... ...
@@ -1,23 +0,0 @@
1
-package execdriver
2
-
3
-import (
4
-	"io"
5
-)
6
-
7
-// Pipes is a wrapper around a containers output for
8
-// stdin, stdout, stderr
9
-type Pipes struct {
10
-	Stdin          io.ReadCloser
11
-	Stdout, Stderr io.Writer
12
-}
13
-
14
-func NewPipes(stdin io.ReadCloser, stdout, stderr io.Writer, useStdin bool) *Pipes {
15
-	p := &Pipes{
16
-		Stdout: stdout,
17
-		Stderr: stderr,
18
-	}
19
-	if useStdin {
20
-		p.Stdin = stdin
21
-	}
22
-	return p
23
-}
24 1
deleted file mode 100644
... ...
@@ -1,126 +0,0 @@
1
-package execdriver
2
-
3
-import (
4
-	"github.com/dotcloud/docker/pkg/term"
5
-	"github.com/kr/pty"
6
-	"io"
7
-	"os"
8
-	"os/exec"
9
-)
10
-
11
-func SetTerminal(command *Command, pipes *Pipes) error {
12
-	var (
13
-		term Terminal
14
-		err  error
15
-	)
16
-	if command.Tty {
17
-		term, err = NewTtyConsole(command, pipes)
18
-	} else {
19
-		term, err = NewStdConsole(command, pipes)
20
-	}
21
-	if err != nil {
22
-		return err
23
-	}
24
-	command.Terminal = term
25
-	return nil
26
-}
27
-
28
-type TtyConsole struct {
29
-	MasterPty *os.File
30
-	SlavePty  *os.File
31
-}
32
-
33
-func NewTtyConsole(command *Command, pipes *Pipes) (*TtyConsole, error) {
34
-	ptyMaster, ptySlave, err := pty.Open()
35
-	if err != nil {
36
-		return nil, err
37
-	}
38
-	tty := &TtyConsole{
39
-		MasterPty: ptyMaster,
40
-		SlavePty:  ptySlave,
41
-	}
42
-	if err := tty.AttachPipes(&command.Cmd, pipes); err != nil {
43
-		tty.Close()
44
-		return nil, err
45
-	}
46
-	command.Console = tty.SlavePty.Name()
47
-	return tty, nil
48
-}
49
-
50
-func (t *TtyConsole) Master() *os.File {
51
-	return t.MasterPty
52
-}
53
-
54
-func (t *TtyConsole) Resize(h, w int) error {
55
-	return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
56
-}
57
-
58
-func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *Pipes) error {
59
-	command.Stdout = t.SlavePty
60
-	command.Stderr = t.SlavePty
61
-
62
-	go func() {
63
-		if wb, ok := pipes.Stdout.(interface {
64
-			CloseWriters() error
65
-		}); ok {
66
-			defer wb.CloseWriters()
67
-		}
68
-		io.Copy(pipes.Stdout, t.MasterPty)
69
-	}()
70
-
71
-	if pipes.Stdin != nil {
72
-		command.Stdin = t.SlavePty
73
-		command.SysProcAttr.Setctty = true
74
-
75
-		go func() {
76
-			defer pipes.Stdin.Close()
77
-			io.Copy(t.MasterPty, pipes.Stdin)
78
-		}()
79
-	}
80
-	return nil
81
-}
82
-
83
-func (t *TtyConsole) Close() error {
84
-	t.SlavePty.Close()
85
-	return t.MasterPty.Close()
86
-}
87
-
88
-type StdConsole struct {
89
-}
90
-
91
-func NewStdConsole(command *Command, pipes *Pipes) (*StdConsole, error) {
92
-	std := &StdConsole{}
93
-
94
-	if err := std.AttachPipes(&command.Cmd, pipes); err != nil {
95
-		return nil, err
96
-	}
97
-	return std, nil
98
-}
99
-
100
-func (s *StdConsole) AttachPipes(command *exec.Cmd, pipes *Pipes) error {
101
-	command.Stdout = pipes.Stdout
102
-	command.Stderr = pipes.Stderr
103
-
104
-	if pipes.Stdin != nil {
105
-		stdin, err := command.StdinPipe()
106
-		if err != nil {
107
-			return err
108
-		}
109
-
110
-		go func() {
111
-			defer stdin.Close()
112
-			io.Copy(stdin, pipes.Stdin)
113
-		}()
114
-	}
115
-	return nil
116
-}
117
-
118
-func (s *StdConsole) Resize(h, w int) error {
119
-	// we do not need to reside a non tty
120
-	return nil
121
-}
122
-
123
-func (s *StdConsole) Close() error {
124
-	// nothing to close here
125
-	return nil
126
-}
... ...
@@ -6,7 +6,7 @@ import (
6 6
 	"fmt"
7 7
 	"github.com/dotcloud/docker/archive"
8 8
 	"github.com/dotcloud/docker/engine"
9
-	"github.com/dotcloud/docker/execdriver"
9
+	"github.com/dotcloud/docker/runtime/execdriver"
10 10
 	"github.com/dotcloud/docker/graphdriver"
11 11
 	"github.com/dotcloud/docker/image"
12 12
 	"github.com/dotcloud/docker/links"
13 13
new file mode 100644
... ...
@@ -0,0 +1,2 @@
0
+Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
1
+Guillaume J. Charmes <guillaume@docker.com> (@creack)
0 2
new file mode 100644
... ...
@@ -0,0 +1,135 @@
0
+package execdriver
1
+
2
+import (
3
+	"errors"
4
+	"io"
5
+	"os"
6
+	"os/exec"
7
+)
8
+
9
+var (
10
+	ErrNotRunning              = errors.New("Process could not be started")
11
+	ErrWaitTimeoutReached      = errors.New("Wait timeout reached")
12
+	ErrDriverAlreadyRegistered = errors.New("A driver already registered this docker init function")
13
+	ErrDriverNotFound          = errors.New("The requested docker init has not been found")
14
+)
15
+
16
+var dockerInitFcts map[string]InitFunc
17
+
18
+type (
19
+	StartCallback func(*Command)
20
+	InitFunc      func(i *InitArgs) error
21
+)
22
+
23
+func RegisterInitFunc(name string, fct InitFunc) error {
24
+	if dockerInitFcts == nil {
25
+		dockerInitFcts = make(map[string]InitFunc)
26
+	}
27
+	if _, ok := dockerInitFcts[name]; ok {
28
+		return ErrDriverAlreadyRegistered
29
+	}
30
+	dockerInitFcts[name] = fct
31
+	return nil
32
+}
33
+
34
+func GetInitFunc(name string) (InitFunc, error) {
35
+	fct, ok := dockerInitFcts[name]
36
+	if !ok {
37
+		return nil, ErrDriverNotFound
38
+	}
39
+	return fct, nil
40
+}
41
+
42
+// Args provided to the init function for a driver
43
+type InitArgs struct {
44
+	User       string
45
+	Gateway    string
46
+	Ip         string
47
+	WorkDir    string
48
+	Privileged bool
49
+	Env        []string
50
+	Args       []string
51
+	Mtu        int
52
+	Driver     string
53
+	Console    string
54
+	Pipe       int
55
+	Root       string
56
+}
57
+
58
+// Driver specific information based on
59
+// processes registered with the driver
60
+type Info interface {
61
+	IsRunning() bool
62
+}
63
+
64
+// Terminal in an interface for drivers to implement
65
+// if they want to support Close and Resize calls from
66
+// the core
67
+type Terminal interface {
68
+	io.Closer
69
+	Resize(height, width int) error
70
+}
71
+
72
+type TtyTerminal interface {
73
+	Master() *os.File
74
+}
75
+
76
+type Driver interface {
77
+	Run(c *Command, pipes *Pipes, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code
78
+	Kill(c *Command, sig int) error
79
+	Name() string                                 // Driver name
80
+	Info(id string) Info                          // "temporary" hack (until we move state from core to plugins)
81
+	GetPidsForContainer(id string) ([]int, error) // Returns a list of pids for the given container.
82
+}
83
+
84
+// Network settings of the container
85
+type Network struct {
86
+	Gateway     string `json:"gateway"`
87
+	IPAddress   string `json:"ip"`
88
+	Bridge      string `json:"bridge"`
89
+	IPPrefixLen int    `json:"ip_prefix_len"`
90
+	Mtu         int    `json:"mtu"`
91
+}
92
+
93
+type Resources struct {
94
+	Memory     int64 `json:"memory"`
95
+	MemorySwap int64 `json:"memory_swap"`
96
+	CpuShares  int64 `json:"cpu_shares"`
97
+}
98
+
99
+type Mount struct {
100
+	Source      string `json:"source"`
101
+	Destination string `json:"destination"`
102
+	Writable    bool   `json:"writable"`
103
+	Private     bool   `json:"private"`
104
+}
105
+
106
+// Process wrapps an os/exec.Cmd to add more metadata
107
+type Command struct {
108
+	exec.Cmd `json:"-"`
109
+
110
+	ID         string     `json:"id"`
111
+	Privileged bool       `json:"privileged"`
112
+	User       string     `json:"user"`
113
+	Rootfs     string     `json:"rootfs"`   // root fs of the container
114
+	InitPath   string     `json:"initpath"` // dockerinit
115
+	Entrypoint string     `json:"entrypoint"`
116
+	Arguments  []string   `json:"arguments"`
117
+	WorkingDir string     `json:"working_dir"`
118
+	ConfigPath string     `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver
119
+	Tty        bool       `json:"tty"`
120
+	Network    *Network   `json:"network"` // if network is nil then networking is disabled
121
+	Config     []string   `json:"config"`  //  generic values that specific drivers can consume
122
+	Resources  *Resources `json:"resources"`
123
+	Mounts     []Mount    `json:"mounts"`
124
+
125
+	Terminal     Terminal `json:"-"`             // standard or tty terminal
126
+	Console      string   `json:"-"`             // dev/console path
127
+	ContainerPid int      `json:"container_pid"` // the pid for the process inside a container
128
+}
129
+
130
+// Return the pid of the process
131
+// If the process is nil -1 will be returned
132
+func (c *Command) Pid() int {
133
+	return c.ContainerPid
134
+}
0 135
new file mode 100644
... ...
@@ -0,0 +1,23 @@
0
+package execdrivers
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/runtime/execdriver"
5
+	"github.com/dotcloud/docker/runtime/execdriver/lxc"
6
+	"github.com/dotcloud/docker/runtime/execdriver/native"
7
+	"github.com/dotcloud/docker/pkg/sysinfo"
8
+	"path"
9
+)
10
+
11
+func NewDriver(name, root, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) {
12
+	switch name {
13
+	case "lxc":
14
+		// we want to five the lxc driver the full docker root because it needs
15
+		// to access and write config and template files in /var/lib/docker/containers/*
16
+		// to be backwards compatible
17
+		return lxc.NewDriver(root, sysInfo.AppArmor)
18
+	case "native":
19
+		return native.NewDriver(path.Join(root, "execdriver", "native"), initPath)
20
+	}
21
+	return nil, fmt.Errorf("unknown exec driver %s", name)
22
+}
0 23
new file mode 100644
... ...
@@ -0,0 +1,396 @@
0
+package lxc
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/runtime/execdriver"
5
+	"github.com/dotcloud/docker/pkg/cgroups"
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
+
19
+const DriverName = "lxc"
20
+
21
+func init() {
22
+	execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
23
+		if err := setupEnv(args); err != nil {
24
+			return err
25
+		}
26
+
27
+		if err := setupHostname(args); err != nil {
28
+			return err
29
+		}
30
+
31
+		if err := setupNetworking(args); err != nil {
32
+			return err
33
+		}
34
+
35
+		if err := setupCapabilities(args); err != nil {
36
+			return err
37
+		}
38
+
39
+		if err := setupWorkingDirectory(args); err != nil {
40
+			return err
41
+		}
42
+
43
+		if err := changeUser(args); err != nil {
44
+			return err
45
+		}
46
+
47
+		path, err := exec.LookPath(args.Args[0])
48
+		if err != nil {
49
+			log.Printf("Unable to locate %v", args.Args[0])
50
+			os.Exit(127)
51
+		}
52
+		if err := syscall.Exec(path, args.Args, os.Environ()); err != nil {
53
+			return fmt.Errorf("dockerinit unable to execute %s - %s", path, err)
54
+		}
55
+		panic("Unreachable")
56
+	})
57
+}
58
+
59
+type driver struct {
60
+	root       string // root path for the driver to use
61
+	apparmor   bool
62
+	sharedRoot bool
63
+}
64
+
65
+func NewDriver(root string, apparmor bool) (*driver, error) {
66
+	// setup unconfined symlink
67
+	if err := linkLxcStart(root); err != nil {
68
+		return nil, err
69
+	}
70
+	return &driver{
71
+		apparmor:   apparmor,
72
+		root:       root,
73
+		sharedRoot: rootIsShared(),
74
+	}, nil
75
+}
76
+
77
+func (d *driver) Name() string {
78
+	version := d.version()
79
+	return fmt.Sprintf("%s-%s", DriverName, version)
80
+}
81
+
82
+func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
83
+	if err := execdriver.SetTerminal(c, pipes); err != nil {
84
+		return -1, err
85
+	}
86
+	configPath, err := d.generateLXCConfig(c)
87
+	if err != nil {
88
+		return -1, err
89
+	}
90
+	params := []string{
91
+		"lxc-start",
92
+		"-n", c.ID,
93
+		"-f", configPath,
94
+		"--",
95
+		c.InitPath,
96
+		"-driver",
97
+		DriverName,
98
+	}
99
+
100
+	if c.Network != nil {
101
+		params = append(params,
102
+			"-g", c.Network.Gateway,
103
+			"-i", fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
104
+			"-mtu", strconv.Itoa(c.Network.Mtu),
105
+		)
106
+	}
107
+
108
+	if c.User != "" {
109
+		params = append(params, "-u", c.User)
110
+	}
111
+
112
+	if c.Privileged {
113
+		if d.apparmor {
114
+			params[0] = path.Join(d.root, "lxc-start-unconfined")
115
+
116
+		}
117
+		params = append(params, "-privileged")
118
+	}
119
+
120
+	if c.WorkingDir != "" {
121
+		params = append(params, "-w", c.WorkingDir)
122
+	}
123
+
124
+	params = append(params, "--", c.Entrypoint)
125
+	params = append(params, c.Arguments...)
126
+
127
+	if d.sharedRoot {
128
+		// lxc-start really needs / to be non-shared, or all kinds of stuff break
129
+		// when lxc-start unmount things and those unmounts propagate to the main
130
+		// mount namespace.
131
+		// What we really want is to clone into a new namespace and then
132
+		// mount / MS_REC|MS_SLAVE, but since we can't really clone or fork
133
+		// without exec in go we have to do this horrible shell hack...
134
+		shellString :=
135
+			"mount --make-rslave /; exec " +
136
+				utils.ShellQuoteArguments(params)
137
+
138
+		params = []string{
139
+			"unshare", "-m", "--", "/bin/sh", "-c", shellString,
140
+		}
141
+	}
142
+
143
+	var (
144
+		name = params[0]
145
+		arg  = params[1:]
146
+	)
147
+	aname, err := exec.LookPath(name)
148
+	if err != nil {
149
+		aname = name
150
+	}
151
+	c.Path = aname
152
+	c.Args = append([]string{name}, arg...)
153
+
154
+	if err := c.Start(); err != nil {
155
+		return -1, err
156
+	}
157
+
158
+	var (
159
+		waitErr  error
160
+		waitLock = make(chan struct{})
161
+	)
162
+	go func() {
163
+		if err := c.Wait(); err != nil {
164
+			if _, ok := err.(*exec.ExitError); !ok { // Do not propagate the error if it's simply a status code != 0
165
+				waitErr = err
166
+			}
167
+		}
168
+		close(waitLock)
169
+	}()
170
+
171
+	// Poll lxc for RUNNING status
172
+	pid, err := d.waitForStart(c, waitLock)
173
+	if err != nil {
174
+		if c.Process != nil {
175
+			c.Process.Kill()
176
+		}
177
+		return -1, err
178
+	}
179
+	c.ContainerPid = pid
180
+
181
+	if startCallback != nil {
182
+		startCallback(c)
183
+	}
184
+
185
+	<-waitLock
186
+
187
+	return getExitCode(c), waitErr
188
+}
189
+
190
+/// Return the exit code of the process
191
+// if the process has not exited -1 will be returned
192
+func getExitCode(c *execdriver.Command) int {
193
+	if c.ProcessState == nil {
194
+		return -1
195
+	}
196
+	return c.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
197
+}
198
+
199
+func (d *driver) Kill(c *execdriver.Command, sig int) error {
200
+	return KillLxc(c.ID, sig)
201
+}
202
+
203
+func (d *driver) version() string {
204
+	var (
205
+		version string
206
+		output  []byte
207
+		err     error
208
+	)
209
+	if _, errPath := exec.LookPath("lxc-version"); errPath == nil {
210
+		output, err = exec.Command("lxc-version").CombinedOutput()
211
+	} else {
212
+		output, err = exec.Command("lxc-start", "--version").CombinedOutput()
213
+	}
214
+	if err == nil {
215
+		version = strings.TrimSpace(string(output))
216
+		if parts := strings.SplitN(version, ":", 2); len(parts) == 2 {
217
+			version = strings.TrimSpace(parts[1])
218
+		}
219
+	}
220
+	return version
221
+}
222
+
223
+func KillLxc(id string, sig int) error {
224
+	var (
225
+		err    error
226
+		output []byte
227
+	)
228
+	_, err = exec.LookPath("lxc-kill")
229
+	if err == nil {
230
+		output, err = exec.Command("lxc-kill", "-n", id, strconv.Itoa(sig)).CombinedOutput()
231
+	} else {
232
+		output, err = exec.Command("lxc-stop", "-k", "-n", id, strconv.Itoa(sig)).CombinedOutput()
233
+	}
234
+	if err != nil {
235
+		return fmt.Errorf("Err: %s Output: %s", err, output)
236
+	}
237
+	return nil
238
+}
239
+
240
+// wait for the process to start and return the pid for the process
241
+func (d *driver) waitForStart(c *execdriver.Command, waitLock chan struct{}) (int, error) {
242
+	var (
243
+		err    error
244
+		output []byte
245
+	)
246
+	// We wait for the container to be fully running.
247
+	// Timeout after 5 seconds. In case of broken pipe, just retry.
248
+	// Note: The container can run and finish correctly before
249
+	// the end of this loop
250
+	for now := time.Now(); time.Since(now) < 5*time.Second; {
251
+		select {
252
+		case <-waitLock:
253
+			// If the process dies while waiting for it, just return
254
+			return -1, nil
255
+		default:
256
+		}
257
+
258
+		output, err = d.getInfo(c.ID)
259
+		if err != nil {
260
+			output, err = d.getInfo(c.ID)
261
+			if err != nil {
262
+				return -1, err
263
+			}
264
+		}
265
+		info, err := parseLxcInfo(string(output))
266
+		if err != nil {
267
+			return -1, err
268
+		}
269
+		if info.Running {
270
+			return info.Pid, nil
271
+		}
272
+		time.Sleep(50 * time.Millisecond)
273
+	}
274
+	return -1, execdriver.ErrNotRunning
275
+}
276
+
277
+func (d *driver) getInfo(id string) ([]byte, error) {
278
+	return exec.Command("lxc-info", "-n", id).CombinedOutput()
279
+}
280
+
281
+type info struct {
282
+	ID     string
283
+	driver *driver
284
+}
285
+
286
+func (i *info) IsRunning() bool {
287
+	var running bool
288
+
289
+	output, err := i.driver.getInfo(i.ID)
290
+	if err != nil {
291
+		utils.Errorf("Error getting info for lxc container %s: %s (%s)", i.ID, err, output)
292
+		return false
293
+	}
294
+	if strings.Contains(string(output), "RUNNING") {
295
+		running = true
296
+	}
297
+	return running
298
+}
299
+
300
+func (d *driver) Info(id string) execdriver.Info {
301
+	return &info{
302
+		ID:     id,
303
+		driver: d,
304
+	}
305
+}
306
+
307
+func (d *driver) GetPidsForContainer(id string) ([]int, error) {
308
+	pids := []int{}
309
+
310
+	// cpu is chosen because it is the only non optional subsystem in cgroups
311
+	subsystem := "cpu"
312
+	cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem)
313
+	if err != nil {
314
+		return pids, err
315
+	}
316
+
317
+	cgroupDir, err := cgroups.GetThisCgroupDir(subsystem)
318
+	if err != nil {
319
+		return pids, err
320
+	}
321
+
322
+	filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks")
323
+	if _, err := os.Stat(filename); os.IsNotExist(err) {
324
+		// With more recent lxc versions use, cgroup will be in lxc/
325
+		filename = filepath.Join(cgroupRoot, cgroupDir, "lxc", id, "tasks")
326
+	}
327
+
328
+	output, err := ioutil.ReadFile(filename)
329
+	if err != nil {
330
+		return pids, err
331
+	}
332
+	for _, p := range strings.Split(string(output), "\n") {
333
+		if len(p) == 0 {
334
+			continue
335
+		}
336
+		pid, err := strconv.Atoi(p)
337
+		if err != nil {
338
+			return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
339
+		}
340
+		pids = append(pids, pid)
341
+	}
342
+	return pids, nil
343
+}
344
+
345
+func linkLxcStart(root string) error {
346
+	sourcePath, err := exec.LookPath("lxc-start")
347
+	if err != nil {
348
+		return err
349
+	}
350
+	targetPath := path.Join(root, "lxc-start-unconfined")
351
+
352
+	if _, err := os.Lstat(targetPath); err != nil && !os.IsNotExist(err) {
353
+		return err
354
+	} else if err == nil {
355
+		if err := os.Remove(targetPath); err != nil {
356
+			return err
357
+		}
358
+	}
359
+	return os.Symlink(sourcePath, targetPath)
360
+}
361
+
362
+// TODO: This can be moved to the mountinfo reader in the mount pkg
363
+func rootIsShared() bool {
364
+	if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil {
365
+		for _, line := range strings.Split(string(data), "\n") {
366
+			cols := strings.Split(line, " ")
367
+			if len(cols) >= 6 && cols[4] == "/" {
368
+				return strings.HasPrefix(cols[6], "shared")
369
+			}
370
+		}
371
+	}
372
+
373
+	// No idea, probably safe to assume so
374
+	return true
375
+}
376
+
377
+func (d *driver) generateLXCConfig(c *execdriver.Command) (string, error) {
378
+	root := path.Join(d.root, "containers", c.ID, "config.lxc")
379
+	fo, err := os.Create(root)
380
+	if err != nil {
381
+		return "", err
382
+	}
383
+	defer fo.Close()
384
+
385
+	if err := LxcTemplateCompiled.Execute(fo, struct {
386
+		*execdriver.Command
387
+		AppArmor bool
388
+	}{
389
+		Command:  c,
390
+		AppArmor: d.apparmor,
391
+	}); err != nil {
392
+		return "", err
393
+	}
394
+	return root, nil
395
+}
0 396
new file mode 100644
... ...
@@ -0,0 +1,50 @@
0
+package lxc
1
+
2
+import (
3
+	"bufio"
4
+	"errors"
5
+	"strconv"
6
+	"strings"
7
+)
8
+
9
+var (
10
+	ErrCannotParse = errors.New("cannot parse raw input")
11
+)
12
+
13
+type lxcInfo struct {
14
+	Running bool
15
+	Pid     int
16
+}
17
+
18
+func parseLxcInfo(raw string) (*lxcInfo, error) {
19
+	if raw == "" {
20
+		return nil, ErrCannotParse
21
+	}
22
+	var (
23
+		err  error
24
+		s    = bufio.NewScanner(strings.NewReader(raw))
25
+		info = &lxcInfo{}
26
+	)
27
+	for s.Scan() {
28
+		text := s.Text()
29
+
30
+		if s.Err() != nil {
31
+			return nil, s.Err()
32
+		}
33
+
34
+		parts := strings.Split(text, ":")
35
+		if len(parts) < 2 {
36
+			continue
37
+		}
38
+		switch strings.ToLower(strings.TrimSpace(parts[0])) {
39
+		case "state":
40
+			info.Running = strings.TrimSpace(parts[1]) == "RUNNING"
41
+		case "pid":
42
+			info.Pid, err = strconv.Atoi(strings.TrimSpace(parts[1]))
43
+			if err != nil {
44
+				return nil, err
45
+			}
46
+		}
47
+	}
48
+	return info, nil
49
+}
0 50
new file mode 100644
... ...
@@ -0,0 +1,36 @@
0
+package lxc
1
+
2
+import (
3
+	"testing"
4
+)
5
+
6
+func TestParseRunningInfo(t *testing.T) {
7
+	raw := `
8
+    state: RUNNING
9
+    pid:    50`
10
+
11
+	info, err := parseLxcInfo(raw)
12
+	if err != nil {
13
+		t.Fatal(err)
14
+	}
15
+	if !info.Running {
16
+		t.Fatal("info should return a running state")
17
+	}
18
+	if info.Pid != 50 {
19
+		t.Fatalf("info should have pid 50 got %d", info.Pid)
20
+	}
21
+}
22
+
23
+func TestEmptyInfo(t *testing.T) {
24
+	_, err := parseLxcInfo("")
25
+	if err == nil {
26
+		t.Fatal("error should not be nil")
27
+	}
28
+}
29
+
30
+func TestBadInfo(t *testing.T) {
31
+	_, err := parseLxcInfo("state")
32
+	if err != nil {
33
+		t.Fatal(err)
34
+	}
35
+}
0 36
new file mode 100644
... ...
@@ -0,0 +1,176 @@
0
+package lxc
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"github.com/dotcloud/docker/runtime/execdriver"
6
+	"github.com/dotcloud/docker/pkg/netlink"
7
+	"github.com/dotcloud/docker/pkg/user"
8
+	"github.com/syndtr/gocapability/capability"
9
+	"io/ioutil"
10
+	"net"
11
+	"os"
12
+	"strings"
13
+	"syscall"
14
+)
15
+
16
+// Clear environment pollution introduced by lxc-start
17
+func setupEnv(args *execdriver.InitArgs) error {
18
+	// Get env
19
+	var env []string
20
+	content, err := ioutil.ReadFile(".dockerenv")
21
+	if err != nil {
22
+		return fmt.Errorf("Unable to load environment variables: %v", err)
23
+	}
24
+	if err := json.Unmarshal(content, &env); err != nil {
25
+		return fmt.Errorf("Unable to unmarshal environment variables: %v", err)
26
+	}
27
+	// Propagate the plugin-specific container env variable
28
+	env = append(env, "container="+os.Getenv("container"))
29
+
30
+	args.Env = env
31
+
32
+	os.Clearenv()
33
+	for _, kv := range args.Env {
34
+		parts := strings.SplitN(kv, "=", 2)
35
+		if len(parts) == 1 {
36
+			parts = append(parts, "")
37
+		}
38
+		os.Setenv(parts[0], parts[1])
39
+	}
40
+
41
+	return nil
42
+}
43
+
44
+func setupHostname(args *execdriver.InitArgs) error {
45
+	hostname := getEnv(args, "HOSTNAME")
46
+	if hostname == "" {
47
+		return nil
48
+	}
49
+	return setHostname(hostname)
50
+}
51
+
52
+// Setup networking
53
+func setupNetworking(args *execdriver.InitArgs) error {
54
+	if args.Ip != "" {
55
+		// eth0
56
+		iface, err := net.InterfaceByName("eth0")
57
+		if err != nil {
58
+			return fmt.Errorf("Unable to set up networking: %v", err)
59
+		}
60
+		ip, ipNet, err := net.ParseCIDR(args.Ip)
61
+		if err != nil {
62
+			return fmt.Errorf("Unable to set up networking: %v", err)
63
+		}
64
+		if err := netlink.NetworkLinkAddIp(iface, ip, ipNet); err != nil {
65
+			return fmt.Errorf("Unable to set up networking: %v", err)
66
+		}
67
+		if err := netlink.NetworkSetMTU(iface, args.Mtu); err != nil {
68
+			return fmt.Errorf("Unable to set MTU: %v", err)
69
+		}
70
+		if err := netlink.NetworkLinkUp(iface); err != nil {
71
+			return fmt.Errorf("Unable to set up networking: %v", err)
72
+		}
73
+
74
+		// loopback
75
+		iface, err = net.InterfaceByName("lo")
76
+		if err != nil {
77
+			return fmt.Errorf("Unable to set up networking: %v", err)
78
+		}
79
+		if err := netlink.NetworkLinkUp(iface); err != nil {
80
+			return fmt.Errorf("Unable to set up networking: %v", err)
81
+		}
82
+	}
83
+	if args.Gateway != "" {
84
+		gw := net.ParseIP(args.Gateway)
85
+		if gw == nil {
86
+			return fmt.Errorf("Unable to set up networking, %s is not a valid gateway IP", args.Gateway)
87
+		}
88
+
89
+		if err := netlink.AddDefaultGw(gw); err != nil {
90
+			return fmt.Errorf("Unable to set up networking: %v", err)
91
+		}
92
+	}
93
+
94
+	return nil
95
+}
96
+
97
+// Setup working directory
98
+func setupWorkingDirectory(args *execdriver.InitArgs) error {
99
+	if args.WorkDir == "" {
100
+		return nil
101
+	}
102
+	if err := syscall.Chdir(args.WorkDir); err != nil {
103
+		return fmt.Errorf("Unable to change dir to %v: %v", args.WorkDir, err)
104
+	}
105
+	return nil
106
+}
107
+
108
+// Takes care of dropping privileges to the desired user
109
+func changeUser(args *execdriver.InitArgs) error {
110
+	uid, gid, suppGids, err := user.GetUserGroupSupplementary(
111
+		args.User,
112
+		syscall.Getuid(), syscall.Getgid(),
113
+	)
114
+	if err != nil {
115
+		return err
116
+	}
117
+
118
+	if err := syscall.Setgroups(suppGids); err != nil {
119
+		return fmt.Errorf("Setgroups failed: %v", err)
120
+	}
121
+	if err := syscall.Setgid(gid); err != nil {
122
+		return fmt.Errorf("Setgid failed: %v", err)
123
+	}
124
+	if err := syscall.Setuid(uid); err != nil {
125
+		return fmt.Errorf("Setuid failed: %v", err)
126
+	}
127
+
128
+	return nil
129
+}
130
+
131
+func setupCapabilities(args *execdriver.InitArgs) error {
132
+	if args.Privileged {
133
+		return nil
134
+	}
135
+
136
+	drop := []capability.Cap{
137
+		capability.CAP_SETPCAP,
138
+		capability.CAP_SYS_MODULE,
139
+		capability.CAP_SYS_RAWIO,
140
+		capability.CAP_SYS_PACCT,
141
+		capability.CAP_SYS_ADMIN,
142
+		capability.CAP_SYS_NICE,
143
+		capability.CAP_SYS_RESOURCE,
144
+		capability.CAP_SYS_TIME,
145
+		capability.CAP_SYS_TTY_CONFIG,
146
+		capability.CAP_MKNOD,
147
+		capability.CAP_AUDIT_WRITE,
148
+		capability.CAP_AUDIT_CONTROL,
149
+		capability.CAP_MAC_OVERRIDE,
150
+		capability.CAP_MAC_ADMIN,
151
+		capability.CAP_NET_ADMIN,
152
+	}
153
+
154
+	c, err := capability.NewPid(os.Getpid())
155
+	if err != nil {
156
+		return err
157
+	}
158
+
159
+	c.Unset(capability.CAPS|capability.BOUNDS, drop...)
160
+
161
+	if err := c.Apply(capability.CAPS | capability.BOUNDS); err != nil {
162
+		return err
163
+	}
164
+	return nil
165
+}
166
+
167
+func getEnv(args *execdriver.InitArgs, key string) string {
168
+	for _, kv := range args.Env {
169
+		parts := strings.SplitN(kv, "=", 2)
170
+		if parts[0] == key && len(parts) == 2 {
171
+			return parts[1]
172
+		}
173
+	}
174
+	return ""
175
+}
0 176
new file mode 100644
... ...
@@ -0,0 +1,11 @@
0
+// +build amd64
1
+
2
+package lxc
3
+
4
+import (
5
+	"syscall"
6
+)
7
+
8
+func setHostname(hostname string) error {
9
+	return syscall.Sethostname([]byte(hostname))
10
+}
0 11
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+// +build !linux !amd64
1
+
2
+package lxc
3
+
4
+func setHostname(hostname string) error {
5
+	panic("Not supported on darwin")
6
+}
0 7
new file mode 100644
... ...
@@ -0,0 +1,155 @@
0
+package lxc
1
+
2
+import (
3
+	"github.com/dotcloud/docker/runtime/execdriver"
4
+	"strings"
5
+	"text/template"
6
+)
7
+
8
+const LxcTemplate = `
9
+{{if .Network}}
10
+# network configuration
11
+lxc.network.type = veth
12
+lxc.network.link = {{.Network.Bridge}}
13
+lxc.network.name = eth0
14
+lxc.network.mtu = {{.Network.Mtu}}
15
+{{else}}
16
+# network is disabled (-n=false)
17
+lxc.network.type = empty
18
+lxc.network.flags = up
19
+{{end}}
20
+
21
+# root filesystem
22
+{{$ROOTFS := .Rootfs}}
23
+lxc.rootfs = {{$ROOTFS}}
24
+
25
+# use a dedicated pts for the container (and limit the number of pseudo terminal
26
+# available)
27
+lxc.pts = 1024
28
+
29
+# disable the main console
30
+lxc.console = none
31
+
32
+# no controlling tty at all
33
+lxc.tty = 1
34
+
35
+{{if .Privileged}}
36
+lxc.cgroup.devices.allow = a
37
+{{else}}
38
+# no implicit access to devices
39
+lxc.cgroup.devices.deny = a
40
+
41
+# /dev/null and zero
42
+lxc.cgroup.devices.allow = c 1:3 rwm
43
+lxc.cgroup.devices.allow = c 1:5 rwm
44
+
45
+# consoles
46
+lxc.cgroup.devices.allow = c 5:1 rwm
47
+lxc.cgroup.devices.allow = c 5:0 rwm
48
+lxc.cgroup.devices.allow = c 4:0 rwm
49
+lxc.cgroup.devices.allow = c 4:1 rwm
50
+
51
+# /dev/urandom,/dev/random
52
+lxc.cgroup.devices.allow = c 1:9 rwm
53
+lxc.cgroup.devices.allow = c 1:8 rwm
54
+
55
+# /dev/pts/ - pts namespaces are "coming soon"
56
+lxc.cgroup.devices.allow = c 136:* rwm
57
+lxc.cgroup.devices.allow = c 5:2 rwm
58
+
59
+# tuntap
60
+lxc.cgroup.devices.allow = c 10:200 rwm
61
+
62
+# fuse
63
+#lxc.cgroup.devices.allow = c 10:229 rwm
64
+
65
+# rtc
66
+#lxc.cgroup.devices.allow = c 254:0 rwm
67
+{{end}}
68
+
69
+# standard mount point
70
+# Use mnt.putold as per https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/986385
71
+lxc.pivotdir = lxc_putold
72
+
73
+# NOTICE: These mounts must be applied within the namespace
74
+
75
+#  WARNING: procfs is a known attack vector and should probably be disabled
76
+#           if your userspace allows it. eg. see http://blog.zx2c4.com/749
77
+lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
78
+
79
+# WARNING: sysfs is a known attack vector and should probably be disabled
80
+# if your userspace allows it. eg. see http://bit.ly/T9CkqJ
81
+lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
82
+
83
+{{if .Tty}}
84
+lxc.mount.entry = {{.Console}} {{escapeFstabSpaces $ROOTFS}}/dev/console none bind,rw 0 0
85
+{{end}}
86
+
87
+lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0
88
+lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0
89
+
90
+{{range $value := .Mounts}}
91
+{{if $value.Writable}}
92
+lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $value.Destination}} none bind,rw 0 0
93
+{{else}}
94
+lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $value.Destination}} none bind,ro 0 0
95
+{{end}}
96
+{{end}}
97
+
98
+{{if .Privileged}}
99
+{{if .AppArmor}}
100
+lxc.aa_profile = unconfined
101
+{{else}}
102
+#lxc.aa_profile = unconfined
103
+{{end}}
104
+{{end}}
105
+
106
+# limits
107
+{{if .Resources}}
108
+{{if .Resources.Memory}}
109
+lxc.cgroup.memory.limit_in_bytes = {{.Resources.Memory}}
110
+lxc.cgroup.memory.soft_limit_in_bytes = {{.Resources.Memory}}
111
+{{with $memSwap := getMemorySwap .Resources}}
112
+lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}}
113
+{{end}}
114
+{{end}}
115
+{{if .Resources.CpuShares}}
116
+lxc.cgroup.cpu.shares = {{.Resources.CpuShares}}
117
+{{end}}
118
+{{end}}
119
+
120
+{{if .Config}}
121
+{{range $value := .Config}}
122
+{{$value}}
123
+{{end}}
124
+{{end}}
125
+`
126
+
127
+var LxcTemplateCompiled *template.Template
128
+
129
+// Escape spaces in strings according to the fstab documentation, which is the
130
+// format for "lxc.mount.entry" lines in lxc.conf. See also "man 5 fstab".
131
+func escapeFstabSpaces(field string) string {
132
+	return strings.Replace(field, " ", "\\040", -1)
133
+}
134
+
135
+func getMemorySwap(v *execdriver.Resources) int64 {
136
+	// By default, MemorySwap is set to twice the size of RAM.
137
+	// If you want to omit MemorySwap, set it to `-1'.
138
+	if v.MemorySwap < 0 {
139
+		return 0
140
+	}
141
+	return v.Memory * 2
142
+}
143
+
144
+func init() {
145
+	var err error
146
+	funcMap := template.FuncMap{
147
+		"getMemorySwap":     getMemorySwap,
148
+		"escapeFstabSpaces": escapeFstabSpaces,
149
+	}
150
+	LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate)
151
+	if err != nil {
152
+		panic(err)
153
+	}
154
+}
0 155
new file mode 100644
... ...
@@ -0,0 +1,125 @@
0
+package lxc
1
+
2
+import (
3
+	"bufio"
4
+	"fmt"
5
+	"github.com/dotcloud/docker/runtime/execdriver"
6
+	"io/ioutil"
7
+	"math/rand"
8
+	"os"
9
+	"path"
10
+	"strings"
11
+	"testing"
12
+	"time"
13
+)
14
+
15
+func TestLXCConfig(t *testing.T) {
16
+	root, err := ioutil.TempDir("", "TestLXCConfig")
17
+	if err != nil {
18
+		t.Fatal(err)
19
+	}
20
+	defer os.RemoveAll(root)
21
+
22
+	os.MkdirAll(path.Join(root, "containers", "1"), 0777)
23
+
24
+	// Memory is allocated randomly for testing
25
+	rand.Seed(time.Now().UTC().UnixNano())
26
+	var (
27
+		memMin = 33554432
28
+		memMax = 536870912
29
+		mem    = memMin + rand.Intn(memMax-memMin)
30
+		cpuMin = 100
31
+		cpuMax = 10000
32
+		cpu    = cpuMin + rand.Intn(cpuMax-cpuMin)
33
+	)
34
+
35
+	driver, err := NewDriver(root, false)
36
+	if err != nil {
37
+		t.Fatal(err)
38
+	}
39
+	command := &execdriver.Command{
40
+		ID: "1",
41
+		Resources: &execdriver.Resources{
42
+			Memory:    int64(mem),
43
+			CpuShares: int64(cpu),
44
+		},
45
+	}
46
+	p, err := driver.generateLXCConfig(command)
47
+	if err != nil {
48
+		t.Fatal(err)
49
+	}
50
+	grepFile(t, p,
51
+		fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
52
+
53
+	grepFile(t, p,
54
+		fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2))
55
+}
56
+
57
+func TestCustomLxcConfig(t *testing.T) {
58
+	root, err := ioutil.TempDir("", "TestCustomLxcConfig")
59
+	if err != nil {
60
+		t.Fatal(err)
61
+	}
62
+	defer os.RemoveAll(root)
63
+
64
+	os.MkdirAll(path.Join(root, "containers", "1"), 0777)
65
+
66
+	driver, err := NewDriver(root, false)
67
+	if err != nil {
68
+		t.Fatal(err)
69
+	}
70
+	command := &execdriver.Command{
71
+		ID:         "1",
72
+		Privileged: false,
73
+		Config: []string{
74
+			"lxc.utsname = docker",
75
+			"lxc.cgroup.cpuset.cpus = 0,1",
76
+		},
77
+	}
78
+
79
+	p, err := driver.generateLXCConfig(command)
80
+	if err != nil {
81
+		t.Fatal(err)
82
+	}
83
+
84
+	grepFile(t, p, "lxc.utsname = docker")
85
+	grepFile(t, p, "lxc.cgroup.cpuset.cpus = 0,1")
86
+}
87
+
88
+func grepFile(t *testing.T, path string, pattern string) {
89
+	f, err := os.Open(path)
90
+	if err != nil {
91
+		t.Fatal(err)
92
+	}
93
+	defer f.Close()
94
+	r := bufio.NewReader(f)
95
+	var (
96
+		line string
97
+	)
98
+	err = nil
99
+	for err == nil {
100
+		line, err = r.ReadString('\n')
101
+		if strings.Contains(line, pattern) == true {
102
+			return
103
+		}
104
+	}
105
+	t.Fatalf("grepFile: pattern \"%s\" not found in \"%s\"", pattern, path)
106
+}
107
+
108
+func TestEscapeFstabSpaces(t *testing.T) {
109
+	var testInputs = map[string]string{
110
+		" ":                      "\\040",
111
+		"":                       "",
112
+		"/double  space":         "/double\\040\\040space",
113
+		"/some long test string": "/some\\040long\\040test\\040string",
114
+		"/var/lib/docker":        "/var/lib/docker",
115
+		" leading":               "\\040leading",
116
+		"trailing ":              "trailing\\040",
117
+	}
118
+	for in, exp := range testInputs {
119
+		if out := escapeFstabSpaces(in); exp != out {
120
+			t.Logf("Expected %s got %s", exp, out)
121
+			t.Fail()
122
+		}
123
+	}
124
+}
0 125
new file mode 100644
... ...
@@ -0,0 +1,94 @@
0
+package native
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/runtime/execdriver"
5
+	"github.com/dotcloud/docker/pkg/cgroups"
6
+	"github.com/dotcloud/docker/pkg/libcontainer"
7
+	"os"
8
+)
9
+
10
+// createContainer populates and configures the container type with the
11
+// data provided by the execdriver.Command
12
+func createContainer(c *execdriver.Command) *libcontainer.Container {
13
+	container := getDefaultTemplate()
14
+
15
+	container.Hostname = getEnv("HOSTNAME", c.Env)
16
+	container.Tty = c.Tty
17
+	container.User = c.User
18
+	container.WorkingDir = c.WorkingDir
19
+	container.Env = c.Env
20
+
21
+	if c.Network != nil {
22
+		container.Networks = []*libcontainer.Network{
23
+			{
24
+				Mtu:     c.Network.Mtu,
25
+				Address: fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
26
+				Gateway: c.Network.Gateway,
27
+				Type:    "veth",
28
+				Context: libcontainer.Context{
29
+					"prefix": "veth",
30
+					"bridge": c.Network.Bridge,
31
+				},
32
+			},
33
+		}
34
+	}
35
+
36
+	container.Cgroups.Name = c.ID
37
+	if c.Privileged {
38
+		container.CapabilitiesMask = nil
39
+		container.Cgroups.DeviceAccess = true
40
+		container.Context["apparmor_profile"] = "unconfined"
41
+	}
42
+	if c.Resources != nil {
43
+		container.Cgroups.CpuShares = c.Resources.CpuShares
44
+		container.Cgroups.Memory = c.Resources.Memory
45
+		container.Cgroups.MemorySwap = c.Resources.MemorySwap
46
+	}
47
+	// check to see if we are running in ramdisk to disable pivot root
48
+	container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
49
+
50
+	for _, m := range c.Mounts {
51
+		container.Mounts = append(container.Mounts, libcontainer.Mount{m.Source, m.Destination, m.Writable, m.Private})
52
+	}
53
+
54
+	return container
55
+}
56
+
57
+// getDefaultTemplate returns the docker default for
58
+// the libcontainer configuration file
59
+func getDefaultTemplate() *libcontainer.Container {
60
+	return &libcontainer.Container{
61
+		CapabilitiesMask: libcontainer.Capabilities{
62
+			libcontainer.GetCapability("SETPCAP"),
63
+			libcontainer.GetCapability("SYS_MODULE"),
64
+			libcontainer.GetCapability("SYS_RAWIO"),
65
+			libcontainer.GetCapability("SYS_PACCT"),
66
+			libcontainer.GetCapability("SYS_ADMIN"),
67
+			libcontainer.GetCapability("SYS_NICE"),
68
+			libcontainer.GetCapability("SYS_RESOURCE"),
69
+			libcontainer.GetCapability("SYS_TIME"),
70
+			libcontainer.GetCapability("SYS_TTY_CONFIG"),
71
+			libcontainer.GetCapability("MKNOD"),
72
+			libcontainer.GetCapability("AUDIT_WRITE"),
73
+			libcontainer.GetCapability("AUDIT_CONTROL"),
74
+			libcontainer.GetCapability("MAC_OVERRIDE"),
75
+			libcontainer.GetCapability("MAC_ADMIN"),
76
+			libcontainer.GetCapability("NET_ADMIN"),
77
+		},
78
+		Namespaces: libcontainer.Namespaces{
79
+			libcontainer.GetNamespace("NEWNS"),
80
+			libcontainer.GetNamespace("NEWUTS"),
81
+			libcontainer.GetNamespace("NEWIPC"),
82
+			libcontainer.GetNamespace("NEWPID"),
83
+			libcontainer.GetNamespace("NEWNET"),
84
+		},
85
+		Cgroups: &cgroups.Cgroup{
86
+			Parent:       "docker",
87
+			DeviceAccess: false,
88
+		},
89
+		Context: libcontainer.Context{
90
+			"apparmor_profile": "docker-default",
91
+		},
92
+	}
93
+}
0 94
new file mode 100644
... ...
@@ -0,0 +1,266 @@
0
+package native
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"github.com/dotcloud/docker/runtime/execdriver"
6
+	"github.com/dotcloud/docker/pkg/cgroups"
7
+	"github.com/dotcloud/docker/pkg/libcontainer"
8
+	"github.com/dotcloud/docker/pkg/libcontainer/apparmor"
9
+	"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
10
+	"github.com/dotcloud/docker/pkg/system"
11
+	"io"
12
+	"io/ioutil"
13
+	"log"
14
+	"os"
15
+	"os/exec"
16
+	"path/filepath"
17
+	"strconv"
18
+	"strings"
19
+	"syscall"
20
+)
21
+
22
+const (
23
+	DriverName = "native"
24
+	Version    = "0.1"
25
+)
26
+
27
+func init() {
28
+	execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
29
+		var (
30
+			container *libcontainer.Container
31
+			ns        = nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{args.Root}, createLogger(""))
32
+		)
33
+		f, err := os.Open(filepath.Join(args.Root, "container.json"))
34
+		if err != nil {
35
+			return err
36
+		}
37
+		if err := json.NewDecoder(f).Decode(&container); err != nil {
38
+			f.Close()
39
+			return err
40
+		}
41
+		f.Close()
42
+
43
+		cwd, err := os.Getwd()
44
+		if err != nil {
45
+			return err
46
+		}
47
+		syncPipe, err := nsinit.NewSyncPipeFromFd(0, uintptr(args.Pipe))
48
+		if err != nil {
49
+			return err
50
+		}
51
+		if err := ns.Init(container, cwd, args.Console, syncPipe, args.Args); err != nil {
52
+			return err
53
+		}
54
+		return nil
55
+	})
56
+}
57
+
58
+type driver struct {
59
+	root     string
60
+	initPath string
61
+}
62
+
63
+func NewDriver(root, initPath string) (*driver, error) {
64
+	if err := os.MkdirAll(root, 0700); err != nil {
65
+		return nil, err
66
+	}
67
+	if err := apparmor.InstallDefaultProfile(); err != nil {
68
+		return nil, err
69
+	}
70
+	return &driver{
71
+		root:     root,
72
+		initPath: initPath,
73
+	}, nil
74
+}
75
+
76
+func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
77
+	if err := d.validateCommand(c); err != nil {
78
+		return -1, err
79
+	}
80
+	var (
81
+		term        nsinit.Terminal
82
+		container   = createContainer(c)
83
+		factory     = &dockerCommandFactory{c: c, driver: d}
84
+		stateWriter = &dockerStateWriter{
85
+			callback: startCallback,
86
+			c:        c,
87
+			dsw:      &nsinit.DefaultStateWriter{filepath.Join(d.root, c.ID)},
88
+		}
89
+		ns   = nsinit.NewNsInit(factory, stateWriter, createLogger(os.Getenv("DEBUG")))
90
+		args = append([]string{c.Entrypoint}, c.Arguments...)
91
+	)
92
+	if err := d.createContainerRoot(c.ID); err != nil {
93
+		return -1, err
94
+	}
95
+	defer d.removeContainerRoot(c.ID)
96
+
97
+	if c.Tty {
98
+		term = &dockerTtyTerm{
99
+			pipes: pipes,
100
+		}
101
+	} else {
102
+		term = &dockerStdTerm{
103
+			pipes: pipes,
104
+		}
105
+	}
106
+	c.Terminal = term
107
+	if err := d.writeContainerFile(container, c.ID); err != nil {
108
+		return -1, err
109
+	}
110
+	return ns.Exec(container, term, args)
111
+}
112
+
113
+func (d *driver) Kill(p *execdriver.Command, sig int) error {
114
+	err := syscall.Kill(p.Process.Pid, syscall.Signal(sig))
115
+	d.removeContainerRoot(p.ID)
116
+	return err
117
+}
118
+
119
+func (d *driver) Info(id string) execdriver.Info {
120
+	return &info{
121
+		ID:     id,
122
+		driver: d,
123
+	}
124
+}
125
+
126
+func (d *driver) Name() string {
127
+	return fmt.Sprintf("%s-%s", DriverName, Version)
128
+}
129
+
130
+// TODO: this can be improved with our driver
131
+// there has to be a better way to do this
132
+func (d *driver) GetPidsForContainer(id string) ([]int, error) {
133
+	pids := []int{}
134
+
135
+	subsystem := "devices"
136
+	cgroupRoot, err := cgroups.FindCgroupMountpoint(subsystem)
137
+	if err != nil {
138
+		return pids, err
139
+	}
140
+	cgroupDir, err := cgroups.GetThisCgroupDir(subsystem)
141
+	if err != nil {
142
+		return pids, err
143
+	}
144
+
145
+	filename := filepath.Join(cgroupRoot, cgroupDir, id, "tasks")
146
+	if _, err := os.Stat(filename); os.IsNotExist(err) {
147
+		filename = filepath.Join(cgroupRoot, cgroupDir, "docker", id, "tasks")
148
+	}
149
+
150
+	output, err := ioutil.ReadFile(filename)
151
+	if err != nil {
152
+		return pids, err
153
+	}
154
+	for _, p := range strings.Split(string(output), "\n") {
155
+		if len(p) == 0 {
156
+			continue
157
+		}
158
+		pid, err := strconv.Atoi(p)
159
+		if err != nil {
160
+			return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
161
+		}
162
+		pids = append(pids, pid)
163
+	}
164
+	return pids, nil
165
+}
166
+
167
+func (d *driver) writeContainerFile(container *libcontainer.Container, id string) error {
168
+	data, err := json.Marshal(container)
169
+	if err != nil {
170
+		return err
171
+	}
172
+	return ioutil.WriteFile(filepath.Join(d.root, id, "container.json"), data, 0655)
173
+}
174
+
175
+func (d *driver) createContainerRoot(id string) error {
176
+	return os.MkdirAll(filepath.Join(d.root, id), 0655)
177
+}
178
+
179
+func (d *driver) removeContainerRoot(id string) error {
180
+	return os.RemoveAll(filepath.Join(d.root, id))
181
+}
182
+
183
+func (d *driver) validateCommand(c *execdriver.Command) error {
184
+	// we need to check the Config of the command to make sure that we
185
+	// do not have any of the lxc-conf variables
186
+	for _, conf := range c.Config {
187
+		if strings.Contains(conf, "lxc") {
188
+			return fmt.Errorf("%s is not supported by the native driver", conf)
189
+		}
190
+	}
191
+	return nil
192
+}
193
+
194
+func getEnv(key string, env []string) string {
195
+	for _, pair := range env {
196
+		parts := strings.Split(pair, "=")
197
+		if parts[0] == key {
198
+			return parts[1]
199
+		}
200
+	}
201
+	return ""
202
+}
203
+
204
+type dockerCommandFactory struct {
205
+	c      *execdriver.Command
206
+	driver *driver
207
+}
208
+
209
+// createCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
210
+// defined on the container's configuration and use the current binary as the init with the
211
+// args provided
212
+func (d *dockerCommandFactory) Create(container *libcontainer.Container, console string, syncFile *os.File, args []string) *exec.Cmd {
213
+	// we need to join the rootfs because nsinit will setup the rootfs and chroot
214
+	initPath := filepath.Join(d.c.Rootfs, d.c.InitPath)
215
+
216
+	d.c.Path = d.driver.initPath
217
+	d.c.Args = append([]string{
218
+		initPath,
219
+		"-driver", DriverName,
220
+		"-console", console,
221
+		"-pipe", "3",
222
+		"-root", filepath.Join(d.driver.root, d.c.ID),
223
+		"--",
224
+	}, args...)
225
+
226
+	// set this to nil so that when we set the clone flags anything else is reset
227
+	d.c.SysProcAttr = nil
228
+	system.SetCloneFlags(&d.c.Cmd, uintptr(nsinit.GetNamespaceFlags(container.Namespaces)))
229
+	d.c.ExtraFiles = []*os.File{syncFile}
230
+
231
+	d.c.Env = container.Env
232
+	d.c.Dir = d.c.Rootfs
233
+
234
+	return &d.c.Cmd
235
+}
236
+
237
+type dockerStateWriter struct {
238
+	dsw      nsinit.StateWriter
239
+	c        *execdriver.Command
240
+	callback execdriver.StartCallback
241
+}
242
+
243
+func (d *dockerStateWriter) WritePid(pid int) error {
244
+	d.c.ContainerPid = pid
245
+	err := d.dsw.WritePid(pid)
246
+	if d.callback != nil {
247
+		d.callback(d.c)
248
+	}
249
+	return err
250
+}
251
+
252
+func (d *dockerStateWriter) DeletePid() error {
253
+	return d.dsw.DeletePid()
254
+}
255
+
256
+func createLogger(debug string) *log.Logger {
257
+	var w io.Writer
258
+	// if we are in debug mode set the logger to stderr
259
+	if debug != "" {
260
+		w = os.Stderr
261
+	} else {
262
+		w = ioutil.Discard
263
+	}
264
+	return log.New(w, "[libcontainer] ", log.LstdFlags)
265
+}
0 266
new file mode 100644
... ...
@@ -0,0 +1,21 @@
0
+package native
1
+
2
+import (
3
+	"os"
4
+	"path/filepath"
5
+)
6
+
7
+type info struct {
8
+	ID     string
9
+	driver *driver
10
+}
11
+
12
+// IsRunning is determined by looking for the
13
+// pid file for a container.  If the file exists then the
14
+// container is currently running
15
+func (i *info) IsRunning() bool {
16
+	if _, err := os.Stat(filepath.Join(i.driver.root, i.ID, "pid")); err == nil {
17
+		return true
18
+	}
19
+	return false
20
+}
0 21
new file mode 100644
... ...
@@ -0,0 +1,42 @@
0
+/*
1
+   These types are wrappers around the libcontainer Terminal interface so that
2
+   we can resuse the docker implementations where possible.
3
+*/
4
+package native
5
+
6
+import (
7
+	"github.com/dotcloud/docker/runtime/execdriver"
8
+	"io"
9
+	"os"
10
+	"os/exec"
11
+)
12
+
13
+type dockerStdTerm struct {
14
+	execdriver.StdConsole
15
+	pipes *execdriver.Pipes
16
+}
17
+
18
+func (d *dockerStdTerm) Attach(cmd *exec.Cmd) error {
19
+	return d.AttachPipes(cmd, d.pipes)
20
+}
21
+
22
+func (d *dockerStdTerm) SetMaster(master *os.File) {
23
+	// do nothing
24
+}
25
+
26
+type dockerTtyTerm struct {
27
+	execdriver.TtyConsole
28
+	pipes *execdriver.Pipes
29
+}
30
+
31
+func (t *dockerTtyTerm) Attach(cmd *exec.Cmd) error {
32
+	go io.Copy(t.pipes.Stdout, t.MasterPty)
33
+	if t.pipes.Stdin != nil {
34
+		go io.Copy(t.MasterPty, t.pipes.Stdin)
35
+	}
36
+	return nil
37
+}
38
+
39
+func (t *dockerTtyTerm) SetMaster(master *os.File) {
40
+	t.MasterPty = master
41
+}
0 42
new file mode 100644
... ...
@@ -0,0 +1,23 @@
0
+package execdriver
1
+
2
+import (
3
+	"io"
4
+)
5
+
6
+// Pipes is a wrapper around a containers output for
7
+// stdin, stdout, stderr
8
+type Pipes struct {
9
+	Stdin          io.ReadCloser
10
+	Stdout, Stderr io.Writer
11
+}
12
+
13
+func NewPipes(stdin io.ReadCloser, stdout, stderr io.Writer, useStdin bool) *Pipes {
14
+	p := &Pipes{
15
+		Stdout: stdout,
16
+		Stderr: stderr,
17
+	}
18
+	if useStdin {
19
+		p.Stdin = stdin
20
+	}
21
+	return p
22
+}
0 23
new file mode 100644
... ...
@@ -0,0 +1,126 @@
0
+package execdriver
1
+
2
+import (
3
+	"github.com/dotcloud/docker/pkg/term"
4
+	"github.com/kr/pty"
5
+	"io"
6
+	"os"
7
+	"os/exec"
8
+)
9
+
10
+func SetTerminal(command *Command, pipes *Pipes) error {
11
+	var (
12
+		term Terminal
13
+		err  error
14
+	)
15
+	if command.Tty {
16
+		term, err = NewTtyConsole(command, pipes)
17
+	} else {
18
+		term, err = NewStdConsole(command, pipes)
19
+	}
20
+	if err != nil {
21
+		return err
22
+	}
23
+	command.Terminal = term
24
+	return nil
25
+}
26
+
27
+type TtyConsole struct {
28
+	MasterPty *os.File
29
+	SlavePty  *os.File
30
+}
31
+
32
+func NewTtyConsole(command *Command, pipes *Pipes) (*TtyConsole, error) {
33
+	ptyMaster, ptySlave, err := pty.Open()
34
+	if err != nil {
35
+		return nil, err
36
+	}
37
+	tty := &TtyConsole{
38
+		MasterPty: ptyMaster,
39
+		SlavePty:  ptySlave,
40
+	}
41
+	if err := tty.AttachPipes(&command.Cmd, pipes); err != nil {
42
+		tty.Close()
43
+		return nil, err
44
+	}
45
+	command.Console = tty.SlavePty.Name()
46
+	return tty, nil
47
+}
48
+
49
+func (t *TtyConsole) Master() *os.File {
50
+	return t.MasterPty
51
+}
52
+
53
+func (t *TtyConsole) Resize(h, w int) error {
54
+	return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
55
+}
56
+
57
+func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *Pipes) error {
58
+	command.Stdout = t.SlavePty
59
+	command.Stderr = t.SlavePty
60
+
61
+	go func() {
62
+		if wb, ok := pipes.Stdout.(interface {
63
+			CloseWriters() error
64
+		}); ok {
65
+			defer wb.CloseWriters()
66
+		}
67
+		io.Copy(pipes.Stdout, t.MasterPty)
68
+	}()
69
+
70
+	if pipes.Stdin != nil {
71
+		command.Stdin = t.SlavePty
72
+		command.SysProcAttr.Setctty = true
73
+
74
+		go func() {
75
+			defer pipes.Stdin.Close()
76
+			io.Copy(t.MasterPty, pipes.Stdin)
77
+		}()
78
+	}
79
+	return nil
80
+}
81
+
82
+func (t *TtyConsole) Close() error {
83
+	t.SlavePty.Close()
84
+	return t.MasterPty.Close()
85
+}
86
+
87
+type StdConsole struct {
88
+}
89
+
90
+func NewStdConsole(command *Command, pipes *Pipes) (*StdConsole, error) {
91
+	std := &StdConsole{}
92
+
93
+	if err := std.AttachPipes(&command.Cmd, pipes); err != nil {
94
+		return nil, err
95
+	}
96
+	return std, nil
97
+}
98
+
99
+func (s *StdConsole) AttachPipes(command *exec.Cmd, pipes *Pipes) error {
100
+	command.Stdout = pipes.Stdout
101
+	command.Stderr = pipes.Stderr
102
+
103
+	if pipes.Stdin != nil {
104
+		stdin, err := command.StdinPipe()
105
+		if err != nil {
106
+			return err
107
+		}
108
+
109
+		go func() {
110
+			defer stdin.Close()
111
+			io.Copy(stdin, pipes.Stdin)
112
+		}()
113
+	}
114
+	return nil
115
+}
116
+
117
+func (s *StdConsole) Resize(h, w int) error {
118
+	// we do not need to reside a non tty
119
+	return nil
120
+}
121
+
122
+func (s *StdConsole) Close() error {
123
+	// nothing to close here
124
+	return nil
125
+}
... ...
@@ -7,9 +7,9 @@ import (
7 7
 	"github.com/dotcloud/docker/daemonconfig"
8 8
 	"github.com/dotcloud/docker/dockerversion"
9 9
 	"github.com/dotcloud/docker/engine"
10
-	"github.com/dotcloud/docker/execdriver"
11
-	"github.com/dotcloud/docker/execdriver/execdrivers"
12
-	"github.com/dotcloud/docker/execdriver/lxc"
10
+	"github.com/dotcloud/docker/runtime/execdriver"
11
+	"github.com/dotcloud/docker/runtime/execdriver/execdrivers"
12
+	"github.com/dotcloud/docker/runtime/execdriver/lxc"
13 13
 	"github.com/dotcloud/docker/graph"
14 14
 	"github.com/dotcloud/docker/graphdriver"
15 15
 	"github.com/dotcloud/docker/graphdriver/aufs"
... ...
@@ -3,7 +3,7 @@ package runtime
3 3
 import (
4 4
 	"fmt"
5 5
 	"github.com/dotcloud/docker/archive"
6
-	"github.com/dotcloud/docker/execdriver"
6
+	"github.com/dotcloud/docker/runtime/execdriver"
7 7
 	"github.com/dotcloud/docker/utils"
8 8
 	"io/ioutil"
9 9
 	"os"
... ...
@@ -3,9 +3,9 @@ package sysinit
3 3
 import (
4 4
 	"flag"
5 5
 	"fmt"
6
-	"github.com/dotcloud/docker/execdriver"
7
-	_ "github.com/dotcloud/docker/execdriver/lxc"
8
-	_ "github.com/dotcloud/docker/execdriver/native"
6
+	"github.com/dotcloud/docker/runtime/execdriver"
7
+	_ "github.com/dotcloud/docker/runtime/execdriver/lxc"
8
+	_ "github.com/dotcloud/docker/runtime/execdriver/native"
9 9
 	"log"
10 10
 	"os"
11 11
 )