Browse code

Update libcontainer to 29363e2d2d7b8f62a5f353be333

Signed-off-by: Michael Crosby <michael@docker.com>

Michael Crosby authored on 2014/08/16 07:41:05
Showing 32 changed files
... ...
@@ -59,7 +59,7 @@ rm -rf src/code.google.com/p/go
59 59
 mkdir -p src/code.google.com/p/go/src/pkg/archive
60 60
 mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar
61 61
 
62
-clone git github.com/docker/libcontainer f2e78425c377acc7a67a35c3148069b6285a3c4b
62
+clone git github.com/docker/libcontainer 29363e2d2d7b8f62a5f353be333758f83df540a9
63 63
 # see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file)
64 64
 rm -rf src/github.com/docker/libcontainer/vendor
65 65
 eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli')"
... ...
@@ -1,5 +1,6 @@
1 1
 Michael Crosby <michael@docker.com> (@crosbymichael)
2 2
 Rohit Jnagal <jnagal@google.com> (@rjnagal)
3 3
 Victor Marmol <vmarmol@google.com> (@vmarmol)
4
+Mrunal Patel <mpatel@redhat.com> (@mrunalp)
4 5
 .travis.yml: Tianon Gravi <admwiggin@gmail.com> (@tianon)
5 6
 update-vendor.sh: Tianon Gravi <admwiggin@gmail.com> (@tianon)
... ...
@@ -2,7 +2,7 @@
2 2
 all:
3 3
 	docker build -t docker/libcontainer .
4 4
 
5
-test:
5
+test: 
6 6
 	# we need NET_ADMIN for the netlink tests and SYS_ADMIN for mounting
7 7
 	docker run --rm -it --cap-add NET_ADMIN --cap-add SYS_ADMIN docker/libcontainer
8 8
 
... ...
@@ -1,4 +1,4 @@
1
-## libcontainer - reference implementation for containers
1
+## libcontainer - reference implementation for containers [![Build Status](https://travis-ci.org/docker/libcontainer.png?branch=master)](https://travis-ci.org/docker/libcontainer)
2 2
 
3 3
 ### Note on API changes:
4 4
 
... ...
@@ -37,4 +37,5 @@ type Cgroup struct {
37 37
 
38 38
 type ActiveCgroup interface {
39 39
 	Cleanup() error
40
+	Paths() (map[string]string, error)
40 41
 }
... ...
@@ -21,12 +21,16 @@ var (
21 21
 		"perf_event": &PerfEventGroup{},
22 22
 		"freezer":    &FreezerGroup{},
23 23
 	}
24
+	CgroupProcesses = "cgroup.procs"
24 25
 )
25 26
 
26 27
 type subsystem interface {
27
-	Set(*data) error
28
+	// Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
29
+	GetStats(path string, stats *cgroups.Stats) error
30
+	// Removes the cgroup represented by 'data'.
28 31
 	Remove(*data) error
29
-	GetStats(string, *cgroups.Stats) error
32
+	// Creates and joins the cgroup represented by data.
33
+	Set(*data) error
30 34
 }
31 35
 
32 36
 type data struct {
... ...
@@ -149,6 +153,18 @@ func (raw *data) parent(subsystem string) (string, error) {
149 149
 	return filepath.Join(raw.root, subsystem, initPath), nil
150 150
 }
151 151
 
152
+func (raw *data) Paths() (map[string]string, error) {
153
+	paths := make(map[string]string)
154
+	for sysname := range subsystems {
155
+		path, err := raw.path(sysname)
156
+		if err != nil {
157
+			return nil, err
158
+		}
159
+		paths[sysname] = path
160
+	}
161
+	return paths, nil
162
+}
163
+
152 164
 func (raw *data) path(subsystem string) (string, error) {
153 165
 	// If the cgroup name/path is absolute do not look relative to the cgroup of the init process.
154 166
 	if filepath.IsAbs(raw.cgroup) {
... ...
@@ -169,7 +185,7 @@ func (raw *data) join(subsystem string) (string, error) {
169 169
 	if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
170 170
 		return "", err
171 171
 	}
172
-	if err := writeFile(path, "cgroup.procs", strconv.Itoa(raw.pid)); err != nil {
172
+	if err := writeFile(path, CgroupProcesses, strconv.Itoa(raw.pid)); err != nil {
173 173
 		return "", err
174 174
 	}
175 175
 	return path, nil
... ...
@@ -54,7 +54,7 @@ func (s *CpuacctGroup) GetStats(path string, stats *cgroups.Stats) error {
54 54
 		return err
55 55
 	}
56 56
 	// sample for 100ms
57
-	time.Sleep(100 * time.Millisecond)
57
+	time.Sleep(1000 * time.Millisecond)
58 58
 	if kernelModeUsage, userModeUsage, err = getCpuUsage(path); err != nil {
59 59
 		return err
60 60
 	}
... ...
@@ -73,7 +73,7 @@ func (s *CpuacctGroup) GetStats(path string, stats *cgroups.Stats) error {
73 73
 		deltaUsage  = lastUsage - startUsage
74 74
 	)
75 75
 	if deltaSystem > 0.0 {
76
-		percentage = ((deltaProc / deltaSystem) * clockTicks) * cpuCount
76
+		percentage = uint64((float64(deltaProc) / float64(deltaSystem)) * float64(clockTicks*cpuCount))
77 77
 	}
78 78
 	// NOTE: a percentage over 100% is valid for POSIX because that means the
79 79
 	// processes is using multiple cores
... ...
@@ -20,19 +20,10 @@ func (s *CpusetGroup) Set(d *data) error {
20 20
 		if err != nil {
21 21
 			return err
22 22
 		}
23
-		if err := s.ensureParent(dir); err != nil {
24
-			return err
25
-		}
26 23
 
27
-		// because we are not using d.join we need to place the pid into the procs file
28
-		// unlike the other subsystems
29
-		if err := writeFile(dir, "cgroup.procs", strconv.Itoa(d.pid)); err != nil {
30
-			return err
31
-		}
32
-		if err := writeFile(dir, "cpuset.cpus", d.c.CpusetCpus); err != nil {
33
-			return err
34
-		}
24
+		return s.SetDir(dir, d.c.CpusetCpus, d.pid)
35 25
 	}
26
+
36 27
 	return nil
37 28
 }
38 29
 
... ...
@@ -44,6 +35,24 @@ func (s *CpusetGroup) GetStats(path string, stats *cgroups.Stats) error {
44 44
 	return nil
45 45
 }
46 46
 
47
+func (s *CpusetGroup) SetDir(dir, value string, pid int) error {
48
+	if err := s.ensureParent(dir); err != nil {
49
+		return err
50
+	}
51
+
52
+	// because we are not using d.join we need to place the pid into the procs file
53
+	// unlike the other subsystems
54
+	if err := writeFile(dir, "cgroup.procs", strconv.Itoa(pid)); err != nil {
55
+		return err
56
+	}
57
+
58
+	if err := writeFile(dir, "cpuset.cpus", value); err != nil {
59
+		return err
60
+	}
61
+
62
+	return nil
63
+}
64
+
47 65
 func (s *CpusetGroup) getSubsystemSettings(parent string) (cpus []byte, mems []byte, err error) {
48 66
 	if cpus, err = ioutil.ReadFile(filepath.Join(parent, "cpuset.cpus")); err != nil {
49 67
 		return
... ...
@@ -14,7 +14,7 @@ type MemoryGroup struct {
14 14
 
15 15
 func (s *MemoryGroup) Set(d *data) error {
16 16
 	dir, err := d.join("memory")
17
-	// only return an error for memory if it was not specified
17
+	// only return an error for memory if it was specified
18 18
 	if err != nil && (d.c.Memory != 0 || d.c.MemoryReservation != 0 || d.c.MemorySwap != 0) {
19 19
 		return err
20 20
 	}
... ...
@@ -21,7 +21,7 @@ import (
21 21
 )
22 22
 
23 23
 type systemdCgroup struct {
24
-	cleanupDirs []string
24
+	cgroup *cgroups.Cgroup
25 25
 }
26 26
 
27 27
 type subsystem interface {
... ...
@@ -84,39 +84,15 @@ func getIfaceForUnit(unitName string) string {
84 84
 	return "Unit"
85 85
 }
86 86
 
87
-type cgroupArg struct {
88
-	File  string
89
-	Value string
90
-}
91
-
92 87
 func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
93 88
 	var (
94 89
 		unitName   = getUnitName(c)
95 90
 		slice      = "system.slice"
96 91
 		properties []systemd1.Property
97
-		cpuArgs    []cgroupArg
98
-		cpusetArgs []cgroupArg
99
-		memoryArgs []cgroupArg
100
-		res        systemdCgroup
92
+		res        = &systemdCgroup{}
101 93
 	)
102 94
 
103
-	// First set up things not supported by systemd
104
-
105
-	// -1 disables memorySwap
106
-	if c.MemorySwap >= 0 && (c.Memory != 0 || c.MemorySwap > 0) {
107
-		memorySwap := c.MemorySwap
108
-
109
-		if memorySwap == 0 {
110
-			// By default, MemorySwap is set to twice the size of RAM.
111
-			memorySwap = c.Memory * 2
112
-		}
113
-
114
-		memoryArgs = append(memoryArgs, cgroupArg{"memory.memsw.limit_in_bytes", strconv.FormatInt(memorySwap, 10)})
115
-	}
116
-
117
-	if c.CpusetCpus != "" {
118
-		cpusetArgs = append(cpusetArgs, cgroupArg{"cpuset.cpus", c.CpusetCpus})
119
-	}
95
+	res.cgroup = c
120 96
 
121 97
 	if c.Slice != "" {
122 98
 		slice = c.Slice
... ...
@@ -150,201 +126,84 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
150 150
 		return nil, err
151 151
 	}
152 152
 
153
-	// To work around the lack of /dev/pts/* support above we need to manually add these
154
-	// so, ask systemd for the cgroup used
155
-	props, err := theConn.GetUnitTypeProperties(unitName, getIfaceForUnit(unitName))
156
-	if err != nil {
157
-		return nil, err
158
-	}
159
-
160
-	cgroup := props["ControlGroup"].(string)
161
-
162 153
 	if !c.AllowAllDevices {
163
-		// Atm we can't use the systemd device support because of two missing things:
164
-		// * Support for wildcards to allow mknod on any device
165
-		// * Support for wildcards to allow /dev/pts support
166
-		//
167
-		// The second is available in more recent systemd as "char-pts", but not in e.g. v208 which is
168
-		// in wide use. When both these are availalable we will be able to switch, but need to keep the old
169
-		// implementation for backwards compat.
170
-		//
171
-		// Note: we can't use systemd to set up the initial limits, and then change the cgroup
172
-		// because systemd will re-write the device settings if it needs to re-apply the cgroup context.
173
-		// This happens at least for v208 when any sibling unit is started.
174
-
175
-		mountpoint, err := cgroups.FindCgroupMountpoint("devices")
176
-		if err != nil {
154
+		if err := joinDevices(c, pid); err != nil {
177 155
 			return nil, err
178 156
 		}
179
-
180
-		initPath, err := cgroups.GetInitCgroupDir("devices")
181
-		if err != nil {
182
-			return nil, err
183
-		}
184
-
185
-		dir := filepath.Join(mountpoint, initPath, c.Parent, c.Name)
186
-
187
-		res.cleanupDirs = append(res.cleanupDirs, dir)
188
-
189
-		if err := os.MkdirAll(dir, 0755); err != nil && !os.IsExist(err) {
190
-			return nil, err
191
-		}
192
-
193
-		if err := ioutil.WriteFile(filepath.Join(dir, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700); err != nil {
194
-			return nil, err
195
-		}
196
-
197
-		if err := writeFile(dir, "devices.deny", "a"); err != nil {
198
-			return nil, err
199
-		}
200
-
201
-		for _, dev := range c.AllowedDevices {
202
-			if err := writeFile(dir, "devices.allow", dev.GetCgroupAllowString()); err != nil {
203
-				return nil, err
204
-			}
205
-		}
206 157
 	}
207 158
 
208
-	if len(cpuArgs) != 0 {
209
-		mountpoint, err := cgroups.FindCgroupMountpoint("cpu")
210
-		if err != nil {
211
-			return nil, err
212
-		}
213
-
214
-		path := filepath.Join(mountpoint, cgroup)
215
-
216
-		for _, arg := range cpuArgs {
217
-			if err := ioutil.WriteFile(filepath.Join(path, arg.File), []byte(arg.Value), 0700); err != nil {
218
-				return nil, err
219
-			}
220
-		}
221
-	}
222
-
223
-	if len(memoryArgs) != 0 {
224
-		mountpoint, err := cgroups.FindCgroupMountpoint("memory")
225
-		if err != nil {
159
+	// -1 disables memorySwap
160
+	if c.MemorySwap >= 0 && (c.Memory != 0 || c.MemorySwap > 0) {
161
+		if err := joinMemory(c, pid); err != nil {
226 162
 			return nil, err
227 163
 		}
228 164
 
229
-		path := filepath.Join(mountpoint, cgroup)
230
-
231
-		for _, arg := range memoryArgs {
232
-			if err := ioutil.WriteFile(filepath.Join(path, arg.File), []byte(arg.Value), 0700); err != nil {
233
-				return nil, err
234
-			}
235
-		}
236 165
 	}
237 166
 
238 167
 	// we need to manually join the freezer cgroup in systemd because it does not currently support it
239 168
 	// via the dbus api
240
-	freezerPath, err := joinFreezer(c, pid)
241
-	if err != nil {
169
+	if err := joinFreezer(c, pid); err != nil {
242 170
 		return nil, err
243 171
 	}
244
-	res.cleanupDirs = append(res.cleanupDirs, freezerPath)
245 172
 
246
-	if len(cpusetArgs) != 0 {
247
-		// systemd does not atm set up the cpuset controller, so we must manually
248
-		// join it. Additionally that is a very finicky controller where each
249
-		// level must have a full setup as the default for a new directory is "no cpus",
250
-		// so we avoid using any hierarchies here, creating a toplevel directory.
251
-		mountpoint, err := cgroups.FindCgroupMountpoint("cpuset")
252
-		if err != nil {
253
-			return nil, err
254
-		}
255
-
256
-		initPath, err := cgroups.GetInitCgroupDir("cpuset")
257
-		if err != nil {
258
-			return nil, err
259
-		}
260
-
261
-		var (
262
-			foundCpus bool
263
-			foundMems bool
264
-
265
-			rootPath = filepath.Join(mountpoint, initPath)
266
-			path     = filepath.Join(mountpoint, initPath, c.Parent+"-"+c.Name)
267
-		)
268
-
269
-		res.cleanupDirs = append(res.cleanupDirs, path)
270
-
271
-		if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
173
+	if c.CpusetCpus != "" {
174
+		if err := joinCpuset(c, pid); err != nil {
272 175
 			return nil, err
273 176
 		}
177
+	}
274 178
 
275
-		for _, arg := range cpusetArgs {
276
-			if arg.File == "cpuset.cpus" {
277
-				foundCpus = true
278
-			}
279
-			if arg.File == "cpuset.mems" {
280
-				foundMems = true
281
-			}
282
-			if err := ioutil.WriteFile(filepath.Join(path, arg.File), []byte(arg.Value), 0700); err != nil {
283
-				return nil, err
284
-			}
285
-		}
286
-
287
-		// These are required, if not specified inherit from parent
288
-		if !foundCpus {
289
-			s, err := ioutil.ReadFile(filepath.Join(rootPath, "cpuset.cpus"))
290
-			if err != nil {
291
-				return nil, err
292
-			}
179
+	return res, nil
180
+}
293 181
 
294
-			if err := ioutil.WriteFile(filepath.Join(path, "cpuset.cpus"), s, 0700); err != nil {
295
-				return nil, err
296
-			}
297
-		}
182
+func writeFile(dir, file, data string) error {
183
+	return ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700)
184
+}
298 185
 
299
-		// These are required, if not specified inherit from parent
300
-		if !foundMems {
301
-			s, err := ioutil.ReadFile(filepath.Join(rootPath, "cpuset.mems"))
302
-			if err != nil {
303
-				return nil, err
304
-			}
186
+func (c *systemdCgroup) Paths() (map[string]string, error) {
187
+	paths := make(map[string]string)
305 188
 
306
-			if err := ioutil.WriteFile(filepath.Join(path, "cpuset.mems"), s, 0700); err != nil {
307
-				return nil, err
189
+	for sysname := range subsystems {
190
+		subsystemPath, err := getSubsystemPath(c.cgroup, sysname)
191
+		if err != nil {
192
+			// Don't fail if a cgroup hierarchy was not found, just skip this subsystem
193
+			if err == cgroups.ErrNotFound {
194
+				continue
308 195
 			}
309
-		}
310 196
 
311
-		if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700); err != nil {
312 197
 			return nil, err
313 198
 		}
314
-	}
315 199
 
316
-	return &res, nil
317
-}
200
+		paths[sysname] = subsystemPath
201
+	}
318 202
 
319
-func writeFile(dir, file, data string) error {
320
-	return ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700)
203
+	return paths, nil
321 204
 }
322 205
 
323 206
 func (c *systemdCgroup) Cleanup() error {
324 207
 	// systemd cleans up, we don't need to do much
208
+	paths, err := c.Paths()
209
+	if err != nil {
210
+		return err
211
+	}
325 212
 
326
-	for _, path := range c.cleanupDirs {
213
+	for _, path := range paths {
327 214
 		os.RemoveAll(path)
328 215
 	}
329 216
 
330 217
 	return nil
331 218
 }
332 219
 
333
-func joinFreezer(c *cgroups.Cgroup, pid int) (string, error) {
220
+func joinFreezer(c *cgroups.Cgroup, pid int) error {
334 221
 	path, err := getSubsystemPath(c, "freezer")
335 222
 	if err != nil {
336
-		return "", err
223
+		return err
337 224
 	}
338 225
 
339 226
 	if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
340
-		return "", err
341
-	}
342
-
343
-	if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700); err != nil {
344
-		return "", err
227
+		return err
345 228
 	}
346 229
 
347
-	return path, nil
230
+	return ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700)
348 231
 }
349 232
 
350 233
 func getSubsystemPath(c *cgroups.Cgroup, subsystem string) (string, error) {
... ...
@@ -389,20 +248,12 @@ func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error {
389 389
 }
390 390
 
391 391
 func GetPids(c *cgroups.Cgroup) ([]int, error) {
392
-	unitName := getUnitName(c)
393
-
394
-	mountpoint, err := cgroups.FindCgroupMountpoint("cpu")
392
+	path, err := getSubsystemPath(c, "cpu")
395 393
 	if err != nil {
396 394
 		return nil, err
397 395
 	}
398 396
 
399
-	props, err := theConn.GetUnitTypeProperties(unitName, getIfaceForUnit(unitName))
400
-	if err != nil {
401
-		return nil, err
402
-	}
403
-	cgroup := props["ControlGroup"].(string)
404
-
405
-	return cgroups.ReadProcsFile(filepath.Join(mountpoint, cgroup))
397
+	return cgroups.ReadProcsFile(path)
406 398
 }
407 399
 
408 400
 func getUnitName(c *cgroups.Cgroup) string {
... ...
@@ -437,3 +288,71 @@ func GetStats(c *cgroups.Cgroup) (*cgroups.Stats, error) {
437 437
 
438 438
 	return stats, nil
439 439
 }
440
+
441
+// Atm we can't use the systemd device support because of two missing things:
442
+// * Support for wildcards to allow mknod on any device
443
+// * Support for wildcards to allow /dev/pts support
444
+//
445
+// The second is available in more recent systemd as "char-pts", but not in e.g. v208 which is
446
+// in wide use. When both these are availalable we will be able to switch, but need to keep the old
447
+// implementation for backwards compat.
448
+//
449
+// Note: we can't use systemd to set up the initial limits, and then change the cgroup
450
+// because systemd will re-write the device settings if it needs to re-apply the cgroup context.
451
+// This happens at least for v208 when any sibling unit is started.
452
+func joinDevices(c *cgroups.Cgroup, pid int) error {
453
+	path, err := getSubsystemPath(c, "devices")
454
+	if err != nil {
455
+		return err
456
+	}
457
+
458
+	if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
459
+		return err
460
+	}
461
+
462
+	if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700); err != nil {
463
+		return err
464
+	}
465
+
466
+	if err := writeFile(path, "devices.deny", "a"); err != nil {
467
+		return err
468
+	}
469
+
470
+	for _, dev := range c.AllowedDevices {
471
+		if err := writeFile(path, "devices.allow", dev.GetCgroupAllowString()); err != nil {
472
+			return err
473
+		}
474
+	}
475
+
476
+	return nil
477
+}
478
+
479
+func joinMemory(c *cgroups.Cgroup, pid int) error {
480
+	memorySwap := c.MemorySwap
481
+
482
+	if memorySwap == 0 {
483
+		// By default, MemorySwap is set to twice the size of RAM.
484
+		memorySwap = c.Memory * 2
485
+	}
486
+
487
+	path, err := getSubsystemPath(c, "memory")
488
+	if err != nil {
489
+		return err
490
+	}
491
+
492
+	return ioutil.WriteFile(filepath.Join(path, "memory.memsw.limit_in_bytes"), []byte(strconv.FormatInt(memorySwap, 10)), 0700)
493
+}
494
+
495
+// systemd does not atm set up the cpuset controller, so we must manually
496
+// join it. Additionally that is a very finicky controller where each
497
+// level must have a full setup as the default for a new directory is "no cpus"
498
+func joinCpuset(c *cgroups.Cgroup, pid int) error {
499
+	path, err := getSubsystemPath(c, "cpuset")
500
+	if err != nil {
501
+		return err
502
+	}
503
+
504
+	s := &fs.CpusetGroup{}
505
+
506
+	return s.SetDir(path, c.CpusetCpus, pid)
507
+}
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"bufio"
5 5
 	"fmt"
6 6
 	"io"
7
+	"io/ioutil"
7 8
 	"os"
8 9
 	"path/filepath"
9 10
 	"strconv"
... ...
@@ -166,3 +167,23 @@ func parseCgroupFile(subsystem string, r io.Reader) (string, error) {
166 166
 	}
167 167
 	return "", ErrNotFound
168 168
 }
169
+
170
+func pathExists(path string) bool {
171
+	if _, err := os.Stat(path); err != nil {
172
+		return false
173
+	}
174
+	return true
175
+}
176
+
177
+func EnterPid(cgroupPaths map[string]string, pid int) error {
178
+	for _, path := range cgroupPaths {
179
+		if pathExists(path) {
180
+			if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"),
181
+				[]byte(strconv.Itoa(pid)), 0700); err != nil {
182
+				return err
183
+			}
184
+		}
185
+	}
186
+
187
+	return nil
188
+}
... ...
@@ -114,7 +114,7 @@ func OpenPtmx() (*os.File, error) {
114 114
 func OpenTerminal(name string, flag int) (*os.File, error) {
115 115
 	r, e := syscall.Open(name, flag, 0)
116 116
 	if e != nil {
117
-		return nil, &os.PathError{"open", name, e}
117
+		return nil, &os.PathError{Op: "open", Path: name, Err: e}
118 118
 	}
119 119
 	return os.NewFile(uintptr(r), name), nil
120 120
 }
... ...
@@ -236,7 +236,7 @@ func reOpenDevNull(rootfs string) error {
236 236
 		if stat.Rdev == devNullStat.Rdev {
237 237
 			// Close and re-open the fd.
238 238
 			if err = syscall.Dup2(int(file.Fd()), fd); err != nil {
239
-				return fmt.Errorf("Failed to dup fd %d to fd %d - %s", file.Fd(), fd)
239
+				return fmt.Errorf("Failed to dup fd %d to fd %d - %s", file.Fd(), fd, err)
240 240
 			}
241 241
 		}
242 242
 	}
... ...
@@ -56,14 +56,19 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri
56 56
 
57 57
 	// Do this before syncing with child so that no children
58 58
 	// can escape the cgroup
59
-	cleaner, err := SetupCgroups(container, command.Process.Pid)
59
+	cgroupRef, err := SetupCgroups(container, command.Process.Pid)
60 60
 	if err != nil {
61 61
 		command.Process.Kill()
62 62
 		command.Wait()
63 63
 		return -1, err
64 64
 	}
65
-	if cleaner != nil {
66
-		defer cleaner.Cleanup()
65
+	defer cgroupRef.Cleanup()
66
+
67
+	cgroupPaths, err := cgroupRef.Paths()
68
+	if err != nil {
69
+		command.Process.Kill()
70
+		command.Wait()
71
+		return -1, err
67 72
 	}
68 73
 
69 74
 	var networkState network.NetworkState
... ...
@@ -77,6 +82,7 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri
77 77
 		InitPid:       command.Process.Pid,
78 78
 		InitStartTime: started,
79 79
 		NetworkState:  networkState,
80
+		CgroupPaths:   cgroupPaths,
80 81
 	}
81 82
 
82 83
 	if err := libcontainer.SaveState(dataPath, state); err != nil {
... ...
@@ -3,6 +3,7 @@
3 3
 package namespaces
4 4
 
5 5
 import (
6
+	"fmt"
6 7
 	"io"
7 8
 	"os"
8 9
 	"os/exec"
... ...
@@ -11,6 +12,7 @@ import (
11 11
 	"syscall"
12 12
 
13 13
 	"github.com/docker/libcontainer"
14
+	"github.com/docker/libcontainer/cgroups"
14 15
 	"github.com/docker/libcontainer/label"
15 16
 	"github.com/docker/libcontainer/syncpipe"
16 17
 	"github.com/docker/libcontainer/system"
... ...
@@ -18,10 +20,10 @@ import (
18 18
 
19 19
 // ExecIn reexec's the initPath with the argv 0 rewrite to "nsenter" so that it is able to run the
20 20
 // setns code in a single threaded environment joining the existing containers' namespaces.
21
-func ExecIn(container *libcontainer.Config, state *libcontainer.State, userArgs []string, initPath string,
21
+func ExecIn(container *libcontainer.Config, state *libcontainer.State, userArgs []string, initPath, action string,
22 22
 	stdin io.Reader, stdout, stderr io.Writer, console string, startCallback func(*exec.Cmd)) (int, error) {
23 23
 
24
-	args := []string{"nsenter", "--nspid", strconv.Itoa(state.InitPid)}
24
+	args := []string{fmt.Sprintf("nsenter-%s", action), "--nspid", strconv.Itoa(state.InitPid)}
25 25
 
26 26
 	if console != "" {
27 27
 		args = append(args, "--console", console)
... ...
@@ -58,6 +60,11 @@ func ExecIn(container *libcontainer.Config, state *libcontainer.State, userArgs
58 58
 	}
59 59
 	pipe.CloseChild()
60 60
 
61
+	// Enter cgroups.
62
+	if err := EnterCgroups(state, cmd.Process.Pid); err != nil {
63
+		return -1, err
64
+	}
65
+
61 66
 	if err := pipe.SendToChild(container); err != nil {
62 67
 		cmd.Process.Kill()
63 68
 		cmd.Wait()
... ...
@@ -101,3 +108,7 @@ func FinalizeSetns(container *libcontainer.Config, args []string) error {
101 101
 
102 102
 	panic("unreachable")
103 103
 }
104
+
105
+func EnterCgroups(state *libcontainer.State, pid int) error {
106
+	return cgroups.EnterPid(state.CgroupPaths, pid)
107
+}
... ...
@@ -31,8 +31,8 @@ void get_args(int *argc, char ***argv)
31 31
 		contents_size += kBufSize;
32 32
 		contents = (char *)realloc(contents, contents_size);
33 33
 		bytes_read =
34
-		    read(fd, contents + contents_offset,
35
-			 contents_size - contents_offset);
34
+			read(fd, contents + contents_offset,
35
+			     contents_size - contents_offset);
36 36
 		contents_offset += bytes_read;
37 37
 	}
38 38
 	while (bytes_read > 0);
... ...
@@ -80,20 +80,20 @@ void nsenter()
80 80
 	char **argv;
81 81
 	get_args(&argc, &argv);
82 82
 
83
-    // check argv 0 to ensure that we are supposed to setns
84
-    // we use strncmp to test for a value of "nsenter" but also allows alternate implmentations
85
-    // after the setns code path to continue to use the argv 0 to determine actions to be run
86
-    // resulting in the ability to specify "nsenter-mknod", "nsenter-exec", etc...
87
-    if (strncmp(argv[0], kNsEnter, strlen(kNsEnter)) != 0) {
88
-        return;
89
-    }
83
+	// check argv 0 to ensure that we are supposed to setns
84
+	// we use strncmp to test for a value of "nsenter" but also allows alternate implmentations
85
+	// after the setns code path to continue to use the argv 0 to determine actions to be run
86
+	// resulting in the ability to specify "nsenter-mknod", "nsenter-exec", etc...
87
+	if (strncmp(argv[0], kNsEnter, strlen(kNsEnter)) != 0) {
88
+		return;
89
+	}
90 90
 
91 91
 	static const struct option longopts[] = {
92 92
 		{"nspid", required_argument, NULL, 'n'},
93 93
 		{"console", required_argument, NULL, 't'},
94 94
 		{NULL, 0, NULL, 0}
95 95
 	};
96
-
96
+    
97 97
 	pid_t init_pid = -1;
98 98
 	char *init_pid_str = NULL;
99 99
 	char *console = NULL;
... ...
@@ -21,3 +21,10 @@ type Route struct {
21 21
 	Iface   *net.Interface
22 22
 	Default bool
23 23
 }
24
+
25
+// An IfAddr defines IP network settings for a given network interface
26
+type IfAddr struct {
27
+	Iface *net.Interface
28
+	IP    net.IP
29
+	IPNet *net.IPNet
30
+}
... ...
@@ -651,30 +651,28 @@ func NetworkSetNsFd(iface *net.Interface, fd int) error {
651 651
 	return s.HandleAck(wb.Seq)
652 652
 }
653 653
 
654
-// Add an Ip address to an interface. This is identical to:
655
-// ip addr add $ip/$ipNet dev $iface
656
-func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
654
+func networkLinkIpAction(action, flags int, ifa IfAddr) error {
657 655
 	s, err := getNetlinkSocket()
658 656
 	if err != nil {
659 657
 		return err
660 658
 	}
661 659
 	defer s.Close()
662 660
 
663
-	family := getIpFamily(ip)
661
+	family := getIpFamily(ifa.IP)
664 662
 
665
-	wb := newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
663
+	wb := newNetlinkRequest(action, flags)
666 664
 
667 665
 	msg := newIfAddrmsg(family)
668
-	msg.Index = uint32(iface.Index)
669
-	prefixLen, _ := ipNet.Mask.Size()
666
+	msg.Index = uint32(ifa.Iface.Index)
667
+	prefixLen, _ := ifa.IPNet.Mask.Size()
670 668
 	msg.Prefixlen = uint8(prefixLen)
671 669
 	wb.AddData(msg)
672 670
 
673 671
 	var ipData []byte
674 672
 	if family == syscall.AF_INET {
675
-		ipData = ip.To4()
673
+		ipData = ifa.IP.To4()
676 674
 	} else {
677
-		ipData = ip.To16()
675
+		ipData = ifa.IP.To16()
678 676
 	}
679 677
 
680 678
 	localData := newRtAttr(syscall.IFA_LOCAL, ipData)
... ...
@@ -690,6 +688,26 @@ func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
690 690
 	return s.HandleAck(wb.Seq)
691 691
 }
692 692
 
693
+// Delete an IP address from an interface. This is identical to:
694
+// ip addr del $ip/$ipNet dev $iface
695
+func NetworkLinkDelIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
696
+	return networkLinkIpAction(
697
+		syscall.RTM_DELADDR,
698
+		syscall.NLM_F_ACK,
699
+		IfAddr{iface, ip, ipNet},
700
+	)
701
+}
702
+
703
+// Add an Ip address to an interface. This is identical to:
704
+// ip addr add $ip/$ipNet dev $iface
705
+func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
706
+	return networkLinkIpAction(
707
+		syscall.RTM_NEWADDR,
708
+		syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK,
709
+		IfAddr{iface, ip, ipNet},
710
+	)
711
+}
712
+
693 713
 func zeroTerminated(s string) []byte {
694 714
 	return []byte(s + "\000")
695 715
 }
... ...
@@ -2,9 +2,55 @@ package netlink
2 2
 
3 3
 import (
4 4
 	"net"
5
+	"strings"
5 6
 	"testing"
6 7
 )
7 8
 
9
+func ipAssigned(iface *net.Interface, ip net.IP) bool {
10
+	addrs, _ := iface.Addrs()
11
+
12
+	for _, addr := range addrs {
13
+		args := strings.SplitN(addr.String(), "/", 2)
14
+		if args[0] == ip.String() {
15
+			return true
16
+		}
17
+	}
18
+
19
+	return false
20
+}
21
+
22
+func TestAddDelNetworkIp(t *testing.T) {
23
+	if testing.Short() {
24
+		return
25
+	}
26
+
27
+	ifaceName := "lo"
28
+	ip := net.ParseIP("127.0.1.1")
29
+	mask := net.IPv4Mask(255, 255, 255, 255)
30
+	ipNet := &net.IPNet{IP: ip, Mask: mask}
31
+
32
+	iface, err := net.InterfaceByName(ifaceName)
33
+	if err != nil {
34
+		t.Skip("No 'lo' interface; skipping tests")
35
+	}
36
+
37
+	if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil {
38
+		t.Fatal(err)
39
+	}
40
+
41
+	if !ipAssigned(iface, ip) {
42
+		t.Fatalf("Could not locate address '%s' in lo address list.", ip.String())
43
+	}
44
+
45
+	if err := NetworkLinkDelIp(iface, ip, ipNet); err != nil {
46
+		t.Fatal(err)
47
+	}
48
+
49
+	if ipAssigned(iface, ip) {
50
+		t.Fatalf("Located address '%s' in lo address list after removal.", ip.String())
51
+	}
52
+}
53
+
8 54
 func TestCreateBridgeWithMac(t *testing.T) {
9 55
 	if testing.Short() {
10 56
 		return
... ...
@@ -31,6 +31,10 @@ func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
31 31
 	return ErrNotImplemented
32 32
 }
33 33
 
34
+func NetworkLinkDelIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
35
+	return ErrNotImplemented
36
+}
37
+
34 38
 func AddRoute(destination, source, gateway, device string) error {
35 39
 	return ErrNotImplemented
36 40
 }
... ...
@@ -44,6 +44,14 @@ func SetInterfaceInNamespacePid(name string, nsPid int) error {
44 44
 	return netlink.NetworkSetNsPid(iface, nsPid)
45 45
 }
46 46
 
47
+func SetInterfaceInNamespaceFd(name string, fd uintptr) error {
48
+	iface, err := net.InterfaceByName(name)
49
+	if err != nil {
50
+		return err
51
+	}
52
+	return netlink.NetworkSetNsFd(iface, int(fd))
53
+}
54
+
47 55
 func SetInterfaceMaster(name, master string) error {
48 56
 	iface, err := net.InterfaceByName(name)
49 57
 	if err != nil {
50 58
deleted file mode 100644
... ...
@@ -1,64 +0,0 @@
1
-package nsinit
2
-
3
-import (
4
-	"log"
5
-	"os"
6
-
7
-	"github.com/codegangsta/cli"
8
-)
9
-
10
-var (
11
-	logPath = os.Getenv("log")
12
-	argvs   = make(map[string]func())
13
-)
14
-
15
-func init() {
16
-	argvs["nsenter"] = nsenter
17
-}
18
-
19
-func preload(context *cli.Context) error {
20
-	if logPath != "" {
21
-		if err := openLog(logPath); err != nil {
22
-			return err
23
-		}
24
-	}
25
-
26
-	return nil
27
-}
28
-
29
-func NsInit() {
30
-	// we need to check our argv 0 for any registred functions to run instead of the
31
-	// normal cli code path
32
-
33
-	action, exists := argvs[os.Args[0]]
34
-	if exists {
35
-		action()
36
-
37
-		return
38
-	}
39
-
40
-	app := cli.NewApp()
41
-
42
-	app.Name = "nsinit"
43
-	app.Version = "0.1"
44
-	app.Author = "libcontainer maintainers"
45
-	app.Flags = []cli.Flag{
46
-		cli.StringFlag{Name: "nspid"},
47
-		cli.StringFlag{Name: "console"},
48
-	}
49
-
50
-	app.Before = preload
51
-
52
-	app.Commands = []cli.Command{
53
-		execCommand,
54
-		initCommand,
55
-		statsCommand,
56
-		configCommand,
57
-		pauseCommand,
58
-		unpauseCommand,
59
-	}
60
-
61
-	if err := app.Run(os.Args); err != nil {
62
-		log.Fatal(err)
63
-	}
64
-}
... ...
@@ -1,4 +1,4 @@
1
-package nsinit
1
+package main
2 2
 
3 3
 import (
4 4
 	"encoding/json"
... ...
@@ -15,7 +15,7 @@ var configCommand = cli.Command{
15 15
 }
16 16
 
17 17
 func configAction(context *cli.Context) {
18
-	container, err := loadContainer()
18
+	container, err := loadConfig()
19 19
 	if err != nil {
20 20
 		log.Fatal(err)
21 21
 	}
... ...
@@ -1,4 +1,4 @@
1
-package nsinit
1
+package main
2 2
 
3 3
 import (
4 4
 	"fmt"
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"os/exec"
9 9
 	"os/signal"
10 10
 	"syscall"
11
+	"text/tabwriter"
11 12
 
12 13
 	"github.com/codegangsta/cli"
13 14
 	"github.com/docker/docker/pkg/term"
... ...
@@ -20,12 +21,29 @@ var execCommand = cli.Command{
20 20
 	Name:   "exec",
21 21
 	Usage:  "execute a new command inside a container",
22 22
 	Action: execAction,
23
+	Flags: []cli.Flag{
24
+		cli.BoolFlag{Name: "list", Usage: "list all registered exec functions"},
25
+		cli.StringFlag{Name: "func", Value: "exec", Usage: "function name to exec inside a container"},
26
+	},
23 27
 }
24 28
 
25 29
 func execAction(context *cli.Context) {
30
+	if context.Bool("list") {
31
+		w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
32
+		fmt.Fprint(w, "NAME\tUSAGE\n")
33
+
34
+		for k, f := range argvs {
35
+			fmt.Fprintf(w, "%s\t%s\n", k, f.Usage)
36
+		}
37
+
38
+		w.Flush()
39
+
40
+		return
41
+	}
42
+
26 43
 	var exitCode int
27 44
 
28
-	container, err := loadContainer()
45
+	container, err := loadConfig()
29 46
 	if err != nil {
30 47
 		log.Fatal(err)
31 48
 	}
... ...
@@ -36,7 +54,7 @@ func execAction(context *cli.Context) {
36 36
 	}
37 37
 
38 38
 	if state != nil {
39
-		exitCode, err = startInExistingContainer(container, state, context)
39
+		exitCode, err = startInExistingContainer(container, state, context.String("func"), context)
40 40
 	} else {
41 41
 		exitCode, err = startContainer(container, dataPath, []string(context.Args()))
42 42
 	}
... ...
@@ -52,7 +70,7 @@ func execAction(context *cli.Context) {
52 52
 // with the nsenter argument so that the C code can setns an the namespaces that we require.  Then that
53 53
 // code path will drop us into the path that we can do the final setup of the namespace and exec the users
54 54
 // application.
55
-func startInExistingContainer(config *libcontainer.Config, state *libcontainer.State, context *cli.Context) (int, error) {
55
+func startInExistingContainer(config *libcontainer.Config, state *libcontainer.State, action string, context *cli.Context) (int, error) {
56 56
 	var (
57 57
 		master  *os.File
58 58
 		console string
... ...
@@ -102,7 +120,7 @@ func startInExistingContainer(config *libcontainer.Config, state *libcontainer.S
102 102
 		}()
103 103
 	}
104 104
 
105
-	return namespaces.ExecIn(config, state, context.Args(), os.Args[0], stdin, stdout, stderr, console, startCallback)
105
+	return namespaces.ExecIn(config, state, context.Args(), os.Args[0], action, stdin, stdout, stderr, console, startCallback)
106 106
 }
107 107
 
108 108
 // startContainer starts the container. Returns the exit status or -1 and an
... ...
@@ -1,4 +1,4 @@
1
-package nsinit
1
+package main
2 2
 
3 3
 import (
4 4
 	"log"
... ...
@@ -26,7 +26,7 @@ var (
26 26
 func initAction(context *cli.Context) {
27 27
 	runtime.LockOSThread()
28 28
 
29
-	container, err := loadContainer()
29
+	container, err := loadConfig()
30 30
 	if err != nil {
31 31
 		log.Fatal(err)
32 32
 	}
33 33
new file mode 100644
... ...
@@ -0,0 +1,67 @@
0
+package main
1
+
2
+import (
3
+	"log"
4
+	"os"
5
+	"strings"
6
+
7
+	"github.com/codegangsta/cli"
8
+)
9
+
10
+var (
11
+	logPath = os.Getenv("log")
12
+	argvs   = make(map[string]*rFunc)
13
+)
14
+
15
+func init() {
16
+	argvs["exec"] = &rFunc{
17
+		Usage:  "execute a process inside an existing container",
18
+		Action: nsenterExec,
19
+	}
20
+
21
+	argvs["mknod"] = &rFunc{
22
+		Usage:  "mknod a device inside an existing container",
23
+		Action: nsenterMknod,
24
+	}
25
+
26
+	argvs["ip"] = &rFunc{
27
+		Usage:  "display the container's network interfaces",
28
+		Action: nsenterIp,
29
+	}
30
+}
31
+
32
+func main() {
33
+	// we need to check our argv 0 for any registred functions to run instead of the
34
+	// normal cli code path
35
+	f, exists := argvs[strings.TrimPrefix(os.Args[0], "nsenter-")]
36
+	if exists {
37
+		runFunc(f)
38
+
39
+		return
40
+	}
41
+
42
+	app := cli.NewApp()
43
+
44
+	app.Name = "nsinit"
45
+	app.Version = "0.1"
46
+	app.Author = "libcontainer maintainers"
47
+	app.Flags = []cli.Flag{
48
+		cli.StringFlag{Name: "nspid"},
49
+		cli.StringFlag{Name: "console"},
50
+	}
51
+
52
+	app.Before = preload
53
+
54
+	app.Commands = []cli.Command{
55
+		execCommand,
56
+		initCommand,
57
+		statsCommand,
58
+		configCommand,
59
+		pauseCommand,
60
+		unpauseCommand,
61
+	}
62
+
63
+	if err := app.Run(os.Args); err != nil {
64
+		log.Fatal(err)
65
+	}
66
+}
... ...
@@ -1,42 +1,84 @@
1
-package nsinit
1
+package main
2 2
 
3 3
 import (
4
+	"fmt"
4 5
 	"log"
6
+	"net"
5 7
 	"os"
8
+	"strconv"
9
+	"strings"
10
+	"text/tabwriter"
6 11
 
7 12
 	"github.com/docker/libcontainer"
13
+	"github.com/docker/libcontainer/devices"
14
+	"github.com/docker/libcontainer/mount/nodes"
8 15
 	"github.com/docker/libcontainer/namespaces"
9 16
 	_ "github.com/docker/libcontainer/namespaces/nsenter"
10
-	"github.com/docker/libcontainer/syncpipe"
11 17
 )
12 18
 
13
-func findUserArgs() []string {
14
-	i := 0
15
-	for _, a := range os.Args {
16
-		i++
19
+// nsenterExec exec's a process inside an existing container
20
+func nsenterExec(config *libcontainer.Config, args []string) {
21
+	if err := namespaces.FinalizeSetns(config, args); err != nil {
22
+		log.Fatalf("failed to nsenter: %s", err)
23
+	}
24
+}
17 25
 
18
-		if a == "--" {
19
-			break
20
-		}
26
+// nsenterMknod runs mknod inside an existing container
27
+//
28
+// mknod <path> <type> <major> <minor>
29
+func nsenterMknod(config *libcontainer.Config, args []string) {
30
+	if len(args) != 4 {
31
+		log.Fatalf("expected mknod to have 4 arguments not %d", len(args))
21 32
 	}
22 33
 
23
-	return os.Args[i:]
24
-}
34
+	t := rune(args[1][0])
25 35
 
26
-// this expects that we already have our namespaces setup by the C initializer
27
-// we are expected to finalize the namespace and exec the user's application
28
-func nsenter() {
29
-	syncPipe, err := syncpipe.NewSyncPipeFromFd(0, 3)
36
+	major, err := strconv.Atoi(args[2])
30 37
 	if err != nil {
31
-		log.Fatalf("unable to create sync pipe: %s", err)
38
+		log.Fatal(err)
32 39
 	}
33 40
 
34
-	var config *libcontainer.Config
35
-	if err := syncPipe.ReadFromParent(&config); err != nil {
36
-		log.Fatalf("reading container config from parent: %s", err)
41
+	minor, err := strconv.Atoi(args[3])
42
+	if err != nil {
43
+		log.Fatal(err)
37 44
 	}
38 45
 
39
-	if err := namespaces.FinalizeSetns(config, findUserArgs()); err != nil {
40
-		log.Fatalf("failed to nsenter: %s", err)
46
+	n := &devices.Device{
47
+		Path:        args[0],
48
+		Type:        t,
49
+		MajorNumber: int64(major),
50
+		MinorNumber: int64(minor),
51
+	}
52
+
53
+	if err := nodes.CreateDeviceNode("/", n); err != nil {
54
+		log.Fatal(err)
55
+	}
56
+}
57
+
58
+// nsenterIp displays the network interfaces inside a container's net namespace
59
+func nsenterIp(config *libcontainer.Config, args []string) {
60
+	interfaces, err := net.Interfaces()
61
+	if err != nil {
62
+		log.Fatal(err)
41 63
 	}
64
+
65
+	w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
66
+	fmt.Fprint(w, "NAME\tMTU\tMAC\tFLAG\tADDRS\n")
67
+
68
+	for _, iface := range interfaces {
69
+		addrs, err := iface.Addrs()
70
+		if err != nil {
71
+			log.Fatal(err)
72
+		}
73
+
74
+		o := []string{}
75
+
76
+		for _, a := range addrs {
77
+			o = append(o, a.String())
78
+		}
79
+
80
+		fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\n", iface.Name, iface.MTU, iface.HardwareAddr, iface.Flags, strings.Join(o, ","))
81
+	}
82
+
83
+	w.Flush()
42 84
 }
43 85
deleted file mode 100644
... ...
@@ -1,7 +0,0 @@
1
-package main
2
-
3
-import "github.com/docker/libcontainer/nsinit"
4
-
5
-func main() {
6
-	nsinit.NsInit()
7
-}
... ...
@@ -1,4 +1,4 @@
1
-package nsinit
1
+package main
2 2
 
3 3
 import (
4 4
 	"log"
... ...
@@ -34,7 +34,7 @@ func unpauseAction(context *cli.Context) {
34 34
 }
35 35
 
36 36
 func toggle(state cgroups.FreezerState) error {
37
-	container, err := loadContainer()
37
+	container, err := loadConfig()
38 38
 	if err != nil {
39 39
 		return err
40 40
 	}
... ...
@@ -1,4 +1,4 @@
1
-package nsinit
1
+package main
2 2
 
3 3
 import (
4 4
 	"encoding/json"
... ...
@@ -16,7 +16,7 @@ var statsCommand = cli.Command{
16 16
 }
17 17
 
18 18
 func statsAction(context *cli.Context) {
19
-	container, err := loadContainer()
19
+	container, err := loadConfig()
20 20
 	if err != nil {
21 21
 		log.Fatal(err)
22 22
 	}
... ...
@@ -1,4 +1,4 @@
1
-package nsinit
1
+package main
2 2
 
3 3
 import (
4 4
 	"encoding/json"
... ...
@@ -6,10 +6,18 @@ import (
6 6
 	"os"
7 7
 	"path/filepath"
8 8
 
9
+	"github.com/codegangsta/cli"
9 10
 	"github.com/docker/libcontainer"
11
+	"github.com/docker/libcontainer/syncpipe"
10 12
 )
11 13
 
12
-func loadContainer() (*libcontainer.Config, error) {
14
+// rFunc is a function registration for calling after an execin
15
+type rFunc struct {
16
+	Usage  string
17
+	Action func(*libcontainer.Config, []string)
18
+}
19
+
20
+func loadConfig() (*libcontainer.Config, error) {
13 21
 	f, err := os.Open(filepath.Join(dataPath, "container.json"))
14 22
 	if err != nil {
15 23
 		return nil, err
... ...
@@ -35,12 +43,52 @@ func openLog(name string) error {
35 35
 	return nil
36 36
 }
37 37
 
38
-func loadContainerFromJson(rawData string) (*libcontainer.Config, error) {
39
-	var container *libcontainer.Config
38
+func findUserArgs() []string {
39
+	i := 0
40
+	for _, a := range os.Args {
41
+		i++
42
+
43
+		if a == "--" {
44
+			break
45
+		}
46
+	}
47
+
48
+	return os.Args[i:]
49
+}
40 50
 
41
-	if err := json.Unmarshal([]byte(rawData), &container); err != nil {
51
+// loadConfigFromFd loads a container's config from the sync pipe that is provided by
52
+// fd 3 when running a process
53
+func loadConfigFromFd() (*libcontainer.Config, error) {
54
+	syncPipe, err := syncpipe.NewSyncPipeFromFd(0, 3)
55
+	if err != nil {
42 56
 		return nil, err
43 57
 	}
44 58
 
45
-	return container, nil
59
+	var config *libcontainer.Config
60
+	if err := syncPipe.ReadFromParent(&config); err != nil {
61
+		return nil, err
62
+	}
63
+
64
+	return config, nil
65
+}
66
+
67
+func preload(context *cli.Context) error {
68
+	if logPath != "" {
69
+		if err := openLog(logPath); err != nil {
70
+			return err
71
+		}
72
+	}
73
+
74
+	return nil
75
+}
76
+
77
+func runFunc(f *rFunc) {
78
+	userArgs := findUserArgs()
79
+
80
+	config, err := loadConfigFromFd()
81
+	if err != nil {
82
+		log.Fatalf("unable to receive config from sync pipe: %s", err)
83
+	}
84
+
85
+	f.Action(config, userArgs)
46 86
 }
... ...
@@ -18,6 +18,9 @@ type State struct {
18 18
 
19 19
 	// Network runtime state.
20 20
 	NetworkState network.NetworkState `json:"network_state,omitempty"`
21
+
22
+	// Path to all the cgroups setup for a container. Key is cgroup subsystem name.
23
+	CgroupPaths map[string]string `json:"cgroup_paths,omitempty"`
21 24
 }
22 25
 
23 26
 // The running state of the container.