3
3
package namespaces
4
4
5
5
import (
6
+ "encoding/json"
6
7
"io"
7
8
"os"
8
9
"os/exec"
@@ -13,7 +14,6 @@ import (
13
14
"github.com/docker/libcontainer/cgroups/fs"
14
15
"github.com/docker/libcontainer/cgroups/systemd"
15
16
"github.com/docker/libcontainer/network"
16
- "github.com/docker/libcontainer/syncpipe"
17
17
"github.com/docker/libcontainer/system"
18
18
)
19
19
@@ -22,19 +22,17 @@ import (
22
22
// Exec performs setup outside of a namespace so that a container can be
23
23
// executed. Exec is a high level function for working with container namespaces.
24
24
func Exec (container * libcontainer.Config , stdin io.Reader , stdout , stderr io.Writer , console , dataPath string , args []string , createCommand CreateCommand , startCallback func ()) (int , error ) {
25
- var (
26
- err error
27
- )
25
+ var err error
28
26
29
27
// create a pipe so that we can syncronize with the namespaced process and
30
- // pass the veth name to the child
31
- syncPipe , err := syncpipe . NewSyncPipe ()
28
+ // pass the state and configuration to the child process
29
+ parent , child , err := newInitPipe ()
32
30
if err != nil {
33
31
return - 1 , err
34
32
}
35
- defer syncPipe .Close ()
33
+ defer parent .Close ()
36
34
37
- command := createCommand (container , console , dataPath , os .Args [0 ], syncPipe . Child () , args )
35
+ command := createCommand (container , console , dataPath , os .Args [0 ], child , args )
38
36
// Note: these are only used in non-tty mode
39
37
// if there is a tty for the container it will be opened within the namespace and the
40
38
// fds will be duped to stdin, stdiout, and stderr
@@ -43,39 +41,47 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri
43
41
command .Stderr = stderr
44
42
45
43
if err := command .Start (); err != nil {
44
+ child .Close ()
46
45
return - 1 , err
47
46
}
47
+ child .Close ()
48
48
49
- // Now we passed the pipe to the child, close our side
50
- syncPipe .CloseChild ()
49
+ terminate := func (terr error ) (int , error ) {
50
+ // TODO: log the errors for kill and wait
51
+ command .Process .Kill ()
52
+ command .Wait ()
53
+ return - 1 , terr
54
+ }
51
55
52
56
started , err := system .GetProcessStartTime (command .Process .Pid )
53
57
if err != nil {
54
- return - 1 , err
58
+ return terminate ( err )
55
59
}
56
60
57
61
// Do this before syncing with child so that no children
58
62
// can escape the cgroup
59
63
cgroupRef , err := SetupCgroups (container , command .Process .Pid )
60
64
if err != nil {
61
- command .Process .Kill ()
62
- command .Wait ()
63
- return - 1 , err
65
+ return terminate (err )
64
66
}
65
67
defer cgroupRef .Cleanup ()
66
68
67
69
cgroupPaths , err := cgroupRef .Paths ()
68
70
if err != nil {
69
- command .Process .Kill ()
70
- command .Wait ()
71
- return - 1 , err
71
+ return terminate (err )
72
72
}
73
73
74
74
var networkState network.NetworkState
75
- if err := InitializeNetworking (container , command .Process .Pid , syncPipe , & networkState ); err != nil {
76
- command .Process .Kill ()
77
- command .Wait ()
78
- return - 1 , err
75
+ if err := InitializeNetworking (container , command .Process .Pid , & networkState ); err != nil {
76
+ return terminate (err )
77
+ }
78
+ // send the state to the container's init process then shutdown writes for the parent
79
+ if err := json .NewEncoder (parent ).Encode (networkState ); err != nil {
80
+ return terminate (err )
81
+ }
82
+ // shutdown writes for the parent side of the pipe
83
+ if err := syscall .Shutdown (int (parent .Fd ()), syscall .SHUT_WR ); err != nil {
84
+ return terminate (err )
79
85
}
80
86
81
87
state := & libcontainer.State {
@@ -86,17 +92,18 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri
86
92
}
87
93
88
94
if err := libcontainer .SaveState (dataPath , state ); err != nil {
89
- command .Process .Kill ()
90
- command .Wait ()
91
- return - 1 , err
95
+ return terminate (err )
92
96
}
93
97
defer libcontainer .DeleteState (dataPath )
94
98
95
- // Sync with child
96
- if err := syncPipe .ReadFromChild (); err != nil {
97
- command .Process .Kill ()
98
- command .Wait ()
99
- return - 1 , err
99
+ // wait for the child process to fully complete and receive an error message
100
+ // if one was encoutered
101
+ var ierr * initError
102
+ if err := json .NewDecoder (parent ).Decode (& ierr ); err != nil && err != io .EOF {
103
+ return terminate (err )
104
+ }
105
+ if ierr != nil {
106
+ return terminate (ierr )
100
107
}
101
108
102
109
if startCallback != nil {
@@ -108,7 +115,6 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri
108
115
return - 1 , err
109
116
}
110
117
}
111
-
112
118
return command .ProcessState .Sys ().(syscall.WaitStatus ).ExitStatus (), nil
113
119
}
114
120
@@ -129,16 +135,6 @@ func DefaultCreateCommand(container *libcontainer.Config, console, dataPath, ini
129
135
"data_path=" + dataPath ,
130
136
}
131
137
132
- /*
133
- TODO: move user and wd into env
134
- if user != "" {
135
- env = append(env, "user="+user)
136
- }
137
- if workingDir != "" {
138
- env = append(env, "wd="+workingDir)
139
- }
140
- */
141
-
142
138
command := exec .Command (init , append ([]string {"init" , "--" }, args ... )... )
143
139
// make sure the process is executed inside the context of the rootfs
144
140
command .Dir = container .RootFs
@@ -173,7 +169,7 @@ func SetupCgroups(container *libcontainer.Config, nspid int) (cgroups.ActiveCgro
173
169
174
170
// InitializeNetworking creates the container's network stack outside of the namespace and moves
175
171
// interfaces into the container's net namespaces if necessary
176
- func InitializeNetworking (container * libcontainer.Config , nspid int , pipe * syncpipe. SyncPipe , networkState * network.NetworkState ) error {
172
+ func InitializeNetworking (container * libcontainer.Config , nspid int , networkState * network.NetworkState ) error {
177
173
for _ , config := range container .Networks {
178
174
strategy , err := network .GetStrategy (config .Type )
179
175
if err != nil {
@@ -183,18 +179,5 @@ func InitializeNetworking(container *libcontainer.Config, nspid int, pipe *syncp
183
179
return err
184
180
}
185
181
}
186
- return pipe .SendToChild (networkState )
187
- }
188
-
189
- // GetNamespaceFlags parses the container's Namespaces options to set the correct
190
- // flags on clone, unshare, and setns
191
- func GetNamespaceFlags (namespaces map [string ]bool ) (flag int ) {
192
- for key , enabled := range namespaces {
193
- if enabled {
194
- if ns := GetNamespace (key ); ns != nil {
195
- flag |= ns .Value
196
- }
197
- }
198
- }
199
- return flag
182
+ return nil
200
183
}
0 commit comments