func execProcess(context *cli.Context) (int, error) { container, err := getContainer(context) if err != nil { return -1, err } path := context.String("process") if path == "" && len(context.Args()) == 1 { return -1, fmt.Errorf("process args cannot be empty") } detach := context.Bool("detach") state, err := container.State() if err != nil { return -1, err } bundle := utils.SearchLabels(state.Config.Labels, "bundle") p, err := getProcess(context, bundle) if err != nil { return -1, err } r := &runner{ enableSubreaper: !context.Bool("no-subreaper"), shouldDestroy: false, container: container, console: context.String("console"), detach: detach, pidFile: context.String("pid-file"), } return r.run(p) }
func (c *linuxContainer) Start(process *Process) error { c.m.Lock() defer c.m.Unlock() status, err := c.currentStatus() if err != nil { return err } doInit := status == Destroyed parent, err := c.newParentProcess(process, doInit) if err != nil { return newSystemErrorWithCause(err, "creating new parent process") } if err := parent.start(); err != nil { // terminate the process to ensure that it properly is reaped. if err := parent.terminate(); err != nil { logrus.Warn(err) } return newSystemErrorWithCause(err, "starting container process") } // generate a timestamp indicating when the container was started c.created = time.Now().UTC() c.state = &runningState{ c: c, } if doInit { if err := c.updateState(parent); err != nil { return err } if c.config.Hooks != nil { s := configs.HookState{ Version: c.config.Version, ID: c.id, Pid: parent.pid(), Root: c.config.Rootfs, BundlePath: utils.SearchLabels(c.config.Labels, "bundle"), } for i, hook := range c.config.Hooks.Poststart { if err := hook.Run(s); err != nil { if err := parent.terminate(); err != nil { logrus.Warn(err) } return newSystemErrorWithCausef(err, "running poststart hook %d", i) } } } } return nil }
func runPoststopHooks(c *linuxContainer) error { if c.config.Hooks != nil { s := configs.HookState{ Version: c.config.Version, ID: c.id, Root: c.config.Rootfs, BundlePath: utils.SearchLabels(c.config.Labels, "bundle"), } for _, hook := range c.config.Hooks.Poststop { if err := hook.Run(s); err != nil { return err } } } return nil }
func getContainers(context *cli.Context) ([]containerState, error) { factory, err := loadFactory(context) if err != nil { return nil, err } root := context.GlobalString("root") absRoot, err := filepath.Abs(root) if err != nil { return nil, err } list, err := ioutil.ReadDir(absRoot) if err != nil { fatal(err) } var s []containerState for _, item := range list { if item.IsDir() { container, err := factory.Load(item.Name()) if err != nil { return nil, err } containerStatus, err := container.Status() if err != nil { return nil, err } state, err := container.State() if err != nil { return nil, err } s = append(s, containerState{ ID: state.BaseState.ID, InitProcessPid: state.BaseState.InitProcessPid, Status: containerStatus.String(), Bundle: utils.SearchLabels(state.Config.Labels, "bundle"), Created: state.BaseState.Created}) } } return s, nil }
func (p *initProcess) start() error { defer p.parentPipe.Close() err := p.cmd.Start() p.process.ops = p p.childPipe.Close() p.rootDir.Close() if err != nil { p.process.ops = nil return newSystemErrorWithCause(err, "starting init process command") } if _, err := io.Copy(p.parentPipe, p.bootstrapData); err != nil { return err } if err := p.execSetns(); err != nil { return newSystemErrorWithCause(err, "running exec setns process for init") } // Save the standard descriptor names before the container process // can potentially move them (e.g., via dup2()). If we don't do this now, // we won't know at checkpoint time which file descriptor to look up. fds, err := getPipeFds(p.pid()) if err != nil { return newSystemErrorWithCausef(err, "getting pipe fds for pid %d", p.pid()) } p.setExternalDescriptors(fds) // Do this before syncing with child so that no children // can escape the cgroup if err := p.manager.Apply(p.pid()); err != nil { return newSystemErrorWithCause(err, "applying cgroup configuration for process") } defer func() { if err != nil { // TODO: should not be the responsibility to call here p.manager.Destroy() } }() if err := p.createNetworkInterfaces(); err != nil { return newSystemErrorWithCause(err, "creating nework interfaces") } if err := p.sendConfig(); err != nil { return newSystemErrorWithCause(err, "sending config to init process") } var ( procSync syncT sentRun bool sentResume bool ierr *genericError ) dec := json.NewDecoder(p.parentPipe) loop: for { if err := dec.Decode(&procSync); err != nil { if err == io.EOF { break loop } return newSystemErrorWithCause(err, "decoding sync type from init pipe") } switch procSync.Type { case procReady: if err := p.manager.Set(p.config.Config); err != nil { return newSystemErrorWithCause(err, "setting cgroup config for ready process") } // set oom_score_adj if err := setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid()); err != nil { return newSystemErrorWithCause(err, "setting oom score for ready process") } // set rlimits, this has to be done here because we lose permissions // to raise the limits once we enter a user-namespace if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil { return newSystemErrorWithCause(err, "setting rlimits for ready process") } // call prestart hooks if !p.config.Config.Namespaces.Contains(configs.NEWNS) { if p.config.Config.Hooks != nil { s := configs.HookState{ Version: p.container.config.Version, ID: p.container.id, Pid: p.pid(), Root: p.config.Config.Rootfs, } for i, hook := range p.config.Config.Hooks.Prestart { if err := hook.Run(s); err != nil { return newSystemErrorWithCausef(err, "running prestart hook %d", i) } } } } // Sync with child. if err := utils.WriteJSON(p.parentPipe, syncT{procRun}); err != nil { return newSystemErrorWithCause(err, "reading syncT run type") } sentRun = true case procHooks: if p.config.Config.Hooks != nil { s := configs.HookState{ Version: p.container.config.Version, ID: p.container.id, Pid: p.pid(), Root: p.config.Config.Rootfs, BundlePath: utils.SearchLabels(p.config.Config.Labels, "bundle"), } for i, hook := range p.config.Config.Hooks.Prestart { if err := hook.Run(s); err != nil { return newSystemErrorWithCausef(err, "running prestart hook %d", i) } } } // Sync with child. if err := utils.WriteJSON(p.parentPipe, syncT{procResume}); err != nil { return newSystemErrorWithCause(err, "reading syncT resume type") } sentResume = true case procError: // wait for the child process to fully complete and receive an error message // if one was encoutered if err := dec.Decode(&ierr); err != nil && err != io.EOF { return newSystemErrorWithCause(err, "decoding proc error from init") } if ierr != nil { break loop } // Programmer error. panic("No error following JSON procError payload.") default: return newSystemError(fmt.Errorf("invalid JSON payload from child")) } } if !sentRun { return newSystemErrorWithCause(ierr, "container init") } if p.config.Config.Namespaces.Contains(configs.NEWNS) && !sentResume { return newSystemError(fmt.Errorf("could not synchronise after executing prestart hooks with container process")) } if err := syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR); err != nil { return newSystemErrorWithCause(err, "shutting down init pipe") } // Must be done after Shutdown so the child will exit and we can wait for it. if ierr != nil { p.wait() return ierr } return nil }
instance of a container.`, Action: func(context *cli.Context) error { container, err := getContainer(context) if err != nil { return err } containerStatus, err := container.Status() if err != nil { return err } state, err := container.State() if err != nil { return err } cs := cState{ Version: state.BaseState.Config.Version, ID: state.BaseState.ID, InitProcessPid: state.BaseState.InitProcessPid, Status: containerStatus.String(), Bundle: utils.SearchLabels(state.Config.Labels, "bundle"), Rootfs: state.BaseState.Config.Rootfs, Created: state.BaseState.Created} data, err := json.MarshalIndent(cs, "", " ") if err != nil { return err } os.Stdout.Write(data) return nil }, }
func (p *initProcess) start() error { defer p.parentPipe.Close() err := p.cmd.Start() p.process.ops = p p.childPipe.Close() p.rootDir.Close() if err != nil { p.process.ops = nil return newSystemErrorWithCause(err, "starting init process command") } if _, err := io.Copy(p.parentPipe, p.bootstrapData); err != nil { return err } if err := p.execSetns(); err != nil { return newSystemErrorWithCause(err, "running exec setns process for init") } // Save the standard descriptor names before the container process // can potentially move them (e.g., via dup2()). If we don't do this now, // we won't know at checkpoint time which file descriptor to look up. fds, err := getPipeFds(p.pid()) if err != nil { return newSystemErrorWithCausef(err, "getting pipe fds for pid %d", p.pid()) } p.setExternalDescriptors(fds) // Do this before syncing with child so that no children // can escape the cgroup if err := p.manager.Apply(p.pid()); err != nil { return newSystemErrorWithCause(err, "applying cgroup configuration for process") } defer func() { if err != nil { // TODO: should not be the responsibility to call here p.manager.Destroy() } }() if err := p.createNetworkInterfaces(); err != nil { return newSystemErrorWithCause(err, "creating network interfaces") } if err := p.sendConfig(); err != nil { return newSystemErrorWithCause(err, "sending config to init process") } var ( sentRun bool sentResume bool ) ierr := parseSync(p.parentPipe, func(sync *syncT) error { switch sync.Type { case procConsole: if err := writeSync(p.parentPipe, procConsoleReq); err != nil { return newSystemErrorWithCause(err, "writing syncT 'request fd'") } masterFile, err := utils.RecvFd(p.parentPipe) if err != nil { return newSystemErrorWithCause(err, "getting master pty from child pipe") } if p.process.consoleChan == nil { // TODO: Don't panic here, do something more sane. panic("consoleChan is nil") } p.process.consoleChan <- masterFile if err := writeSync(p.parentPipe, procConsoleAck); err != nil { return newSystemErrorWithCause(err, "writing syncT 'ack fd'") } case procReady: if err := p.manager.Set(p.config.Config); err != nil { return newSystemErrorWithCause(err, "setting cgroup config for ready process") } // set oom_score_adj if err := setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid()); err != nil { return newSystemErrorWithCause(err, "setting oom score for ready process") } // set rlimits, this has to be done here because we lose permissions // to raise the limits once we enter a user-namespace if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil { return newSystemErrorWithCause(err, "setting rlimits for ready process") } // call prestart hooks if !p.config.Config.Namespaces.Contains(configs.NEWNS) { if p.config.Config.Hooks != nil { s := configs.HookState{ Version: p.container.config.Version, ID: p.container.id, Pid: p.pid(), Root: p.config.Config.Rootfs, } for i, hook := range p.config.Config.Hooks.Prestart { if err := hook.Run(s); err != nil { return newSystemErrorWithCausef(err, "running prestart hook %d", i) } } } } // Sync with child. if err := writeSync(p.parentPipe, procRun); err != nil { return newSystemErrorWithCause(err, "writing syncT 'run'") } sentRun = true case procHooks: if p.config.Config.Hooks != nil { s := configs.HookState{ Version: p.container.config.Version, ID: p.container.id, Pid: p.pid(), Root: p.config.Config.Rootfs, BundlePath: utils.SearchLabels(p.config.Config.Labels, "bundle"), } for i, hook := range p.config.Config.Hooks.Prestart { if err := hook.Run(s); err != nil { return newSystemErrorWithCausef(err, "running prestart hook %d", i) } } } // Sync with child. if err := writeSync(p.parentPipe, procResume); err != nil { return newSystemErrorWithCause(err, "writing syncT 'resume'") } sentResume = true default: return newSystemError(fmt.Errorf("invalid JSON payload from child")) } return nil }) if !sentRun { return newSystemErrorWithCause(ierr, "container init") } if p.config.Config.Namespaces.Contains(configs.NEWNS) && !sentResume { return newSystemError(fmt.Errorf("could not synchronise after executing prestart hooks with container process")) } if err := syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR); err != nil { return newSystemErrorWithCause(err, "shutting down init pipe") } // Must be done after Shutdown so the child will exit and we can wait for it. if ierr != nil { p.wait() return ierr } return nil }