Skip to content

Commit 2be6766

Browse files
committed
Remove syncpipe pkg
This removes the entire syncpipe package and replaces it with standard operations on the pipes. The syncpipe type just never felt right and probably should not have been there. Signed-off-by: Michael Crosby <[email protected]>
1 parent ee6f15a commit 2be6766

File tree

10 files changed

+114
-289
lines changed

10 files changed

+114
-289
lines changed

Diff for: integration/init_test.go

+1-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"runtime"
77

88
"github.com/docker/libcontainer/namespaces"
9-
"github.com/docker/libcontainer/syncpipe"
109
)
1110

1211
// init runs the libcontainer initialization code because of the busybox style needs
@@ -27,12 +26,7 @@ func init() {
2726
log.Fatal(err)
2827
}
2928

30-
syncPipe, err := syncpipe.NewSyncPipeFromFd(0, 3)
31-
if err != nil {
32-
log.Fatalf("unable to create sync pipe: %s", err)
33-
}
34-
35-
if err := namespaces.Init(container, rootfs, "", syncPipe, os.Args[3:]); err != nil {
29+
if err := namespaces.Init(container, rootfs, "", os.NewFile(3, "pipe"), os.Args[3:]); err != nil {
3630
log.Fatalf("unable to initialize for container: %s", err)
3731
}
3832
os.Exit(1)

Diff for: namespaces/exec.go

+38-55
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package namespaces
44

55
import (
6+
"encoding/json"
67
"io"
78
"os"
89
"os/exec"
@@ -13,7 +14,6 @@ import (
1314
"github.com/docker/libcontainer/cgroups/fs"
1415
"github.com/docker/libcontainer/cgroups/systemd"
1516
"github.com/docker/libcontainer/network"
16-
"github.com/docker/libcontainer/syncpipe"
1717
"github.com/docker/libcontainer/system"
1818
)
1919

@@ -22,19 +22,17 @@ import (
2222
// Exec performs setup outside of a namespace so that a container can be
2323
// executed. Exec is a high level function for working with container namespaces.
2424
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
2826

2927
// 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()
3230
if err != nil {
3331
return -1, err
3432
}
35-
defer syncPipe.Close()
33+
defer parent.Close()
3634

37-
command := createCommand(container, console, dataPath, os.Args[0], syncPipe.Child(), args)
35+
command := createCommand(container, console, dataPath, os.Args[0], child, args)
3836
// Note: these are only used in non-tty mode
3937
// if there is a tty for the container it will be opened within the namespace and the
4038
// 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
4341
command.Stderr = stderr
4442

4543
if err := command.Start(); err != nil {
44+
child.Close()
4645
return -1, err
4746
}
47+
child.Close()
4848

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+
}
5155

5256
started, err := system.GetProcessStartTime(command.Process.Pid)
5357
if err != nil {
54-
return -1, err
58+
return terminate(err)
5559
}
5660

5761
// Do this before syncing with child so that no children
5862
// can escape the cgroup
5963
cgroupRef, err := SetupCgroups(container, command.Process.Pid)
6064
if err != nil {
61-
command.Process.Kill()
62-
command.Wait()
63-
return -1, err
65+
return terminate(err)
6466
}
6567
defer cgroupRef.Cleanup()
6668

6769
cgroupPaths, err := cgroupRef.Paths()
6870
if err != nil {
69-
command.Process.Kill()
70-
command.Wait()
71-
return -1, err
71+
return terminate(err)
7272
}
7373

7474
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)
7985
}
8086

8187
state := &libcontainer.State{
@@ -86,17 +92,18 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri
8692
}
8793

8894
if err := libcontainer.SaveState(dataPath, state); err != nil {
89-
command.Process.Kill()
90-
command.Wait()
91-
return -1, err
95+
return terminate(err)
9296
}
9397
defer libcontainer.DeleteState(dataPath)
9498

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)
100107
}
101108

102109
if startCallback != nil {
@@ -108,7 +115,6 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri
108115
return -1, err
109116
}
110117
}
111-
112118
return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
113119
}
114120

@@ -129,16 +135,6 @@ func DefaultCreateCommand(container *libcontainer.Config, console, dataPath, ini
129135
"data_path=" + dataPath,
130136
}
131137

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-
142138
command := exec.Command(init, append([]string{"init", "--"}, args...)...)
143139
// make sure the process is executed inside the context of the rootfs
144140
command.Dir = container.RootFs
@@ -173,7 +169,7 @@ func SetupCgroups(container *libcontainer.Config, nspid int) (cgroups.ActiveCgro
173169

174170
// InitializeNetworking creates the container's network stack outside of the namespace and moves
175171
// 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 {
177173
for _, config := range container.Networks {
178174
strategy, err := network.GetStrategy(config.Type)
179175
if err != nil {
@@ -183,18 +179,5 @@ func InitializeNetworking(container *libcontainer.Config, nspid int, pipe *syncp
183179
return err
184180
}
185181
}
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
200183
}

Diff for: namespaces/execin.go

+16-12
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package namespaces
44

55
import (
6+
"encoding/json"
67
"fmt"
78
"io"
89
"os"
@@ -15,7 +16,6 @@ import (
1516
"github.com/docker/libcontainer/apparmor"
1617
"github.com/docker/libcontainer/cgroups"
1718
"github.com/docker/libcontainer/label"
18-
"github.com/docker/libcontainer/syncpipe"
1919
"github.com/docker/libcontainer/system"
2020
)
2121

@@ -41,35 +41,40 @@ func ExecIn(container *libcontainer.Config, state *libcontainer.State, userArgs
4141
}
4242
}
4343

44-
pipe, err := syncpipe.NewSyncPipe()
44+
parent, child, err := newInitPipe()
4545
if err != nil {
4646
return -1, err
4747
}
48-
defer pipe.Close()
48+
defer parent.Close()
4949

5050
// Note: these are only used in non-tty mode
5151
// if there is a tty for the container it will be opened within the namespace and the
5252
// fds will be duped to stdin, stdiout, and stderr
5353
cmd.Stdin = stdin
5454
cmd.Stdout = stdout
5555
cmd.Stderr = stderr
56-
57-
cmd.ExtraFiles = []*os.File{pipe.Child()}
56+
cmd.ExtraFiles = []*os.File{child}
5857

5958
if err := cmd.Start(); err != nil {
59+
child.Close()
6060
return -1, err
6161
}
62-
pipe.CloseChild()
62+
child.Close()
63+
64+
terminate := func(terr error) (int, error) {
65+
// TODO: log the errors for kill and wait
66+
cmd.Process.Kill()
67+
cmd.Wait()
68+
return -1, terr
69+
}
6370

6471
// Enter cgroups.
6572
if err := EnterCgroups(state, cmd.Process.Pid); err != nil {
66-
return -1, err
73+
return terminate(err)
6774
}
6875

69-
if err := pipe.SendToChild(container); err != nil {
70-
cmd.Process.Kill()
71-
cmd.Wait()
72-
return -1, err
76+
if err := json.NewEncoder(parent).Encode(container); err != nil {
77+
return terminate(err)
7378
}
7479

7580
if startCallback != nil {
@@ -81,7 +86,6 @@ func ExecIn(container *libcontainer.Config, state *libcontainer.State, userArgs
8186
return -1, err
8287
}
8388
}
84-
8589
return cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
8690
}
8791

Diff for: namespaces/init.go

+16-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
package namespaces
44

55
import (
6+
"encoding/json"
67
"fmt"
8+
"io/ioutil"
79
"os"
810
"strings"
911
"syscall"
@@ -18,7 +20,6 @@ import (
1820
"github.com/docker/libcontainer/network"
1921
"github.com/docker/libcontainer/security/capabilities"
2022
"github.com/docker/libcontainer/security/restrict"
21-
"github.com/docker/libcontainer/syncpipe"
2223
"github.com/docker/libcontainer/system"
2324
"github.com/docker/libcontainer/user"
2425
"github.com/docker/libcontainer/utils"
@@ -30,11 +31,22 @@ import (
3031
// and other options required for the new container.
3132
// The caller of Init function has to ensure that the go runtime is locked to an OS thread
3233
// (using runtime.LockOSThread) else system calls like setns called within Init may not work as intended.
33-
func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syncPipe *syncpipe.SyncPipe, args []string) (err error) {
34+
func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, pipe *os.File, args []string) (err error) {
3435
defer func() {
36+
// if we have an error during the initialization of the container's init then send it back to the
37+
// parent process in the form of an initError.
3538
if err != nil {
36-
syncPipe.ReportChildError(err)
39+
// ensure that any data sent from the parent is consumed so it doesn't
40+
// receive ECONNRESET when the child writes to the pipe.
41+
ioutil.ReadAll(pipe)
42+
if err := json.NewEncoder(pipe).Encode(initError{
43+
Message: err.Error(),
44+
}); err != nil {
45+
panic(err)
46+
}
3747
}
48+
// ensure that this pipe is always closed
49+
pipe.Close()
3850
}()
3951

4052
rootfs, err := utils.ResolveRootfs(uncleanRootfs)
@@ -50,7 +62,7 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syn
5062

5163
// We always read this as it is a way to sync with the parent as well
5264
var networkState *network.NetworkState
53-
if err := syncPipe.ReadFromParent(&networkState); err != nil {
65+
if err := json.NewDecoder(pipe).Decode(&networkState); err != nil {
5466
return err
5567
}
5668

Diff for: namespaces/utils.go

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// +build linux
2+
3+
package namespaces
4+
5+
import (
6+
"os"
7+
"syscall"
8+
)
9+
10+
type initError struct {
11+
Message string `json:"message,omitempty"`
12+
}
13+
14+
func (i initError) Error() string {
15+
return i.Message
16+
}
17+
18+
// New returns a newly initialized Pipe for communication between processes
19+
func newInitPipe() (parent *os.File, child *os.File, err error) {
20+
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
21+
if err != nil {
22+
return nil, nil, err
23+
}
24+
return os.NewFile(uintptr(fds[1]), "parent"), os.NewFile(uintptr(fds[0]), "child"), nil
25+
}
26+
27+
// GetNamespaceFlags parses the container's Namespaces options to set the correct
28+
// flags on clone, unshare, and setns
29+
func GetNamespaceFlags(namespaces map[string]bool) (flag int) {
30+
for key, enabled := range namespaces {
31+
if enabled {
32+
if ns := GetNamespace(key); ns != nil {
33+
flag |= ns.Value
34+
}
35+
}
36+
}
37+
return flag
38+
}

Diff for: nsinit/init.go

+2-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88

99
"github.com/codegangsta/cli"
1010
"github.com/docker/libcontainer/namespaces"
11-
"github.com/docker/libcontainer/syncpipe"
1211
)
1312

1413
var (
@@ -41,12 +40,8 @@ func initAction(context *cli.Context) {
4140
log.Fatal(err)
4241
}
4342

44-
syncPipe, err := syncpipe.NewSyncPipeFromFd(0, uintptr(pipeFd))
45-
if err != nil {
46-
log.Fatalf("unable to create sync pipe: %s", err)
47-
}
48-
49-
if err := namespaces.Init(container, rootfs, console, syncPipe, []string(context.Args())); err != nil {
43+
pipe := os.NewFile(uintptr(pipeFd), "pipe")
44+
if err := namespaces.Init(container, rootfs, console, pipe, []string(context.Args())); err != nil {
5045
log.Fatalf("unable to initialize for container: %s", err)
5146
}
5247
}

0 commit comments

Comments
 (0)