Browse code

Merge pull request #3580 from crosbymichael/extract-lxc

Move LXC into a sub pkg and provide initial execution driver interface

Guillaume J. Charmes authored on 2014/01/18 11:10:10
Showing 24 changed files
... ...
@@ -657,16 +657,16 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
657 657
 	for scanner.Scan() {
658 658
 		out.Warnings = append(out.Warnings, scanner.Text())
659 659
 	}
660
-	if job.GetenvInt("Memory") > 0 && !srv.runtime.capabilities.MemoryLimit {
660
+	if job.GetenvInt("Memory") > 0 && !srv.runtime.sysInfo.MemoryLimit {
661 661
 		log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
662 662
 		out.Warnings = append(out.Warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.")
663 663
 	}
664
-	if job.GetenvInt("Memory") > 0 && !srv.runtime.capabilities.SwapLimit {
664
+	if job.GetenvInt("Memory") > 0 && !srv.runtime.sysInfo.SwapLimit {
665 665
 		log.Println("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.")
666 666
 		out.Warnings = append(out.Warnings, "Your kernel does not support memory swap capabilities. Limitation discarded.")
667 667
 	}
668 668
 
669
-	if !job.GetenvBool("NetworkDisabled") && srv.runtime.capabilities.IPv4ForwardingDisabled {
669
+	if !job.GetenvBool("NetworkDisabled") && srv.runtime.sysInfo.IPv4ForwardingDisabled {
670 670
 		log.Println("Warning: IPv4 forwarding is disabled.")
671 671
 		out.Warnings = append(out.Warnings, "IPv4 forwarding is disabled.")
672 672
 	}
... ...
@@ -12,8 +12,13 @@ import (
12 12
 	"strings"
13 13
 )
14 14
 
15
-// https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt
15
+type Values struct {
16
+	Memory     int64 `json:"memory"`
17
+	MemorySwap int64 `json:"memory_swap"`
18
+	CpuShares  int64 `json:"cpu_shares"`
19
+}
16 20
 
21
+// https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt
17 22
 func FindCgroupMountpoint(subsystem string) (string, error) {
18 23
 	mounts, err := mount.GetMounts()
19 24
 	if err != nil {
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"github.com/dotcloud/docker/auth"
13 13
 	"github.com/dotcloud/docker/engine"
14 14
 	flag "github.com/dotcloud/docker/pkg/mflag"
15
+	"github.com/dotcloud/docker/pkg/sysinfo"
15 16
 	"github.com/dotcloud/docker/pkg/term"
16 17
 	"github.com/dotcloud/docker/registry"
17 18
 	"github.com/dotcloud/docker/utils"
... ...
@@ -470,7 +471,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
470 470
 		fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
471 471
 		fmt.Fprintf(cli.out, "Fds: %d\n", remoteInfo.GetInt("NFd"))
472 472
 		fmt.Fprintf(cli.out, "Goroutines: %d\n", remoteInfo.GetInt("NGoroutines"))
473
-		fmt.Fprintf(cli.out, "LXC Version: %s\n", remoteInfo.Get("LXCVersion"))
473
+		fmt.Fprintf(cli.out, "Execution Driver: %s\n", remoteInfo.Get("ExecutionDriver"))
474 474
 		fmt.Fprintf(cli.out, "EventsListeners: %d\n", remoteInfo.GetInt("NEventsListener"))
475 475
 		fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion"))
476 476
 
... ...
@@ -1745,14 +1746,14 @@ func (cli *DockerCli) CmdTag(args ...string) error {
1745 1745
 }
1746 1746
 
1747 1747
 //FIXME Only used in tests
1748
-func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
1748
+func ParseRun(args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
1749 1749
 	cmd := flag.NewFlagSet("run", flag.ContinueOnError)
1750 1750
 	cmd.SetOutput(ioutil.Discard)
1751 1751
 	cmd.Usage = nil
1752
-	return parseRun(cmd, args, capabilities)
1752
+	return parseRun(cmd, args, sysInfo)
1753 1753
 }
1754 1754
 
1755
-func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
1755
+func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
1756 1756
 	var (
1757 1757
 		// FIXME: use utils.ListOpts for attach and volumes?
1758 1758
 		flAttach  = NewListOpts(ValidateAttach)
... ...
@@ -1802,7 +1803,7 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co
1802 1802
 	}
1803 1803
 
1804 1804
 	// Check if the kernel supports memory limit cgroup.
1805
-	if capabilities != nil && *flMemoryString != "" && !capabilities.MemoryLimit {
1805
+	if sysInfo != nil && *flMemoryString != "" && !sysInfo.MemoryLimit {
1806 1806
 		*flMemoryString = ""
1807 1807
 	}
1808 1808
 
... ...
@@ -1934,7 +1935,7 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co
1934 1934
 		PublishAllPorts: *flPublishAll,
1935 1935
 	}
1936 1936
 
1937
-	if capabilities != nil && flMemory > 0 && !capabilities.SwapLimit {
1937
+	if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
1938 1938
 		//fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
1939 1939
 		config.MemorySwap = -1
1940 1940
 	}
... ...
@@ -1,11 +1,12 @@
1 1
 package docker
2 2
 
3 3
 import (
4
-	"bytes"
5 4
 	"encoding/json"
6 5
 	"errors"
7 6
 	"fmt"
8 7
 	"github.com/dotcloud/docker/archive"
8
+	"github.com/dotcloud/docker/cgroups"
9
+	"github.com/dotcloud/docker/execdriver"
9 10
 	"github.com/dotcloud/docker/graphdriver"
10 11
 	"github.com/dotcloud/docker/mount"
11 12
 	"github.com/dotcloud/docker/pkg/term"
... ...
@@ -16,9 +17,7 @@ import (
16 16
 	"log"
17 17
 	"net"
18 18
 	"os"
19
-	"os/exec"
20 19
 	"path"
21
-	"strconv"
22 20
 	"strings"
23 21
 	"sync"
24 22
 	"syscall"
... ...
@@ -55,7 +54,7 @@ type Container struct {
55 55
 	Name           string
56 56
 	Driver         string
57 57
 
58
-	cmd       *exec.Cmd
58
+	process   *execdriver.Process
59 59
 	stdout    *utils.WriteBroadcaster
60 60
 	stderr    *utils.WriteBroadcaster
61 61
 	stdin     io.ReadCloser
... ...
@@ -235,10 +234,6 @@ func (container *Container) Inject(file io.Reader, pth string) error {
235 235
 	return nil
236 236
 }
237 237
 
238
-func (container *Container) Cmd() *exec.Cmd {
239
-	return container.cmd
240
-}
241
-
242 238
 func (container *Container) When() time.Time {
243 239
 	return container.Created
244 240
 }
... ...
@@ -305,23 +300,14 @@ func (container *Container) generateEnvConfig(env []string) error {
305 305
 	return nil
306 306
 }
307 307
 
308
-func (container *Container) generateLXCConfig() error {
309
-	fo, err := os.Create(container.lxcConfigPath())
310
-	if err != nil {
311
-		return err
312
-	}
313
-	defer fo.Close()
314
-	return LxcTemplateCompiled.Execute(fo, container)
315
-}
316
-
317
-func (container *Container) startPty() error {
308
+func (container *Container) setupPty() error {
318 309
 	ptyMaster, ptySlave, err := pty.Open()
319 310
 	if err != nil {
320 311
 		return err
321 312
 	}
322 313
 	container.ptyMaster = ptyMaster
323
-	container.cmd.Stdout = ptySlave
324
-	container.cmd.Stderr = ptySlave
314
+	container.process.Stdout = ptySlave
315
+	container.process.Stderr = ptySlave
325 316
 
326 317
 	// Copy the PTYs to our broadcasters
327 318
 	go func() {
... ...
@@ -333,8 +319,8 @@ func (container *Container) startPty() error {
333 333
 
334 334
 	// stdin
335 335
 	if container.Config.OpenStdin {
336
-		container.cmd.Stdin = ptySlave
337
-		container.cmd.SysProcAttr.Setctty = true
336
+		container.process.Stdin = ptySlave
337
+		container.process.SysProcAttr.Setctty = true
338 338
 		go func() {
339 339
 			defer container.stdin.Close()
340 340
 			utils.Debugf("startPty: begin of stdin pipe")
... ...
@@ -342,18 +328,14 @@ func (container *Container) startPty() error {
342 342
 			utils.Debugf("startPty: end of stdin pipe")
343 343
 		}()
344 344
 	}
345
-	if err := container.cmd.Start(); err != nil {
346
-		return err
347
-	}
348
-	ptySlave.Close()
349 345
 	return nil
350 346
 }
351 347
 
352
-func (container *Container) start() error {
353
-	container.cmd.Stdout = container.stdout
354
-	container.cmd.Stderr = container.stderr
348
+func (container *Container) setupStd() error {
349
+	container.process.Stdout = container.stdout
350
+	container.process.Stderr = container.stderr
355 351
 	if container.Config.OpenStdin {
356
-		stdin, err := container.cmd.StdinPipe()
352
+		stdin, err := container.process.StdinPipe()
357 353
 		if err != nil {
358 354
 			return err
359 355
 		}
... ...
@@ -364,7 +346,7 @@ func (container *Container) start() error {
364 364
 			utils.Debugf("start: end of stdin pipe")
365 365
 		}()
366 366
 	}
367
-	return container.cmd.Start()
367
+	return nil
368 368
 }
369 369
 
370 370
 func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
... ...
@@ -384,12 +366,14 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
384 384
 				if container.Config.StdinOnce && !container.Config.Tty {
385 385
 					defer cStdin.Close()
386 386
 				} else {
387
-					if cStdout != nil {
388
-						defer cStdout.Close()
389
-					}
390
-					if cStderr != nil {
391
-						defer cStderr.Close()
392
-					}
387
+					defer func() {
388
+						if cStdout != nil {
389
+							cStdout.Close()
390
+						}
391
+						if cStderr != nil {
392
+							cStderr.Close()
393
+						}
394
+					}()
393 395
 				}
394 396
 				if container.Config.Tty {
395 397
 					_, err = utils.CopyEscapable(cStdin, stdin)
... ...
@@ -485,12 +469,15 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s
485 485
 	}
486 486
 
487 487
 	return utils.Go(func() error {
488
-		if cStdout != nil {
489
-			defer cStdout.Close()
490
-		}
491
-		if cStderr != nil {
492
-			defer cStderr.Close()
493
-		}
488
+		defer func() {
489
+			if cStdout != nil {
490
+				cStdout.Close()
491
+			}
492
+			if cStderr != nil {
493
+				cStderr.Close()
494
+			}
495
+		}()
496
+
494 497
 		// FIXME: how to clean up the stdin goroutine without the unwanted side effect
495 498
 		// of closing the passed stdin? Add an intermediary io.Pipe?
496 499
 		for i := 0; i < nJobs; i += 1 {
... ...
@@ -532,16 +519,16 @@ func (container *Container) Start() (err error) {
532 532
 	}
533 533
 
534 534
 	// Make sure the config is compatible with the current kernel
535
-	if container.Config.Memory > 0 && !container.runtime.capabilities.MemoryLimit {
535
+	if container.Config.Memory > 0 && !container.runtime.sysInfo.MemoryLimit {
536 536
 		log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
537 537
 		container.Config.Memory = 0
538 538
 	}
539
-	if container.Config.Memory > 0 && !container.runtime.capabilities.SwapLimit {
539
+	if container.Config.Memory > 0 && !container.runtime.sysInfo.SwapLimit {
540 540
 		log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
541 541
 		container.Config.MemorySwap = -1
542 542
 	}
543 543
 
544
-	if container.runtime.capabilities.IPv4ForwardingDisabled {
544
+	if container.runtime.sysInfo.IPv4ForwardingDisabled {
545 545
 		log.Printf("WARNING: IPv4 forwarding is disabled. Networking will not work")
546 546
 	}
547 547
 
... ...
@@ -559,38 +546,6 @@ func (container *Container) Start() (err error) {
559 559
 		return err
560 560
 	}
561 561
 
562
-	if err := container.generateLXCConfig(); err != nil {
563
-		return err
564
-	}
565
-
566
-	var lxcStart string = "lxc-start"
567
-	if container.hostConfig.Privileged && container.runtime.capabilities.AppArmor {
568
-		lxcStart = path.Join(container.runtime.config.Root, "lxc-start-unconfined")
569
-	}
570
-
571
-	params := []string{
572
-		lxcStart,
573
-		"-n", container.ID,
574
-		"-f", container.lxcConfigPath(),
575
-		"--",
576
-		"/.dockerinit",
577
-	}
578
-
579
-	// Networking
580
-	if !container.Config.NetworkDisabled {
581
-		network := container.NetworkSettings
582
-		params = append(params,
583
-			"-g", network.Gateway,
584
-			"-i", fmt.Sprintf("%s/%d", network.IPAddress, network.IPPrefixLen),
585
-			"-mtu", strconv.Itoa(container.runtime.config.Mtu),
586
-		)
587
-	}
588
-
589
-	// User
590
-	if container.Config.User != "" {
591
-		params = append(params, "-u", container.Config.User)
592
-	}
593
-
594 562
 	// Setup environment
595 563
 	env := []string{
596 564
 		"HOME=/",
... ...
@@ -602,10 +557,6 @@ func (container *Container) Start() (err error) {
602 602
 		env = append(env, "TERM=xterm")
603 603
 	}
604 604
 
605
-	if container.hostConfig.Privileged {
606
-		params = append(params, "-privileged")
607
-	}
608
-
609 605
 	// Init any links between the parent and children
610 606
 	runtime := container.runtime
611 607
 
... ...
@@ -653,37 +604,12 @@ func (container *Container) Start() (err error) {
653 653
 		return err
654 654
 	}
655 655
 
656
+	var workingDir string
656 657
 	if container.Config.WorkingDir != "" {
657
-		workingDir := path.Clean(container.Config.WorkingDir)
658
-		utils.Debugf("[working dir] working dir is %s", workingDir)
659
-
658
+		workingDir = path.Clean(container.Config.WorkingDir)
660 659
 		if err := os.MkdirAll(path.Join(container.RootfsPath(), workingDir), 0755); err != nil {
661 660
 			return nil
662 661
 		}
663
-
664
-		params = append(params,
665
-			"-w", workingDir,
666
-		)
667
-	}
668
-
669
-	// Program
670
-	params = append(params, "--", container.Path)
671
-	params = append(params, container.Args...)
672
-
673
-	if RootIsShared() {
674
-		// lxc-start really needs / to be non-shared, or all kinds of stuff break
675
-		// when lxc-start unmount things and those unmounts propagate to the main
676
-		// mount namespace.
677
-		// What we really want is to clone into a new namespace and then
678
-		// mount / MS_REC|MS_SLAVE, but since we can't really clone or fork
679
-		// without exec in go we have to do this horrible shell hack...
680
-		shellString :=
681
-			"mount --make-rslave /; exec " +
682
-				utils.ShellQuoteArguments(params)
683
-
684
-		params = []string{
685
-			"unshare", "-m", "--", "/bin/sh", "-c", shellString,
686
-		}
687 662
 	}
688 663
 
689 664
 	root := container.RootfsPath()
... ...
@@ -713,7 +639,6 @@ func (container *Container) Start() (err error) {
713 713
 	}
714 714
 
715 715
 	// Mount user specified volumes
716
-
717 716
 	for r, v := range container.Volumes {
718 717
 		mountAs := "ro"
719 718
 		if container.VolumesRW[r] {
... ...
@@ -725,7 +650,48 @@ func (container *Container) Start() (err error) {
725 725
 		}
726 726
 	}
727 727
 
728
-	container.cmd = exec.Command(params[0], params[1:]...)
728
+	var (
729
+		en           *execdriver.Network
730
+		driverConfig []string
731
+	)
732
+
733
+	if !container.Config.NetworkDisabled {
734
+		network := container.NetworkSettings
735
+		en = &execdriver.Network{
736
+			Gateway:     network.Gateway,
737
+			Bridge:      network.Bridge,
738
+			IPAddress:   network.IPAddress,
739
+			IPPrefixLen: network.IPPrefixLen,
740
+			Mtu:         container.runtime.config.Mtu,
741
+		}
742
+	}
743
+
744
+	if lxcConf := container.hostConfig.LxcConf; lxcConf != nil {
745
+		for _, pair := range lxcConf {
746
+			driverConfig = append(driverConfig, fmt.Sprintf("%s = %s", pair.Key, pair.Value))
747
+		}
748
+	}
749
+	cgroupValues := &cgroups.Values{
750
+		Memory:     container.Config.Memory,
751
+		MemorySwap: container.Config.MemorySwap,
752
+		CpuShares:  container.Config.CpuShares,
753
+	}
754
+
755
+	container.process = &execdriver.Process{
756
+		ID:         container.ID,
757
+		Privileged: container.hostConfig.Privileged,
758
+		Rootfs:     root,
759
+		InitPath:   "/.dockerinit",
760
+		Entrypoint: container.Path,
761
+		Arguments:  container.Args,
762
+		WorkingDir: workingDir,
763
+		Network:    en,
764
+		Tty:        container.Config.Tty,
765
+		User:       container.Config.User,
766
+		Config:     driverConfig,
767
+		Cgroups:    cgroupValues,
768
+	}
769
+	container.process.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
729 770
 
730 771
 	// Setup logging of stdout and stderr to disk
731 772
 	if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
... ...
@@ -734,59 +700,47 @@ func (container *Container) Start() (err error) {
734 734
 	if err := container.runtime.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil {
735 735
 		return err
736 736
 	}
737
+	container.waitLock = make(chan struct{})
737 738
 
738
-	container.cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
739
-
739
+	// Setuping pipes and/or Pty
740
+	var setup func() error
740 741
 	if container.Config.Tty {
741
-		err = container.startPty()
742
+		setup = container.setupPty
742 743
 	} else {
743
-		err = container.start()
744
+		setup = container.setupStd
744 745
 	}
745
-	if err != nil {
746
+	if err := setup(); err != nil {
746 747
 		return err
747 748
 	}
748
-	// FIXME: save state on disk *first*, then converge
749
-	// this way disk state is used as a journal, eg. we can restore after crash etc.
750
-	container.State.SetRunning(container.cmd.Process.Pid)
751
-
752
-	// Init the lock
753
-	container.waitLock = make(chan struct{})
754 749
 
755
-	container.ToDisk()
756
-	go container.monitor()
757
-
758
-	defer utils.Debugf("Container running: %v", container.State.IsRunning())
759
-	// We wait for the container to be fully running.
760
-	// Timeout after 5 seconds. In case of broken pipe, just retry.
761
-	// Note: The container can run and finish correctly before
762
-	//       the end of this loop
763
-	for now := time.Now(); time.Since(now) < 5*time.Second; {
764
-		// If the container dies while waiting for it, just return
765
-		if !container.State.IsRunning() {
766
-			return nil
767
-		}
768
-		output, err := exec.Command("lxc-info", "-s", "-n", container.ID).CombinedOutput()
769
-		if err != nil {
770
-			utils.Debugf("Error with lxc-info: %s (%s)", err, output)
771
-
772
-			output, err = exec.Command("lxc-info", "-s", "-n", container.ID).CombinedOutput()
773
-			if err != nil {
774
-				utils.Debugf("Second Error with lxc-info: %s (%s)", err, output)
775
-				return err
750
+	callbackLock := make(chan struct{})
751
+	callback := func(process *execdriver.Process) {
752
+		container.State.SetRunning(process.Pid())
753
+		if process.Tty {
754
+			// The callback is called after the process Start()
755
+			// so we are in the parent process. In TTY mode, stdin/out/err is the PtySlace
756
+			// which we close here.
757
+			if c, ok := process.Stdout.(io.Closer); ok {
758
+				c.Close()
776 759
 			}
777
-
778 760
 		}
779
-		if strings.Contains(string(output), "RUNNING") {
780
-			return nil
761
+		if err := container.ToDisk(); err != nil {
762
+			utils.Debugf("%s", err)
781 763
 		}
782
-		utils.Debugf("Waiting for the container to start (running: %v): %s", container.State.IsRunning(), bytes.TrimSpace(output))
783
-		time.Sleep(50 * time.Millisecond)
764
+		close(callbackLock)
784 765
 	}
785 766
 
786
-	if container.State.IsRunning() {
787
-		return ErrContainerStartTimeout
767
+	// We use a callback here instead of a goroutine and an chan for
768
+	// syncronization purposes
769
+	cErr := utils.Go(func() error { return container.monitor(callback) })
770
+
771
+	// Start should not return until the process is actually running
772
+	select {
773
+	case <-callbackLock:
774
+	case err := <-cErr:
775
+		return err
788 776
 	}
789
-	return ErrContainerStart
777
+	return nil
790 778
 }
791 779
 
792 780
 func (container *Container) getBindMap() (map[string]BindMap, error) {
... ...
@@ -1159,47 +1113,23 @@ func (container *Container) releaseNetwork() {
1159 1159
 	container.NetworkSettings = &NetworkSettings{}
1160 1160
 }
1161 1161
 
1162
-// FIXME: replace this with a control socket within dockerinit
1163
-func (container *Container) waitLxc() error {
1164
-	for {
1165
-		output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
1166
-		if err != nil {
1167
-			return err
1168
-		}
1169
-		if !strings.Contains(string(output), "RUNNING") {
1170
-			return nil
1171
-		}
1172
-		time.Sleep(500 * time.Millisecond)
1173
-	}
1174
-}
1175
-
1176
-func (container *Container) monitor() {
1177
-	// Wait for the program to exit
1162
+func (container *Container) monitor(callback execdriver.StartCallback) error {
1163
+	var (
1164
+		err      error
1165
+		exitCode int
1166
+	)
1178 1167
 
1179
-	// If the command does not exist, try to wait via lxc
1180
-	// (This probably happens only for ghost containers, i.e. containers that were running when Docker started)
1181
-	if container.cmd == nil {
1182
-		utils.Debugf("monitor: waiting for container %s using waitLxc", container.ID)
1183
-		if err := container.waitLxc(); err != nil {
1184
-			utils.Errorf("monitor: while waiting for container %s, waitLxc had a problem: %s", container.ID, err)
1185
-		}
1168
+	if container.process == nil {
1169
+		// This happends when you have a GHOST container with lxc
1170
+		err = container.runtime.WaitGhost(container)
1186 1171
 	} else {
1187
-		utils.Debugf("monitor: waiting for container %s using cmd.Wait", container.ID)
1188
-		if err := container.cmd.Wait(); err != nil {
1189
-			// Since non-zero exit status and signal terminations will cause err to be non-nil,
1190
-			// we have to actually discard it. Still, log it anyway, just in case.
1191
-			utils.Debugf("monitor: cmd.Wait reported exit status %s for container %s", err, container.ID)
1192
-		}
1193
-	}
1194
-	utils.Debugf("monitor: container %s finished", container.ID)
1195
-
1196
-	exitCode := -1
1197
-	if container.cmd != nil {
1198
-		exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
1172
+		exitCode, err = container.runtime.Run(container, callback)
1199 1173
 	}
1200 1174
 
1201
-	if container.runtime != nil && container.runtime.srv != nil {
1202
-		container.runtime.srv.LogEvent("die", container.ID, container.runtime.repositories.ImageName(container.Image))
1175
+	if err != nil {
1176
+		if container.runtime != nil && container.runtime.srv != nil {
1177
+			container.runtime.srv.LogEvent("die", container.ID, container.runtime.repositories.ImageName(container.Image))
1178
+		}
1203 1179
 	}
1204 1180
 
1205 1181
 	// Cleanup
... ...
@@ -1210,21 +1140,20 @@ func (container *Container) monitor() {
1210 1210
 		container.stdin, container.stdinPipe = io.Pipe()
1211 1211
 	}
1212 1212
 
1213
-	// Report status back
1214 1213
 	container.State.SetStopped(exitCode)
1215 1214
 
1216
-	// Release the lock
1217 1215
 	close(container.waitLock)
1218 1216
 
1219
-	if err := container.ToDisk(); err != nil {
1220
-		// FIXME: there is a race condition here which causes this to fail during the unit tests.
1221
-		// If another goroutine was waiting for Wait() to return before removing the container's root
1222
-		// from the filesystem... At this point it may already have done so.
1223
-		// This is because State.setStopped() has already been called, and has caused Wait()
1224
-		// to return.
1225
-		// FIXME: why are we serializing running state to disk in the first place?
1226
-		//log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
1227
-	}
1217
+	// FIXME: there is a race condition here which causes this to fail during the unit tests.
1218
+	// If another goroutine was waiting for Wait() to return before removing the container's root
1219
+	// from the filesystem... At this point it may already have done so.
1220
+	// This is because State.setStopped() has already been called, and has caused Wait()
1221
+	// to return.
1222
+	// FIXME: why are we serializing running state to disk in the first place?
1223
+	//log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
1224
+	container.ToDisk()
1225
+
1226
+	return err
1228 1227
 }
1229 1228
 
1230 1229
 func (container *Container) cleanup() {
... ...
@@ -1267,13 +1196,7 @@ func (container *Container) kill(sig int) error {
1267 1267
 	if !container.State.IsRunning() {
1268 1268
 		return nil
1269 1269
 	}
1270
-
1271
-	if output, err := exec.Command("lxc-kill", "-n", container.ID, strconv.Itoa(sig)).CombinedOutput(); err != nil {
1272
-		log.Printf("error killing container %s (%s, %s)", utils.TruncateID(container.ID), output, err)
1273
-		return err
1274
-	}
1275
-
1276
-	return nil
1270
+	return container.runtime.Kill(container, sig)
1277 1271
 }
1278 1272
 
1279 1273
 func (container *Container) Kill() error {
... ...
@@ -1288,11 +1211,11 @@ func (container *Container) Kill() error {
1288 1288
 
1289 1289
 	// 2. Wait for the process to die, in last resort, try to kill the process directly
1290 1290
 	if err := container.WaitTimeout(10 * time.Second); err != nil {
1291
-		if container.cmd == nil {
1291
+		if container.process == nil {
1292 1292
 			return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", utils.TruncateID(container.ID))
1293 1293
 		}
1294 1294
 		log.Printf("Container %s failed to exit within 10 seconds of lxc-kill %s - trying direct SIGKILL", "SIGKILL", utils.TruncateID(container.ID))
1295
-		if err := container.cmd.Process.Kill(); err != nil {
1295
+		if err := container.runtime.Kill(container, 9); err != nil {
1296 1296
 			return err
1297 1297
 		}
1298 1298
 	}
... ...
@@ -1463,10 +1386,6 @@ func (container *Container) EnvConfigPath() (string, error) {
1463 1463
 	return p, nil
1464 1464
 }
1465 1465
 
1466
-func (container *Container) lxcConfigPath() string {
1467
-	return path.Join(container.root, "config.lxc")
1468
-}
1469
-
1470 1466
 // This method must be exported to be used from the lxc template
1471 1467
 func (container *Container) RootfsPath() string {
1472 1468
 	return container.rootfs
1473 1469
new file mode 100644
... ...
@@ -0,0 +1,2 @@
0
+Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
1
+Guillaume Charmes <guillaume@dotcloud.com> (@creack)
0 2
new file mode 100644
... ...
@@ -0,0 +1,87 @@
0
+package chroot
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/execdriver"
5
+	"github.com/dotcloud/docker/mount"
6
+	"os"
7
+	"os/exec"
8
+)
9
+
10
+const (
11
+	DriverName = "chroot"
12
+	Version    = "0.1"
13
+)
14
+
15
+func init() {
16
+	execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
17
+		if err := mount.ForceMount("proc", "proc", "proc", ""); err != nil {
18
+			return err
19
+		}
20
+		defer mount.ForceUnmount("proc")
21
+		cmd := exec.Command(args.Args[0], args.Args[1:]...)
22
+
23
+		cmd.Stderr = os.Stderr
24
+		cmd.Stdout = os.Stdout
25
+		cmd.Stdin = os.Stdin
26
+
27
+		return cmd.Run()
28
+	})
29
+}
30
+
31
+type driver struct {
32
+}
33
+
34
+func NewDriver() (*driver, error) {
35
+	return &driver{}, nil
36
+}
37
+
38
+func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) {
39
+	params := []string{
40
+		"chroot",
41
+		c.Rootfs,
42
+		"/.dockerinit",
43
+		"-driver",
44
+		DriverName,
45
+	}
46
+	params = append(params, c.Entrypoint)
47
+	params = append(params, c.Arguments...)
48
+
49
+	var (
50
+		name = params[0]
51
+		arg  = params[1:]
52
+	)
53
+	aname, err := exec.LookPath(name)
54
+	if err != nil {
55
+		aname = name
56
+	}
57
+	c.Path = aname
58
+	c.Args = append([]string{name}, arg...)
59
+
60
+	if err := c.Start(); err != nil {
61
+		return -1, err
62
+	}
63
+
64
+	if startCallback != nil {
65
+		startCallback(c)
66
+	}
67
+
68
+	err = c.Wait()
69
+	return c.GetExitCode(), err
70
+}
71
+
72
+func (d *driver) Kill(p *execdriver.Process, sig int) error {
73
+	return p.Process.Kill()
74
+}
75
+
76
+func (d *driver) Wait(id string) error {
77
+	panic("Not Implemented")
78
+}
79
+
80
+func (d *driver) Info(id string) execdriver.Info {
81
+	panic("Not implemented")
82
+}
83
+
84
+func (d *driver) Name() string {
85
+	return fmt.Sprintf("%s-%s", DriverName, Version)
86
+}
0 87
new file mode 100644
... ...
@@ -0,0 +1,115 @@
0
+package execdriver
1
+
2
+import (
3
+	"errors"
4
+	"github.com/dotcloud/docker/cgroups"
5
+	"os/exec"
6
+	"syscall"
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(*Process)
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
+}
54
+
55
+// Driver specific information based on
56
+// processes registered with the driver
57
+type Info interface {
58
+	IsRunning() bool
59
+}
60
+
61
+type Driver interface {
62
+	Run(c *Process, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code
63
+	Kill(c *Process, sig int) error
64
+	Wait(id string) error // Wait on an out of process...process - lxc ghosts TODO: Rename to reattach, reconnect
65
+	Name() string         // Driver name
66
+	Info(id string) Info  // "temporary" hack (until we move state from core to plugins)
67
+}
68
+
69
+// Network settings of the container
70
+type Network struct {
71
+	Gateway     string `json:"gateway"`
72
+	IPAddress   string `json:"ip"`
73
+	Bridge      string `json:"bridge"`
74
+	IPPrefixLen int    `json:"ip_prefix_len"`
75
+	Mtu         int    `json:"mtu"`
76
+}
77
+
78
+// Process wrapps an os/exec.Cmd to add more metadata
79
+// TODO: Rename to Command
80
+type Process struct {
81
+	exec.Cmd
82
+
83
+	ID         string          `json:"id"`
84
+	Privileged bool            `json:"privileged"`
85
+	User       string          `json:"user"`
86
+	Rootfs     string          `json:"rootfs"`   // root fs of the container
87
+	InitPath   string          `json:"initpath"` // dockerinit
88
+	Entrypoint string          `json:"entrypoint"`
89
+	Arguments  []string        `json:"arguments"`
90
+	WorkingDir string          `json:"working_dir"`
91
+	ConfigPath string          `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver
92
+	Tty        bool            `json:"tty"`
93
+	Network    *Network        `json:"network"` // if network is nil then networking is disabled
94
+	Config     []string        `json:"config"`  //  generic values that specific drivers can consume
95
+	Cgroups    *cgroups.Values `json:"cgroups"`
96
+}
97
+
98
+// Return the pid of the process
99
+// If the process is nil -1 will be returned
100
+func (c *Process) Pid() int {
101
+	if c.Process == nil {
102
+		return -1
103
+	}
104
+	return c.Process.Pid
105
+}
106
+
107
+// Return the exit code of the process
108
+// if the process has not exited -1 will be returned
109
+func (c *Process) GetExitCode() int {
110
+	if c.ProcessState == nil {
111
+		return -1
112
+	}
113
+	return c.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
114
+}
0 115
new file mode 100644
... ...
@@ -0,0 +1,323 @@
0
+package lxc
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/execdriver"
5
+	"github.com/dotcloud/docker/utils"
6
+	"io/ioutil"
7
+	"log"
8
+	"os"
9
+	"os/exec"
10
+	"path"
11
+	"strconv"
12
+	"strings"
13
+	"syscall"
14
+	"time"
15
+)
16
+
17
+const DriverName = "lxc"
18
+
19
+func init() {
20
+	execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
21
+		if err := setupHostname(args); err != nil {
22
+			return err
23
+		}
24
+
25
+		if err := setupNetworking(args); err != nil {
26
+			return err
27
+		}
28
+
29
+		if err := setupCapabilities(args); err != nil {
30
+			return err
31
+		}
32
+		if err := setupWorkingDirectory(args); err != nil {
33
+			return err
34
+		}
35
+
36
+		if err := changeUser(args); err != nil {
37
+			return err
38
+		}
39
+		path, err := exec.LookPath(args.Args[0])
40
+		if err != nil {
41
+			log.Printf("Unable to locate %v", args.Args[0])
42
+			os.Exit(127)
43
+		}
44
+		if err := syscall.Exec(path, args.Args, os.Environ()); err != nil {
45
+			return fmt.Errorf("dockerinit unable to execute %s - %s", path, err)
46
+		}
47
+		panic("Unreachable")
48
+	})
49
+}
50
+
51
+type driver struct {
52
+	root       string // root path for the driver to use
53
+	apparmor   bool
54
+	sharedRoot bool
55
+}
56
+
57
+func NewDriver(root string, apparmor bool) (*driver, error) {
58
+	// setup unconfined symlink
59
+	if err := linkLxcStart(root); err != nil {
60
+		return nil, err
61
+	}
62
+	return &driver{
63
+		apparmor:   apparmor,
64
+		root:       root,
65
+		sharedRoot: rootIsShared(),
66
+	}, nil
67
+}
68
+
69
+func (d *driver) Name() string {
70
+	version := d.version()
71
+	return fmt.Sprintf("%s-%s", DriverName, version)
72
+}
73
+
74
+func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) {
75
+	configPath, err := d.generateLXCConfig(c)
76
+	if err != nil {
77
+		return -1, err
78
+	}
79
+	params := []string{
80
+		"lxc-start",
81
+		"-n", c.ID,
82
+		"-f", configPath,
83
+		"--",
84
+		c.InitPath,
85
+		"-driver",
86
+		DriverName,
87
+	}
88
+
89
+	if c.Network != nil {
90
+		params = append(params,
91
+			"-g", c.Network.Gateway,
92
+			"-i", fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
93
+			"-mtu", strconv.Itoa(c.Network.Mtu),
94
+		)
95
+	}
96
+
97
+	if c.User != "" {
98
+		params = append(params, "-u", c.User)
99
+	}
100
+
101
+	if c.Privileged {
102
+		if d.apparmor {
103
+			params[0] = path.Join(d.root, "lxc-start-unconfined")
104
+
105
+		}
106
+		params = append(params, "-privileged")
107
+	}
108
+
109
+	if c.WorkingDir != "" {
110
+		params = append(params, "-w", c.WorkingDir)
111
+	}
112
+
113
+	if d.sharedRoot {
114
+		// lxc-start really needs / to be non-shared, or all kinds of stuff break
115
+		// when lxc-start unmount things and those unmounts propagate to the main
116
+		// mount namespace.
117
+		// What we really want is to clone into a new namespace and then
118
+		// mount / MS_REC|MS_SLAVE, but since we can't really clone or fork
119
+		// without exec in go we have to do this horrible shell hack...
120
+		shellString :=
121
+			"mount --make-rslave /; exec " +
122
+				utils.ShellQuoteArguments(params)
123
+
124
+		params = []string{
125
+			"unshare", "-m", "--", "/bin/sh", "-c", shellString,
126
+		}
127
+	}
128
+
129
+	params = append(params, "--", c.Entrypoint)
130
+	params = append(params, c.Arguments...)
131
+
132
+	var (
133
+		name = params[0]
134
+		arg  = params[1:]
135
+	)
136
+	aname, err := exec.LookPath(name)
137
+	if err != nil {
138
+		aname = name
139
+	}
140
+	c.Path = aname
141
+	c.Args = append([]string{name}, arg...)
142
+
143
+	if err := c.Start(); err != nil {
144
+		return -1, err
145
+	}
146
+
147
+	var (
148
+		waitErr  error
149
+		waitLock = make(chan struct{})
150
+	)
151
+	go func() {
152
+		if err := c.Wait(); err != nil {
153
+			waitErr = err
154
+		}
155
+		close(waitLock)
156
+	}()
157
+
158
+	// Poll lxc for RUNNING status
159
+	if err := d.waitForStart(c, waitLock); err != nil {
160
+		return -1, err
161
+	}
162
+
163
+	if startCallback != nil {
164
+		startCallback(c)
165
+	}
166
+
167
+	<-waitLock
168
+
169
+	return c.GetExitCode(), waitErr
170
+}
171
+
172
+func (d *driver) Kill(c *execdriver.Process, sig int) error {
173
+	return d.kill(c, sig)
174
+}
175
+
176
+func (d *driver) Wait(id string) error {
177
+	for {
178
+		output, err := exec.Command("lxc-info", "-n", id).CombinedOutput()
179
+		if err != nil {
180
+			return err
181
+		}
182
+		if !strings.Contains(string(output), "RUNNING") {
183
+			return nil
184
+		}
185
+		time.Sleep(500 * time.Millisecond)
186
+	}
187
+}
188
+
189
+func (d *driver) version() string {
190
+	version := ""
191
+	if output, err := exec.Command("lxc-version").CombinedOutput(); err == nil {
192
+		outputStr := string(output)
193
+		if len(strings.SplitN(outputStr, ":", 2)) == 2 {
194
+			version = strings.TrimSpace(strings.SplitN(outputStr, ":", 2)[1])
195
+		}
196
+	}
197
+	return version
198
+}
199
+
200
+func (d *driver) kill(c *execdriver.Process, sig int) error {
201
+	output, err := exec.Command("lxc-kill", "-n", c.ID, strconv.Itoa(sig)).CombinedOutput()
202
+	if err != nil {
203
+		return fmt.Errorf("Err: %s Output: %s", err, output)
204
+	}
205
+	return nil
206
+}
207
+
208
+func (d *driver) waitForStart(c *execdriver.Process, waitLock chan struct{}) error {
209
+	var (
210
+		err    error
211
+		output []byte
212
+	)
213
+	// We wait for the container to be fully running.
214
+	// Timeout after 5 seconds. In case of broken pipe, just retry.
215
+	// Note: The container can run and finish correctly before
216
+	// the end of this loop
217
+	for now := time.Now(); time.Since(now) < 5*time.Second; {
218
+		select {
219
+		case <-waitLock:
220
+			// If the process dies while waiting for it, just return
221
+			return nil
222
+			if c.ProcessState != nil && c.ProcessState.Exited() {
223
+				return nil
224
+			}
225
+		default:
226
+		}
227
+
228
+		output, err = d.getInfo(c.ID)
229
+		if err != nil {
230
+			output, err = d.getInfo(c.ID)
231
+			if err != nil {
232
+				return err
233
+			}
234
+		}
235
+		if strings.Contains(string(output), "RUNNING") {
236
+			return nil
237
+		}
238
+		time.Sleep(50 * time.Millisecond)
239
+	}
240
+	return execdriver.ErrNotRunning
241
+}
242
+
243
+func (d *driver) getInfo(id string) ([]byte, error) {
244
+	return exec.Command("lxc-info", "-s", "-n", id).CombinedOutput()
245
+}
246
+
247
+type info struct {
248
+	ID     string
249
+	driver *driver
250
+}
251
+
252
+func (i *info) IsRunning() bool {
253
+	var running bool
254
+
255
+	output, err := i.driver.getInfo(i.ID)
256
+	if err != nil {
257
+		panic(err)
258
+	}
259
+	if strings.Contains(string(output), "RUNNING") {
260
+		running = true
261
+	}
262
+	return running
263
+}
264
+
265
+func (d *driver) Info(id string) execdriver.Info {
266
+	return &info{
267
+		ID:     id,
268
+		driver: d,
269
+	}
270
+}
271
+
272
+func linkLxcStart(root string) error {
273
+	sourcePath, err := exec.LookPath("lxc-start")
274
+	if err != nil {
275
+		return err
276
+	}
277
+	targetPath := path.Join(root, "lxc-start-unconfined")
278
+
279
+	if _, err := os.Lstat(targetPath); err != nil && !os.IsNotExist(err) {
280
+		return err
281
+	} else if err == nil {
282
+		if err := os.Remove(targetPath); err != nil {
283
+			return err
284
+		}
285
+	}
286
+	return os.Symlink(sourcePath, targetPath)
287
+}
288
+
289
+// TODO: This can be moved to the mountinfo reader in the mount pkg
290
+func rootIsShared() bool {
291
+	if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil {
292
+		for _, line := range strings.Split(string(data), "\n") {
293
+			cols := strings.Split(line, " ")
294
+			if len(cols) >= 6 && cols[4] == "/" {
295
+				return strings.HasPrefix(cols[6], "shared")
296
+			}
297
+		}
298
+	}
299
+
300
+	// No idea, probably safe to assume so
301
+	return true
302
+}
303
+
304
+func (d *driver) generateLXCConfig(p *execdriver.Process) (string, error) {
305
+	root := path.Join(d.root, "containers", p.ID, "config.lxc")
306
+	fo, err := os.Create(root)
307
+	if err != nil {
308
+		return "", err
309
+	}
310
+	defer fo.Close()
311
+
312
+	if err := LxcTemplateCompiled.Execute(fo, struct {
313
+		*execdriver.Process
314
+		AppArmor bool
315
+	}{
316
+		Process:  p,
317
+		AppArmor: d.apparmor,
318
+	}); err != nil {
319
+		return "", err
320
+	}
321
+	return root, nil
322
+}
0 323
new file mode 100644
... ...
@@ -0,0 +1,153 @@
0
+package lxc
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/execdriver"
5
+	"github.com/dotcloud/docker/pkg/netlink"
6
+	"github.com/dotcloud/docker/utils"
7
+	"github.com/syndtr/gocapability/capability"
8
+	"net"
9
+	"os"
10
+	"strconv"
11
+	"strings"
12
+	"syscall"
13
+)
14
+
15
+func setupHostname(args *execdriver.InitArgs) error {
16
+	hostname := getEnv(args, "HOSTNAME")
17
+	if hostname == "" {
18
+		return nil
19
+	}
20
+	return setHostname(hostname)
21
+}
22
+
23
+// Setup networking
24
+func setupNetworking(args *execdriver.InitArgs) error {
25
+	if args.Ip != "" {
26
+		// eth0
27
+		iface, err := net.InterfaceByName("eth0")
28
+		if err != nil {
29
+			return fmt.Errorf("Unable to set up networking: %v", err)
30
+		}
31
+		ip, ipNet, err := net.ParseCIDR(args.Ip)
32
+		if err != nil {
33
+			return fmt.Errorf("Unable to set up networking: %v", err)
34
+		}
35
+		if err := netlink.NetworkLinkAddIp(iface, ip, ipNet); err != nil {
36
+			return fmt.Errorf("Unable to set up networking: %v", err)
37
+		}
38
+		if err := netlink.NetworkSetMTU(iface, args.Mtu); err != nil {
39
+			return fmt.Errorf("Unable to set MTU: %v", err)
40
+		}
41
+		if err := netlink.NetworkLinkUp(iface); err != nil {
42
+			return fmt.Errorf("Unable to set up networking: %v", err)
43
+		}
44
+
45
+		// loopback
46
+		iface, err = net.InterfaceByName("lo")
47
+		if err != nil {
48
+			return fmt.Errorf("Unable to set up networking: %v", err)
49
+		}
50
+		if err := netlink.NetworkLinkUp(iface); err != nil {
51
+			return fmt.Errorf("Unable to set up networking: %v", err)
52
+		}
53
+	}
54
+	if args.Gateway != "" {
55
+		gw := net.ParseIP(args.Gateway)
56
+		if gw == nil {
57
+			return fmt.Errorf("Unable to set up networking, %s is not a valid gateway IP", args.Gateway)
58
+		}
59
+
60
+		if err := netlink.AddDefaultGw(gw); err != nil {
61
+			return fmt.Errorf("Unable to set up networking: %v", err)
62
+		}
63
+	}
64
+
65
+	return nil
66
+}
67
+
68
+// Setup working directory
69
+func setupWorkingDirectory(args *execdriver.InitArgs) error {
70
+	if args.WorkDir == "" {
71
+		return nil
72
+	}
73
+	if err := syscall.Chdir(args.WorkDir); err != nil {
74
+		return fmt.Errorf("Unable to change dir to %v: %v", args.WorkDir, err)
75
+	}
76
+	return nil
77
+}
78
+
79
+// Takes care of dropping privileges to the desired user
80
+func changeUser(args *execdriver.InitArgs) error {
81
+	if args.User == "" {
82
+		return nil
83
+	}
84
+	userent, err := utils.UserLookup(args.User)
85
+	if err != nil {
86
+		return fmt.Errorf("Unable to find user %v: %v", args.User, err)
87
+	}
88
+
89
+	uid, err := strconv.Atoi(userent.Uid)
90
+	if err != nil {
91
+		return fmt.Errorf("Invalid uid: %v", userent.Uid)
92
+	}
93
+	gid, err := strconv.Atoi(userent.Gid)
94
+	if err != nil {
95
+		return fmt.Errorf("Invalid gid: %v", userent.Gid)
96
+	}
97
+
98
+	if err := syscall.Setgid(gid); err != nil {
99
+		return fmt.Errorf("setgid failed: %v", err)
100
+	}
101
+	if err := syscall.Setuid(uid); err != nil {
102
+		return fmt.Errorf("setuid failed: %v", err)
103
+	}
104
+
105
+	return nil
106
+}
107
+
108
+func setupCapabilities(args *execdriver.InitArgs) error {
109
+
110
+	if args.Privileged {
111
+		return nil
112
+	}
113
+
114
+	drop := []capability.Cap{
115
+		capability.CAP_SETPCAP,
116
+		capability.CAP_SYS_MODULE,
117
+		capability.CAP_SYS_RAWIO,
118
+		capability.CAP_SYS_PACCT,
119
+		capability.CAP_SYS_ADMIN,
120
+		capability.CAP_SYS_NICE,
121
+		capability.CAP_SYS_RESOURCE,
122
+		capability.CAP_SYS_TIME,
123
+		capability.CAP_SYS_TTY_CONFIG,
124
+		capability.CAP_MKNOD,
125
+		capability.CAP_AUDIT_WRITE,
126
+		capability.CAP_AUDIT_CONTROL,
127
+		capability.CAP_MAC_OVERRIDE,
128
+		capability.CAP_MAC_ADMIN,
129
+	}
130
+
131
+	c, err := capability.NewPid(os.Getpid())
132
+	if err != nil {
133
+		return err
134
+	}
135
+
136
+	c.Unset(capability.CAPS|capability.BOUNDS, drop...)
137
+
138
+	if err := c.Apply(capability.CAPS | capability.BOUNDS); err != nil {
139
+		return err
140
+	}
141
+	return nil
142
+}
143
+
144
+func getEnv(args *execdriver.InitArgs, key string) string {
145
+	for _, kv := range args.Env {
146
+		parts := strings.SplitN(kv, "=", 2)
147
+		if parts[0] == key && len(parts) == 2 {
148
+			return parts[1]
149
+		}
150
+	}
151
+	return ""
152
+}
0 153
new file mode 100644
... ...
@@ -0,0 +1,5 @@
0
+package lxc
1
+
2
+func setHostname(hostname string) error {
3
+	panic("Not supported on darwin")
4
+}
0 5
new file mode 100644
... ...
@@ -0,0 +1,9 @@
0
+package lxc
1
+
2
+import (
3
+	"syscall"
4
+)
5
+
6
+func setHostname(hostname string) error {
7
+	return syscall.Sethostname([]byte(hostname))
8
+}
0 9
new file mode 100644
... ...
@@ -0,0 +1,141 @@
0
+package lxc
1
+
2
+import (
3
+	"github.com/dotcloud/docker/cgroups"
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
+{{else}}
15
+# network is disabled (-n=false)
16
+lxc.network.type = empty
17
+{{end}}
18
+
19
+# root filesystem
20
+{{$ROOTFS := .Rootfs}}
21
+lxc.rootfs = {{$ROOTFS}}
22
+
23
+# use a dedicated pts for the container (and limit the number of pseudo terminal
24
+# available)
25
+lxc.pts = 1024
26
+
27
+# disable the main console
28
+lxc.console = none
29
+
30
+# no controlling tty at all
31
+lxc.tty = 1
32
+
33
+{{if .Privileged}}
34
+lxc.cgroup.devices.allow = a
35
+{{else}}
36
+# no implicit access to devices
37
+lxc.cgroup.devices.deny = a
38
+
39
+# /dev/null and zero
40
+lxc.cgroup.devices.allow = c 1:3 rwm
41
+lxc.cgroup.devices.allow = c 1:5 rwm
42
+
43
+# consoles
44
+lxc.cgroup.devices.allow = c 5:1 rwm
45
+lxc.cgroup.devices.allow = c 5:0 rwm
46
+lxc.cgroup.devices.allow = c 4:0 rwm
47
+lxc.cgroup.devices.allow = c 4:1 rwm
48
+
49
+# /dev/urandom,/dev/random
50
+lxc.cgroup.devices.allow = c 1:9 rwm
51
+lxc.cgroup.devices.allow = c 1:8 rwm
52
+
53
+# /dev/pts/ - pts namespaces are "coming soon"
54
+lxc.cgroup.devices.allow = c 136:* rwm
55
+lxc.cgroup.devices.allow = c 5:2 rwm
56
+
57
+# tuntap
58
+lxc.cgroup.devices.allow = c 10:200 rwm
59
+
60
+# fuse
61
+#lxc.cgroup.devices.allow = c 10:229 rwm
62
+
63
+# rtc
64
+#lxc.cgroup.devices.allow = c 254:0 rwm
65
+{{end}}
66
+
67
+# standard mount point
68
+# Use mnt.putold as per https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/986385
69
+lxc.pivotdir = lxc_putold
70
+
71
+# NOTICE: These mounts must be applied within the namespace
72
+
73
+#  WARNING: procfs is a known attack vector and should probably be disabled
74
+#           if your userspace allows it. eg. see http://blog.zx2c4.com/749
75
+lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
76
+
77
+# WARNING: sysfs is a known attack vector and should probably be disabled
78
+# if your userspace allows it. eg. see http://bit.ly/T9CkqJ
79
+lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
80
+
81
+lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0
82
+lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0
83
+
84
+{{if .Privileged}}
85
+{{if .AppArmor}}
86
+lxc.aa_profile = unconfined
87
+{{else}}
88
+#lxc.aa_profile = unconfined
89
+{{end}}
90
+{{end}}
91
+
92
+# limits
93
+{{if .Cgroups}}
94
+{{if .Cgroups.Memory}}
95
+lxc.cgroup.memory.limit_in_bytes = {{.Cgroups.Memory}}
96
+lxc.cgroup.memory.soft_limit_in_bytes = {{.Cgroups.Memory}}
97
+{{with $memSwap := getMemorySwap .Cgroups}}
98
+lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}}
99
+{{end}}
100
+{{end}}
101
+{{if .Cgroups.CpuShares}}
102
+lxc.cgroup.cpu.shares = {{.Cgroups.CpuShares}}
103
+{{end}}
104
+{{end}}
105
+
106
+{{if .Config}}
107
+{{range $value := .Config}}
108
+{{$value}}
109
+{{end}}
110
+{{end}}
111
+`
112
+
113
+var LxcTemplateCompiled *template.Template
114
+
115
+// Escape spaces in strings according to the fstab documentation, which is the
116
+// format for "lxc.mount.entry" lines in lxc.conf. See also "man 5 fstab".
117
+func escapeFstabSpaces(field string) string {
118
+	return strings.Replace(field, " ", "\\040", -1)
119
+}
120
+
121
+func getMemorySwap(v *cgroups.Values) int64 {
122
+	// By default, MemorySwap is set to twice the size of RAM.
123
+	// If you want to omit MemorySwap, set it to `-1'.
124
+	if v.MemorySwap < 0 {
125
+		return 0
126
+	}
127
+	return v.Memory * 2
128
+}
129
+
130
+func init() {
131
+	var err error
132
+	funcMap := template.FuncMap{
133
+		"getMemorySwap":     getMemorySwap,
134
+		"escapeFstabSpaces": escapeFstabSpaces,
135
+	}
136
+	LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate)
137
+	if err != nil {
138
+		panic(err)
139
+	}
140
+}
0 141
new file mode 100644
... ...
@@ -0,0 +1,126 @@
0
+package lxc
1
+
2
+import (
3
+	"bufio"
4
+	"fmt"
5
+	"github.com/dotcloud/docker/cgroups"
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
+	process := &execdriver.Process{
41
+		ID: "1",
42
+		Cgroups: &cgroups.Values{
43
+			Memory:    int64(mem),
44
+			CpuShares: int64(cpu),
45
+		},
46
+	}
47
+	p, err := driver.generateLXCConfig(process)
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
+	process := &execdriver.Process{
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(process)
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
+}
... ...
@@ -38,7 +38,6 @@ func mkRuntime(f utils.Fataler) *docker.Runtime {
38 38
 	if err != nil {
39 39
 		f.Fatal(err)
40 40
 	}
41
-	r.UpdateCapabilities(true)
42 41
 	return r
43 42
 }
44 43
 
45 44
deleted file mode 100644
... ...
@@ -1,148 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"strings"
5
-	"text/template"
6
-)
7
-
8
-const LxcTemplate = `
9
-{{if .Config.NetworkDisabled}}
10
-# network is disabled (-n=false)
11
-lxc.network.type = empty
12
-{{else}}
13
-# network configuration
14
-lxc.network.type = veth
15
-lxc.network.link = {{.NetworkSettings.Bridge}}
16
-lxc.network.name = eth0
17
-{{end}}
18
-
19
-# root filesystem
20
-{{$ROOTFS := .RootfsPath}}
21
-lxc.rootfs = {{$ROOTFS}}
22
-
23
-# use a dedicated pts for the container (and limit the number of pseudo terminal
24
-# available)
25
-lxc.pts = 1024
26
-
27
-# disable the main console
28
-lxc.console = none
29
-
30
-# no controlling tty at all
31
-lxc.tty = 1
32
-
33
-{{if (getHostConfig .).Privileged}}
34
-lxc.cgroup.devices.allow = a 
35
-{{else}}
36
-# no implicit access to devices
37
-lxc.cgroup.devices.deny = a
38
-
39
-# /dev/null and zero
40
-lxc.cgroup.devices.allow = c 1:3 rwm
41
-lxc.cgroup.devices.allow = c 1:5 rwm
42
-
43
-# consoles
44
-lxc.cgroup.devices.allow = c 5:1 rwm
45
-lxc.cgroup.devices.allow = c 5:0 rwm
46
-lxc.cgroup.devices.allow = c 4:0 rwm
47
-lxc.cgroup.devices.allow = c 4:1 rwm
48
-
49
-# /dev/urandom,/dev/random
50
-lxc.cgroup.devices.allow = c 1:9 rwm
51
-lxc.cgroup.devices.allow = c 1:8 rwm
52
-
53
-# /dev/pts/ - pts namespaces are "coming soon"
54
-lxc.cgroup.devices.allow = c 136:* rwm
55
-lxc.cgroup.devices.allow = c 5:2 rwm
56
-
57
-# tuntap
58
-lxc.cgroup.devices.allow = c 10:200 rwm
59
-
60
-# fuse
61
-#lxc.cgroup.devices.allow = c 10:229 rwm
62
-
63
-# rtc
64
-#lxc.cgroup.devices.allow = c 254:0 rwm
65
-{{end}}
66
-
67
-# standard mount point
68
-# Use mnt.putold as per https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/986385
69
-lxc.pivotdir = lxc_putold
70
-
71
-# NOTICE: These mounts must be applied within the namespace
72
-
73
-#  WARNING: procfs is a known attack vector and should probably be disabled
74
-#           if your userspace allows it. eg. see http://blog.zx2c4.com/749
75
-lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
76
-
77
-# WARNING: sysfs is a known attack vector and should probably be disabled
78
-# if your userspace allows it. eg. see http://bit.ly/T9CkqJ
79
-lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
80
-
81
-lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0
82
-lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0
83
-
84
-{{if (getHostConfig .).Privileged}}
85
-{{if (getCapabilities .).AppArmor}}
86
-lxc.aa_profile = unconfined
87
-{{else}}
88
-#lxc.aa_profile = unconfined
89
-{{end}}
90
-{{end}}
91
-
92
-# limits
93
-{{if .Config.Memory}}
94
-lxc.cgroup.memory.limit_in_bytes = {{.Config.Memory}}
95
-lxc.cgroup.memory.soft_limit_in_bytes = {{.Config.Memory}}
96
-{{with $memSwap := getMemorySwap .Config}}
97
-lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}}
98
-{{end}}
99
-{{end}}
100
-{{if .Config.CpuShares}}
101
-lxc.cgroup.cpu.shares = {{.Config.CpuShares}}
102
-{{end}}
103
-
104
-{{if (getHostConfig .).LxcConf}}
105
-{{range $pair := (getHostConfig .).LxcConf}}
106
-{{$pair.Key}} = {{$pair.Value}}
107
-{{end}}
108
-{{end}}
109
-`
110
-
111
-var LxcTemplateCompiled *template.Template
112
-
113
-// Escape spaces in strings according to the fstab documentation, which is the
114
-// format for "lxc.mount.entry" lines in lxc.conf. See also "man 5 fstab".
115
-func escapeFstabSpaces(field string) string {
116
-	return strings.Replace(field, " ", "\\040", -1)
117
-}
118
-
119
-func getMemorySwap(config *Config) int64 {
120
-	// By default, MemorySwap is set to twice the size of RAM.
121
-	// If you want to omit MemorySwap, set it to `-1'.
122
-	if config.MemorySwap < 0 {
123
-		return 0
124
-	}
125
-	return config.Memory * 2
126
-}
127
-
128
-func getHostConfig(container *Container) *HostConfig {
129
-	return container.hostConfig
130
-}
131
-
132
-func getCapabilities(container *Container) *Capabilities {
133
-	return container.runtime.capabilities
134
-}
135
-
136
-func init() {
137
-	var err error
138
-	funcMap := template.FuncMap{
139
-		"getMemorySwap":     getMemorySwap,
140
-		"getHostConfig":     getHostConfig,
141
-		"getCapabilities":   getCapabilities,
142
-		"escapeFstabSpaces": escapeFstabSpaces,
143
-	}
144
-	LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate)
145
-	if err != nil {
146
-		panic(err)
147
-	}
148
-}
149 1
deleted file mode 100644
... ...
@@ -1,118 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"bufio"
5
-	"fmt"
6
-	"io/ioutil"
7
-	"math/rand"
8
-	"os"
9
-	"strings"
10
-	"testing"
11
-	"time"
12
-)
13
-
14
-func TestLXCConfig(t *testing.T) {
15
-	root, err := ioutil.TempDir("", "TestLXCConfig")
16
-	if err != nil {
17
-		t.Fatal(err)
18
-	}
19
-	defer os.RemoveAll(root)
20
-	// Memory is allocated randomly for testing
21
-	rand.Seed(time.Now().UTC().UnixNano())
22
-	memMin := 33554432
23
-	memMax := 536870912
24
-	mem := memMin + rand.Intn(memMax-memMin)
25
-	// CPU shares as well
26
-	cpuMin := 100
27
-	cpuMax := 10000
28
-	cpu := cpuMin + rand.Intn(cpuMax-cpuMin)
29
-	container := &Container{
30
-		root: root,
31
-		Config: &Config{
32
-			Memory:          int64(mem),
33
-			CpuShares:       int64(cpu),
34
-			NetworkDisabled: true,
35
-		},
36
-		hostConfig: &HostConfig{
37
-			Privileged: false,
38
-		},
39
-	}
40
-	if err := container.generateLXCConfig(); err != nil {
41
-		t.Fatal(err)
42
-	}
43
-	grepFile(t, container.lxcConfigPath(),
44
-		fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
45
-	grepFile(t, container.lxcConfigPath(),
46
-		fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2))
47
-}
48
-
49
-func TestCustomLxcConfig(t *testing.T) {
50
-	root, err := ioutil.TempDir("", "TestCustomLxcConfig")
51
-	if err != nil {
52
-		t.Fatal(err)
53
-	}
54
-	defer os.RemoveAll(root)
55
-	container := &Container{
56
-		root: root,
57
-		Config: &Config{
58
-			Hostname:        "foobar",
59
-			NetworkDisabled: true,
60
-		},
61
-		hostConfig: &HostConfig{
62
-			Privileged: false,
63
-			LxcConf: []KeyValuePair{
64
-				{
65
-					Key:   "lxc.utsname",
66
-					Value: "docker",
67
-				},
68
-				{
69
-					Key:   "lxc.cgroup.cpuset.cpus",
70
-					Value: "0,1",
71
-				},
72
-			},
73
-		},
74
-	}
75
-	if err := container.generateLXCConfig(); err != nil {
76
-		t.Fatal(err)
77
-	}
78
-	grepFile(t, container.lxcConfigPath(), "lxc.utsname = docker")
79
-	grepFile(t, container.lxcConfigPath(), "lxc.cgroup.cpuset.cpus = 0,1")
80
-}
81
-
82
-func grepFile(t *testing.T, path string, pattern string) {
83
-	f, err := os.Open(path)
84
-	if err != nil {
85
-		t.Fatal(err)
86
-	}
87
-	defer f.Close()
88
-	r := bufio.NewReader(f)
89
-	var (
90
-		line string
91
-	)
92
-	err = nil
93
-	for err == nil {
94
-		line, err = r.ReadString('\n')
95
-		if strings.Contains(line, pattern) == true {
96
-			return
97
-		}
98
-	}
99
-	t.Fatalf("grepFile: pattern \"%s\" not found in \"%s\"", pattern, path)
100
-}
101
-
102
-func TestEscapeFstabSpaces(t *testing.T) {
103
-	var testInputs = map[string]string{
104
-		" ":                      "\\040",
105
-		"":                       "",
106
-		"/double  space":         "/double\\040\\040space",
107
-		"/some long test string": "/some\\040long\\040test\\040string",
108
-		"/var/lib/docker":        "/var/lib/docker",
109
-		" leading":               "\\040leading",
110
-		"trailing ":              "trailing\\040",
111
-	}
112
-	for in, exp := range testInputs {
113
-		if out := escapeFstabSpaces(in); exp != out {
114
-			t.Logf("Expected %s got %s", exp, out)
115
-			t.Fail()
116
-		}
117
-	}
118
-}
... ...
@@ -25,27 +25,37 @@ func Mounted(mountpoint string) (bool, error) {
25 25
 	return false, nil
26 26
 }
27 27
 
28
-// Mount the specified options at the target path
28
+// Mount the specified options at the target path only if
29
+// the target is not mounted
29 30
 // Options must be specified as fstab style
30 31
 func Mount(device, target, mType, options string) error {
31 32
 	if mounted, err := Mounted(target); err != nil || mounted {
32 33
 		return err
33 34
 	}
35
+	return ForceMount(device, target, mType, options)
36
+}
34 37
 
38
+// Mount the specified options at the target path
39
+// reguardless if the target is mounted or not
40
+// Options must be specified as fstab style
41
+func ForceMount(device, target, mType, options string) error {
35 42
 	flag, data := parseOptions(options)
36 43
 	if err := mount(device, target, mType, uintptr(flag), data); err != nil {
37 44
 		return err
38 45
 	}
39 46
 	return nil
40
-
41 47
 }
42 48
 
43 49
 // Unmount the target only if it is mounted
44
-func Unmount(target string) (err error) {
50
+func Unmount(target string) error {
45 51
 	if mounted, err := Mounted(target); err != nil || !mounted {
46 52
 		return err
47 53
 	}
54
+	return ForceUnmount(target)
55
+}
48 56
 
57
+// Unmount the target reguardless if it is mounted or not
58
+func ForceUnmount(target string) (err error) {
49 59
 	// Simple retry logic for unmount
50 60
 	for i := 0; i < 10; i++ {
51 61
 		if err = unmount(target, 0); err == nil {
52 62
new file mode 100644
... ...
@@ -0,0 +1,55 @@
0
+package sysinfo
1
+
2
+import (
3
+	"github.com/dotcloud/docker/cgroups"
4
+	"github.com/dotcloud/docker/utils"
5
+	"io/ioutil"
6
+	"log"
7
+	"os"
8
+	"path"
9
+)
10
+
11
+type SysInfo struct {
12
+	MemoryLimit            bool
13
+	SwapLimit              bool
14
+	IPv4ForwardingDisabled bool
15
+	AppArmor               bool
16
+}
17
+
18
+func New(quiet bool) *SysInfo {
19
+	sysInfo := &SysInfo{}
20
+	if cgroupMemoryMountpoint, err := cgroups.FindCgroupMountpoint("memory"); err != nil {
21
+		if !quiet {
22
+			log.Printf("WARNING: %s\n", err)
23
+		}
24
+	} else {
25
+		_, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.limit_in_bytes"))
26
+		_, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes"))
27
+		sysInfo.MemoryLimit = err1 == nil && err2 == nil
28
+		if !sysInfo.MemoryLimit && !quiet {
29
+			log.Printf("WARNING: Your kernel does not support cgroup memory limit.")
30
+		}
31
+
32
+		_, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes"))
33
+		sysInfo.SwapLimit = err == nil
34
+		if !sysInfo.SwapLimit && !quiet {
35
+			log.Printf("WARNING: Your kernel does not support cgroup swap limit.")
36
+		}
37
+	}
38
+
39
+	content, err3 := ioutil.ReadFile("/proc/sys/net/ipv4/ip_forward")
40
+	sysInfo.IPv4ForwardingDisabled = err3 != nil || len(content) == 0 || content[0] != '1'
41
+	if sysInfo.IPv4ForwardingDisabled && !quiet {
42
+		log.Printf("WARNING: IPv4 forwarding is disabled.")
43
+	}
44
+
45
+	// Check if AppArmor seems to be enabled on this system.
46
+	if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) {
47
+		utils.Debugf("/sys/kernel/security/apparmor not found; assuming AppArmor is not enabled.")
48
+		sysInfo.AppArmor = false
49
+	} else {
50
+		utils.Debugf("/sys/kernel/security/apparmor found; assuming AppArmor is enabled.")
51
+		sysInfo.AppArmor = true
52
+	}
53
+	return sysInfo
54
+}
... ...
@@ -4,18 +4,19 @@ import (
4 4
 	"container/list"
5 5
 	"fmt"
6 6
 	"github.com/dotcloud/docker/archive"
7
-	"github.com/dotcloud/docker/cgroups"
7
+	"github.com/dotcloud/docker/execdriver"
8
+	"github.com/dotcloud/docker/execdriver/chroot"
9
+	"github.com/dotcloud/docker/execdriver/lxc"
8 10
 	"github.com/dotcloud/docker/graphdriver"
9 11
 	"github.com/dotcloud/docker/graphdriver/aufs"
10 12
 	_ "github.com/dotcloud/docker/graphdriver/devmapper"
11 13
 	_ "github.com/dotcloud/docker/graphdriver/vfs"
12 14
 	"github.com/dotcloud/docker/pkg/graphdb"
15
+	"github.com/dotcloud/docker/pkg/sysinfo"
13 16
 	"github.com/dotcloud/docker/utils"
14 17
 	"io"
15 18
 	"io/ioutil"
16
-	"log"
17 19
 	"os"
18
-	"os/exec"
19 20
 	"path"
20 21
 	"regexp"
21 22
 	"sort"
... ...
@@ -35,13 +36,6 @@ var (
35 35
 	validContainerNamePattern = regexp.MustCompile(`^/?` + validContainerNameChars + `+$`)
36 36
 )
37 37
 
38
-type Capabilities struct {
39
-	MemoryLimit            bool
40
-	SwapLimit              bool
41
-	IPv4ForwardingDisabled bool
42
-	AppArmor               bool
43
-}
44
-
45 38
 type Runtime struct {
46 39
 	repository     string
47 40
 	sysInitPath    string
... ...
@@ -50,12 +44,13 @@ type Runtime struct {
50 50
 	graph          *Graph
51 51
 	repositories   *TagStore
52 52
 	idIndex        *utils.TruncIndex
53
-	capabilities   *Capabilities
53
+	sysInfo        *sysinfo.SysInfo
54 54
 	volumes        *Graph
55 55
 	srv            *Server
56 56
 	config         *DaemonConfig
57 57
 	containerGraph *graphdb.Database
58 58
 	driver         graphdriver.Driver
59
+	execDriver     execdriver.Driver
59 60
 }
60 61
 
61 62
 // List returns an array of all containers registered in the runtime.
... ...
@@ -160,11 +155,9 @@ func (runtime *Runtime) Register(container *Container) error {
160 160
 	//        if so, then we need to restart monitor and init a new lock
161 161
 	// If the container is supposed to be running, make sure of it
162 162
 	if container.State.IsRunning() {
163
-		output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
164
-		if err != nil {
165
-			return err
166
-		}
167
-		if !strings.Contains(string(output), "RUNNING") {
163
+		info := runtime.execDriver.Info(container.ID)
164
+
165
+		if !info.IsRunning() {
168 166
 			utils.Debugf("Container %s was supposed to be running but is not.", container.ID)
169 167
 			if runtime.config.AutoRestart {
170 168
 				utils.Debugf("Restarting")
... ...
@@ -188,7 +181,7 @@ func (runtime *Runtime) Register(container *Container) error {
188 188
 			}
189 189
 
190 190
 			container.waitLock = make(chan struct{})
191
-			go container.monitor()
191
+			go container.monitor(nil)
192 192
 		}
193 193
 	}
194 194
 	return nil
... ...
@@ -331,43 +324,6 @@ func (runtime *Runtime) restore() error {
331 331
 	return nil
332 332
 }
333 333
 
334
-// FIXME: comment please!
335
-func (runtime *Runtime) UpdateCapabilities(quiet bool) {
336
-	if cgroupMemoryMountpoint, err := cgroups.FindCgroupMountpoint("memory"); err != nil {
337
-		if !quiet {
338
-			log.Printf("WARNING: %s\n", err)
339
-		}
340
-	} else {
341
-		_, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.limit_in_bytes"))
342
-		_, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes"))
343
-		runtime.capabilities.MemoryLimit = err1 == nil && err2 == nil
344
-		if !runtime.capabilities.MemoryLimit && !quiet {
345
-			log.Printf("WARNING: Your kernel does not support cgroup memory limit.")
346
-		}
347
-
348
-		_, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes"))
349
-		runtime.capabilities.SwapLimit = err == nil
350
-		if !runtime.capabilities.SwapLimit && !quiet {
351
-			log.Printf("WARNING: Your kernel does not support cgroup swap limit.")
352
-		}
353
-	}
354
-
355
-	content, err3 := ioutil.ReadFile("/proc/sys/net/ipv4/ip_forward")
356
-	runtime.capabilities.IPv4ForwardingDisabled = err3 != nil || len(content) == 0 || content[0] != '1'
357
-	if runtime.capabilities.IPv4ForwardingDisabled && !quiet {
358
-		log.Printf("WARNING: IPv4 forwarding is disabled.")
359
-	}
360
-
361
-	// Check if AppArmor seems to be enabled on this system.
362
-	if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) {
363
-		utils.Debugf("/sys/kernel/security/apparmor not found; assuming AppArmor is not enabled.")
364
-		runtime.capabilities.AppArmor = false
365
-	} else {
366
-		utils.Debugf("/sys/kernel/security/apparmor found; assuming AppArmor is enabled.")
367
-		runtime.capabilities.AppArmor = true
368
-	}
369
-}
370
-
371 334
 // Create creates a new container from the given configuration with a given name.
372 335
 func (runtime *Runtime) Create(config *Config, name string) (*Container, []string, error) {
373 336
 	// Lookup image
... ...
@@ -646,7 +602,6 @@ func NewRuntime(config *DaemonConfig) (*Runtime, error) {
646 646
 	if err != nil {
647 647
 		return nil, err
648 648
 	}
649
-	runtime.UpdateCapabilities(false)
650 649
 	return runtime, nil
651 650
 }
652 651
 
... ...
@@ -675,10 +630,6 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
675 675
 		}
676 676
 	}
677 677
 
678
-	utils.Debugf("Escaping AppArmor confinement")
679
-	if err := linkLxcStart(config.Root); err != nil {
680
-		return nil, err
681
-	}
682 678
 	utils.Debugf("Creating images graph")
683 679
 	g, err := NewGraph(path.Join(config.Root, "graph"), driver)
684 680
 	if err != nil {
... ...
@@ -735,6 +686,26 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
735 735
 		sysInitPath = localCopy
736 736
 	}
737 737
 
738
+	sysInfo := sysinfo.New(false)
739
+
740
+	/*
741
+		temporarilly disabled.
742
+	*/
743
+	if false {
744
+		var ed execdriver.Driver
745
+		if driver := os.Getenv("EXEC_DRIVER"); driver == "lxc" {
746
+			ed, err = lxc.NewDriver(config.Root, sysInfo.AppArmor)
747
+		} else {
748
+			ed, err = chroot.NewDriver()
749
+		}
750
+		if ed != nil {
751
+		}
752
+	}
753
+	ed, err := lxc.NewDriver(config.Root, sysInfo.AppArmor)
754
+	if err != nil {
755
+		return nil, err
756
+	}
757
+
738 758
 	runtime := &Runtime{
739 759
 		repository:     runtimeRepo,
740 760
 		containers:     list.New(),
... ...
@@ -742,12 +713,13 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
742 742
 		graph:          g,
743 743
 		repositories:   repositories,
744 744
 		idIndex:        utils.NewTruncIndex(),
745
-		capabilities:   &Capabilities{},
745
+		sysInfo:        sysInfo,
746 746
 		volumes:        volumes,
747 747
 		config:         config,
748 748
 		containerGraph: graph,
749 749
 		driver:         driver,
750 750
 		sysInitPath:    sysInitPath,
751
+		execDriver:     ed,
751 752
 	}
752 753
 
753 754
 	if err := runtime.restore(); err != nil {
... ...
@@ -829,6 +801,18 @@ func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) {
829 829
 	return archive.ExportChanges(cDir, changes)
830 830
 }
831 831
 
832
+func (runtime *Runtime) Run(c *Container, startCallback execdriver.StartCallback) (int, error) {
833
+	return runtime.execDriver.Run(c.process, startCallback)
834
+}
835
+
836
+func (runtime *Runtime) Kill(c *Container, sig int) error {
837
+	return runtime.execDriver.Kill(c.process, sig)
838
+}
839
+
840
+func (runtime *Runtime) WaitGhost(c *Container) error {
841
+	return runtime.execDriver.Wait(c.ID)
842
+}
843
+
832 844
 // Nuke kills all containers then removes all content
833 845
 // from the content root, including images, volumes and
834 846
 // container filesystems.
... ...
@@ -848,23 +832,6 @@ func (runtime *Runtime) Nuke() error {
848 848
 	return os.RemoveAll(runtime.config.Root)
849 849
 }
850 850
 
851
-func linkLxcStart(root string) error {
852
-	sourcePath, err := exec.LookPath("lxc-start")
853
-	if err != nil {
854
-		return err
855
-	}
856
-	targetPath := path.Join(root, "lxc-start-unconfined")
857
-
858
-	if _, err := os.Lstat(targetPath); err != nil && !os.IsNotExist(err) {
859
-		return err
860
-	} else if err == nil {
861
-		if err := os.Remove(targetPath); err != nil {
862
-			return err
863
-		}
864
-	}
865
-	return os.Symlink(sourcePath, targetPath)
866
-}
867
-
868 851
 // FIXME: this is a convenience function for integration tests
869 852
 // which need direct access to runtime.graph.
870 853
 // Once the tests switch to using engine and jobs, this method
... ...
@@ -516,7 +516,7 @@ func (srv *Server) ImageInsert(job *engine.Job) engine.Status {
516 516
 	}
517 517
 	defer file.Body.Close()
518 518
 
519
-	config, _, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.capabilities)
519
+	config, _, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.sysInfo)
520 520
 	if err != nil {
521 521
 		job.Error(err)
522 522
 		return engine.StatusErr
... ...
@@ -661,13 +661,6 @@ func (srv *Server) DockerInfo(job *engine.Job) engine.Status {
661 661
 	} else {
662 662
 		imgcount = len(images)
663 663
 	}
664
-	lxcVersion := ""
665
-	if output, err := exec.Command("lxc-version").CombinedOutput(); err == nil {
666
-		outputStr := string(output)
667
-		if len(strings.SplitN(outputStr, ":", 2)) == 2 {
668
-			lxcVersion = strings.TrimSpace(strings.SplitN(string(output), ":", 2)[1])
669
-		}
670
-	}
671 664
 	kernelVersion := "<unknown>"
672 665
 	if kv, err := utils.GetKernelVersion(); err == nil {
673 666
 		kernelVersion = kv.String()
... ...
@@ -685,13 +678,13 @@ func (srv *Server) DockerInfo(job *engine.Job) engine.Status {
685 685
 	v.SetInt("Images", imgcount)
686 686
 	v.Set("Driver", srv.runtime.driver.String())
687 687
 	v.SetJson("DriverStatus", srv.runtime.driver.Status())
688
-	v.SetBool("MemoryLimit", srv.runtime.capabilities.MemoryLimit)
689
-	v.SetBool("SwapLimit", srv.runtime.capabilities.SwapLimit)
690
-	v.SetBool("IPv4Forwarding", !srv.runtime.capabilities.IPv4ForwardingDisabled)
688
+	v.SetBool("MemoryLimit", srv.runtime.sysInfo.MemoryLimit)
689
+	v.SetBool("SwapLimit", srv.runtime.sysInfo.SwapLimit)
690
+	v.SetBool("IPv4Forwarding", !srv.runtime.sysInfo.IPv4ForwardingDisabled)
691 691
 	v.SetBool("Debug", os.Getenv("DEBUG") != "")
692 692
 	v.SetInt("NFd", utils.GetTotalUsedFds())
693 693
 	v.SetInt("NGoroutines", runtime.NumGoroutine())
694
-	v.Set("LXCVersion", lxcVersion)
694
+	v.Set("ExecutionDriver", srv.runtime.execDriver.Name())
695 695
 	v.SetInt("NEventsListener", len(srv.events))
696 696
 	v.Set("KernelVersion", kernelVersion)
697 697
 	v.Set("IndexServerAddress", auth.IndexServerAddress())
... ...
@@ -1477,10 +1470,10 @@ func (srv *Server) ContainerCreate(job *engine.Job) engine.Status {
1477 1477
 		job.Errorf("Minimum memory limit allowed is 512k")
1478 1478
 		return engine.StatusErr
1479 1479
 	}
1480
-	if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
1480
+	if config.Memory > 0 && !srv.runtime.sysInfo.MemoryLimit {
1481 1481
 		config.Memory = 0
1482 1482
 	}
1483
-	if config.Memory > 0 && !srv.runtime.capabilities.SwapLimit {
1483
+	if config.Memory > 0 && !srv.runtime.sysInfo.SwapLimit {
1484 1484
 		config.MemorySwap = -1
1485 1485
 	}
1486 1486
 	container, buildWarnings, err := srv.runtime.Create(&config, name)
... ...
@@ -4,163 +4,19 @@ import (
4 4
 	"encoding/json"
5 5
 	"flag"
6 6
 	"fmt"
7
-	"github.com/dotcloud/docker/pkg/netlink"
8
-	"github.com/dotcloud/docker/utils"
9
-	"github.com/syndtr/gocapability/capability"
7
+	"github.com/dotcloud/docker/execdriver"
8
+	_ "github.com/dotcloud/docker/execdriver/chroot"
9
+	_ "github.com/dotcloud/docker/execdriver/lxc"
10 10
 	"io/ioutil"
11 11
 	"log"
12
-	"net"
13 12
 	"os"
14
-	"os/exec"
15
-	"strconv"
16 13
 	"strings"
17
-	"syscall"
18 14
 )
19 15
 
20
-type DockerInitArgs struct {
21
-	user       string
22
-	gateway    string
23
-	ip         string
24
-	workDir    string
25
-	privileged bool
26
-	env        []string
27
-	args       []string
28
-	mtu        int
29
-}
30
-
31
-func setupHostname(args *DockerInitArgs) error {
32
-	hostname := getEnv(args, "HOSTNAME")
33
-	if hostname == "" {
34
-		return nil
35
-	}
36
-	return setHostname(hostname)
37
-}
38
-
39
-// Setup networking
40
-func setupNetworking(args *DockerInitArgs) error {
41
-	if args.ip != "" {
42
-		// eth0
43
-		iface, err := net.InterfaceByName("eth0")
44
-		if err != nil {
45
-			return fmt.Errorf("Unable to set up networking: %v", err)
46
-		}
47
-		ip, ipNet, err := net.ParseCIDR(args.ip)
48
-		if err != nil {
49
-			return fmt.Errorf("Unable to set up networking: %v", err)
50
-		}
51
-		if err := netlink.NetworkLinkAddIp(iface, ip, ipNet); err != nil {
52
-			return fmt.Errorf("Unable to set up networking: %v", err)
53
-		}
54
-		if err := netlink.NetworkSetMTU(iface, args.mtu); err != nil {
55
-			return fmt.Errorf("Unable to set MTU: %v", err)
56
-		}
57
-		if err := netlink.NetworkLinkUp(iface); err != nil {
58
-			return fmt.Errorf("Unable to set up networking: %v", err)
59
-		}
60
-
61
-		// loopback
62
-		iface, err = net.InterfaceByName("lo")
63
-		if err != nil {
64
-			return fmt.Errorf("Unable to set up networking: %v", err)
65
-		}
66
-		if err := netlink.NetworkLinkUp(iface); err != nil {
67
-			return fmt.Errorf("Unable to set up networking: %v", err)
68
-		}
69
-	}
70
-	if args.gateway != "" {
71
-		gw := net.ParseIP(args.gateway)
72
-		if gw == nil {
73
-			return fmt.Errorf("Unable to set up networking, %s is not a valid gateway IP", args.gateway)
74
-		}
75
-
76
-		if err := netlink.AddDefaultGw(gw); err != nil {
77
-			return fmt.Errorf("Unable to set up networking: %v", err)
78
-		}
79
-	}
80
-
81
-	return nil
82
-}
83
-
84
-// Setup working directory
85
-func setupWorkingDirectory(args *DockerInitArgs) error {
86
-	if args.workDir == "" {
87
-		return nil
88
-	}
89
-	if err := syscall.Chdir(args.workDir); err != nil {
90
-		return fmt.Errorf("Unable to change dir to %v: %v", args.workDir, err)
91
-	}
92
-	return nil
93
-}
94
-
95
-// Takes care of dropping privileges to the desired user
96
-func changeUser(args *DockerInitArgs) error {
97
-	if args.user == "" {
98
-		return nil
99
-	}
100
-	userent, err := utils.UserLookup(args.user)
101
-	if err != nil {
102
-		return fmt.Errorf("Unable to find user %v: %v", args.user, err)
103
-	}
104
-
105
-	uid, err := strconv.Atoi(userent.Uid)
106
-	if err != nil {
107
-		return fmt.Errorf("Invalid uid: %v", userent.Uid)
108
-	}
109
-	gid, err := strconv.Atoi(userent.Gid)
110
-	if err != nil {
111
-		return fmt.Errorf("Invalid gid: %v", userent.Gid)
112
-	}
113
-
114
-	if err := syscall.Setgid(gid); err != nil {
115
-		return fmt.Errorf("setgid failed: %v", err)
116
-	}
117
-	if err := syscall.Setuid(uid); err != nil {
118
-		return fmt.Errorf("setuid failed: %v", err)
119
-	}
120
-
121
-	return nil
122
-}
123
-
124
-func setupCapabilities(args *DockerInitArgs) error {
125
-
126
-	if args.privileged {
127
-		return nil
128
-	}
129
-
130
-	drop := []capability.Cap{
131
-		capability.CAP_SETPCAP,
132
-		capability.CAP_SYS_MODULE,
133
-		capability.CAP_SYS_RAWIO,
134
-		capability.CAP_SYS_PACCT,
135
-		capability.CAP_SYS_ADMIN,
136
-		capability.CAP_SYS_NICE,
137
-		capability.CAP_SYS_RESOURCE,
138
-		capability.CAP_SYS_TIME,
139
-		capability.CAP_SYS_TTY_CONFIG,
140
-		capability.CAP_MKNOD,
141
-		capability.CAP_AUDIT_WRITE,
142
-		capability.CAP_AUDIT_CONTROL,
143
-		capability.CAP_MAC_OVERRIDE,
144
-		capability.CAP_MAC_ADMIN,
145
-	}
146
-
147
-	c, err := capability.NewPid(os.Getpid())
148
-	if err != nil {
149
-		return err
150
-	}
151
-
152
-	c.Unset(capability.CAPS|capability.BOUNDS, drop...)
153
-
154
-	if err := c.Apply(capability.CAPS | capability.BOUNDS); err != nil {
155
-		return err
156
-	}
157
-	return nil
158
-}
159
-
160 16
 // Clear environment pollution introduced by lxc-start
161
-func setupEnv(args *DockerInitArgs) {
17
+func setupEnv(args *execdriver.InitArgs) {
162 18
 	os.Clearenv()
163
-	for _, kv := range args.env {
19
+	for _, kv := range args.Env {
164 20
 		parts := strings.SplitN(kv, "=", 2)
165 21
 		if len(parts) == 1 {
166 22
 			parts = append(parts, "")
... ...
@@ -169,50 +25,19 @@ func setupEnv(args *DockerInitArgs) {
169 169
 	}
170 170
 }
171 171
 
172
-func getEnv(args *DockerInitArgs, key string) string {
173
-	for _, kv := range args.env {
174
-		parts := strings.SplitN(kv, "=", 2)
175
-		if parts[0] == key && len(parts) == 2 {
176
-			return parts[1]
177
-		}
178
-	}
179
-	return ""
180
-}
181
-
182
-func executeProgram(args *DockerInitArgs) error {
172
+func executeProgram(args *execdriver.InitArgs) error {
183 173
 	setupEnv(args)
184
-
185
-	if err := setupHostname(args); err != nil {
186
-		return err
187
-	}
188
-
189
-	if err := setupNetworking(args); err != nil {
190
-		return err
191
-	}
192
-
193
-	if err := setupCapabilities(args); err != nil {
194
-		return err
195
-	}
196
-
197
-	if err := setupWorkingDirectory(args); err != nil {
198
-		return err
199
-	}
200
-
201
-	if err := changeUser(args); err != nil {
202
-		return err
203
-	}
204
-
205
-	path, err := exec.LookPath(args.args[0])
174
+	dockerInitFct, err := execdriver.GetInitFunc(args.Driver)
206 175
 	if err != nil {
207
-		log.Printf("Unable to locate %v", args.args[0])
208
-		os.Exit(127)
176
+		panic(err)
209 177
 	}
178
+	return dockerInitFct(args)
210 179
 
211
-	if err := syscall.Exec(path, args.args, os.Environ()); err != nil {
212
-		return fmt.Errorf("dockerinit unable to execute %s - %s", path, err)
180
+	if args.Driver == "lxc" {
181
+		// Will never reach
182
+	} else if args.Driver == "chroot" {
213 183
 	}
214 184
 
215
-	// Will never reach here
216 185
 	return nil
217 186
 }
218 187
 
... ...
@@ -232,6 +57,7 @@ func SysInit() {
232 232
 	workDir := flag.String("w", "", "workdir")
233 233
 	privileged := flag.Bool("privileged", false, "privileged mode")
234 234
 	mtu := flag.Int("mtu", 1500, "interface mtu")
235
+	driver := flag.String("driver", "", "exec driver")
235 236
 	flag.Parse()
236 237
 
237 238
 	// Get env
... ...
@@ -247,15 +73,16 @@ func SysInit() {
247 247
 	// Propagate the plugin-specific container env variable
248 248
 	env = append(env, "container="+os.Getenv("container"))
249 249
 
250
-	args := &DockerInitArgs{
251
-		user:       *user,
252
-		gateway:    *gateway,
253
-		ip:         *ip,
254
-		workDir:    *workDir,
255
-		privileged: *privileged,
256
-		env:        env,
257
-		args:       flag.Args(),
258
-		mtu:        *mtu,
250
+	args := &execdriver.InitArgs{
251
+		User:       *user,
252
+		Gateway:    *gateway,
253
+		Ip:         *ip,
254
+		WorkDir:    *workDir,
255
+		Privileged: *privileged,
256
+		Env:        env,
257
+		Args:       flag.Args(),
258
+		Mtu:        *mtu,
259
+		Driver:     *driver,
259 260
 	}
260 261
 
261 262
 	if err := executeProgram(args); err != nil {
262 263
deleted file mode 100644
... ...
@@ -1,5 +0,0 @@
1
-package sysinit
2
-
3
-func setHostname(hostname string) error {
4
-	panic("Not supported on darwin")
5
-}
6 1
deleted file mode 100644
... ...
@@ -1,9 +0,0 @@
1
-package sysinit
2
-
3
-import (
4
-	"syscall"
5
-)
6
-
7
-func setHostname(hostname string) error {
8
-	return syscall.Sethostname([]byte(hostname))
9
-}
... ...
@@ -5,7 +5,6 @@ import (
5 5
 	"github.com/dotcloud/docker/archive"
6 6
 	"github.com/dotcloud/docker/pkg/namesgenerator"
7 7
 	"github.com/dotcloud/docker/utils"
8
-	"io/ioutil"
9 8
 	"strconv"
10 9
 	"strings"
11 10
 )
... ...
@@ -328,20 +327,6 @@ func parseLink(rawLink string) (map[string]string, error) {
328 328
 	return utils.PartParser("name:alias", rawLink)
329 329
 }
330 330
 
331
-func RootIsShared() bool {
332
-	if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil {
333
-		for _, line := range strings.Split(string(data), "\n") {
334
-			cols := strings.Split(line, " ")
335
-			if len(cols) >= 6 && cols[4] == "/" {
336
-				return strings.HasPrefix(cols[6], "shared")
337
-			}
338
-		}
339
-	}
340
-
341
-	// No idea, probably safe to assume so
342
-	return true
343
-}
344
-
345 331
 type checker struct {
346 332
 	runtime *Runtime
347 333
 }