Add initial system pkg to libcontainer
Port over console and ptmx code into console package Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@docker.com> (github: crosbymichael)
This commit is contained in:
parent
65c37a84af
commit
a2dc64cf30
|
@ -7,22 +7,24 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/docker/libcontainer/label"
|
"github.com/docker/libcontainer/label"
|
||||||
"github.com/dotcloud/docker/pkg/system"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Setup initializes the proper /dev/console inside the rootfs path
|
// Setup initializes the proper /dev/console inside the rootfs path
|
||||||
func Setup(rootfs, consolePath, mountLabel string) error {
|
func Setup(rootfs, consolePath, mountLabel string) error {
|
||||||
oldMask := system.Umask(0000)
|
oldMask := syscall.Umask(0000)
|
||||||
defer system.Umask(oldMask)
|
defer syscall.Umask(oldMask)
|
||||||
|
|
||||||
if err := os.Chmod(consolePath, 0600); err != nil {
|
if err := os.Chmod(consolePath, 0600); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.Chown(consolePath, 0, 0); err != nil {
|
if err := os.Chown(consolePath, 0, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := label.SetFileLabel(consolePath, mountLabel); err != nil {
|
if err := label.SetFileLabel(consolePath, mountLabel); err != nil {
|
||||||
return fmt.Errorf("set file label %s %s", consolePath, err)
|
return fmt.Errorf("set file label %s %s", consolePath, err)
|
||||||
}
|
}
|
||||||
|
@ -33,26 +35,91 @@ func Setup(rootfs, consolePath, mountLabel string) error {
|
||||||
if err != nil && !os.IsExist(err) {
|
if err != nil && !os.IsExist(err) {
|
||||||
return fmt.Errorf("create %s %s", dest, err)
|
return fmt.Errorf("create %s %s", dest, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if f != nil {
|
if f != nil {
|
||||||
f.Close()
|
f.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := system.Mount(consolePath, dest, "bind", syscall.MS_BIND, ""); err != nil {
|
if err := syscall.Mount(consolePath, dest, "bind", syscall.MS_BIND, ""); err != nil {
|
||||||
return fmt.Errorf("bind %s to %s %s", consolePath, dest, err)
|
return fmt.Errorf("bind %s to %s %s", consolePath, dest, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func OpenAndDup(consolePath string) error {
|
func OpenAndDup(consolePath string) error {
|
||||||
slave, err := system.OpenTerminal(consolePath, syscall.O_RDWR)
|
slave, err := OpenTerminal(consolePath, syscall.O_RDWR)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("open terminal %s", err)
|
return fmt.Errorf("open terminal %s", err)
|
||||||
}
|
}
|
||||||
if err := system.Dup2(slave.Fd(), 0); err != nil {
|
|
||||||
|
if err := syscall.Dup2(int(slave.Fd()), 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := system.Dup2(slave.Fd(), 1); err != nil {
|
|
||||||
|
if err := syscall.Dup2(int(slave.Fd()), 1); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return system.Dup2(slave.Fd(), 2)
|
|
||||||
|
return syscall.Dup2(int(slave.Fd()), 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
|
||||||
|
// Unlockpt should be called before opening the slave side of a pseudoterminal.
|
||||||
|
func Unlockpt(f *os.File) error {
|
||||||
|
var u int
|
||||||
|
|
||||||
|
return Ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ptsname retrieves the name of the first available pts for the given master.
|
||||||
|
func Ptsname(f *os.File) (string, error) {
|
||||||
|
var n int
|
||||||
|
|
||||||
|
if err := Ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("/dev/pts/%d", n), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMasterAndConsole will open /dev/ptmx on the host and retreive the
|
||||||
|
// pts name for use as the pty slave inside the container
|
||||||
|
func CreateMasterAndConsole() (*os.File, string, error) {
|
||||||
|
master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
console, err := Ptsname(master)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
if err := Unlockpt(master); err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
return master, console, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenPtmx opens /dev/ptmx, i.e. the PTY master.
|
||||||
|
func OpenPtmx() (*os.File, error) {
|
||||||
|
// O_NOCTTY and O_CLOEXEC are not present in os package so we use the syscall's one for all.
|
||||||
|
return os.OpenFile("/dev/ptmx", syscall.O_RDONLY|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenTerminal is a clone of os.OpenFile without the O_CLOEXEC
|
||||||
|
// used to open the pty slave inside the container namespace
|
||||||
|
func OpenTerminal(name string, flag int) (*os.File, error) {
|
||||||
|
r, e := syscall.Open(name, flag, 0)
|
||||||
|
if e != nil {
|
||||||
|
return nil, &os.PathError{"open", name, e}
|
||||||
|
}
|
||||||
|
return os.NewFile(uintptr(r), name), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ioctl(fd uintptr, flag, data uintptr) error {
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, flag, data); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/docker/libcontainer/cgroups"
|
"github.com/docker/libcontainer/cgroups"
|
||||||
"github.com/docker/libcontainer/cgroups/fs"
|
"github.com/docker/libcontainer/cgroups/fs"
|
||||||
"github.com/docker/libcontainer/cgroups/systemd"
|
"github.com/docker/libcontainer/cgroups/systemd"
|
||||||
|
consolePkg "github.com/docker/libcontainer/console"
|
||||||
"github.com/docker/libcontainer/network"
|
"github.com/docker/libcontainer/network"
|
||||||
"github.com/docker/libcontainer/syncpipe"
|
"github.com/docker/libcontainer/syncpipe"
|
||||||
"github.com/dotcloud/docker/pkg/system"
|
"github.com/dotcloud/docker/pkg/system"
|
||||||
|
@ -36,7 +37,7 @@ func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string
|
||||||
defer syncPipe.Close()
|
defer syncPipe.Close()
|
||||||
|
|
||||||
if container.Tty {
|
if container.Tty {
|
||||||
master, console, err = system.CreateMasterAndConsole()
|
master, console, err = consolePkg.CreateMasterAndConsole()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
@ -110,6 +111,7 @@ func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
|
return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +147,11 @@ func DefaultCreateCommand(container *libcontainer.Config, console, rootfs, dataP
|
||||||
command.Dir = rootfs
|
command.Dir = rootfs
|
||||||
command.Env = append(os.Environ(), env...)
|
command.Env = append(os.Environ(), env...)
|
||||||
|
|
||||||
system.SetCloneFlags(command, uintptr(GetNamespaceFlags(container.Namespaces)))
|
if command.SysProcAttr == nil {
|
||||||
|
command.SysProcAttr = &syscall.SysProcAttr{}
|
||||||
|
}
|
||||||
|
command.SysProcAttr.Cloneflags = uintptr(GetNamespaceFlags(container.Namespaces))
|
||||||
|
|
||||||
command.SysProcAttr.Pdeathsig = syscall.SIGKILL
|
command.SysProcAttr.Pdeathsig = syscall.SIGKILL
|
||||||
command.ExtraFiles = []*os.File{pipe}
|
command.ExtraFiles = []*os.File{pipe}
|
||||||
|
|
||||||
|
@ -157,11 +163,14 @@ func DefaultCreateCommand(container *libcontainer.Config, console, rootfs, dataP
|
||||||
func SetupCgroups(container *libcontainer.Config, nspid int) (cgroups.ActiveCgroup, error) {
|
func SetupCgroups(container *libcontainer.Config, nspid int) (cgroups.ActiveCgroup, error) {
|
||||||
if container.Cgroups != nil {
|
if container.Cgroups != nil {
|
||||||
c := container.Cgroups
|
c := container.Cgroups
|
||||||
|
|
||||||
if systemd.UseSystemd() {
|
if systemd.UseSystemd() {
|
||||||
return systemd.Apply(c, nspid)
|
return systemd.Apply(c, nspid)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fs.Apply(c, nspid)
|
return fs.Apply(c, nspid)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/libcontainer"
|
"github.com/docker/libcontainer"
|
||||||
"github.com/docker/libcontainer/label"
|
"github.com/docker/libcontainer/label"
|
||||||
"github.com/dotcloud/docker/pkg/system"
|
"github.com/docker/libcontainer/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExecIn uses an existing pid and joins the pid's namespaces with the new command.
|
// ExecIn uses an existing pid and joins the pid's namespaces with the new command.
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/dotcloud/docker/pkg/system"
|
"github.com/docker/libcontainer/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
// crosbymichael: could make a network strategy that instead of returning veth pair names it returns a pid to an existing network namespace
|
// crosbymichael: could make a network strategy that instead of returning veth pair names it returns a pid to an existing network namespace
|
||||||
|
@ -23,12 +23,15 @@ func (v *NetNS) Initialize(config *Network, networkState *NetworkState) error {
|
||||||
if networkState.NsPath == "" {
|
if networkState.NsPath == "" {
|
||||||
return fmt.Errorf("nspath does is not specified in NetworkState")
|
return fmt.Errorf("nspath does is not specified in NetworkState")
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := os.OpenFile(networkState.NsPath, os.O_RDONLY, 0)
|
f, err := os.OpenFile(networkState.NsPath, os.O_RDONLY, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed get network namespace fd: %v", err)
|
return fmt.Errorf("failed get network namespace fd: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := system.Setns(f.Fd(), syscall.CLONE_NEWNET); err != nil {
|
if err := system.Setns(f.Fd(), syscall.CLONE_NEWNET); err != nil {
|
||||||
return fmt.Errorf("failed to setns current network namespace: %v", err)
|
return fmt.Errorf("failed to setns current network namespace: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Execv(cmd string, args []string, env []string) error {
|
||||||
|
name, err := exec.LookPath(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return syscall.Exec(name, args, env)
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// look in /proc to find the process start time so that we can verify
|
||||||
|
// that this pid has started after ourself
|
||||||
|
func GetProcessStartTime(pid int) (string, error) {
|
||||||
|
data, err := ioutil.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "stat"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(string(data), " ")
|
||||||
|
// the starttime is located at pos 22
|
||||||
|
// from the man page
|
||||||
|
//
|
||||||
|
// starttime %llu (was %lu before Linux 2.6)
|
||||||
|
// (22) The time the process started after system boot. In kernels before Linux 2.6, this
|
||||||
|
// value was expressed in jiffies. Since Linux 2.6, the value is expressed in clock ticks
|
||||||
|
// (divide by sysconf(_SC_CLK_TCK)).
|
||||||
|
return parts[22-1], nil // starts at 1
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Via http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=7b21fddd087678a70ad64afc0f632e0f1071b092
|
||||||
|
//
|
||||||
|
// We need different setns values for the different platforms and arch
|
||||||
|
// We are declaring the macro here because the SETNS syscall does not exist in th stdlib
|
||||||
|
var setNsMap = map[string]uintptr{
|
||||||
|
"linux/amd64": 308,
|
||||||
|
}
|
||||||
|
|
||||||
|
func Setns(fd uintptr, flags uintptr) error {
|
||||||
|
ns, exists := setNsMap[fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)]
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := syscall.RawSyscall(ns, fd, flags, 0)
|
||||||
|
if err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue