Remove nsinit
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
b675770455
commit
4b5bf88a08
|
@ -1,2 +0,0 @@
|
|||
all:
|
||||
go build -o nsinit .
|
128
nsinit/README.md
128
nsinit/README.md
|
@ -1,128 +0,0 @@
|
|||
## nsinit
|
||||
|
||||
`nsinit` is a cli application which demonstrates the use of libcontainer.
|
||||
It is able to spawn new containers or join existing containers.
|
||||
|
||||
### How to build?
|
||||
|
||||
First add the `libcontainer/vendor` into your GOPATH. It's because libcontainer
|
||||
vendors all its dependencies, so it can be built predictably.
|
||||
|
||||
```
|
||||
export GOPATH=$GOPATH:/your/path/to/libcontainer/vendor
|
||||
```
|
||||
|
||||
Then get into the nsinit folder and get the imported file. Use `make` command
|
||||
to make the nsinit binary.
|
||||
|
||||
```
|
||||
cd libcontainer/nsinit
|
||||
go get
|
||||
make
|
||||
```
|
||||
|
||||
We have finished compiling the nsinit package, but a root filesystem must be
|
||||
provided for use along with a container configuration file.
|
||||
|
||||
Choose a proper place to run your container. For example we use `/busybox`.
|
||||
|
||||
```
|
||||
mkdir /busybox
|
||||
curl -sSL 'https://github.com/jpetazzo/docker-busybox/raw/buildroot-2014.11/rootfs.tar' | tar -xC /busybox
|
||||
```
|
||||
|
||||
Then you may need to write a configuration file named `container.json` in the
|
||||
`/busybox` folder. Environment, networking, and different capabilities for
|
||||
the container are specified in this file. The configuration is used for each
|
||||
process executed inside the container.
|
||||
|
||||
See the `sample_configs` folder for examples of what the container configuration
|
||||
should look like.
|
||||
|
||||
```
|
||||
cp libcontainer/sample_configs/minimal.json /busybox/container.json
|
||||
cd /busybox
|
||||
```
|
||||
|
||||
You can customize `container.json` per your needs. After that, nsinit is
|
||||
ready to work.
|
||||
|
||||
To execute `/bin/bash` in the current directory as a container just run the
|
||||
following **as root**:
|
||||
|
||||
```bash
|
||||
nsinit exec --tty --config container.json /bin/bash
|
||||
```
|
||||
|
||||
If you wish to spawn another process inside the container while your current
|
||||
bash session is running, run the same command again to get another bash shell
|
||||
(or change the command). If the original process (PID 1) dies, all other
|
||||
processes spawned inside the container will be killed and the namespace will
|
||||
be removed.
|
||||
|
||||
You can identify if a process is running in a container by looking to see if
|
||||
`state.json` is in the root of the directory.
|
||||
|
||||
You may also specify an alternate root directory from where the `container.json`
|
||||
file is read and where the `state.json` file will be saved.
|
||||
|
||||
### How to use?
|
||||
|
||||
Currently nsinit has 9 commands. Type `nsinit -h` to list all of them.
|
||||
And for every alternative command, you can also use `--help` to get more
|
||||
detailed help documents. For example, `nsinit config --help`.
|
||||
|
||||
`nsinit` cli application is implemented using [cli.go](https://github.com/codegangsta/cli).
|
||||
Lots of details are handled in cli.go, so the implementation of `nsinit` itself
|
||||
is very clean and clear.
|
||||
|
||||
* **config**
|
||||
It will generate a standard configuration file for a container. By default, it
|
||||
will generate as the template file in [config.go](https://github.com/docker/libcontainer/blob/f28dff5539855bac2adbc5699f57f84349605b5f/nsinit/config.go#L234).
|
||||
It will modify the template if you have specified some configuration by options.
|
||||
* **exec**
|
||||
Starts a container and execute a new command inside it. Besides common options, it
|
||||
has some special options as below.
|
||||
- `--tty,-t`: allocate a TTY to the container.
|
||||
- `--config`: you can specify a configuration file. By default, it will use
|
||||
template configuration.
|
||||
- `--id`: specify the ID for a container. By default, the id is "nsinit".
|
||||
- `--user,-u`: set the user, uid, and/or gid for the process. By default the
|
||||
value is "root".
|
||||
- `--cwd`: set the current working dir.
|
||||
- `--env`: set environment variables for the process.
|
||||
* **init**
|
||||
It's an internal command that is called inside the container's namespaces to
|
||||
initialize the namespace and exec the user's process. It should not be called
|
||||
externally.
|
||||
* **oom**
|
||||
Display oom notifications for a container, you should specify container id.
|
||||
* **pause**
|
||||
Pause the container's processes, you should specify container id. It will use
|
||||
cgroup freeze subsystem to help.
|
||||
* **unpause**
|
||||
Unpause the container's processes. Same with `pause`.
|
||||
* **stats**
|
||||
Display statistics for the container, it will mainly show cgroup and network
|
||||
statistics.
|
||||
* **state**
|
||||
Get the container's current state. You can also read the state from `state.json`
|
||||
in your container_id folder.
|
||||
* **checkpoint**
|
||||
Checkpoint a running container. You can read [this](http://criu.org/Advanced_usage)
|
||||
for more detailed information about options.
|
||||
- `--id`: specify the ID for a container. By default, the id is "nsinit".
|
||||
- `--image-path`: path for saving criu image files. You must specify this option.
|
||||
- `--work-path`: path for saving work files and logs. By default it will
|
||||
generate a folder named "criu.work" in root directory.
|
||||
- `--leave-running`: leave the process running after checkpointing.
|
||||
- `--tcp-established`: allow open tcp connections.
|
||||
- `--ext-unix-sk`: allow external unix sockets.
|
||||
- `--shell-job`: allow shell jobs.
|
||||
- `--page-server`: ADDRESS:PORT of the page server. The dump image can be
|
||||
sent to a criu page server if we have a page server.
|
||||
* **restore**
|
||||
Restore a container from a previous checkpoint. Options are almost the same
|
||||
with checkpoint and `--image-path` must be specified.
|
||||
* **help, h**
|
||||
Shows a list of commands or help for one command.
|
|
@ -1,67 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/libcontainer"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var checkpointCommand = cli.Command{
|
||||
Name: "checkpoint",
|
||||
Usage: "checkpoint a running container",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
||||
cli.StringFlag{Name: "image-path", Value: "", Usage: "path for saving criu image files"},
|
||||
cli.StringFlag{Name: "work-path", Value: "", Usage: "path for saving work files and logs"},
|
||||
cli.BoolFlag{Name: "leave-running", Usage: "leave the process running after checkpointing"},
|
||||
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.StringFlag{Name: "page-server", Value: "", Usage: "ADDRESS:PORT of the page server"},
|
||||
},
|
||||
Action: func(context *cli.Context) {
|
||||
imagePath := context.String("image-path")
|
||||
if imagePath == "" {
|
||||
fatal(fmt.Errorf("The --image-path option isn't specified"))
|
||||
}
|
||||
|
||||
container, err := getContainer(context)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
// these are the mandatory criu options for a container
|
||||
criuOpts := &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"),
|
||||
}
|
||||
|
||||
// xxx following criu opts are optional
|
||||
// The dump image can be sent to a criu page server
|
||||
if psOpt := context.String("page-server"); psOpt != "" {
|
||||
addressPort := strings.Split(psOpt, ":")
|
||||
if len(addressPort) != 2 {
|
||||
fatal(fmt.Errorf("Use --page-server ADDRESS:PORT to specify page server"))
|
||||
}
|
||||
|
||||
port_int, err := strconv.Atoi(addressPort[1])
|
||||
if err != nil {
|
||||
fatal(fmt.Errorf("Invalid port number"))
|
||||
}
|
||||
criuOpts.PageServer = libcontainer.CriuPageServerInfo{
|
||||
Address: addressPort[0],
|
||||
Port: int32(port_int),
|
||||
}
|
||||
}
|
||||
|
||||
if err := container.Checkpoint(criuOpts); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
323
nsinit/config.go
323
nsinit/config.go
|
@ -1,323 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/libcontainer/configs"
|
||||
"github.com/docker/libcontainer/utils"
|
||||
)
|
||||
|
||||
const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
|
||||
|
||||
var createFlags = []cli.Flag{
|
||||
cli.BoolFlag{Name: "cgroup", Usage: "mount the cgroup data for the container"},
|
||||
cli.BoolFlag{Name: "read-only", Usage: "set the container's rootfs as read-only"},
|
||||
cli.IntFlag{Name: "cpushares", Usage: "set the cpushares for the container"},
|
||||
cli.IntFlag{Name: "memory-limit", Usage: "set the memory limit for the container"},
|
||||
cli.IntFlag{Name: "memory-swap", Usage: "set the memory swap limit for the container"},
|
||||
cli.IntFlag{Name: "memory-swappiness", Usage: "set the memory swappiness value (0-100 inclusive) for the container"},
|
||||
cli.IntFlag{Name: "parent-death-signal", Usage: "set the signal that will be delivered to the process in case the parent dies"},
|
||||
cli.IntFlag{Name: "userns-root-uid", Usage: "set the user namespace root uid"},
|
||||
cli.IntFlag{Name: "veth-mtu", Usage: "veth mtu"},
|
||||
cli.StringFlag{Name: "apparmor-profile", Usage: "set the apparmor profile"},
|
||||
cli.StringFlag{Name: "cpuset-cpus", Usage: "set the cpuset cpus"},
|
||||
cli.StringFlag{Name: "cpuset-mems", Usage: "set the cpuset mems"},
|
||||
cli.StringFlag{Name: "hostname", Value: "nsinit", Usage: "hostname value for the container"},
|
||||
cli.StringFlag{Name: "ipc", Value: "", Usage: "ipc namespace"},
|
||||
cli.StringFlag{Name: "mnt", Value: "", Usage: "mount namespace"},
|
||||
cli.StringFlag{Name: "mount-label", Usage: "set the mount label"},
|
||||
cli.StringFlag{Name: "net", Value: "", Usage: "network namespace"},
|
||||
cli.StringFlag{Name: "pid", Value: "", Usage: "pid namespace"},
|
||||
cli.StringFlag{Name: "process-label", Usage: "set the process label"},
|
||||
cli.StringFlag{Name: "rootfs", Usage: "set the rootfs"},
|
||||
cli.StringFlag{Name: "security", Value: "", Usage: "set the security profile (high, medium, low)"},
|
||||
cli.StringFlag{Name: "uts", Value: "", Usage: "uts namespace"},
|
||||
cli.StringFlag{Name: "veth-address", Usage: "veth ip address"},
|
||||
cli.StringFlag{Name: "veth-bridge", Usage: "veth bridge"},
|
||||
cli.StringFlag{Name: "veth-gateway", Usage: "veth gateway address"},
|
||||
cli.StringSliceFlag{Name: "bind", Value: &cli.StringSlice{}, Usage: "add bind mounts to the container"},
|
||||
cli.StringSliceFlag{Name: "sysctl", Value: &cli.StringSlice{}, Usage: "set system properties in the container"},
|
||||
cli.StringSliceFlag{Name: "tmpfs", Value: &cli.StringSlice{}, Usage: "add tmpfs mounts to the container"},
|
||||
cli.StringSliceFlag{Name: "groups", Value: &cli.StringSlice{}, Usage: "add additional groups"},
|
||||
}
|
||||
|
||||
var configCommand = cli.Command{
|
||||
Name: "config",
|
||||
Usage: "generate a standard configuration file for a container",
|
||||
Flags: append([]cli.Flag{
|
||||
cli.StringFlag{Name: "file,f", Value: "stdout", Usage: "write the configuration to the specified file"},
|
||||
}, createFlags...),
|
||||
Action: func(context *cli.Context) {
|
||||
template := getTemplate()
|
||||
modify(template, context)
|
||||
data, err := json.MarshalIndent(template, "", "\t")
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
var f *os.File
|
||||
filePath := context.String("file")
|
||||
switch filePath {
|
||||
case "stdout", "":
|
||||
f = os.Stdout
|
||||
default:
|
||||
if f, err = os.Create(filePath); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
}
|
||||
if _, err := io.Copy(f, bytes.NewBuffer(data)); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func modify(config *configs.Config, context *cli.Context) {
|
||||
config.ParentDeathSignal = context.Int("parent-death-signal")
|
||||
config.Readonlyfs = context.Bool("read-only")
|
||||
config.Cgroups.CpusetCpus = context.String("cpuset-cpus")
|
||||
config.Cgroups.CpusetMems = context.String("cpuset-mems")
|
||||
config.Cgroups.CpuShares = int64(context.Int("cpushares"))
|
||||
config.Cgroups.Memory = int64(context.Int("memory-limit"))
|
||||
config.Cgroups.MemorySwap = int64(context.Int("memory-swap"))
|
||||
config.Cgroups.MemorySwappiness = int64(context.Int("memory-swappiness"))
|
||||
config.AppArmorProfile = context.String("apparmor-profile")
|
||||
config.ProcessLabel = context.String("process-label")
|
||||
config.MountLabel = context.String("mount-label")
|
||||
|
||||
rootfs := context.String("rootfs")
|
||||
if rootfs != "" {
|
||||
config.Rootfs = rootfs
|
||||
}
|
||||
|
||||
userns_uid := context.Int("userns-root-uid")
|
||||
if userns_uid != 0 {
|
||||
config.Namespaces.Add(configs.NEWUSER, "")
|
||||
config.UidMappings = []configs.IDMap{
|
||||
{ContainerID: 0, HostID: userns_uid, Size: 1},
|
||||
{ContainerID: 1, HostID: 1, Size: userns_uid - 1},
|
||||
{ContainerID: userns_uid + 1, HostID: userns_uid + 1, Size: math.MaxInt32 - userns_uid},
|
||||
}
|
||||
config.GidMappings = []configs.IDMap{
|
||||
{ContainerID: 0, HostID: userns_uid, Size: 1},
|
||||
{ContainerID: 1, HostID: 1, Size: userns_uid - 1},
|
||||
{ContainerID: userns_uid + 1, HostID: userns_uid + 1, Size: math.MaxInt32 - userns_uid},
|
||||
}
|
||||
for _, node := range config.Devices {
|
||||
node.Uid = uint32(userns_uid)
|
||||
node.Gid = uint32(userns_uid)
|
||||
}
|
||||
}
|
||||
|
||||
config.SystemProperties = make(map[string]string)
|
||||
for _, sysProp := range context.StringSlice("sysctl") {
|
||||
parts := strings.SplitN(sysProp, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
logrus.Fatalf("invalid system property %s", sysProp)
|
||||
}
|
||||
config.SystemProperties[parts[0]] = parts[1]
|
||||
}
|
||||
|
||||
for _, group := range context.StringSlice("groups") {
|
||||
config.AdditionalGroups = append(config.AdditionalGroups, group)
|
||||
}
|
||||
|
||||
for _, rawBind := range context.StringSlice("bind") {
|
||||
mount := &configs.Mount{
|
||||
Device: "bind",
|
||||
Flags: syscall.MS_BIND | syscall.MS_REC,
|
||||
}
|
||||
parts := strings.SplitN(rawBind, ":", 3)
|
||||
switch len(parts) {
|
||||
default:
|
||||
logrus.Fatalf("invalid bind mount %s", rawBind)
|
||||
case 2:
|
||||
mount.Source, mount.Destination = parts[0], parts[1]
|
||||
case 3:
|
||||
mount.Source, mount.Destination = parts[0], parts[1]
|
||||
switch parts[2] {
|
||||
case "ro":
|
||||
mount.Flags |= syscall.MS_RDONLY
|
||||
case "rw":
|
||||
default:
|
||||
logrus.Fatalf("invalid bind mount mode %s", parts[2])
|
||||
}
|
||||
}
|
||||
config.Mounts = append(config.Mounts, mount)
|
||||
}
|
||||
for _, tmpfs := range context.StringSlice("tmpfs") {
|
||||
config.Mounts = append(config.Mounts, &configs.Mount{
|
||||
Device: "tmpfs",
|
||||
Destination: tmpfs,
|
||||
Flags: syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV,
|
||||
})
|
||||
}
|
||||
for flag, value := range map[string]configs.NamespaceType{
|
||||
"net": configs.NEWNET,
|
||||
"mnt": configs.NEWNS,
|
||||
"pid": configs.NEWPID,
|
||||
"ipc": configs.NEWIPC,
|
||||
"uts": configs.NEWUTS,
|
||||
} {
|
||||
switch v := context.String(flag); v {
|
||||
case "host":
|
||||
config.Namespaces.Remove(value)
|
||||
case "", "private":
|
||||
if !config.Namespaces.Contains(value) {
|
||||
config.Namespaces.Add(value, "")
|
||||
}
|
||||
if flag == "net" {
|
||||
config.Networks = []*configs.Network{
|
||||
{
|
||||
Type: "loopback",
|
||||
Address: "127.0.0.1/0",
|
||||
Gateway: "localhost",
|
||||
},
|
||||
}
|
||||
}
|
||||
if flag == "uts" {
|
||||
config.Hostname = context.String("hostname")
|
||||
}
|
||||
default:
|
||||
config.Namespaces.Remove(value)
|
||||
config.Namespaces.Add(value, v)
|
||||
}
|
||||
}
|
||||
if bridge := context.String("veth-bridge"); bridge != "" {
|
||||
hostName, err := utils.GenerateRandomName("veth", 7)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
network := &configs.Network{
|
||||
Type: "veth",
|
||||
Name: "eth0",
|
||||
Bridge: bridge,
|
||||
Address: context.String("veth-address"),
|
||||
Gateway: context.String("veth-gateway"),
|
||||
Mtu: context.Int("veth-mtu"),
|
||||
HostInterfaceName: hostName,
|
||||
}
|
||||
config.Networks = append(config.Networks, network)
|
||||
}
|
||||
if context.Bool("cgroup") {
|
||||
config.Mounts = append(config.Mounts, &configs.Mount{
|
||||
Destination: "/sys/fs/cgroup",
|
||||
Device: "cgroup",
|
||||
})
|
||||
}
|
||||
modifySecurityProfile(context, config)
|
||||
}
|
||||
|
||||
func modifySecurityProfile(context *cli.Context, config *configs.Config) {
|
||||
profileName := context.String("security")
|
||||
if profileName == "" {
|
||||
return
|
||||
}
|
||||
profile := profiles[profileName]
|
||||
if profile == nil {
|
||||
logrus.Fatalf("invalid profile name %q", profileName)
|
||||
}
|
||||
config.Rlimits = profile.Rlimits
|
||||
config.Capabilities = profile.Capabilities
|
||||
config.Seccomp = profile.Seccomp
|
||||
config.AppArmorProfile = profile.ApparmorProfile
|
||||
config.MountLabel = profile.MountLabel
|
||||
config.ProcessLabel = profile.ProcessLabel
|
||||
}
|
||||
|
||||
func getTemplate() *configs.Config {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &configs.Config{
|
||||
Rootfs: cwd,
|
||||
ParentDeathSignal: int(syscall.SIGKILL),
|
||||
Capabilities: []string{
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FSETID",
|
||||
"FOWNER",
|
||||
"MKNOD",
|
||||
"NET_RAW",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SETFCAP",
|
||||
"SETPCAP",
|
||||
"NET_BIND_SERVICE",
|
||||
"SYS_CHROOT",
|
||||
"KILL",
|
||||
"AUDIT_WRITE",
|
||||
},
|
||||
Namespaces: configs.Namespaces([]configs.Namespace{
|
||||
{Type: configs.NEWNS},
|
||||
{Type: configs.NEWUTS},
|
||||
{Type: configs.NEWIPC},
|
||||
{Type: configs.NEWPID},
|
||||
{Type: configs.NEWNET},
|
||||
}),
|
||||
Cgroups: &configs.Cgroup{
|
||||
Name: filepath.Base(cwd),
|
||||
Parent: "nsinit",
|
||||
AllowAllDevices: false,
|
||||
AllowedDevices: configs.DefaultAllowedDevices,
|
||||
MemorySwappiness: -1,
|
||||
},
|
||||
Devices: configs.DefaultAutoCreatedDevices,
|
||||
MaskPaths: []string{
|
||||
"/proc/kcore",
|
||||
},
|
||||
ReadonlyPaths: []string{
|
||||
"/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus",
|
||||
},
|
||||
Mounts: []*configs.Mount{
|
||||
{
|
||||
Source: "proc",
|
||||
Destination: "/proc",
|
||||
Device: "proc",
|
||||
Flags: defaultMountFlags,
|
||||
},
|
||||
{
|
||||
Source: "tmpfs",
|
||||
Destination: "/dev",
|
||||
Device: "tmpfs",
|
||||
Flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME,
|
||||
Data: "mode=755",
|
||||
},
|
||||
{
|
||||
Source: "devpts",
|
||||
Destination: "/dev/pts",
|
||||
Device: "devpts",
|
||||
Flags: syscall.MS_NOSUID | syscall.MS_NOEXEC,
|
||||
Data: "newinstance,ptmxmode=0666,mode=0620,gid=5",
|
||||
},
|
||||
{
|
||||
Device: "tmpfs",
|
||||
Source: "shm",
|
||||
Destination: "/dev/shm",
|
||||
Data: "mode=1777,size=65536k",
|
||||
Flags: defaultMountFlags,
|
||||
},
|
||||
{
|
||||
Source: "mqueue",
|
||||
Destination: "/dev/mqueue",
|
||||
Device: "mqueue",
|
||||
Flags: defaultMountFlags,
|
||||
},
|
||||
{
|
||||
Source: "sysfs",
|
||||
Destination: "/sys",
|
||||
Device: "sysfs",
|
||||
Flags: defaultMountFlags | syscall.MS_RDONLY,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
124
nsinit/exec.go
124
nsinit/exec.go
|
@ -1,124 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/libcontainer"
|
||||
"github.com/docker/libcontainer/utils"
|
||||
)
|
||||
|
||||
var standardEnvironment = &cli.StringSlice{
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"HOSTNAME=nsinit",
|
||||
"TERM=xterm",
|
||||
}
|
||||
|
||||
var execCommand = cli.Command{
|
||||
Name: "exec",
|
||||
Usage: "execute a new command inside a container",
|
||||
Action: execAction,
|
||||
Flags: append([]cli.Flag{
|
||||
cli.BoolFlag{Name: "tty,t", Usage: "allocate a TTY to the container"},
|
||||
cli.BoolFlag{Name: "systemd", Usage: "Use systemd for managing cgroups, if available"},
|
||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
||||
cli.StringFlag{Name: "config", Value: "", Usage: "path to the configuration file"},
|
||||
cli.StringFlag{Name: "user,u", Value: "root", Usage: "set the user, uid, and/or gid for the process"},
|
||||
cli.StringFlag{Name: "cwd", Value: "", Usage: "set the current working dir"},
|
||||
cli.StringSliceFlag{Name: "env", Value: standardEnvironment, Usage: "set environment variables for the process"},
|
||||
}, createFlags...),
|
||||
}
|
||||
|
||||
func execAction(context *cli.Context) {
|
||||
factory, err := loadFactory(context)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
config, err := loadConfig(context)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
created := false
|
||||
container, err := factory.Load(context.String("id"))
|
||||
if err != nil {
|
||||
created = true
|
||||
if container, err = factory.Create(context.String("id"), config); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
}
|
||||
process := &libcontainer.Process{
|
||||
Args: context.Args(),
|
||||
Env: context.StringSlice("env"),
|
||||
User: context.String("user"),
|
||||
Cwd: context.String("cwd"),
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
}
|
||||
rootuid, err := config.HostUID()
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
tty, err := newTty(context, process, rootuid)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if err := tty.attach(process); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
go handleSignals(process, tty)
|
||||
err = container.Start(process)
|
||||
if err != nil {
|
||||
tty.Close()
|
||||
if created {
|
||||
container.Destroy()
|
||||
}
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
status, err := process.Wait()
|
||||
if err != nil {
|
||||
exitError, ok := err.(*exec.ExitError)
|
||||
if ok {
|
||||
status = exitError.ProcessState
|
||||
} else {
|
||||
tty.Close()
|
||||
if created {
|
||||
container.Destroy()
|
||||
}
|
||||
fatal(err)
|
||||
}
|
||||
}
|
||||
if created {
|
||||
status, err := container.Status()
|
||||
if err != nil {
|
||||
tty.Close()
|
||||
fatal(err)
|
||||
}
|
||||
if status != libcontainer.Checkpointed {
|
||||
if err := container.Destroy(); err != nil {
|
||||
tty.Close()
|
||||
fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
tty.Close()
|
||||
os.Exit(utils.ExitStatus(status.Sys().(syscall.WaitStatus)))
|
||||
}
|
||||
|
||||
func handleSignals(container *libcontainer.Process, tty *tty) {
|
||||
sigc := make(chan os.Signal, 10)
|
||||
signal.Notify(sigc)
|
||||
tty.resize()
|
||||
for sig := range sigc {
|
||||
switch sig {
|
||||
case syscall.SIGWINCH:
|
||||
tty.resize()
|
||||
default:
|
||||
container.Signal(sig)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/libcontainer"
|
||||
_ "github.com/docker/libcontainer/nsenter"
|
||||
)
|
||||
|
||||
var initCommand = cli.Command{
|
||||
Name: "init",
|
||||
Usage: "runs the init process inside the namespace",
|
||||
Action: func(context *cli.Context) {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
runtime.GOMAXPROCS(1)
|
||||
runtime.LockOSThread()
|
||||
factory, err := libcontainer.New("")
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if err := factory.StartInitialization(); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
panic("This line should never been executed")
|
||||
},
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "nsinit"
|
||||
app.Version = "2"
|
||||
app.Author = "libcontainer maintainers"
|
||||
app.Flags = []cli.Flag{
|
||||
cli.BoolFlag{Name: "debug", Usage: "enable debug output in the logs"},
|
||||
cli.StringFlag{Name: "root", Value: "/var/run/nsinit", Usage: "root directory for containers"},
|
||||
cli.StringFlag{Name: "log-file", Usage: "set the log file to output logs to"},
|
||||
cli.StringFlag{Name: "criu", Value: "criu", Usage: "path to the criu binary for checkpoint and restore"},
|
||||
}
|
||||
app.Commands = []cli.Command{
|
||||
checkpointCommand,
|
||||
configCommand,
|
||||
execCommand,
|
||||
initCommand,
|
||||
oomCommand,
|
||||
pauseCommand,
|
||||
stateCommand,
|
||||
statsCommand,
|
||||
unpauseCommand,
|
||||
restoreCommand,
|
||||
}
|
||||
app.Before = func(context *cli.Context) error {
|
||||
if context.GlobalBool("debug") {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
if path := context.GlobalString("log-file"); path != "" {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.SetOutput(f)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var oomCommand = cli.Command{
|
||||
Name: "oom",
|
||||
Usage: "display oom notifications for a container",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
||||
},
|
||||
Action: func(context *cli.Context) {
|
||||
container, err := getContainer(context)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
n, err := container.NotifyOOM()
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
for x := range n {
|
||||
// hack for calm down go1.4 gofmt
|
||||
_ = x
|
||||
logrus.Printf("OOM notification received")
|
||||
}
|
||||
},
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var pauseCommand = cli.Command{
|
||||
Name: "pause",
|
||||
Usage: "pause the container's processes",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
||||
},
|
||||
Action: func(context *cli.Context) {
|
||||
container, err := getContainer(context)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
if err = container.Pause(); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
var unpauseCommand = cli.Command{
|
||||
Name: "unpause",
|
||||
Usage: "unpause the container's processes",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
||||
},
|
||||
Action: func(context *cli.Context) {
|
||||
container, err := getContainer(context)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
if err = container.Resume(); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/libcontainer"
|
||||
"github.com/docker/libcontainer/utils"
|
||||
)
|
||||
|
||||
var restoreCommand = cli.Command{
|
||||
Name: "restore",
|
||||
Usage: "restore a container from a previous checkpoint",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
||||
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"},
|
||||
},
|
||||
Action: func(context *cli.Context) {
|
||||
imagePath := context.String("image-path")
|
||||
if imagePath == "" {
|
||||
fatal(fmt.Errorf("The --image-path option isn't specified"))
|
||||
}
|
||||
|
||||
var (
|
||||
container libcontainer.Container
|
||||
err error
|
||||
)
|
||||
|
||||
factory, err := loadFactory(context)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
config, err := loadConfig(context)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
created := false
|
||||
container, err = factory.Load(context.String("id"))
|
||||
if err != nil {
|
||||
created = true
|
||||
if container, err = factory.Create(context.String("id"), config); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
process := &libcontainer.Process{
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
}
|
||||
//rootuid, err := config.HostUID()
|
||||
//if err != nil {
|
||||
//fatal(err)
|
||||
//}
|
||||
rootuid := 0 // XXX
|
||||
tty, err := newTty(context, process, rootuid)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if err := tty.attach(process); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
go handleSignals(process, tty)
|
||||
|
||||
err = container.Restore(process, &libcontainer.CriuOpts{
|
||||
ImagesDirectory: imagePath,
|
||||
WorkDirectory: context.String("work-path"),
|
||||
TcpEstablished: context.Bool("tcp-established"),
|
||||
ExternalUnixConnections: context.Bool("ext-unix-sk"),
|
||||
ShellJob: context.Bool("shell-job"),
|
||||
})
|
||||
if err != nil {
|
||||
tty.Close()
|
||||
if created {
|
||||
container.Destroy()
|
||||
}
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
status, err := process.Wait()
|
||||
if err != nil {
|
||||
exitError, ok := err.(*exec.ExitError)
|
||||
if ok {
|
||||
status = exitError.ProcessState
|
||||
} else {
|
||||
tty.Close()
|
||||
if created {
|
||||
container.Destroy()
|
||||
}
|
||||
fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if created {
|
||||
status, err := container.Status()
|
||||
if err != nil {
|
||||
tty.Close()
|
||||
fatal(err)
|
||||
}
|
||||
if status != libcontainer.Checkpointed {
|
||||
if err := container.Destroy(); err != nil {
|
||||
tty.Close()
|
||||
fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tty.Close()
|
||||
os.Exit(utils.ExitStatus(status.Sys().(syscall.WaitStatus)))
|
||||
},
|
||||
}
|
|
@ -1,272 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/libcontainer/configs"
|
||||
"github.com/docker/libcontainer/system"
|
||||
)
|
||||
|
||||
var profiles = map[string]*securityProfile{
|
||||
"high": highProfile,
|
||||
"medium": mediumProfile,
|
||||
"low": lowProfile,
|
||||
}
|
||||
|
||||
type securityProfile struct {
|
||||
Capabilities []string `json:"capabilities"`
|
||||
ApparmorProfile string `json:"apparmor_profile"`
|
||||
MountLabel string `json:"mount_label"`
|
||||
ProcessLabel string `json:"process_label"`
|
||||
Rlimits []configs.Rlimit `json:"rlimits"`
|
||||
Seccomp *configs.Seccomp `json:"seccomp"`
|
||||
}
|
||||
|
||||
// this should be a runtime config that is not able to do things like apt-get or yum install.
|
||||
var highProfile = &securityProfile{
|
||||
Capabilities: []string{
|
||||
"NET_BIND_SERVICE",
|
||||
"KILL",
|
||||
"AUDIT_WRITE",
|
||||
},
|
||||
Rlimits: []configs.Rlimit{
|
||||
{
|
||||
Type: syscall.RLIMIT_NOFILE,
|
||||
Hard: 1024,
|
||||
Soft: 1024,
|
||||
},
|
||||
},
|
||||
// http://man7.org/linux/man-pages/man2/syscalls.2.html
|
||||
Seccomp: &configs.Seccomp{
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Value: syscall.SYS_CAPSET, // http://man7.org/linux/man-pages/man2/capset.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_UNSHARE, // http://man7.org/linux/man-pages/man2/unshare.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: int(system.SysSetns()),
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_MOUNT, // http://man7.org/linux/man-pages/man2/mount.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_UMOUNT2, // http://man7.org/linux/man-pages/man2/umount.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_CREATE_MODULE, // http://man7.org/linux/man-pages/man2/create_module.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_DELETE_MODULE, // http://man7.org/linux/man-pages/man2/delete_module.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_CHMOD, // http://man7.org/linux/man-pages/man2/chmod.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_CHOWN, // http://man7.org/linux/man-pages/man2/chown.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_LINK, // http://man7.org/linux/man-pages/man2/link.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_LINKAT, // http://man7.org/linux/man-pages/man2/linkat.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_UNLINK, // http://man7.org/linux/man-pages/man2/unlink.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_UNLINKAT, // http://man7.org/linux/man-pages/man2/unlinkat.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_CHROOT, // http://man7.org/linux/man-pages/man2/chroot.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_KEXEC_LOAD, // http://man7.org/linux/man-pages/man2/kexec_load.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_SETDOMAINNAME, // http://man7.org/linux/man-pages/man2/setdomainname.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_SETHOSTNAME, // http://man7.org/linux/man-pages/man2/sethostname.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_CLONE, // http://man7.org/linux/man-pages/man2/clone.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
Args: []*configs.Arg{
|
||||
{
|
||||
Index: 0, // the glibc wrapper has the flags at arg2 but the raw syscall has flags at arg0
|
||||
Value: syscall.CLONE_NEWUSER,
|
||||
Op: configs.MaskEqualTo,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// This is a medium level profile that should be able to do things like installing from
|
||||
// apt-get or yum.
|
||||
var mediumProfile = &securityProfile{
|
||||
Capabilities: []string{
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FSETID",
|
||||
"FOWNER",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SETFCAP",
|
||||
"SETPCAP",
|
||||
"NET_BIND_SERVICE",
|
||||
"KILL",
|
||||
"AUDIT_WRITE",
|
||||
},
|
||||
Rlimits: []configs.Rlimit{
|
||||
{
|
||||
Type: syscall.RLIMIT_NOFILE,
|
||||
Hard: 1024,
|
||||
Soft: 1024,
|
||||
},
|
||||
},
|
||||
// http://man7.org/linux/man-pages/man2/syscalls.2.html
|
||||
Seccomp: &configs.Seccomp{
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Value: syscall.SYS_UNSHARE, // http://man7.org/linux/man-pages/man2/unshare.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: int(system.SysSetns()),
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_MOUNT, // http://man7.org/linux/man-pages/man2/mount.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_UMOUNT2, // http://man7.org/linux/man-pages/man2/umount.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_CHROOT, // http://man7.org/linux/man-pages/man2/chroot.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_CREATE_MODULE, // http://man7.org/linux/man-pages/man2/create_module.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_DELETE_MODULE, // http://man7.org/linux/man-pages/man2/delete_module.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_KEXEC_LOAD, // http://man7.org/linux/man-pages/man2/kexec_load.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_SETDOMAINNAME, // http://man7.org/linux/man-pages/man2/setdomainname.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_SETHOSTNAME, // http://man7.org/linux/man-pages/man2/sethostname.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_CLONE, // http://man7.org/linux/man-pages/man2/clone.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
Args: []*configs.Arg{
|
||||
{
|
||||
Index: 0, // the glibc wrapper has the flags at arg2 but the raw syscall has flags at arg0
|
||||
Value: syscall.CLONE_NEWUSER,
|
||||
Op: configs.MaskEqualTo,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var lowProfile = &securityProfile{
|
||||
Capabilities: []string{
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FSETID",
|
||||
"FOWNER",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SYS_CHROOT",
|
||||
"SETFCAP",
|
||||
"SETPCAP",
|
||||
"NET_BIND_SERVICE",
|
||||
"KILL",
|
||||
"AUDIT_WRITE",
|
||||
},
|
||||
Rlimits: []configs.Rlimit{
|
||||
{
|
||||
Type: syscall.RLIMIT_NOFILE,
|
||||
Hard: 1024,
|
||||
Soft: 1024,
|
||||
},
|
||||
},
|
||||
// http://man7.org/linux/man-pages/man2/syscalls.2.html
|
||||
Seccomp: &configs.Seccomp{
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Value: syscall.SYS_UNSHARE, // http://man7.org/linux/man-pages/man2/unshare.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: int(system.SysSetns()),
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_MOUNT, // http://man7.org/linux/man-pages/man2/mount.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_UMOUNT2, // http://man7.org/linux/man-pages/man2/umount.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_CREATE_MODULE, // http://man7.org/linux/man-pages/man2/create_module.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_DELETE_MODULE, // http://man7.org/linux/man-pages/man2/delete_module.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_KEXEC_LOAD, // http://man7.org/linux/man-pages/man2/kexec_load.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
},
|
||||
{
|
||||
Value: syscall.SYS_CLONE, // http://man7.org/linux/man-pages/man2/clone.2.html
|
||||
Action: configs.Action(syscall.EPERM),
|
||||
Args: []*configs.Arg{
|
||||
{
|
||||
Index: 0, // the glibc wrapper has the flags at arg2 but the raw syscall has flags at arg0
|
||||
Value: syscall.CLONE_NEWUSER,
|
||||
Op: configs.MaskEqualTo,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var stateCommand = cli.Command{
|
||||
Name: "state",
|
||||
Usage: "get the container's current state",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
||||
},
|
||||
Action: func(context *cli.Context) {
|
||||
container, err := getContainer(context)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
state, err := container.State()
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
data, err := json.MarshalIndent(state, "", "\t")
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
fmt.Printf("%s", data)
|
||||
},
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var statsCommand = cli.Command{
|
||||
Name: "stats",
|
||||
Usage: "display statistics for the container",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
||||
},
|
||||
Action: func(context *cli.Context) {
|
||||
container, err := getContainer(context)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
stats, err := container.Stats()
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
data, err := json.MarshalIndent(stats, "", "\t")
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
fmt.Printf("%s", data)
|
||||
},
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/docker/libcontainer"
|
||||
)
|
||||
|
||||
func newTty(context *cli.Context, p *libcontainer.Process, rootuid int) (*tty, error) {
|
||||
if context.Bool("tty") {
|
||||
console, err := p.NewConsole(rootuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tty{
|
||||
console: console,
|
||||
closers: []io.Closer{
|
||||
console,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return &tty{}, nil
|
||||
}
|
||||
|
||||
type tty struct {
|
||||
console libcontainer.Console
|
||||
state *term.State
|
||||
closers []io.Closer
|
||||
}
|
||||
|
||||
func (t *tty) Close() error {
|
||||
for _, c := range t.closers {
|
||||
c.Close()
|
||||
}
|
||||
if t.state != nil {
|
||||
term.RestoreTerminal(os.Stdin.Fd(), t.state)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tty) attach(process *libcontainer.Process) error {
|
||||
if t.console != nil {
|
||||
go io.Copy(t.console, os.Stdin)
|
||||
go io.Copy(os.Stdout, t.console)
|
||||
state, err := term.SetRawTerminal(os.Stdin.Fd())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.state = state
|
||||
process.Stderr = nil
|
||||
process.Stdout = nil
|
||||
process.Stdin = nil
|
||||
} else {
|
||||
// setup standard pipes so that the TTY of the calling nsinit process
|
||||
// is not inherited by the container.
|
||||
r, w, err := os.Pipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go io.Copy(w, os.Stdin)
|
||||
t.closers = append(t.closers, w)
|
||||
process.Stdin = r
|
||||
if r, w, err = os.Pipe(); err != nil {
|
||||
return err
|
||||
}
|
||||
go io.Copy(os.Stdout, r)
|
||||
process.Stdout = w
|
||||
t.closers = append(t.closers, r)
|
||||
if r, w, err = os.Pipe(); err != nil {
|
||||
return err
|
||||
}
|
||||
go io.Copy(os.Stderr, r)
|
||||
process.Stderr = w
|
||||
t.closers = append(t.closers, r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tty) setupPipe() {
|
||||
}
|
||||
|
||||
func (t *tty) resize() error {
|
||||
if t.console == nil {
|
||||
return nil
|
||||
}
|
||||
ws, err := term.GetWinsize(os.Stdin.Fd())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return term.SetWinsize(t.console.Fd(), ws)
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/libcontainer"
|
||||
"github.com/docker/libcontainer/cgroups/systemd"
|
||||
"github.com/docker/libcontainer/configs"
|
||||
)
|
||||
|
||||
func loadConfig(context *cli.Context) (*configs.Config, error) {
|
||||
if path := context.String("config"); path != "" {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
var config *configs.Config
|
||||
if err := json.NewDecoder(f).Decode(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
config := getTemplate()
|
||||
modify(config, context)
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
|
||||
cgm := libcontainer.Cgroupfs
|
||||
if context.Bool("systemd") {
|
||||
if systemd.UseSystemd() {
|
||||
cgm = libcontainer.SystemdCgroups
|
||||
} else {
|
||||
logrus.Warn("systemd cgroup flag passed, but systemd support for managing cgroups is not available.")
|
||||
}
|
||||
}
|
||||
root := context.GlobalString("root")
|
||||
abs, err := filepath.Abs(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return libcontainer.New(abs, cgm, func(l *libcontainer.LinuxFactory) error {
|
||||
l.CriuPath = context.GlobalString("criu")
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func getContainer(context *cli.Context) (libcontainer.Container, error) {
|
||||
factory, err := loadFactory(context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
container, err := factory.Load(context.String("id"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return container, nil
|
||||
}
|
||||
|
||||
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 fatalf(t string, v ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, t, v...)
|
||||
os.Exit(1)
|
||||
}
|
Loading…
Reference in New Issue