mirror of
https://github.com/opencontainers/runc.git
synced 2025-10-18 13:30:54 +08:00

Add a waitgroup to wait for the io.Copy of stdout/err to finish before existing runc. The problem happens more in exec because it is really fast and the pipe has data buffered but not yet read after the process has already exited. Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
180 lines
4.2 KiB
Go
180 lines
4.2 KiB
Go
// +build linux
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"syscall"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/codegangsta/cli"
|
|
"github.com/opencontainers/runc/libcontainer"
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
"github.com/opencontainers/specs"
|
|
)
|
|
|
|
var restoreCommand = cli.Command{
|
|
Name: "restore",
|
|
Usage: "restore a container from a previous checkpoint",
|
|
ArgsUsage: `<container-id>
|
|
|
|
Where "<container-id>" is the name for the instance of the container to be
|
|
restored.`,
|
|
Description: `Restores the saved state of the container instance that was previously saved
|
|
using the runc checkpoint command.`,
|
|
Flags: []cli.Flag{
|
|
cli.StringFlag{
|
|
Name: "image-path",
|
|
Value: "",
|
|
Usage: "path to criu image files for restoring",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "work-path",
|
|
Value: "",
|
|
Usage: "path for saving work files and logs",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "tcp-established",
|
|
Usage: "allow open tcp connections",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "ext-unix-sk",
|
|
Usage: "allow external unix sockets",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "shell-job",
|
|
Usage: "allow shell jobs",
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "file-locks",
|
|
Usage: "handle file locks, for safety",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "manage-cgroups-mode",
|
|
Value: "",
|
|
Usage: "cgroups mode: 'soft' (default), 'full' and 'strict'.",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "bundle, b",
|
|
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")
|
|
id := context.Args().First()
|
|
if id == "" {
|
|
fatal(errEmptyID)
|
|
}
|
|
if imagePath == "" {
|
|
imagePath = getDefaultImagePath(context)
|
|
}
|
|
bundle := context.String("bundle")
|
|
if bundle != "" {
|
|
if err := os.Chdir(bundle); err != nil {
|
|
fatal(err)
|
|
}
|
|
}
|
|
spec, err := loadSpec(specConfig)
|
|
if err != nil {
|
|
fatal(err)
|
|
}
|
|
config, err := createLibcontainerConfig(id, spec)
|
|
if err != nil {
|
|
fatal(err)
|
|
}
|
|
status, err := restoreContainer(context, spec, config, imagePath)
|
|
if err != nil {
|
|
fatal(err)
|
|
}
|
|
os.Exit(status)
|
|
},
|
|
}
|
|
|
|
func restoreContainer(context *cli.Context, spec *specs.LinuxSpec, config *configs.Config, imagePath string) (code int, err error) {
|
|
var (
|
|
rootuid = 0
|
|
id = context.Args().First()
|
|
)
|
|
factory, err := loadFactory(context)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
container, err := factory.Load(id)
|
|
if err != nil {
|
|
container, err = factory.Create(id, config)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
}
|
|
options := criuOptions(context)
|
|
|
|
status, err := container.Status()
|
|
if err != nil {
|
|
logrus.Error(err)
|
|
}
|
|
if status == libcontainer.Running {
|
|
fatal(fmt.Errorf("Container with id %s already running", id))
|
|
}
|
|
|
|
setManageCgroupsMode(context, options)
|
|
|
|
// ensure that the container is always removed if we were the process
|
|
// that created it.
|
|
detach := context.Bool("detach")
|
|
if !detach {
|
|
defer destroy(container)
|
|
}
|
|
process := &libcontainer.Process{}
|
|
tty, err := setupIO(process, rootuid, "", false, detach)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
defer tty.Close()
|
|
handler := newSignalHandler(tty)
|
|
defer handler.Close()
|
|
if err := container.Restore(process, options); err != nil {
|
|
return -1, err
|
|
}
|
|
if err := tty.ClosePostStart(); 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
|
|
}
|
|
return handler.forward(process)
|
|
}
|
|
|
|
func criuOptions(context *cli.Context) *libcontainer.CriuOpts {
|
|
imagePath := getCheckpointImagePath(context)
|
|
if err := os.MkdirAll(imagePath, 0655); err != nil {
|
|
fatal(err)
|
|
}
|
|
return &libcontainer.CriuOpts{
|
|
ImagesDirectory: imagePath,
|
|
WorkDirectory: context.String("work-path"),
|
|
LeaveRunning: context.Bool("leave-running"),
|
|
TcpEstablished: context.Bool("tcp-established"),
|
|
ExternalUnixConnections: context.Bool("ext-unix-sk"),
|
|
ShellJob: context.Bool("shell-job"),
|
|
FileLocks: context.Bool("file-locks"),
|
|
}
|
|
}
|