From 82050a5b8f1021da88b7ed972146bcfe9b18fe7e Mon Sep 17 00:00:00 2001 From: Amit Krishnan Date: Tue, 12 Apr 2016 14:41:09 -0700 Subject: [PATCH] Get runc to build clean on Solaris Signed-off-by: Amit Krishnan --- delete.go | 2 + libcontainer/console_solaris.go | 11 ++ libcontainer/container_solaris.go | 20 +++ libcontainer/stats_solaris.go | 7 + main_solaris.go | 21 +++ main_unsupported.go | 2 +- utils.go | 270 ----------------------------- utils_linux.go | 278 ++++++++++++++++++++++++++++++ 8 files changed, 340 insertions(+), 271 deletions(-) create mode 100644 libcontainer/console_solaris.go create mode 100644 libcontainer/container_solaris.go create mode 100644 libcontainer/stats_solaris.go create mode 100644 main_solaris.go create mode 100644 utils_linux.go diff --git a/delete.go b/delete.go index 3468d106..e1165a32 100644 --- a/delete.go +++ b/delete.go @@ -1,3 +1,5 @@ +// +build !solaris + package main import ( diff --git a/libcontainer/console_solaris.go b/libcontainer/console_solaris.go new file mode 100644 index 00000000..9e89f505 --- /dev/null +++ b/libcontainer/console_solaris.go @@ -0,0 +1,11 @@ +package libcontainer + +import ( + "errors" +) + +// NewConsole returns an initalized console that can be used within a container by copying bytes +// from the master side to the slave that is attached as the tty for the container's init process. +func NewConsole(uid, gid int) (Console, error) { + return nil, errors.New("libcontainer console is not supported on Solaris") +} diff --git a/libcontainer/container_solaris.go b/libcontainer/container_solaris.go new file mode 100644 index 00000000..bb84ff74 --- /dev/null +++ b/libcontainer/container_solaris.go @@ -0,0 +1,20 @@ +package libcontainer + +// State represents a running container's state +type State struct { + BaseState + + // Platform specific fields below here +} + +// A libcontainer container object. +// +// Each container is thread-safe within the same process. Since a container can +// be destroyed by a separate process, any function may return that the container +// was not found. +type Container interface { + BaseContainer + + // Methods below here are platform specific + +} diff --git a/libcontainer/stats_solaris.go b/libcontainer/stats_solaris.go new file mode 100644 index 00000000..da78c1c2 --- /dev/null +++ b/libcontainer/stats_solaris.go @@ -0,0 +1,7 @@ +package libcontainer + +// Solaris - TODO + +type Stats struct { + Interfaces []*NetworkInterface +} diff --git a/main_solaris.go b/main_solaris.go new file mode 100644 index 00000000..8b0f6325 --- /dev/null +++ b/main_solaris.go @@ -0,0 +1,21 @@ +// +build solaris + +package main + +import "github.com/codegangsta/cli" + +var ( + checkpointCommand cli.Command + eventsCommand cli.Command + restoreCommand cli.Command + specCommand cli.Command + killCommand cli.Command + deleteCommand cli.Command + execCommand cli.Command + initCommand cli.Command + listCommand cli.Command + pauseCommand cli.Command + resumeCommand cli.Command + startCommand cli.Command + stateCommand cli.Command +) diff --git a/main_unsupported.go b/main_unsupported.go index 32ce1ac9..17e11010 100644 --- a/main_unsupported.go +++ b/main_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux +// +build !linux,!solaris package main diff --git a/utils.go b/utils.go index d4986a0c..d1888a2e 100644 --- a/utils.go +++ b/utils.go @@ -1,61 +1,12 @@ -// +build linux - package main import ( - "errors" "fmt" "os" - "path/filepath" - "syscall" "github.com/Sirupsen/logrus" - "github.com/codegangsta/cli" - "github.com/opencontainers/runc/libcontainer" - "github.com/opencontainers/runc/libcontainer/cgroups/systemd" - "github.com/opencontainers/runc/libcontainer/specconv" - "github.com/opencontainers/runtime-spec/specs-go" ) -var errEmptyID = errors.New("container id cannot be empty") - -var container libcontainer.Container - -// 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 - } - cgroupManager := libcontainer.Cgroupfs - if context.GlobalBool("systemd-cgroup") { - if systemd.UseSystemd() { - cgroupManager = libcontainer.SystemdCgroups - } else { - return nil, fmt.Errorf("systemd cgroup flag passed, but systemd support for managing cgroups is not available") - } - } - return libcontainer.New(abs, cgroupManager, 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) { - id := context.Args().First() - if id == "" { - return nil, errEmptyID - } - factory, err := loadFactory(context) - if err != nil { - return nil, err - } - return factory.Load(id) -} - // fatal prints the error's details if it is a libcontainer specific error type // then exits the program with an exit status of 1. func fatal(err error) { @@ -64,224 +15,3 @@ func fatal(err error) { fmt.Fprintln(os.Stderr, err) os.Exit(1) } - -func fatalf(t string, v ...interface{}) { - fatal(fmt.Errorf(t, v...)) -} - -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. -func newProcess(p specs.Process) (*libcontainer.Process, error) { - lp := &libcontainer.Process{ - Args: p.Args, - Env: p.Env, - // TODO: fix libcontainer's API to better support uid/gid in a typesafe way. - User: fmt.Sprintf("%d:%d", p.User.UID, p.User.GID), - Cwd: p.Cwd, - Capabilities: p.Capabilities, - Label: p.SelinuxLabel, - NoNewPrivileges: &p.NoNewPrivileges, - AppArmorProfile: p.ApparmorProfile, - } - for _, rlimit := range p.Rlimits { - rl, err := createLibContainerRlimit(rlimit) - if err != nil { - return nil, err - } - lp.Rlimits = append(lp.Rlimits, rl) - } - return lp, nil -} - -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.Spec, notifySocket string) { - spec.Mounts = append(spec.Mounts, specs.Mount{Destination: notifySocket, Type: "bind", Source: notifySocket, Options: []string{"bind"}}) - 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) - } -} - -// setupIO sets the proper IO on the process depending on the configuration -// If there is a nil error then there must be a non nil tty returned -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 &tty{}, nil - } - return createStdioPipes(process, rootuid) -} - -// createPidFile creates a file with the processes pid inside it atomically -// it creates a temp file with the paths filename + '.' infront of it -// then renames the file -func createPidFile(path string, process *libcontainer.Process) error { - pid, err := process.Pid() - if err != nil { - return err - } - var ( - tmpDir = filepath.Dir(path) - tmpName = filepath.Join(tmpDir, fmt.Sprintf(".%s", filepath.Base(path))) - ) - f, err := os.OpenFile(tmpName, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666) - if err != nil { - return err - } - _, err = fmt.Fprintf(f, "%d", pid) - f.Close() - if err != nil { - return err - } - return os.Rename(tmpName, path) -} - -func createContainer(context *cli.Context, id string, spec *specs.Spec) (libcontainer.Container, error) { - config, err := specconv.CreateLibcontainerConfig(&specconv.CreateOpts{ - CgroupName: id, - UseSystemdCgroup: context.GlobalBool("systemd-cgroup"), - NoPivotRoot: context.Bool("no-pivot"), - Spec: 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) -} - -type runner struct { - enableSubreaper bool - shouldDestroy bool - detach bool - listenFDs []*os.File - pidFile string - console string - container libcontainer.Container -} - -func (r *runner) run(config *specs.Process) (int, error) { - process, err := newProcess(*config) - if err != nil { - r.destroy() - return -1, err - } - if len(r.listenFDs) > 0 { - process.Env = append(process.Env, fmt.Sprintf("LISTEN_FDS=%d", len(r.listenFDs)), "LISTEN_PID=1") - process.ExtraFiles = append(process.ExtraFiles, r.listenFDs...) - } - rootuid, err := r.container.Config().HostUID() - if err != nil { - r.destroy() - return -1, err - } - tty, err := setupIO(process, rootuid, r.console, config.Terminal, r.detach) - if err != nil { - r.destroy() - return -1, err - } - handler := newSignalHandler(tty, r.enableSubreaper) - if err := r.container.Start(process); err != nil { - r.destroy() - tty.Close() - return -1, err - } - if err := tty.ClosePostStart(); err != nil { - r.terminate(process) - r.destroy() - tty.Close() - return -1, err - } - if r.pidFile != "" { - if err := createPidFile(r.pidFile, process); err != nil { - r.terminate(process) - r.destroy() - tty.Close() - return -1, err - } - } - if r.detach { - tty.Close() - return 0, nil - } - status, err := handler.forward(process) - if err != nil { - r.terminate(process) - } - r.destroy() - tty.Close() - return status, err -} - -func (r *runner) destroy() { - if r.shouldDestroy { - destroy(r.container) - } -} - -func (r *runner) terminate(p *libcontainer.Process) { - p.Signal(syscall.SIGKILL) - p.Wait() -} - -func validateProcessSpec(spec *specs.Process) error { - if spec.Cwd == "" { - return fmt.Errorf("Cwd property must not be empty") - } - if !filepath.IsAbs(spec.Cwd) { - return fmt.Errorf("Cwd must be an absolute path") - } - if len(spec.Args) == 0 { - return fmt.Errorf("args must not be empty") - } - return nil -} diff --git a/utils_linux.go b/utils_linux.go new file mode 100644 index 00000000..bafb6417 --- /dev/null +++ b/utils_linux.go @@ -0,0 +1,278 @@ +// +build linux + +package main + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "syscall" + + "github.com/Sirupsen/logrus" + "github.com/codegangsta/cli" + "github.com/opencontainers/runc/libcontainer" + "github.com/opencontainers/runc/libcontainer/cgroups/systemd" + "github.com/opencontainers/runc/libcontainer/specconv" + "github.com/opencontainers/runtime-spec/specs-go" +) + +var errEmptyID = errors.New("container id cannot be empty") + +var container libcontainer.Container + +// 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 + } + cgroupManager := libcontainer.Cgroupfs + if context.GlobalBool("systemd-cgroup") { + if systemd.UseSystemd() { + cgroupManager = libcontainer.SystemdCgroups + } else { + return nil, fmt.Errorf("systemd cgroup flag passed, but systemd support for managing cgroups is not available") + } + } + return libcontainer.New(abs, cgroupManager, 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) { + id := context.Args().First() + if id == "" { + return nil, errEmptyID + } + factory, err := loadFactory(context) + if err != nil { + return nil, err + } + return factory.Load(id) +} + +func fatalf(t string, v ...interface{}) { + fatal(fmt.Errorf(t, v...)) +} + +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. +func newProcess(p specs.Process) (*libcontainer.Process, error) { + lp := &libcontainer.Process{ + Args: p.Args, + Env: p.Env, + // TODO: fix libcontainer's API to better support uid/gid in a typesafe way. + User: fmt.Sprintf("%d:%d", p.User.UID, p.User.GID), + Cwd: p.Cwd, + Capabilities: p.Capabilities, + Label: p.SelinuxLabel, + NoNewPrivileges: &p.NoNewPrivileges, + AppArmorProfile: p.ApparmorProfile, + } + for _, rlimit := range p.Rlimits { + rl, err := createLibContainerRlimit(rlimit) + if err != nil { + return nil, err + } + lp.Rlimits = append(lp.Rlimits, rl) + } + return lp, nil +} + +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.Spec, notifySocket string) { + spec.Mounts = append(spec.Mounts, specs.Mount{Destination: notifySocket, Type: "bind", Source: notifySocket, Options: []string{"bind"}}) + 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) + } +} + +// setupIO sets the proper IO on the process depending on the configuration +// If there is a nil error then there must be a non nil tty returned +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 &tty{}, nil + } + return createStdioPipes(process, rootuid) +} + +// createPidFile creates a file with the processes pid inside it atomically +// it creates a temp file with the paths filename + '.' infront of it +// then renames the file +func createPidFile(path string, process *libcontainer.Process) error { + pid, err := process.Pid() + if err != nil { + return err + } + var ( + tmpDir = filepath.Dir(path) + tmpName = filepath.Join(tmpDir, fmt.Sprintf(".%s", filepath.Base(path))) + ) + f, err := os.OpenFile(tmpName, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666) + if err != nil { + return err + } + _, err = fmt.Fprintf(f, "%d", pid) + f.Close() + if err != nil { + return err + } + return os.Rename(tmpName, path) +} + +func createContainer(context *cli.Context, id string, spec *specs.Spec) (libcontainer.Container, error) { + config, err := specconv.CreateLibcontainerConfig(&specconv.CreateOpts{ + CgroupName: id, + UseSystemdCgroup: context.GlobalBool("systemd-cgroup"), + NoPivotRoot: context.Bool("no-pivot"), + Spec: 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) +} + +type runner struct { + enableSubreaper bool + shouldDestroy bool + detach bool + listenFDs []*os.File + pidFile string + console string + container libcontainer.Container +} + +func (r *runner) run(config *specs.Process) (int, error) { + process, err := newProcess(*config) + if err != nil { + r.destroy() + return -1, err + } + if len(r.listenFDs) > 0 { + process.Env = append(process.Env, fmt.Sprintf("LISTEN_FDS=%d", len(r.listenFDs)), "LISTEN_PID=1") + process.ExtraFiles = append(process.ExtraFiles, r.listenFDs...) + } + rootuid, err := r.container.Config().HostUID() + if err != nil { + r.destroy() + return -1, err + } + tty, err := setupIO(process, rootuid, r.console, config.Terminal, r.detach) + if err != nil { + r.destroy() + return -1, err + } + handler := newSignalHandler(tty, r.enableSubreaper) + if err := r.container.Start(process); err != nil { + r.destroy() + tty.Close() + return -1, err + } + if err := tty.ClosePostStart(); err != nil { + r.terminate(process) + r.destroy() + tty.Close() + return -1, err + } + if r.pidFile != "" { + if err := createPidFile(r.pidFile, process); err != nil { + r.terminate(process) + r.destroy() + tty.Close() + return -1, err + } + } + if r.detach { + tty.Close() + return 0, nil + } + status, err := handler.forward(process) + if err != nil { + r.terminate(process) + } + r.destroy() + tty.Close() + return status, err +} + +func (r *runner) destroy() { + if r.shouldDestroy { + destroy(r.container) + } +} + +func (r *runner) terminate(p *libcontainer.Process) { + p.Signal(syscall.SIGKILL) + p.Wait() +} + +func validateProcessSpec(spec *specs.Process) error { + if spec.Cwd == "" { + return fmt.Errorf("Cwd property must not be empty") + } + if !filepath.IsAbs(spec.Cwd) { + return fmt.Errorf("Cwd must be an absolute path") + } + if len(spec.Args) == 0 { + return fmt.Errorf("args must not be empty") + } + return nil +}