Skip to content

Commit 85873d9

Browse files
authored
Merge pull request #886 from crosbymichael/start-pipe
Use fifo for create / start instead of signal handling
2 parents 8109ec1 + 3aacff6 commit 85873d9

File tree

12 files changed

+123
-74
lines changed

12 files changed

+123
-74
lines changed

libcontainer/container.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ type BaseContainer interface {
124124
Start(process *Process) (err error)
125125

126126
// Run immediatly starts the process inside the conatiner. Returns error if process
127-
// fails to start. It does not block waiting for a SIGCONT after start returns but
128-
// sends the signal when the process has completed.
127+
// fails to start. It does not block waiting for the exec fifo after start returns but
128+
// opens the fifo after start returns.
129129
//
130130
// errors:
131131
// ContainerDestroyed - Container no longer exists,
@@ -148,4 +148,10 @@ type BaseContainer interface {
148148
// errors:
149149
// SystemError - System error.
150150
Signal(s os.Signal) error
151+
152+
// Exec signals the container to exec the users process at the end of the init.
153+
//
154+
// errors:
155+
// SystemError - System error.
156+
Exec() error
151157
}

libcontainer/container_linux.go

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ import (
2929

3030
const stdioFdCount = 3
3131

32-
// InitContinueSignal is used to signal the container init process to
33-
// start the users specified process after the container create has finished.
34-
const InitContinueSignal = syscall.SIGCONT
35-
3632
type linuxContainer struct {
3733
id string
3834
root string
@@ -195,16 +191,39 @@ func (c *linuxContainer) Run(process *Process) error {
195191
if err != nil {
196192
return err
197193
}
198-
isInit := status == Stopped
199-
if err := c.start(process, isInit); err != nil {
194+
if err := c.start(process, status == Stopped); err != nil {
200195
return err
201196
}
202-
if isInit {
203-
return process.ops.signal(InitContinueSignal)
197+
if status == Stopped {
198+
return c.exec()
204199
}
205200
return nil
206201
}
207202

203+
func (c *linuxContainer) Exec() error {
204+
c.m.Lock()
205+
defer c.m.Unlock()
206+
return c.exec()
207+
}
208+
209+
func (c *linuxContainer) exec() error {
210+
path := filepath.Join(c.root, execFifoFilename)
211+
f, err := os.OpenFile(path, os.O_RDONLY, 0)
212+
if err != nil {
213+
return newSystemErrorWithCause(err, "open exec fifo for reading")
214+
}
215+
defer f.Close()
216+
data, err := ioutil.ReadAll(f)
217+
if err != nil {
218+
return err
219+
}
220+
if len(data) > 0 {
221+
os.Remove(path)
222+
return nil
223+
}
224+
return fmt.Errorf("cannot start an already running container")
225+
}
226+
208227
func (c *linuxContainer) start(process *Process, isInit bool) error {
209228
parent, err := c.newParentProcess(process, isInit)
210229
if err != nil {
@@ -262,17 +281,21 @@ func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProces
262281
if err != nil {
263282
return nil, newSystemErrorWithCause(err, "creating new init pipe")
264283
}
265-
cmd, err := c.commandTemplate(p, childPipe)
284+
rootDir, err := os.Open(c.root)
285+
if err != nil {
286+
return nil, err
287+
}
288+
cmd, err := c.commandTemplate(p, childPipe, rootDir)
266289
if err != nil {
267290
return nil, newSystemErrorWithCause(err, "creating new command template")
268291
}
269292
if !doInit {
270-
return c.newSetnsProcess(p, cmd, parentPipe, childPipe)
293+
return c.newSetnsProcess(p, cmd, parentPipe, childPipe, rootDir)
271294
}
272-
return c.newInitProcess(p, cmd, parentPipe, childPipe)
295+
return c.newInitProcess(p, cmd, parentPipe, childPipe, rootDir)
273296
}
274297

275-
func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.Cmd, error) {
298+
func (c *linuxContainer) commandTemplate(p *Process, childPipe, rootDir *os.File) (*exec.Cmd, error) {
276299
cmd := &exec.Cmd{
277300
Path: c.initPath,
278301
Args: c.initArgs,
@@ -284,8 +307,10 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.
284307
if cmd.SysProcAttr == nil {
285308
cmd.SysProcAttr = &syscall.SysProcAttr{}
286309
}
287-
cmd.ExtraFiles = append(p.ExtraFiles, childPipe)
288-
cmd.Env = append(cmd.Env, fmt.Sprintf("_LIBCONTAINER_INITPIPE=%d", stdioFdCount+len(cmd.ExtraFiles)-1))
310+
cmd.ExtraFiles = append(p.ExtraFiles, childPipe, rootDir)
311+
cmd.Env = append(cmd.Env,
312+
fmt.Sprintf("_LIBCONTAINER_INITPIPE=%d", stdioFdCount+len(cmd.ExtraFiles)-2),
313+
fmt.Sprintf("_LIBCONTAINER_STATEDIR=%d", stdioFdCount+len(cmd.ExtraFiles)-1))
289314
// NOTE: when running a container with no PID namespace and the parent process spawning the container is
290315
// PID1 the pdeathsig is being delivered to the container's init process by the kernel for some reason
291316
// even with the parent still running.
@@ -295,7 +320,7 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.
295320
return cmd, nil
296321
}
297322

298-
func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) (*initProcess, error) {
323+
func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe, rootDir *os.File) (*initProcess, error) {
299324
cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initStandard))
300325
nsMaps := make(map[configs.NamespaceType]string)
301326
for _, ns := range c.config.Namespaces {
@@ -318,10 +343,11 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, c
318343
process: p,
319344
bootstrapData: data,
320345
sharePidns: sharePidns,
346+
rootDir: rootDir,
321347
}, nil
322348
}
323349

324-
func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) (*setnsProcess, error) {
350+
func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe, rootDir *os.File) (*setnsProcess, error) {
325351
cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initSetns))
326352
state, err := c.currentState()
327353
if err != nil {
@@ -342,6 +368,7 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe,
342368
config: c.newInitConfig(p),
343369
process: p,
344370
bootstrapData: data,
371+
rootDir: rootDir,
345372
}, nil
346373
}
347374

@@ -360,6 +387,7 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig {
360387
AppArmorProfile: c.config.AppArmorProfile,
361388
ProcessLabel: c.config.ProcessLabel,
362389
Rlimits: c.config.Rlimits,
390+
ExecFifoPath: filepath.Join(c.root, execFifoFilename),
363391
}
364392
if process.NoNewPrivileges != nil {
365393
cfg.NoNewPrivileges = *process.NoNewPrivileges

libcontainer/factory_linux.go

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"fmt"
88
"os"
99
"os/exec"
10-
"os/signal"
1110
"path/filepath"
1211
"regexp"
1312
"runtime/debug"
@@ -24,7 +23,8 @@ import (
2423
)
2524

2625
const (
27-
stateFilename = "state.json"
26+
stateFilename = "state.json"
27+
execFifoFilename = "exec.fifo"
2828
)
2929

3030
var (
@@ -168,6 +168,9 @@ func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, err
168168
if err := os.MkdirAll(containerRoot, 0700); err != nil {
169169
return nil, newGenericError(err, SystemError)
170170
}
171+
if err := syscall.Mkfifo(filepath.Join(containerRoot, execFifoFilename), 0666); err != nil {
172+
return nil, newGenericError(err, SystemError)
173+
}
171174
c := &linuxContainer{
172175
id: id,
173176
root: containerRoot,
@@ -220,13 +223,18 @@ func (l *LinuxFactory) Type() string {
220223
// StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state
221224
// This is a low level implementation detail of the reexec and should not be consumed externally
222225
func (l *LinuxFactory) StartInitialization() (err error) {
223-
// start the signal handler as soon as we can
224-
s := make(chan os.Signal, 1)
225-
signal.Notify(s, InitContinueSignal)
226-
fdStr := os.Getenv("_LIBCONTAINER_INITPIPE")
227-
pipefd, err := strconv.Atoi(fdStr)
228-
if err != nil {
229-
return fmt.Errorf("error converting env var _LIBCONTAINER_INITPIPE(%q) to an int: %s", fdStr, err)
226+
var pipefd, rootfd int
227+
for k, v := range map[string]*int{
228+
"_LIBCONTAINER_INITPIPE": &pipefd,
229+
"_LIBCONTAINER_STATEDIR": &rootfd,
230+
} {
231+
s := os.Getenv(k)
232+
233+
i, err := strconv.Atoi(s)
234+
if err != nil {
235+
return fmt.Errorf("unable to convert %s=%s to int", k, s)
236+
}
237+
*v = i
230238
}
231239
var (
232240
pipe = os.NewFile(uintptr(pipefd), "pipe")
@@ -235,6 +243,7 @@ func (l *LinuxFactory) StartInitialization() (err error) {
235243
// clear the current process's environment to clean any libcontainer
236244
// specific env vars.
237245
os.Clearenv()
246+
238247
var i initer
239248
defer func() {
240249
// We have an error during the initialization of the container's init,
@@ -253,18 +262,16 @@ func (l *LinuxFactory) StartInitialization() (err error) {
253262
// ensure that this pipe is always closed
254263
pipe.Close()
255264
}()
256-
257265
defer func() {
258266
if e := recover(); e != nil {
259267
err = fmt.Errorf("panic from initialization: %v, %v", e, string(debug.Stack()))
260268
}
261269
}()
262-
263-
i, err = newContainerInit(it, pipe)
270+
i, err = newContainerInit(it, pipe, rootfd)
264271
if err != nil {
265272
return err
266273
}
267-
return i.Init(s)
274+
return i.Init()
268275
}
269276

270277
func (l *LinuxFactory) loadState(root string) (*State, error) {

libcontainer/init_linux.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,14 @@ type initConfig struct {
5858
PassedFilesCount int `json:"passed_files_count"`
5959
ContainerId string `json:"containerid"`
6060
Rlimits []configs.Rlimit `json:"rlimits"`
61+
ExecFifoPath string `json:"start_pipe_path"`
6162
}
6263

6364
type initer interface {
64-
Init(s chan os.Signal) error
65+
Init() error
6566
}
6667

67-
func newContainerInit(t initType, pipe *os.File) (initer, error) {
68+
func newContainerInit(t initType, pipe *os.File, stateDirFD int) (initer, error) {
6869
var config *initConfig
6970
if err := json.NewDecoder(pipe).Decode(&config); err != nil {
7071
return nil, err
@@ -79,9 +80,10 @@ func newContainerInit(t initType, pipe *os.File) (initer, error) {
7980
}, nil
8081
case initStandard:
8182
return &linuxStandardInit{
82-
pipe: pipe,
83-
parentPid: syscall.Getppid(),
84-
config: config,
83+
pipe: pipe,
84+
parentPid: syscall.Getppid(),
85+
config: config,
86+
stateDirFD: stateDirFD,
8587
}, nil
8688
}
8789
return nil, fmt.Errorf("unknown init type %q", t)

libcontainer/integration/init_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ func TestMain(m *testing.M) {
4242
logrus.SetOutput(os.Stderr)
4343
logrus.SetLevel(logrus.InfoLevel)
4444

45-
factory, err = libcontainer.New(".", libcontainer.Cgroupfs)
45+
factory, err = libcontainer.New("/run/libctTests", libcontainer.Cgroupfs)
4646
if err != nil {
4747
logrus.Error(err)
4848
os.Exit(1)
4949
}
5050
if systemd.UseSystemd() {
51-
systemdFactory, err = libcontainer.New(".", libcontainer.SystemdCgroups)
51+
systemdFactory, err = libcontainer.New("/run/libctTests", libcontainer.SystemdCgroups)
5252
if err != nil {
5353
logrus.Error(err)
5454
os.Exit(1)

libcontainer/integration/seccomp_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ func TestSeccompPermitWriteConditional(t *testing.T) {
101101
Args: []*configs.Arg{
102102
{
103103
Index: 0,
104-
Value: 1,
105-
Op: configs.GreaterThan,
104+
Value: 2,
105+
Op: configs.EqualTo,
106106
},
107107
},
108108
},
@@ -162,8 +162,8 @@ func TestSeccompDenyWriteConditional(t *testing.T) {
162162
Args: []*configs.Arg{
163163
{
164164
Index: 0,
165-
Value: 1,
166-
Op: configs.GreaterThan,
165+
Value: 2,
166+
Op: configs.EqualTo,
167167
},
168168
},
169169
},

libcontainer/integration/utils_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package integration
22

33
import (
44
"bytes"
5+
"crypto/md5"
6+
"encoding/hex"
57
"fmt"
68
"io/ioutil"
79
"os"
@@ -11,6 +13,7 @@ import (
1113
"strings"
1214
"syscall"
1315
"testing"
16+
"time"
1417

1518
"github.com/opencontainers/runc/libcontainer"
1619
"github.com/opencontainers/runc/libcontainer/configs"
@@ -92,7 +95,9 @@ func copyBusybox(dest string) error {
9295
}
9396

9497
func newContainer(config *configs.Config) (libcontainer.Container, error) {
95-
return newContainerWithName("testCT", config)
98+
h := md5.New()
99+
h.Write([]byte(time.Now().String()))
100+
return newContainerWithName(hex.EncodeToString(h.Sum(nil)), config)
96101
}
97102

98103
func newContainerWithName(name string, config *configs.Config) (libcontainer.Container, error) {

libcontainer/process_linux.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ type setnsProcess struct {
5151
fds []string
5252
process *Process
5353
bootstrapData io.Reader
54+
rootDir *os.File
5455
}
5556

5657
func (p *setnsProcess) startTime() (string, error) {
@@ -69,6 +70,7 @@ func (p *setnsProcess) start() (err error) {
6970
defer p.parentPipe.Close()
7071
err = p.cmd.Start()
7172
p.childPipe.Close()
73+
p.rootDir.Close()
7274
if err != nil {
7375
return newSystemErrorWithCause(err, "starting setns process")
7476
}
@@ -186,6 +188,7 @@ type initProcess struct {
186188
process *Process
187189
bootstrapData io.Reader
188190
sharePidns bool
191+
rootDir *os.File
189192
}
190193

191194
func (p *initProcess) pid() int {
@@ -230,6 +233,7 @@ func (p *initProcess) start() error {
230233
err := p.cmd.Start()
231234
p.process.ops = p
232235
p.childPipe.Close()
236+
p.rootDir.Close()
233237
if err != nil {
234238
p.process.ops = nil
235239
return newSystemErrorWithCause(err, "starting init process command")

libcontainer/setns_init_linux.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package libcontainer
55
import (
66
"fmt"
77
"os"
8-
"os/signal"
98

109
"github.com/opencontainers/runc/libcontainer/apparmor"
1110
"github.com/opencontainers/runc/libcontainer/keys"
@@ -24,7 +23,7 @@ func (l *linuxSetnsInit) getSessionRingName() string {
2423
return fmt.Sprintf("_ses.%s", l.config.ContainerId)
2524
}
2625

27-
func (l *linuxSetnsInit) Init(s chan os.Signal) error {
26+
func (l *linuxSetnsInit) Init() error {
2827
if !l.config.Config.NoNewKeyring {
2928
// do not inherit the parent's session keyring
3029
if _, err := keyctl.JoinSessionKeyring(l.getSessionRingName()); err != nil {
@@ -50,7 +49,5 @@ func (l *linuxSetnsInit) Init(s chan os.Signal) error {
5049
if err := label.SetProcessLabel(l.config.ProcessLabel); err != nil {
5150
return err
5251
}
53-
signal.Stop(s)
54-
close(s)
5552
return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ())
5653
}

0 commit comments

Comments
 (0)