Refactor and improve libcontainer and driver
Remove logging for now because it is complicating things Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
This commit is contained in:
parent
0e4d946dc4
commit
6daf56799f
11 changed files with 84 additions and 116 deletions
|
@ -6,6 +6,9 @@ import (
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer/utils"
|
"github.com/dotcloud/docker/pkg/libcontainer/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Veth is a network strategy that uses a bridge and creates
|
||||||
|
// a veth pair, one that stays outside on the host and the other
|
||||||
|
// is placed inside the container's namespace
|
||||||
type Veth struct {
|
type Veth struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,11 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CommandFactory takes the container's configuration and options passed by the
|
||||||
|
// parent processes and creates an *exec.Cmd that will be used to fork/exec the
|
||||||
|
// namespaced init process
|
||||||
type CommandFactory interface {
|
type CommandFactory interface {
|
||||||
Create(container *libcontainer.Container, console, logFile string, syncFd uintptr, args []string) *exec.Cmd
|
Create(container *libcontainer.Container, console string, syncFd uintptr, args []string) *exec.Cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultCommandFactory struct{}
|
type DefaultCommandFactory struct{}
|
||||||
|
@ -17,13 +20,12 @@ type DefaultCommandFactory struct{}
|
||||||
// Create will return an exec.Cmd with the Cloneflags set to the proper namespaces
|
// Create will return an exec.Cmd with the Cloneflags set to the proper namespaces
|
||||||
// defined on the container's configuration and use the current binary as the init with the
|
// defined on the container's configuration and use the current binary as the init with the
|
||||||
// args provided
|
// args provided
|
||||||
func (c *DefaultCommandFactory) Create(container *libcontainer.Container, console, logFile string, pipe uintptr, args []string) *exec.Cmd {
|
func (c *DefaultCommandFactory) Create(container *libcontainer.Container, console string, pipe uintptr, args []string) *exec.Cmd {
|
||||||
// get our binary name so we can always reexec ourself
|
// get our binary name so we can always reexec ourself
|
||||||
name := os.Args[0]
|
name := os.Args[0]
|
||||||
command := exec.Command(name, append([]string{
|
command := exec.Command(name, append([]string{
|
||||||
"-console", console,
|
"-console", console,
|
||||||
"-pipe", fmt.Sprint(pipe),
|
"-pipe", fmt.Sprint(pipe),
|
||||||
"-log", logFile,
|
|
||||||
"init"}, args...)...)
|
"init"}, args...)...)
|
||||||
|
|
||||||
command.SysProcAttr = &syscall.SysProcAttr{
|
command.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
|
|
@ -28,31 +28,27 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [
|
||||||
}
|
}
|
||||||
|
|
||||||
if container.Tty {
|
if container.Tty {
|
||||||
ns.logger.Printf("setting up master and console")
|
master, console, err = system.CreateMasterAndConsole()
|
||||||
master, console, err = CreateMasterAndConsole()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
term.SetMaster(master)
|
term.SetMaster(master)
|
||||||
}
|
}
|
||||||
|
|
||||||
command := ns.commandFactory.Create(container, console, ns.logFile, syncPipe.child.Fd(), args)
|
command := ns.commandFactory.Create(container, console, syncPipe.child.Fd(), args)
|
||||||
if err := term.Attach(command); err != nil {
|
if err := term.Attach(command); err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
defer term.Close()
|
defer term.Close()
|
||||||
|
|
||||||
ns.logger.Printf("staring init")
|
|
||||||
if err := command.Start(); err != nil {
|
if err := command.Start(); err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
ns.logger.Printf("writing state file")
|
|
||||||
if err := ns.stateWriter.WritePid(command.Process.Pid); err != nil {
|
if err := ns.stateWriter.WritePid(command.Process.Pid); err != nil {
|
||||||
command.Process.Kill()
|
command.Process.Kill()
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
ns.logger.Printf("removing state file")
|
|
||||||
ns.stateWriter.DeletePid()
|
ns.stateWriter.DeletePid()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -68,24 +64,18 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync with child
|
// Sync with child
|
||||||
ns.logger.Printf("closing sync pipes")
|
|
||||||
syncPipe.Close()
|
syncPipe.Close()
|
||||||
|
|
||||||
ns.logger.Printf("waiting on process")
|
|
||||||
if err := command.Wait(); err != nil {
|
if err := command.Wait(); err != nil {
|
||||||
if _, ok := err.(*exec.ExitError); !ok {
|
if _, ok := err.(*exec.ExitError); !ok {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
|
||||||
exitCode := command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
|
||||||
ns.logger.Printf("process ended with exit code %d", exitCode)
|
|
||||||
return exitCode, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) error {
|
func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) error {
|
||||||
if container.Cgroups != nil {
|
if container.Cgroups != nil {
|
||||||
ns.logger.Printf("setting up cgroups")
|
|
||||||
if err := container.Cgroups.Apply(nspid); err != nil {
|
if err := container.Cgroups.Apply(nspid); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -95,7 +85,6 @@ func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) er
|
||||||
|
|
||||||
func (ns *linuxNs) InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error {
|
func (ns *linuxNs) InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error {
|
||||||
if container.Network != nil {
|
if container.Network != nil {
|
||||||
ns.logger.Printf("creating host network configuration type %s", container.Network.Type)
|
|
||||||
strategy, err := network.GetStrategy(container.Network.Type)
|
strategy, err := network.GetStrategy(container.Network.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -104,27 +93,9 @@ func (ns *linuxNs) InitializeNetworking(container *libcontainer.Container, nspid
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ns.logger.Printf("sending %v as network context", networkContext)
|
|
||||||
if err := pipe.SendToChild(networkContext); err != nil {
|
if err := pipe.SendToChild(networkContext); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return 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 := system.Ptsname(master)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
if err := system.Unlockpt(master); err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
return master, console, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ func (ns *linuxNs) ExecIn(container *libcontainer.Container, nspid int, args []s
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fds, err := getNsFds(nspid, container)
|
fds, err := ns.getNsFds(nspid, container)
|
||||||
closeFds := func() {
|
closeFds := func() {
|
||||||
for _, f := range fds {
|
for _, f := range fds {
|
||||||
system.Closefd(f)
|
system.Closefd(f)
|
||||||
|
@ -75,13 +75,13 @@ dropAndExec:
|
||||||
if err := capabilities.DropCapabilities(container); err != nil {
|
if err := capabilities.DropCapabilities(container); err != nil {
|
||||||
return -1, fmt.Errorf("drop capabilities %s", err)
|
return -1, fmt.Errorf("drop capabilities %s", err)
|
||||||
}
|
}
|
||||||
if err := system.Exec(args[0], args[0:], container.Env); err != nil {
|
if err := system.Execv(args[0], args[0:], container.Env); err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNsFds(pid int, container *libcontainer.Container) ([]uintptr, error) {
|
func (ns *linuxNs) getNsFds(pid int, container *libcontainer.Container) ([]uintptr, error) {
|
||||||
fds := make([]uintptr, len(container.Namespaces))
|
fds := make([]uintptr, len(container.Namespaces))
|
||||||
for i, ns := range container.Namespaces {
|
for i, ns := range container.Namespaces {
|
||||||
f, err := os.OpenFile(filepath.Join("/proc/", strconv.Itoa(pid), "ns", namespaceFileMap[ns]), os.O_RDONLY, 0)
|
f, err := os.OpenFile(filepath.Join("/proc/", strconv.Itoa(pid), "ns", namespaceFileMap[ns]), os.O_RDONLY, 0)
|
||||||
|
|
|
@ -7,18 +7,17 @@ import (
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer"
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer/capabilities"
|
"github.com/dotcloud/docker/pkg/libcontainer/capabilities"
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer/network"
|
"github.com/dotcloud/docker/pkg/libcontainer/network"
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer/utils"
|
||||||
"github.com/dotcloud/docker/pkg/system"
|
"github.com/dotcloud/docker/pkg/system"
|
||||||
"github.com/dotcloud/docker/pkg/user"
|
"github.com/dotcloud/docker/pkg/user"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
|
// Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
|
||||||
// and other options required for the new container.
|
// and other options required for the new container.
|
||||||
func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, console string, syncPipe *SyncPipe, args []string) error {
|
func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, console string, syncPipe *SyncPipe, args []string) error {
|
||||||
rootfs, err := resolveRootfs(uncleanRootfs)
|
rootfs, err := utils.ResolveRootfs(uncleanRootfs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -34,7 +33,7 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol
|
||||||
if console != "" {
|
if console != "" {
|
||||||
// close pipes so that we can replace it with the pty
|
// close pipes so that we can replace it with the pty
|
||||||
closeStdPipes()
|
closeStdPipes()
|
||||||
slave, err := openTerminal(console, syscall.O_RDWR)
|
slave, err := system.OpenTerminal(console, syscall.O_RDWR)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("open terminal %s", err)
|
return fmt.Errorf("open terminal %s", err)
|
||||||
}
|
}
|
||||||
|
@ -50,6 +49,7 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol
|
||||||
return fmt.Errorf("setctty %s", err)
|
return fmt.Errorf("setctty %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := system.ParentDeathSignal(); err != nil {
|
if err := system.ParentDeathSignal(); err != nil {
|
||||||
return fmt.Errorf("parent deth signal %s", err)
|
return fmt.Errorf("parent deth signal %s", err)
|
||||||
}
|
}
|
||||||
|
@ -73,18 +73,7 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol
|
||||||
return fmt.Errorf("chdir to %s %s", container.WorkingDir, err)
|
return fmt.Errorf("chdir to %s %s", container.WorkingDir, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return execArgs(args, container.Env)
|
return system.Execv(args[0], args[0:], container.Env)
|
||||||
}
|
|
||||||
|
|
||||||
func execArgs(args []string, env []string) error {
|
|
||||||
name, err := exec.LookPath(args[0])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := system.Exec(name, args[0:], env); err != nil {
|
|
||||||
return fmt.Errorf("exec %s", err)
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func closeStdPipes() {
|
func closeStdPipes() {
|
||||||
|
@ -93,18 +82,19 @@ func closeStdPipes() {
|
||||||
os.Stderr.Close()
|
os.Stderr.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveRootfs ensures that the current working directory is
|
|
||||||
// not a symlink and returns the absolute path to the rootfs
|
|
||||||
func resolveRootfs(uncleanRootfs string) (string, error) {
|
|
||||||
rootfs, err := filepath.Abs(uncleanRootfs)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return filepath.EvalSymlinks(rootfs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupUser(container *libcontainer.Container) error {
|
func setupUser(container *libcontainer.Container) error {
|
||||||
if container.User != "" && container.User != "root" {
|
switch container.User {
|
||||||
|
case "root", "":
|
||||||
|
if err := system.Setgroups(nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := system.Setresgid(0, 0, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := system.Setresuid(0, 0, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
uid, gid, suppGids, err := user.GetUserGroupSupplementary(container.User, syscall.Getuid(), syscall.Getgid())
|
uid, gid, suppGids, err := user.GetUserGroupSupplementary(container.User, syscall.Getuid(), syscall.Getgid())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -118,16 +108,6 @@ func setupUser(container *libcontainer.Container) error {
|
||||||
if err := system.Setuid(uid); err != nil {
|
if err := system.Setuid(uid); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if err := system.Setgroups(nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := system.Setresgid(0, 0, 0); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := system.Setresuid(0, 0, 0); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -147,16 +127,6 @@ func dupSlave(slave *os.File) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// setupVethNetwork uses the Network config if it is not nil to initialize
|
// setupVethNetwork uses the Network config if it is not nil to initialize
|
||||||
// the new veth interface inside the container for use by changing the name to eth0
|
// the new veth interface inside the container for use by changing the name to eth0
|
||||||
// setting the MTU and IP address along with the default gateway
|
// setting the MTU and IP address along with the default gateway
|
||||||
|
|
|
@ -2,9 +2,10 @@ package nsinit
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer"
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||||
"log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NsInit is an interface with the public facing methods to provide high level
|
||||||
|
// exec operations on a container
|
||||||
type NsInit interface {
|
type NsInit interface {
|
||||||
Exec(container *libcontainer.Container, term Terminal, args []string) (int, error)
|
Exec(container *libcontainer.Container, term Terminal, args []string) (int, error)
|
||||||
ExecIn(container *libcontainer.Container, nspid int, args []string) (int, error)
|
ExecIn(container *libcontainer.Container, nspid int, args []string) (int, error)
|
||||||
|
@ -13,17 +14,13 @@ type NsInit interface {
|
||||||
|
|
||||||
type linuxNs struct {
|
type linuxNs struct {
|
||||||
root string
|
root string
|
||||||
logFile string
|
|
||||||
logger *log.Logger
|
|
||||||
commandFactory CommandFactory
|
commandFactory CommandFactory
|
||||||
stateWriter StateWriter
|
stateWriter StateWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNsInit(logger *log.Logger, logFile string, command CommandFactory, state StateWriter) NsInit {
|
func NewNsInit(command CommandFactory, state StateWriter) NsInit {
|
||||||
return &linuxNs{
|
return &linuxNs{
|
||||||
logger: logger,
|
|
||||||
commandFactory: command,
|
commandFactory: command,
|
||||||
stateWriter: state,
|
stateWriter: state,
|
||||||
logFile: logFile,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer"
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
|
"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@ -16,7 +15,6 @@ import (
|
||||||
var (
|
var (
|
||||||
console string
|
console string
|
||||||
pipeFd int
|
pipeFd int
|
||||||
logFile string
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -26,7 +24,6 @@ var (
|
||||||
|
|
||||||
func registerFlags() {
|
func registerFlags() {
|
||||||
flag.StringVar(&console, "console", "", "console (pty slave) path")
|
flag.StringVar(&console, "console", "", "console (pty slave) path")
|
||||||
flag.StringVar(&logFile, "log", "none", "log options (none, stderr, or a file path)")
|
|
||||||
flag.IntVar(&pipeFd, "pipe", 0, "sync pipe fd")
|
flag.IntVar(&pipeFd, "pipe", 0, "sync pipe fd")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
@ -113,26 +110,5 @@ func readPid() (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNsInit() (nsinit.NsInit, error) {
|
func newNsInit() (nsinit.NsInit, error) {
|
||||||
logger, err := setupLogging()
|
return nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{}), nil
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return nsinit.NewNsInit(logger, logFile, &nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupLogging() (logger *log.Logger, err error) {
|
|
||||||
var writer io.Writer
|
|
||||||
|
|
||||||
switch logFile {
|
|
||||||
case "stderr":
|
|
||||||
writer = os.Stderr
|
|
||||||
case "none", "":
|
|
||||||
writer = ioutil.Discard
|
|
||||||
default:
|
|
||||||
if writer, err = os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger = log.New(writer, "", log.LstdFlags)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// StateWriter handles writing and deleting the pid file
|
||||||
|
// on disk
|
||||||
type StateWriter interface {
|
type StateWriter interface {
|
||||||
WritePid(pid int) error
|
WritePid(pid int) error
|
||||||
DeletePid() error
|
DeletePid() error
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"io"
|
"io"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GenerateRandomName returns a new name joined with a prefix. This size
|
// GenerateRandomName returns a new name joined with a prefix. This size
|
||||||
|
@ -15,3 +16,13 @@ func GenerateRandomName(prefix string, size int) (string, error) {
|
||||||
}
|
}
|
||||||
return prefix + hex.EncodeToString(id)[:size], nil
|
return prefix + hex.EncodeToString(id)[:size], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResolveRootfs ensures that the current working directory is
|
||||||
|
// not a symlink and returns the absolute path to the rootfs
|
||||||
|
func ResolveRootfs(uncleanRootfs string) (string, error) {
|
||||||
|
rootfs, err := filepath.Abs(uncleanRootfs)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.EvalSymlinks(rootfs)
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os/exec"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,6 +17,14 @@ func Exec(cmd string, args []string, env []string) error {
|
||||||
return syscall.Exec(cmd, args, env)
|
return syscall.Exec(cmd, args, env)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Execv(cmd string, args []string, env []string) error {
|
||||||
|
name, err := exec.LookPath(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return Exec(name, args, env)
|
||||||
|
}
|
||||||
|
|
||||||
func Fork() (int, error) {
|
func Fork() (int, error) {
|
||||||
syscall.ForkLock.Lock()
|
syscall.ForkLock.Lock()
|
||||||
pid, _, err := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0)
|
pid, _, err := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0)
|
||||||
|
|
|
@ -24,8 +24,35 @@ func Ptsname(f *os.File) (string, error) {
|
||||||
return fmt.Sprintf("/dev/pts/%d", n), nil
|
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.
|
// OpenPtmx opens /dev/ptmx, i.e. the PTY master.
|
||||||
func OpenPtmx() (*os.File, error) {
|
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.
|
// 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)
|
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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue