From bb6a747825509fb63d2e2355c840ead4c2e3bbf9 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 11 Jan 2016 16:57:18 -0800 Subject: [PATCH] Add detach to runc By adding detach to runc the container process is the only thing running on the system is the containers process. This allows better usage of memeory and no runc process being long lived. With this addition you also need a delete command because the detached container will not be able to remove state and the left over cgroups directories. Signed-off-by: Michael Crosby --- delete.go | 15 ++++++++ libcontainer/state_linux.go | 5 ++- main.go | 14 ++++---- start.go | 72 ++++++++++++++++++++++++++++++++----- 4 files changed, 91 insertions(+), 15 deletions(-) create mode 100644 delete.go diff --git a/delete.go b/delete.go new file mode 100644 index 000000000..41b377855 --- /dev/null +++ b/delete.go @@ -0,0 +1,15 @@ +package main + +import "github.com/codegangsta/cli" + +var deleteCommand = cli.Command{ + Name: "delete", + Usage: "delete any resources held by the container often used with detached containers", + Action: func(context *cli.Context) { + container, err := getContainer(context) + if err != nil { + fatal(err) + } + destroy(container) + }, +} diff --git a/libcontainer/state_linux.go b/libcontainer/state_linux.go index fb71ef97d..9ffe15a43 100644 --- a/libcontainer/state_linux.go +++ b/libcontainer/state_linux.go @@ -219,5 +219,8 @@ func (n *createdState) transition(s containerState) error { } func (n *createdState) destroy() error { - return nil + if err := n.c.refreshState(); err != nil { + return err + } + return n.c.state.destroy() } diff --git a/main.go b/main.go index ba7d8928d..4142172b3 100644 --- a/main.go +++ b/main.go @@ -70,16 +70,18 @@ func main() { }, } app.Commands = []cli.Command{ - startCommand, checkpointCommand, + deleteCommand, eventsCommand, - restoreCommand, - killCommand, - specCommand, - pauseCommand, - resumeCommand, execCommand, + execCommand, + killCommand, listCommand, + pauseCommand, + restoreCommand, + resumeCommand, + specCommand, + startCommand, } app.Before = func(context *cli.Context) error { if context.GlobalBool("debug") { diff --git a/start.go b/start.go index 8f2d85845..7ba4d589a 100644 --- a/start.go +++ b/start.go @@ -7,6 +7,7 @@ import ( "os" "runtime" "strconv" + "syscall" "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" @@ -31,6 +32,15 @@ var startCommand = cli.Command{ Value: "", Usage: "specify the pty slave path for use with the container", }, + cli.BoolFlag{ + Name: "detach,d", + Usage: "detach from the container's process", + }, + cli.StringFlag{ + Name: "pid-file", + Value: "", + Usage: "specify the file to write the process id to", + }, }, Action: func(context *cli.Context) { bundle := context.String("bundle") @@ -107,10 +117,11 @@ func startContainer(context *cli.Context, spec *specs.LinuxSpec, rspec *specs.Li } // ensure that the container is always removed if we were the process // that created it. - defer destroy(container) - + detach := context.Bool("detach") + if !detach { + defer destroy(container) + } process := newProcess(spec.Process) - // Support on-demand socket activation by passing file descriptors into the container init process. if os.Getenv("LISTEN_FDS") != "" { listenFdsInt, err := strconv.Atoi(os.Getenv("LISTEN_FDS")) @@ -121,18 +132,63 @@ func startContainer(context *cli.Context, spec *specs.LinuxSpec, rspec *specs.Li process.ExtraFiles = append(process.ExtraFiles, os.NewFile(uintptr(i), "")) } } - tty, err := newTty(spec.Process.Terminal, process, rootuid, context.String("console")) - if err != nil { - return -1, err + var tty *tty + if spec.Process.Terminal { + if tty, err = createTty(process, rootuid, context.String("console")); err != nil { + return -1, err + } + } else if detach { + if err := dupStdio(process, rootuid); err != nil { + return -1, err + } + } else { + if tty, err = createStdioPipes(process, rootuid); err != nil { + return -1, err + } } - handler := newSignalHandler(tty) - defer handler.Close() if err := container.Start(process); err != nil { return -1, err } + if pidFile := context.String("pid-file"); pidFile != "" { + pid, err := process.Pid() + if err != nil { + return -1, err + } + f, err := os.Create(pidFile) + if err != nil { + logrus.WithField("pid", pid).Error("create pid file") + } else { + _, err = fmt.Fprintf(f, "%d", pid) + f.Close() + if err != nil { + logrus.WithField("error", err).Error("write pid file") + } + } + } + if detach { + return 0, nil + } + handler := newSignalHandler(tty) + defer handler.Close() return handler.forward(process) } +func dupStdio(process *libcontainer.Process, rootuid int) error { + process.Stdin = os.Stdin + process.Stdout = os.Stdout + process.Stderr = os.Stderr + for _, fd := range []uintptr{ + os.Stdin.Fd(), + os.Stdout.Fd(), + os.Stderr.Fd(), + } { + if err := syscall.Fchown(int(fd), rootuid, rootuid); err != nil { + return err + } + } + return nil +} + // If systemd is supporting sd_notify protocol, this function will add support // for sd_notify protocol from within the container. func setupSdNotify(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, notifySocket string) {