2015-06-30 07:49:13 +08:00
|
|
|
// +build linux
|
|
|
|
|
2015-06-22 10:31:12 +08:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2016-02-09 06:25:03 +08:00
|
|
|
"errors"
|
2015-06-22 10:31:12 +08:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2016-02-02 07:00:09 +08:00
|
|
|
"syscall"
|
2015-06-22 10:31:12 +08:00
|
|
|
|
2016-02-02 07:00:09 +08:00
|
|
|
"github.com/Sirupsen/logrus"
|
2015-06-22 10:31:12 +08:00
|
|
|
"github.com/codegangsta/cli"
|
|
|
|
"github.com/opencontainers/runc/libcontainer"
|
|
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
2015-07-03 00:55:24 +08:00
|
|
|
"github.com/opencontainers/specs"
|
2015-06-22 10:31:12 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
const wildcard = -1
|
|
|
|
|
2016-02-09 06:25:03 +08:00
|
|
|
var errEmptyID = errors.New("container id cannot be empty")
|
|
|
|
|
2015-06-22 10:31:12 +08:00
|
|
|
var allowedDevices = []*configs.Device{
|
|
|
|
// allow mknod for any device
|
|
|
|
{
|
|
|
|
Type: 'c',
|
|
|
|
Major: wildcard,
|
|
|
|
Minor: wildcard,
|
|
|
|
Permissions: "m",
|
2016-02-06 07:15:25 +08:00
|
|
|
Allow: true,
|
2015-06-22 10:31:12 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: 'b',
|
|
|
|
Major: wildcard,
|
|
|
|
Minor: wildcard,
|
|
|
|
Permissions: "m",
|
2016-02-06 07:15:25 +08:00
|
|
|
Allow: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: 'c',
|
|
|
|
Path: "/dev/null",
|
|
|
|
Major: 1,
|
|
|
|
Minor: 3,
|
|
|
|
Permissions: "rwm",
|
|
|
|
Allow: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: 'c',
|
|
|
|
Path: "/dev/random",
|
|
|
|
Major: 1,
|
|
|
|
Minor: 8,
|
|
|
|
Permissions: "rwm",
|
|
|
|
Allow: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: 'c',
|
|
|
|
Path: "/dev/full",
|
|
|
|
Major: 1,
|
|
|
|
Minor: 7,
|
|
|
|
Permissions: "rwm",
|
|
|
|
Allow: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: 'c',
|
|
|
|
Path: "/dev/tty",
|
|
|
|
Major: 5,
|
|
|
|
Minor: 0,
|
|
|
|
Permissions: "rwm",
|
|
|
|
Allow: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: 'c',
|
|
|
|
Path: "/dev/zero",
|
|
|
|
Major: 1,
|
|
|
|
Minor: 5,
|
|
|
|
Permissions: "rwm",
|
|
|
|
Allow: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: 'c',
|
|
|
|
Path: "/dev/urandom",
|
|
|
|
Major: 1,
|
|
|
|
Minor: 9,
|
|
|
|
Permissions: "rwm",
|
|
|
|
Allow: true,
|
2015-06-22 10:31:12 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: "/dev/console",
|
|
|
|
Type: 'c',
|
|
|
|
Major: 5,
|
|
|
|
Minor: 1,
|
|
|
|
Permissions: "rwm",
|
2016-02-06 07:15:25 +08:00
|
|
|
Allow: true,
|
2015-06-22 10:31:12 +08:00
|
|
|
},
|
|
|
|
// /dev/pts/ - pts namespaces are "coming soon"
|
|
|
|
{
|
|
|
|
Path: "",
|
|
|
|
Type: 'c',
|
|
|
|
Major: 136,
|
|
|
|
Minor: wildcard,
|
|
|
|
Permissions: "rwm",
|
2016-02-06 07:15:25 +08:00
|
|
|
Allow: true,
|
2015-06-22 10:31:12 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Path: "",
|
|
|
|
Type: 'c',
|
|
|
|
Major: 5,
|
|
|
|
Minor: 2,
|
|
|
|
Permissions: "rwm",
|
2016-02-06 07:15:25 +08:00
|
|
|
Allow: true,
|
2015-06-22 10:31:12 +08:00
|
|
|
},
|
|
|
|
// tuntap
|
|
|
|
{
|
|
|
|
Path: "",
|
|
|
|
Type: 'c',
|
|
|
|
Major: 10,
|
|
|
|
Minor: 200,
|
|
|
|
Permissions: "rwm",
|
2016-02-06 07:15:25 +08:00
|
|
|
Allow: true,
|
2015-06-22 10:31:12 +08:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var container libcontainer.Container
|
|
|
|
|
|
|
|
func containerPreload(context *cli.Context) error {
|
|
|
|
c, err := getContainer(context)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
container = c
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// loadFactory returns the configured factory instance for execing containers.
|
|
|
|
func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
|
|
|
|
root := context.GlobalString("root")
|
|
|
|
abs, err := filepath.Abs(root)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return libcontainer.New(abs, libcontainer.Cgroupfs, func(l *libcontainer.LinuxFactory) error {
|
|
|
|
l.CriuPath = context.GlobalString("criu")
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// getContainer returns the specified container instance by loading it from state
|
|
|
|
// with the default factory.
|
|
|
|
func getContainer(context *cli.Context) (libcontainer.Container, error) {
|
2016-02-09 06:25:03 +08:00
|
|
|
id := context.Args().First()
|
|
|
|
if id == "" {
|
|
|
|
return nil, errEmptyID
|
2015-06-22 10:31:12 +08:00
|
|
|
}
|
2016-02-09 06:25:03 +08:00
|
|
|
factory, err := loadFactory(context)
|
2015-06-22 10:31:12 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-02-09 06:25:03 +08:00
|
|
|
return factory.Load(id)
|
2015-06-22 10:31:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// fatal prints the error's details if it is a libcontainer specific error type
|
2015-07-11 07:19:18 +08:00
|
|
|
// then exits the program with an exit status of 1.
|
2015-06-22 10:31:12 +08:00
|
|
|
func fatal(err error) {
|
|
|
|
if lerr, ok := err.(libcontainer.Error); ok {
|
|
|
|
lerr.Detail(os.Stderr)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
fmt.Fprintln(os.Stderr, err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getDefaultImagePath(context *cli.Context) string {
|
|
|
|
cwd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return filepath.Join(cwd, "checkpoint")
|
|
|
|
}
|
|
|
|
|
|
|
|
// newProcess returns a new libcontainer Process with the arguments from the
|
|
|
|
// spec and stdio from the current process.
|
2015-07-03 00:55:24 +08:00
|
|
|
func newProcess(p specs.Process) *libcontainer.Process {
|
2015-06-22 10:31:12 +08:00
|
|
|
return &libcontainer.Process{
|
2015-07-03 00:59:30 +08:00
|
|
|
Args: p.Args,
|
|
|
|
Env: p.Env,
|
|
|
|
// TODO: fix libcontainer's API to better support uid/gid in a typesafe way.
|
2015-12-11 07:46:26 +08:00
|
|
|
User: fmt.Sprintf("%d:%d", p.User.UID, p.User.GID),
|
|
|
|
Cwd: p.Cwd,
|
2015-06-22 10:31:12 +08:00
|
|
|
}
|
|
|
|
}
|
2016-02-02 07:00:09 +08:00
|
|
|
|
|
|
|
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.
|
2016-02-06 02:46:12 +08:00
|
|
|
func setupSdNotify(spec *specs.LinuxSpec, notifySocket string) {
|
|
|
|
spec.Mounts = append(spec.Mounts, specs.Mount{Destination: notifySocket, Type: "bind", Source: notifySocket, Options: []string{"bind"}})
|
2016-02-02 07:00:09 +08:00
|
|
|
spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", notifySocket))
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
2016-02-05 09:58:46 +08:00
|
|
|
|
|
|
|
func createContainer(context *cli.Context, id string, spec *specs.LinuxSpec) (libcontainer.Container, error) {
|
|
|
|
config, err := createLibcontainerConfig(id, spec)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := os.Stat(config.Rootfs); err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return nil, fmt.Errorf("rootfs (%q) does not exist", config.Rootfs)
|
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
factory, err := loadFactory(context)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return factory.Create(id, config)
|
|
|
|
}
|
|
|
|
|
|
|
|
// runProcess will create a new process in the specified container
|
|
|
|
// by executing the process specified in the 'config'.
|
|
|
|
func runProcess(container libcontainer.Container, config *specs.Process, listenFDs []*os.File, console string, pidFile string, detach bool) (int, error) {
|
|
|
|
process := newProcess(*config)
|
|
|
|
|
|
|
|
// Add extra file descriptors if needed
|
|
|
|
if len(listenFDs) > 0 {
|
|
|
|
process.Env = append(process.Env, fmt.Sprintf("LISTEN_FDS=%d", len(listenFDs)), "LISTEN_PID=1")
|
|
|
|
process.ExtraFiles = append(process.ExtraFiles, listenFDs...)
|
|
|
|
}
|
|
|
|
|
|
|
|
rootuid, err := container.Config().HostUID()
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
|
|
|
|
tty, err := setupIO(process, rootuid, console, config.Terminal, detach)
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
|
2016-02-16 17:59:16 +08:00
|
|
|
handler := newSignalHandler(tty)
|
|
|
|
defer handler.Close()
|
|
|
|
|
2016-02-05 09:58:46 +08:00
|
|
|
if err := container.Start(process); err != nil {
|
2016-02-17 07:17:54 +08:00
|
|
|
if tty != nil {
|
|
|
|
tty.Close()
|
|
|
|
}
|
2016-02-05 09:58:46 +08:00
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if pidFile != "" {
|
|
|
|
if err := createPidFile(pidFile, process); err != nil {
|
|
|
|
process.Signal(syscall.SIGKILL)
|
|
|
|
process.Wait()
|
2016-02-17 07:17:54 +08:00
|
|
|
if tty != nil {
|
|
|
|
tty.Close()
|
|
|
|
}
|
2016-02-05 09:58:46 +08:00
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if detach {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return handler.forward(process)
|
|
|
|
}
|