diff --git a/exec.go b/exec.go index 41ce6c91..0369f171 100644 --- a/exec.go +++ b/exec.go @@ -9,6 +9,7 @@ import ( "path" "strconv" "strings" + "syscall" "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" @@ -72,13 +73,13 @@ func execProcess(context *cli.Context) (int, error) { } var ( detach = context.Bool("detach") - bundle = container.Config().Rootfs + rootfs = container.Config().Rootfs ) rootuid, err := container.Config().HostUID() if err != nil { return -1, err } - p, err := getProcess(context, path.Dir(bundle)) + p, err := getProcess(context, path.Dir(rootfs)) if err != nil { return -1, err } @@ -91,7 +92,9 @@ func execProcess(context *cli.Context) (int, error) { return -1, err } if pidFile := context.String("pid-file"); pidFile != "" { - if err := createPidile(pidFile, process); err != nil { + if err := createPidFile(pidFile, process); err != nil { + process.Signal(syscall.SIGKILL) + process.Wait() return -1, err } } diff --git a/restore.go b/restore.go index 55cff56a..b05160d6 100644 --- a/restore.go +++ b/restore.go @@ -5,6 +5,7 @@ package main import ( "fmt" "os" + "syscall" "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" @@ -53,6 +54,15 @@ var restoreCommand = cli.Command{ Value: "", Usage: "path to the root of the bundle directory", }, + 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) { imagePath := context.String("image-path") @@ -108,21 +118,30 @@ func restoreContainer(context *cli.Context, spec *specs.LinuxSpec, config *confi // ensure that the container is always removed if we were the process // that created it. - defer destroy(container) - process := &libcontainer.Process{ - Stdin: os.Stdin, - Stdout: os.Stdout, - Stderr: os.Stderr, + detach := context.Bool("detach") + if !detach { + defer destroy(container) } - tty, err := newTty(spec.Process.Terminal, process, rootuid, "") + process := &libcontainer.Process{} + tty, err := setupIO(process, rootuid, "", false, detach) if err != nil { return -1, err } - handler := newSignalHandler(tty) - defer handler.Close() if err := container.Restore(process, options); err != nil { return -1, err } + if pidFile := context.String("pid-file"); pidFile != "" { + if err := createPidFile(pidFile, process); err != nil { + process.Signal(syscall.SIGKILL) + process.Wait() + return -1, err + } + } + if detach { + return 0, nil + } + handler := newSignalHandler(tty) + defer handler.Close() return handler.forward(process) } diff --git a/start.go b/start.go index 69ef6142..46039c6c 100644 --- a/start.go +++ b/start.go @@ -140,7 +140,9 @@ func startContainer(context *cli.Context, spec *specs.LinuxSpec, rspec *specs.Li return -1, err } if pidFile := context.String("pid-file"); pidFile != "" { - if err := createPidile(pidFile, process); err != nil { + if err := createPidFile(pidFile, process); err != nil { + process.Signal(syscall.SIGKILL) + process.Wait() return -1, err } } @@ -151,76 +153,3 @@ func startContainer(context *cli.Context, spec *specs.LinuxSpec, rspec *specs.Li 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) { - mountName := "sdNotify" - spec.Mounts = append(spec.Mounts, specs.MountPoint{Name: mountName, Path: notifySocket}) - spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", notifySocket)) - rspec.Mounts[mountName] = specs.Mount{Type: "bind", Source: notifySocket, Options: []string{"bind"}} -} - -// If systemd is supporting on-demand socket activation, this function will add support -// for on-demand socket activation for the containerized service. -func setupSocketActivation(spec *specs.LinuxSpec, listenFds string) { - spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("LISTEN_FDS=%s", listenFds), "LISTEN_PID=1") -} - -func destroy(container libcontainer.Container) { - if err := container.Destroy(); err != nil { - logrus.Error(err) - } -} - -func setupIO(process *libcontainer.Process, rootuid int, console string, createTTY, detach bool) (*tty, error) { - // detach and createTty will not work unless a console path is passed - // so error out here before changing any terminal settings - if createTTY && detach && console == "" { - return nil, fmt.Errorf("cannot allocate tty if runc will detach") - } - if createTTY { - return createTty(process, rootuid, console) - } - if detach { - if err := dupStdio(process, rootuid); err != nil { - return nil, err - } - return nil, nil - } - return createStdioPipes(process, rootuid) -} - -func createPidile(path string, process *libcontainer.Process) error { - pid, err := process.Pid() - if err != nil { - return err - } - f, err := os.Create(path) - 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") - } - } - return nil -} diff --git a/tty.go b/tty.go index 77d275af..b552621b 100644 --- a/tty.go +++ b/tty.go @@ -35,7 +35,10 @@ func createStdioPipes(p *libcontainer.Process, rootuid int) (*tty, error) { i.Stderr, }, } - go io.Copy(i.Stdin, os.Stdin) + go func() { + io.Copy(i.Stdin, os.Stdin) + i.Stdin.Close() + }() go io.Copy(os.Stdout, i.Stdout) go io.Copy(os.Stderr, i.Stderr) return t, nil diff --git a/utils.go b/utils.go index c6a8fde7..4e510e7b 100644 --- a/utils.go +++ b/utils.go @@ -6,7 +6,9 @@ import ( "fmt" "os" "path/filepath" + "syscall" + "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" "github.com/opencontainers/runc/libcontainer" "github.com/opencontainers/runc/libcontainer/configs" @@ -154,3 +156,72 @@ func newProcess(p specs.Process) *libcontainer.Process { Cwd: p.Cwd, } } + +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) { + mountName := "sdNotify" + spec.Mounts = append(spec.Mounts, specs.MountPoint{Name: mountName, Path: notifySocket}) + spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", notifySocket)) + rspec.Mounts[mountName] = specs.Mount{Type: "bind", Source: notifySocket, Options: []string{"bind"}} +} + +// If systemd is supporting on-demand socket activation, this function will add support +// for on-demand socket activation for the containerized service. +func setupSocketActivation(spec *specs.LinuxSpec, listenFds string) { + spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("LISTEN_FDS=%s", listenFds), "LISTEN_PID=1") +} + +func destroy(container libcontainer.Container) { + if err := container.Destroy(); err != nil { + logrus.Error(err) + } +} + +func setupIO(process *libcontainer.Process, rootuid int, console string, createTTY, detach bool) (*tty, error) { + // detach and createTty will not work unless a console path is passed + // so error out here before changing any terminal settings + if createTTY && detach && console == "" { + return nil, fmt.Errorf("cannot allocate tty if runc will detach") + } + if createTTY { + return createTty(process, rootuid, console) + } + if detach { + if err := dupStdio(process, rootuid); err != nil { + return nil, err + } + return nil, nil + } + return createStdioPipes(process, rootuid) +} + +func createPidFile(path string, process *libcontainer.Process) error { + pid, err := process.Pid() + if err != nil { + return err + } + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + _, err = fmt.Fprintf(f, "%d", pid) + return err +}