Browse code

Add syncpipe for passing context Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby authored on 2014/02/22 15:58:30
Showing 4 changed files
... ...
@@ -3,7 +3,6 @@
3 3
 package nsinit
4 4
 
5 5
 import (
6
-	"encoding/json"
7 6
 	"fmt"
8 7
 	"github.com/dotcloud/docker/pkg/libcontainer"
9 8
 	"github.com/dotcloud/docker/pkg/libcontainer/network"
... ...
@@ -28,11 +27,10 @@ func Exec(container *libcontainer.Container, stdin io.Reader, stdout, stderr io.
28 28
 
29 29
 	// create a pipe so that we can syncronize with the namespaced process and
30 30
 	// pass the veth name to the child
31
-	r, w, err := os.Pipe()
31
+	syncPipe, err := NewSyncPipe()
32 32
 	if err != nil {
33 33
 		return -1, err
34 34
 	}
35
-	system.UsetCloseOnExec(r.Fd())
36 35
 
37 36
 	if container.Tty {
38 37
 		log.Printf("setting up master and console")
... ...
@@ -42,8 +40,7 @@ func Exec(container *libcontainer.Container, stdin io.Reader, stdout, stderr io.
42 42
 		}
43 43
 	}
44 44
 
45
-	command := CreateCommand(container, console, logFile, r.Fd(), args)
46
-
45
+	command := CreateCommand(container, console, logFile, syncPipe.child.Fd(), args)
47 46
 	if container.Tty {
48 47
 		log.Printf("starting copy for tty")
49 48
 		go io.Copy(stdout, master)
... ...
@@ -79,15 +76,14 @@ func Exec(container *libcontainer.Container, stdin io.Reader, stdout, stderr io.
79 79
 		command.Process.Kill()
80 80
 		return -1, err
81 81
 	}
82
-	if err := InitializeNetworking(container, command.Process.Pid, w); err != nil {
82
+	if err := InitializeNetworking(container, command.Process.Pid, syncPipe); err != nil {
83 83
 		command.Process.Kill()
84 84
 		return -1, err
85 85
 	}
86 86
 
87 87
 	// Sync with child
88 88
 	log.Printf("closing sync pipes")
89
-	w.Close()
90
-	r.Close()
89
+	syncPipe.Close()
91 90
 
92 91
 	log.Printf("waiting on process")
93 92
 	if err := command.Wait(); err != nil {
... ...
@@ -109,7 +105,7 @@ func SetupCgroups(container *libcontainer.Container, nspid int) error {
109 109
 	return nil
110 110
 }
111 111
 
112
-func InitializeNetworking(container *libcontainer.Container, nspid int, pipe io.Writer) error {
112
+func InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error {
113 113
 	if container.Network != nil {
114 114
 		log.Printf("creating host network configuration type %s", container.Network.Type)
115 115
 		strategy, err := network.GetStrategy(container.Network.Type)
... ...
@@ -121,24 +117,13 @@ func InitializeNetworking(container *libcontainer.Container, nspid int, pipe io.
121 121
 			return err
122 122
 		}
123 123
 		log.Printf("sending %v as network context", networkContext)
124
-		if err := SendContext(pipe, networkContext); err != nil {
124
+		if err := pipe.SendToChild(networkContext); err != nil {
125 125
 			return err
126 126
 		}
127 127
 	}
128 128
 	return nil
129 129
 }
130 130
 
131
-// SendContext writes the veth pair name to the child's stdin then closes the
132
-// pipe so that the child stops waiting for more data
133
-func SendContext(pipe io.Writer, context libcontainer.Context) error {
134
-	data, err := json.Marshal(context)
135
-	if err != nil {
136
-		return err
137
-	}
138
-	pipe.Write(data)
139
-	return nil
140
-}
141
-
142 131
 // SetupWindow gets the parent window size and sets the master
143 132
 // pty to the current size and set the parents mode to RAW
144 133
 func SetupWindow(master, parent *os.File) (*term.State, error) {
... ...
@@ -3,14 +3,11 @@
3 3
 package nsinit
4 4
 
5 5
 import (
6
-	"encoding/json"
7 6
 	"fmt"
8 7
 	"github.com/dotcloud/docker/pkg/libcontainer"
9 8
 	"github.com/dotcloud/docker/pkg/libcontainer/capabilities"
10 9
 	"github.com/dotcloud/docker/pkg/libcontainer/network"
11 10
 	"github.com/dotcloud/docker/pkg/system"
12
-	"io"
13
-	"io/ioutil"
14 11
 	"log"
15 12
 	"os"
16 13
 	"os/exec"
... ...
@@ -20,7 +17,7 @@ import (
20 20
 
21 21
 // Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
22 22
 // and other options required for the new container.
23
-func Init(container *libcontainer.Container, uncleanRootfs, console string, pipe io.ReadCloser, args []string) error {
23
+func Init(container *libcontainer.Container, uncleanRootfs, console string, syncPipe *SyncPipe, args []string) error {
24 24
 	rootfs, err := resolveRootfs(uncleanRootfs)
25 25
 	if err != nil {
26 26
 		return err
... ...
@@ -28,16 +25,18 @@ func Init(container *libcontainer.Container, uncleanRootfs, console string, pipe
28 28
 	log.Printf("initializing namespace at %s", rootfs)
29 29
 
30 30
 	// We always read this as it is a way to sync with the parent as well
31
-	context, err := GetContextFromParent(pipe)
31
+	context, err := syncPipe.ReadFromParent()
32 32
 	if err != nil {
33
+		syncPipe.Close()
33 34
 		return err
34 35
 	}
36
+	syncPipe.Close()
37
+	log.Printf("received context from parent %v", context)
38
+
35 39
 	if console != "" {
36 40
 		log.Printf("setting up console for %s", console)
37 41
 		// close pipes so that we can replace it with the pty
38
-		os.Stdin.Close()
39
-		os.Stdout.Close()
40
-		os.Stderr.Close()
42
+		closeStdPipes()
41 43
 		slave, err := openTerminal(console, syscall.O_RDWR)
42 44
 		if err != nil {
43 45
 			return fmt.Errorf("open terminal %s", err)
... ...
@@ -79,18 +78,27 @@ func Init(container *libcontainer.Container, uncleanRootfs, console string, pipe
79 79
 			return fmt.Errorf("chdir to %s %s", container.WorkingDir, err)
80 80
 		}
81 81
 	}
82
+	return execArgs(args, container.Env)
83
+}
84
+
85
+func execArgs(args []string, env []string) error {
82 86
 	name, err := exec.LookPath(args[0])
83 87
 	if err != nil {
84 88
 		return err
85 89
 	}
86
-
87 90
 	log.Printf("execing %s goodbye", name)
88
-	if err := system.Exec(name, args[0:], container.Env); err != nil {
91
+	if err := system.Exec(name, args[0:], env); err != nil {
89 92
 		return fmt.Errorf("exec %s", err)
90 93
 	}
91 94
 	panic("unreachable")
92 95
 }
93 96
 
97
+func closeStdPipes() {
98
+	os.Stdin.Close()
99
+	os.Stdout.Close()
100
+	os.Stderr.Close()
101
+}
102
+
94 103
 // resolveRootfs ensures that the current working directory is
95 104
 // not a symlink and returns the absolute path to the rootfs
96 105
 func resolveRootfs(uncleanRootfs string) (string, error) {
... ...
@@ -153,19 +161,3 @@ func setupNetwork(config *libcontainer.Network, context libcontainer.Context) er
153 153
 	}
154 154
 	return nil
155 155
 }
156
-
157
-func GetContextFromParent(pipe io.ReadCloser) (libcontainer.Context, error) {
158
-	defer pipe.Close()
159
-	data, err := ioutil.ReadAll(pipe)
160
-	if err != nil {
161
-		return nil, fmt.Errorf("error reading from stdin %s", err)
162
-	}
163
-	var context libcontainer.Context
164
-	if len(data) > 0 {
165
-		if err := json.Unmarshal(data, &context); err != nil {
166
-			return nil, err
167
-		}
168
-		log.Printf("received context %v", context)
169
-	}
170
-	return context, nil
171
-}
... ...
@@ -74,7 +74,11 @@ func main() {
74 74
 		if flag.NArg() < 2 {
75 75
 			log.Fatal(ErrWrongArguments)
76 76
 		}
77
-		if err := nsinit.Init(container, cwd, console, os.NewFile(uintptr(pipeFd), "pipe"), flag.Args()[1:]); err != nil {
77
+		syncPipe, err := nsinit.NewSyncPipeFromFd(0, uintptr(pipeFd))
78
+		if err != nil {
79
+			log.Fatal(err)
80
+		}
81
+		if err := nsinit.Init(container, cwd, console, syncPipe, flag.Args()[1:]); err != nil {
78 82
 			log.Fatal(err)
79 83
 		}
80 84
 	default:
81 85
new file mode 100644
... ...
@@ -0,0 +1,73 @@
0
+package nsinit
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"github.com/dotcloud/docker/pkg/libcontainer"
6
+	"github.com/dotcloud/docker/pkg/system"
7
+	"io/ioutil"
8
+	"os"
9
+)
10
+
11
+// SyncPipe allows communication to and from the child processes
12
+// to it's parent and allows the two independent processes to
13
+// syncronize their state.
14
+type SyncPipe struct {
15
+	parent, child *os.File
16
+}
17
+
18
+func NewSyncPipe() (s *SyncPipe, err error) {
19
+	s = &SyncPipe{}
20
+	s.child, s.parent, err = os.Pipe()
21
+	if err != nil {
22
+		return nil, err
23
+	}
24
+	system.UsetCloseOnExec(s.child.Fd())
25
+	return s, nil
26
+}
27
+
28
+func NewSyncPipeFromFd(parendFd, childFd uintptr) (*SyncPipe, error) {
29
+	s := &SyncPipe{}
30
+	if parendFd > 0 {
31
+		s.parent = os.NewFile(parendFd, "parendPipe")
32
+	} else if childFd > 0 {
33
+		s.child = os.NewFile(childFd, "childPipe")
34
+	} else {
35
+		return nil, fmt.Errorf("no valid sync pipe fd specified")
36
+	}
37
+	return s, nil
38
+}
39
+
40
+func (s *SyncPipe) SendToChild(context libcontainer.Context) error {
41
+	data, err := json.Marshal(context)
42
+	if err != nil {
43
+		return err
44
+	}
45
+	s.parent.Write(data)
46
+	return nil
47
+}
48
+
49
+func (s *SyncPipe) ReadFromParent() (libcontainer.Context, error) {
50
+	data, err := ioutil.ReadAll(s.child)
51
+	if err != nil {
52
+		return nil, fmt.Errorf("error reading from sync pipe %s", err)
53
+	}
54
+	var context libcontainer.Context
55
+	if len(data) > 0 {
56
+		if err := json.Unmarshal(data, &context); err != nil {
57
+			return nil, err
58
+		}
59
+	}
60
+	return context, nil
61
+
62
+}
63
+
64
+func (s *SyncPipe) Close() error {
65
+	if s.parent != nil {
66
+		s.parent.Close()
67
+	}
68
+	if s.child != nil {
69
+		s.child.Close()
70
+	}
71
+	return nil
72
+}