diff options
author | Lantao Liu <lantaol@google.com> | 2018-09-13 16:36:53 -0700 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2018-09-13 16:38:03 -0700 |
commit | bde2a91433cfbac426577a691bf13817115b53be (patch) | |
tree | 1403a6e5ffca3345da142bf68535763b6f34e5a9 /runsc/boot | |
parent | adf8f339703922211886d3e5588160f65bc131b3 (diff) |
runsc: Support container signal/wait.
This CL:
1) Fix `runsc wait`, it now also works after the container exits;
2) Generate correct container state in Load;
2) Make sure `Destory` cleanup everything before successfully return.
PiperOrigin-RevId: 212900107
Change-Id: Ie129cbb9d74f8151a18364f1fc0b2603eac4109a
Diffstat (limited to 'runsc/boot')
-rw-r--r-- | runsc/boot/controller.go | 25 | ||||
-rw-r--r-- | runsc/boot/loader.go | 75 |
2 files changed, 48 insertions, 52 deletions
diff --git a/runsc/boot/controller.go b/runsc/boot/controller.go index aaac852e0..69154ff23 100644 --- a/runsc/boot/controller.go +++ b/runsc/boot/controller.go @@ -161,8 +161,11 @@ func (cm *containerManager) StartRoot(cid *string, _ *struct{}) error { log.Debugf("containerManager.StartRoot") // Tell the root container to start and wait for the result. cm.startChan <- struct{}{} + if err := <-cm.startResultChan; err != nil { + return fmt.Errorf("failed to start sandbox: %v", err) + } cm.l.setRootContainerID(*cid) - return <-cm.startResultChan + return nil } // Processes retrieves information about processes running in the sandbox. @@ -216,11 +219,11 @@ func (cm *containerManager) Start(args *StartArgs, _ *struct{}) error { return fmt.Errorf("start arguments must contain at least one file for the container root") } - tgid, err := cm.l.startContainer(cm.l.k, args.Spec, args.Conf, args.CID, args.FilePayload.Files) + err := cm.l.startContainer(cm.l.k, args.Spec, args.Conf, args.CID, args.FilePayload.Files) if err != nil { return err } - log.Debugf("Container %q started with root PID of %d", args.CID, tgid) + log.Debugf("Container %q started", args.CID) return nil } @@ -241,16 +244,12 @@ func (cm *containerManager) ExecuteAsync(args *ExecArgs, pid *int32) error { // Get the container Root Dirent from the Task, since we must run this // process with the same Root. cm.l.mu.Lock() - tgid, ok := cm.l.containerRootTGIDs[args.CID] + tg, ok := cm.l.containerRootTGs[args.CID] cm.l.mu.Unlock() if !ok { return fmt.Errorf("cannot exec in container %q: no such container", args.CID) } - t := cm.l.k.TaskSet().Root.TaskWithID(kernel.ThreadID(tgid)) - if t == nil { - return fmt.Errorf("cannot exec in container %q: no thread group with ID %d", args.CID, tgid) - } - t.WithMuLocked(func(t *kernel.Task) { + tg.Leader().WithMuLocked(func(t *kernel.Task) { args.Root = t.FSContext().RootDirectory() }) if args.Root != nil { @@ -378,12 +377,15 @@ func (cm *containerManager) Restore(o *RestoreOpts, _ *struct{}) error { cm.l.k = k cm.l.watchdog = watchdog cm.l.rootProcArgs = kernel.CreateProcessArgs{} - cm.l.setRootContainerID(o.SandboxID) cm.l.restore = true // Tell the root container to start and wait for the result. cm.startChan <- struct{}{} - return <-cm.startResultChan + if err := <-cm.startResultChan; err != nil { + return fmt.Errorf("failed to start sandbox: %v", err) + } + cm.l.setRootContainerID(o.SandboxID) + return nil } // Resume unpauses a container. @@ -423,6 +425,7 @@ type SignalArgs struct { } // Signal sends a signal to the init process of the container. +// TODO: Send signal to exec process. func (cm *containerManager) Signal(args *SignalArgs, _ *struct{}) error { log.Debugf("containerManager.Signal") return cm.l.signal(args.CID, args.Signo) diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 30d22b9c6..2ddb358bd 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -16,7 +16,6 @@ package boot import ( - "errors" "fmt" "math/rand" "os" @@ -101,15 +100,15 @@ type Loader struct { // sandboxID is the ID for the whole sandbox. sandboxID string - // mu guards containerRootTGIDs. + // mu guards containerRootTGs. mu sync.Mutex - // containerRootTGIDs maps container IDs to their root processes. It + // containerRootTGs maps container IDs to their root processes. It // can be used to determine which process to manipulate when clients // call methods on particular containers. // - // containerRootTGIDs is guarded by mu. - containerRootTGIDs map[string]kernel.ThreadID + // containerRootTGs is guarded by mu. + containerRootTGs map[string]*kernel.ThreadGroup } func init() { @@ -399,11 +398,11 @@ func (l *Loader) run() error { // startContainer starts a child container. It returns the thread group ID of // the newly created process. -func (l *Loader) startContainer(k *kernel.Kernel, spec *specs.Spec, conf *Config, cid string, files []*os.File) (kernel.ThreadID, error) { +func (l *Loader) startContainer(k *kernel.Kernel, spec *specs.Spec, conf *Config, cid string, files []*os.File) error { // Create capabilities. caps, err := specutils.Capabilities(spec.Process.Capabilities) if err != nil { - return 0, fmt.Errorf("error creating capabilities: %v", err) + return fmt.Errorf("error creating capabilities: %v", err) } // Convert the spec's additional GIDs to KGIDs. @@ -429,7 +428,7 @@ func (l *Loader) startContainer(k *kernel.Kernel, spec *specs.Spec, conf *Config procArgs, err := newProcess(spec, creds, l.k) if err != nil { - return 0, fmt.Errorf("failed to create new process: %v", err) + return fmt.Errorf("failed to create new process: %v", err) } // Can't take ownership away from os.File. dup them to get a new FDs. @@ -437,7 +436,7 @@ func (l *Loader) startContainer(k *kernel.Kernel, spec *specs.Spec, conf *Config for _, f := range files { fd, err := syscall.Dup(int(f.Fd())) if err != nil { - return 0, fmt.Errorf("failed to dup file: %v", err) + return fmt.Errorf("failed to dup file: %v", err) } f.Close() ioFDs = append(ioFDs, fd) @@ -453,24 +452,18 @@ func (l *Loader) startContainer(k *kernel.Kernel, spec *specs.Spec, conf *Config procArgs.Limits, k, cid); err != nil { - return 0, fmt.Errorf("failed to create new process: %v", err) + return fmt.Errorf("failed to create new process: %v", err) } ctx := procArgs.NewContext(l.k) mns := k.RootMountNamespace() if err := setExecutablePath(ctx, mns, &procArgs); err != nil { - return 0, fmt.Errorf("error setting executable path for %+v: %v", procArgs, err) + return fmt.Errorf("error setting executable path for %+v: %v", procArgs, err) } tg, err := l.k.CreateProcess(procArgs) if err != nil { - return 0, fmt.Errorf("failed to create process in sentry: %v", err) - } - - ts := l.k.TaskSet() - tgid := ts.Root.IDOfThreadGroup(tg) - if tgid == 0 { - return 0, errors.New("failed to get thread group ID of new process") + return fmt.Errorf("failed to create process in sentry: %v", err) } // CreateProcess takes a reference on FDMap if successful. @@ -478,9 +471,9 @@ func (l *Loader) startContainer(k *kernel.Kernel, spec *specs.Spec, conf *Config l.mu.Lock() defer l.mu.Unlock() - l.containerRootTGIDs[cid] = tgid + l.containerRootTGs[cid] = tg - return tgid, nil + return nil } // TODO: Per-container namespaces must be supported for -pid. @@ -490,53 +483,56 @@ func (l *Loader) waitContainer(cid string, waitStatus *uint32) error { // Don't defer unlock, as doing so would make it impossible for // multiple clients to wait on the same container. l.mu.Lock() - tgid, ok := l.containerRootTGIDs[cid] + tg, ok := l.containerRootTGs[cid] if !ok { defer l.mu.Unlock() - return fmt.Errorf("can't find process for container %q in %v", cid, l.containerRootTGIDs) + return fmt.Errorf("can't find process for container %q in %v", cid, l.containerRootTGs) } l.mu.Unlock() // If the thread either has already exited or exits during waiting, // consider the container exited. + // TODO: Multiple calls to waitContainer() should return + // the same exit status. defer func() { l.mu.Lock() defer l.mu.Unlock() // TODO: Containers don't map 1:1 with their root // processes. Container exits should be managed explicitly // rather than via PID. - delete(l.containerRootTGIDs, cid) + delete(l.containerRootTGs, cid) }() - return l.wait(tgid, cid, waitStatus) + l.wait(tg, waitStatus) + return nil } func (l *Loader) waitPID(tgid kernel.ThreadID, cid string, waitStatus *uint32) error { // TODO: Containers all currently share a PID namespace. // When per-container PID namespaces are supported, wait should use cid // to find the appropriate PID namespace. - if cid != l.sandboxID { + /*if cid != l.sandboxID { return errors.New("non-sandbox PID namespaces are not yet implemented") + }*/ + // TODO: This won't work if the exec process already exited. + tg := l.k.TaskSet().Root.ThreadGroupWithID(kernel.ThreadID(tgid)) + if tg == nil { + return fmt.Errorf("no thread group with ID %d", tgid) } - return l.wait(tgid, cid, waitStatus) + l.wait(tg, waitStatus) + return nil } // wait waits for the process with TGID 'tgid' in a container's PID namespace // to exit. -func (l *Loader) wait(tgid kernel.ThreadID, cid string, waitStatus *uint32) error { - tg := l.k.TaskSet().Root.ThreadGroupWithID(kernel.ThreadID(tgid)) - if tg == nil { - return fmt.Errorf("no thread group with ID %d", tgid) - } +func (l *Loader) wait(tg *kernel.ThreadGroup, waitStatus *uint32) { tg.WaitExited() *waitStatus = tg.ExitStatus().Status() - return nil } func (l *Loader) setRootContainerID(cid string) { l.mu.Lock() defer l.mu.Unlock() - // The root container has PID 1. - l.containerRootTGIDs = map[string]kernel.ThreadID{cid: 1} + l.containerRootTGs = map[string]*kernel.ThreadGroup{cid: l.k.GlobalInit()} l.sandboxID = cid } @@ -579,18 +575,15 @@ func newEmptyNetworkStack(conf *Config, clock tcpip.Clock) (inet.Stack, error) { } } +// TODO: Support sending signal to all. func (l *Loader) signal(cid string, signo int32) error { l.mu.Lock() - tgid, ok := l.containerRootTGIDs[cid] + tg, ok := l.containerRootTGs[cid] l.mu.Unlock() if !ok { return fmt.Errorf("failed to signal container %q: no such container", cid) } - // The thread group ID of a process is the leading task's thread ID. - t := l.k.TaskSet().Root.TaskWithID(tgid) - if t == nil { - return fmt.Errorf("cannot signal: no task with ID %d", tgid) - } - return t.SendSignal(&arch.SignalInfo{Signo: signo}) + si := arch.SignalInfo{Signo: signo} + return tg.Leader().SendSignal(&si) } |