Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
| ... | ... |
@@ -16,6 +16,7 @@ import ( |
| 16 | 16 |
|
| 17 | 17 |
"github.com/Sirupsen/logrus" |
| 18 | 18 |
"github.com/docker/docker/daemon/execdriver" |
| 19 |
+ "github.com/docker/docker/daemon/execdriver/native/template" |
|
| 19 | 20 |
"github.com/docker/docker/pkg/parsers" |
| 20 | 21 |
"github.com/docker/docker/pkg/pools" |
| 21 | 22 |
"github.com/docker/docker/pkg/reexec" |
| ... | ... |
@@ -89,6 +90,7 @@ func NewDriver(root string, options []string) (*Driver, error) {
|
| 89 | 89 |
case "systemd": |
| 90 | 90 |
if systemd.UseSystemd() {
|
| 91 | 91 |
cgm = libcontainer.SystemdCgroups |
| 92 |
+ template.SystemdCgroups = true |
|
| 92 | 93 |
} else {
|
| 93 | 94 |
// warn them that they chose the wrong driver |
| 94 | 95 |
logrus.Warn("You cannot use systemd as native.cgroupdriver, using cgroupfs instead")
|
| ... | ... |
@@ -9,6 +9,9 @@ import ( |
| 9 | 9 |
|
| 10 | 10 |
const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV |
| 11 | 11 |
|
| 12 |
+// SystemdCgroups indicates whether systemd cgroup implemenation is in use or not |
|
| 13 |
+var SystemdCgroups = false |
|
| 14 |
+ |
|
| 12 | 15 |
// New returns the docker default configuration for libcontainer |
| 13 | 16 |
func New() *configs.Config {
|
| 14 | 17 |
container := &configs.Config{
|
| ... | ... |
@@ -94,5 +97,10 @@ func New() *configs.Config {
|
| 94 | 94 |
container.AppArmorProfile = "docker-default" |
| 95 | 95 |
} |
| 96 | 96 |
|
| 97 |
+ if SystemdCgroups {
|
|
| 98 |
+ container.Cgroups.Parent = "system.slice" |
|
| 99 |
+ container.Cgroups.ScopePrefix = "docker" |
|
| 100 |
+ } |
|
| 101 |
+ |
|
| 97 | 102 |
return container |
| 98 | 103 |
} |
| ... | ... |
@@ -49,7 +49,7 @@ clone git github.com/miekg/pkcs11 80f102b5cac759de406949c47f0928b99bd64cdf |
| 49 | 49 |
clone git github.com/jfrazelle/go v1.5.1-1 |
| 50 | 50 |
clone git github.com/agl/ed25519 d2b94fd789ea21d12fac1a4443dd3a3f79cda72c |
| 51 | 51 |
|
| 52 |
-clone git github.com/opencontainers/runc v0.0.5 # libcontainer |
|
| 52 |
+clone git github.com/opencontainers/runc v0.0.6 # libcontainer |
|
| 53 | 53 |
clone git github.com/opencontainers/specs 46d949ea81080c5f60dfb72ee91468b1e9fb2998 # specs |
| 54 | 54 |
clone git github.com/seccomp/libseccomp-golang 1b506fc7c24eec5a3693cdcbed40d9c226cfc6a1 |
| 55 | 55 |
# libcontainer deps (see src/github.com/opencontainers/runc/Godeps/Godeps.json) |
| ... | ... |
@@ -2,10 +2,19 @@ |
| 2 | 2 |
|
| 3 | 3 |
package apparmor |
| 4 | 4 |
|
| 5 |
+import ( |
|
| 6 |
+ "errors" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+var ErrApparmorNotEnabled = errors.New("apparmor: config provided but apparmor not supported")
|
|
| 10 |
+ |
|
| 5 | 11 |
func IsEnabled() bool {
|
| 6 | 12 |
return false |
| 7 | 13 |
} |
| 8 | 14 |
|
| 9 | 15 |
func ApplyProfile(name string) error {
|
| 16 |
+ if name != "" {
|
|
| 17 |
+ return ErrApparmorNotEnabled |
|
| 18 |
+ } |
|
| 10 | 19 |
return nil |
| 11 | 20 |
} |
| ... | ... |
@@ -167,8 +167,8 @@ func (m *Manager) Apply(pid int) error {
|
| 167 | 167 |
properties []systemdDbus.Property |
| 168 | 168 |
) |
| 169 | 169 |
|
| 170 |
- if c.Slice != "" {
|
|
| 171 |
- slice = c.Slice |
|
| 170 |
+ if c.Parent != "" {
|
|
| 171 |
+ slice = c.Parent |
|
| 172 | 172 |
} |
| 173 | 173 |
|
| 174 | 174 |
properties = append(properties, |
| ... | ... |
@@ -406,8 +406,8 @@ func getSubsystemPath(c *configs.Cgroup, subsystem string) (string, error) {
|
| 406 | 406 |
} |
| 407 | 407 |
|
| 408 | 408 |
slice := "system.slice" |
| 409 |
- if c.Slice != "" {
|
|
| 410 |
- slice = c.Slice |
|
| 409 |
+ if c.Parent != "" {
|
|
| 410 |
+ slice = c.Parent |
|
| 411 | 411 |
} |
| 412 | 412 |
|
| 413 | 413 |
return filepath.Join(mountpoint, initPath, slice, getUnitName(c)), nil |
| ... | ... |
@@ -472,7 +472,7 @@ func (m *Manager) Set(container *configs.Config) error {
|
| 472 | 472 |
} |
| 473 | 473 |
|
| 474 | 474 |
func getUnitName(c *configs.Cgroup) string {
|
| 475 |
- return fmt.Sprintf("%s-%s.scope", c.Parent, c.Name)
|
|
| 475 |
+ return fmt.Sprintf("%s-%s.scope", c.ScopePrefix, c.Name)
|
|
| 476 | 476 |
} |
| 477 | 477 |
|
| 478 | 478 |
// Atm we can't use the systemd device support because of two missing things: |
| ... | ... |
@@ -83,8 +83,8 @@ type Cgroup struct {
|
| 83 | 83 |
// Hugetlb limit (in bytes) |
| 84 | 84 |
HugetlbLimit []*HugepageLimit `json:"hugetlb_limit"` |
| 85 | 85 |
|
| 86 |
- // Parent slice to use for systemd TODO: remove in favor or parent |
|
| 87 |
- Slice string `json:"slice"` |
|
| 86 |
+ // ScopePrefix decribes prefix for the scope name |
|
| 87 |
+ ScopePrefix string `json:"scope_prefix"` |
|
| 88 | 88 |
|
| 89 | 89 |
// Whether to disable OOM Killer |
| 90 | 90 |
OomKillDisable bool `json:"oom_kill_disable"` |
| ... | ... |
@@ -6,8 +6,8 @@ import ( |
| 6 | 6 |
"errors" |
| 7 | 7 |
) |
| 8 | 8 |
|
| 9 |
-// newConsole returns an initalized console that can be used within a container by copying bytes |
|
| 9 |
+// NewConsole returns an initalized console that can be used within a container by copying bytes |
|
| 10 | 10 |
// from the master side to the slave that is attached as the tty for the container's init process. |
| 11 |
-func newConsole(uid, gid int) (Console, error) {
|
|
| 11 |
+func NewConsole(uid, gid int) (Console, error) {
|
|
| 12 | 12 |
return nil, errors.New("libcontainer console is not supported on FreeBSD")
|
| 13 | 13 |
} |
| ... | ... |
@@ -10,9 +10,9 @@ import ( |
| 10 | 10 |
"github.com/opencontainers/runc/libcontainer/label" |
| 11 | 11 |
) |
| 12 | 12 |
|
| 13 |
-// newConsole returns an initalized console that can be used within a container by copying bytes |
|
| 13 |
+// NewConsole returns an initalized console that can be used within a container by copying bytes |
|
| 14 | 14 |
// from the master side to the slave that is attached as the tty for the container's init process. |
| 15 |
-func newConsole(uid, gid int) (Console, error) {
|
|
| 15 |
+func NewConsole(uid, gid int) (Console, error) {
|
|
| 16 | 16 |
master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
|
| 17 | 17 |
if err != nil {
|
| 18 | 18 |
return nil, err |
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
package libcontainer |
| 2 | 2 |
|
| 3 |
-// newConsole returns an initalized console that can be used within a container |
|
| 4 |
-func newConsole(uid, gid int) (Console, error) {
|
|
| 3 |
+// NewConsole returns an initalized console that can be used within a container |
|
| 4 |
+func NewConsole(uid, gid int) (Console, error) {
|
|
| 5 | 5 |
return &windowsConsole{}, nil
|
| 6 | 6 |
} |
| 7 | 7 |
|
| ... | ... |
@@ -3,8 +3,10 @@ |
| 3 | 3 |
package libcontainer |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 |
+ "bytes" |
|
| 6 | 7 |
"encoding/json" |
| 7 | 8 |
"fmt" |
| 9 |
+ "io" |
|
| 8 | 10 |
"io/ioutil" |
| 9 | 11 |
"os" |
| 10 | 12 |
"os/exec" |
| ... | ... |
@@ -19,6 +21,7 @@ import ( |
| 19 | 19 |
"github.com/opencontainers/runc/libcontainer/cgroups" |
| 20 | 20 |
"github.com/opencontainers/runc/libcontainer/configs" |
| 21 | 21 |
"github.com/opencontainers/runc/libcontainer/criurpc" |
| 22 |
+ "github.com/vishvananda/netlink/nl" |
|
| 22 | 23 |
) |
| 23 | 24 |
|
| 24 | 25 |
const stdioFdCount = 3 |
| ... | ... |
@@ -218,7 +221,7 @@ func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProces |
| 218 | 218 |
return nil, newSystemError(err) |
| 219 | 219 |
} |
| 220 | 220 |
if !doInit {
|
| 221 |
- return c.newSetnsProcess(p, cmd, parentPipe, childPipe), nil |
|
| 221 |
+ return c.newSetnsProcess(p, cmd, parentPipe, childPipe) |
|
| 222 | 222 |
} |
| 223 | 223 |
return c.newInitProcess(p, cmd, parentPipe, childPipe) |
| 224 | 224 |
} |
| ... | ... |
@@ -273,23 +276,24 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, c |
| 273 | 273 |
}, nil |
| 274 | 274 |
} |
| 275 | 275 |
|
| 276 |
-func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) *setnsProcess {
|
|
| 277 |
- cmd.Env = append(cmd.Env, |
|
| 278 |
- fmt.Sprintf("_LIBCONTAINER_INITPID=%d", c.initProcess.pid()),
|
|
| 279 |
- "_LIBCONTAINER_INITTYPE=setns", |
|
| 280 |
- ) |
|
| 281 |
- if p.consolePath != "" {
|
|
| 282 |
- cmd.Env = append(cmd.Env, "_LIBCONTAINER_CONSOLE_PATH="+p.consolePath) |
|
| 276 |
+func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) (*setnsProcess, error) {
|
|
| 277 |
+ cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE=setns") |
|
| 278 |
+ // for setns process, we dont have to set cloneflags as the process namespaces |
|
| 279 |
+ // will only be set via setns syscall |
|
| 280 |
+ data, err := c.bootstrapData(0, c.initProcess.pid(), p.consolePath) |
|
| 281 |
+ if err != nil {
|
|
| 282 |
+ return nil, err |
|
| 283 | 283 |
} |
| 284 | 284 |
// TODO: set on container for process management |
| 285 | 285 |
return &setnsProcess{
|
| 286 |
- cmd: cmd, |
|
| 287 |
- cgroupPaths: c.cgroupManager.GetPaths(), |
|
| 288 |
- childPipe: childPipe, |
|
| 289 |
- parentPipe: parentPipe, |
|
| 290 |
- config: c.newInitConfig(p), |
|
| 291 |
- process: p, |
|
| 292 |
- } |
|
| 286 |
+ cmd: cmd, |
|
| 287 |
+ cgroupPaths: c.cgroupManager.GetPaths(), |
|
| 288 |
+ childPipe: childPipe, |
|
| 289 |
+ parentPipe: parentPipe, |
|
| 290 |
+ config: c.newInitConfig(p), |
|
| 291 |
+ process: p, |
|
| 292 |
+ bootstrapData: data, |
|
| 293 |
+ }, nil |
|
| 293 | 294 |
} |
| 294 | 295 |
|
| 295 | 296 |
func (c *linuxContainer) newInitConfig(process *Process) *initConfig {
|
| ... | ... |
@@ -1021,3 +1025,25 @@ func (c *linuxContainer) currentState() (*State, error) {
|
| 1021 | 1021 |
} |
| 1022 | 1022 |
return state, nil |
| 1023 | 1023 |
} |
| 1024 |
+ |
|
| 1025 |
+// bootstrapData encodes the necessary data in netlink binary format as a io.Reader. |
|
| 1026 |
+// Consumer can write the data to a bootstrap program such as one that uses |
|
| 1027 |
+// nsenter package to bootstrap the container's init process correctly, i.e. with |
|
| 1028 |
+// correct namespaces, uid/gid mapping etc. |
|
| 1029 |
+func (c *linuxContainer) bootstrapData(cloneFlags uintptr, pid int, consolePath string) (io.Reader, error) {
|
|
| 1030 |
+ // create the netlink message |
|
| 1031 |
+ r := nl.NewNetlinkRequest(int(InitMsg), 0) |
|
| 1032 |
+ // write pid |
|
| 1033 |
+ r.AddData(&Int32msg{
|
|
| 1034 |
+ Type: PidAttr, |
|
| 1035 |
+ Value: uint32(pid), |
|
| 1036 |
+ }) |
|
| 1037 |
+ // write console path |
|
| 1038 |
+ if consolePath != "" {
|
|
| 1039 |
+ r.AddData(&Bytemsg{
|
|
| 1040 |
+ Type: ConsolePathAttr, |
|
| 1041 |
+ Value: []byte(consolePath), |
|
| 1042 |
+ }) |
|
| 1043 |
+ } |
|
| 1044 |
+ return bytes.NewReader(r.Serialize()), nil |
|
| 1045 |
+} |
| ... | ... |
@@ -22,6 +22,7 @@ const ( |
| 22 | 22 |
|
| 23 | 23 |
// Common errors |
| 24 | 24 |
ConfigInvalid |
| 25 |
+ ConsoleExists |
|
| 25 | 26 |
SystemError |
| 26 | 27 |
) |
| 27 | 28 |
|
| ... | ... |
@@ -43,6 +44,8 @@ func (c ErrorCode) String() string {
|
| 43 | 43 |
return "Container is not stopped" |
| 44 | 44 |
case ContainerNotRunning: |
| 45 | 45 |
return "Container is not running" |
| 46 |
+ case ConsoleExists: |
|
| 47 |
+ return "Console exists for process" |
|
| 46 | 48 |
default: |
| 47 | 49 |
return "Unknown error" |
| 48 | 50 |
} |
| 49 | 51 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,60 @@ |
| 0 |
+// +build linux |
|
| 1 |
+ |
|
| 2 |
+package libcontainer |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "syscall" |
|
| 6 |
+ |
|
| 7 |
+ "github.com/vishvananda/netlink/nl" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+// list of known message types we want to send to bootstrap program |
|
| 11 |
+// The number is randomly chosen to not conflict with known netlink types |
|
| 12 |
+const ( |
|
| 13 |
+ InitMsg uint16 = 62000 |
|
| 14 |
+ PidAttr uint16 = 27281 |
|
| 15 |
+ ConsolePathAttr uint16 = 27282 |
|
| 16 |
+) |
|
| 17 |
+ |
|
| 18 |
+type Int32msg struct {
|
|
| 19 |
+ Type uint16 |
|
| 20 |
+ Value uint32 |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+// int32msg has the following representation |
|
| 24 |
+// | nlattr len | nlattr type | |
|
| 25 |
+// | uint32 value | |
|
| 26 |
+func (msg *Int32msg) Serialize() []byte {
|
|
| 27 |
+ buf := make([]byte, msg.Len()) |
|
| 28 |
+ native := nl.NativeEndian() |
|
| 29 |
+ native.PutUint16(buf[0:2], uint16(msg.Len())) |
|
| 30 |
+ native.PutUint16(buf[2:4], msg.Type) |
|
| 31 |
+ native.PutUint32(buf[4:8], msg.Value) |
|
| 32 |
+ return buf |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+func (msg *Int32msg) Len() int {
|
|
| 36 |
+ return syscall.NLA_HDRLEN + 4 |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+// bytemsg has the following representation |
|
| 40 |
+// | nlattr len | nlattr type | |
|
| 41 |
+// | value | pad | |
|
| 42 |
+type Bytemsg struct {
|
|
| 43 |
+ Type uint16 |
|
| 44 |
+ Value []byte |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func (msg *Bytemsg) Serialize() []byte {
|
|
| 48 |
+ l := msg.Len() |
|
| 49 |
+ buf := make([]byte, (l+syscall.NLA_ALIGNTO-1) & ^(syscall.NLA_ALIGNTO-1)) |
|
| 50 |
+ native := nl.NativeEndian() |
|
| 51 |
+ native.PutUint16(buf[0:2], uint16(l)) |
|
| 52 |
+ native.PutUint16(buf[2:4], msg.Type) |
|
| 53 |
+ copy(buf[4:], msg.Value) |
|
| 54 |
+ return buf |
|
| 55 |
+} |
|
| 56 |
+ |
|
| 57 |
+func (msg *Bytemsg) Len() int {
|
|
| 58 |
+ return syscall.NLA_HDRLEN + len(msg.Value) + 1 // null-terminated |
|
| 59 |
+} |
| ... | ... |
@@ -93,7 +93,7 @@ func (l *loopback) create(n *network, nspid int) error {
|
| 93 | 93 |
} |
| 94 | 94 |
|
| 95 | 95 |
func (l *loopback) initialize(config *network) error {
|
| 96 |
- return netlink.LinkSetUp(&netlink.Device{netlink.LinkAttrs{Name: "lo"}})
|
|
| 96 |
+ return netlink.LinkSetUp(&netlink.Device{LinkAttrs: netlink.LinkAttrs{Name: "lo"}})
|
|
| 97 | 97 |
} |
| 98 | 98 |
|
| 99 | 99 |
func (l *loopback) attach(n *configs.Network) (err error) {
|
| ... | ... |
@@ -111,7 +111,7 @@ type veth struct {
|
| 111 | 111 |
} |
| 112 | 112 |
|
| 113 | 113 |
func (v *veth) detach(n *configs.Network) (err error) {
|
| 114 |
- return netlink.LinkSetMaster(&netlink.Device{netlink.LinkAttrs{Name: n.HostInterfaceName}}, nil)
|
|
| 114 |
+ return netlink.LinkSetMaster(&netlink.Device{LinkAttrs: netlink.LinkAttrs{Name: n.HostInterfaceName}}, nil)
|
|
| 115 | 115 |
} |
| 116 | 116 |
|
| 117 | 117 |
// attach a container network interface to an external network |
| ... | ... |
@@ -17,6 +17,11 @@ |
| 17 | 17 |
#include <sched.h> |
| 18 | 18 |
#include <signal.h> |
| 19 | 19 |
|
| 20 |
+#include <linux/netlink.h> |
|
| 21 |
+#include <linux/types.h> |
|
| 22 |
+#include <stdint.h> |
|
| 23 |
+#include <sys/socket.h> |
|
| 24 |
+ |
|
| 20 | 25 |
/* All arguments should be above stack, because it grows down */ |
| 21 | 26 |
struct clone_arg {
|
| 22 | 27 |
/* |
| ... | ... |
@@ -63,24 +68,33 @@ static int clone_parent(jmp_buf * env) |
| 63 | 63 |
return child; |
| 64 | 64 |
} |
| 65 | 65 |
|
| 66 |
+static uint32_t readint32(char *buf) |
|
| 67 |
+{
|
|
| 68 |
+ return *(uint32_t *) buf; |
|
| 69 |
+} |
|
| 70 |
+ |
|
| 71 |
+// list of known message types we want to send to bootstrap program |
|
| 72 |
+// These are defined in libcontainer/message_linux.go |
|
| 73 |
+#define INIT_MSG 62000 |
|
| 74 |
+#define PID_ATTR 27281 |
|
| 75 |
+#define CONSOLE_PATH_ATTR 27282 |
|
| 76 |
+ |
|
| 66 | 77 |
void nsexec() |
| 67 | 78 |
{
|
| 68 | 79 |
char *namespaces[] = { "ipc", "uts", "net", "pid", "mnt", "user" };
|
| 69 | 80 |
const int num = sizeof(namespaces) / sizeof(char *); |
| 70 | 81 |
jmp_buf env; |
| 71 | 82 |
char buf[PATH_MAX], *val; |
| 72 |
- int i, tfd, self_tfd, child, len, pipenum, consolefd = -1; |
|
| 73 |
- pid_t pid; |
|
| 74 |
- char *console; |
|
| 83 |
+ int i, tfd, self_tfd, child, n, len, pipenum, consolefd = -1; |
|
| 84 |
+ pid_t pid = 0; |
|
| 75 | 85 |
|
| 76 |
- val = getenv("_LIBCONTAINER_INITPID");
|
|
| 77 |
- if (val == NULL) |
|
| 86 |
+ // if we dont have INITTYPE or this is the init process, skip the bootstrap process |
|
| 87 |
+ val = getenv("_LIBCONTAINER_INITTYPE");
|
|
| 88 |
+ if (val == NULL || strcmp(val, "standard") == 0) {
|
|
| 78 | 89 |
return; |
| 79 |
- |
|
| 80 |
- pid = atoi(val); |
|
| 81 |
- snprintf(buf, sizeof(buf), "%d", pid); |
|
| 82 |
- if (strcmp(val, buf)) {
|
|
| 83 |
- pr_perror("Unable to parse _LIBCONTAINER_INITPID");
|
|
| 90 |
+ } |
|
| 91 |
+ if (strcmp(val, "setns") != 0) {
|
|
| 92 |
+ pr_perror("Invalid inittype %s", val);
|
|
| 84 | 93 |
exit(1); |
| 85 | 94 |
} |
| 86 | 95 |
|
| ... | ... |
@@ -89,7 +103,6 @@ void nsexec() |
| 89 | 89 |
pr_perror("Child pipe not found");
|
| 90 | 90 |
exit(1); |
| 91 | 91 |
} |
| 92 |
- |
|
| 93 | 92 |
pipenum = atoi(val); |
| 94 | 93 |
snprintf(buf, sizeof(buf), "%d", pipenum); |
| 95 | 94 |
if (strcmp(val, buf)) {
|
| ... | ... |
@@ -97,13 +110,56 @@ void nsexec() |
| 97 | 97 |
exit(1); |
| 98 | 98 |
} |
| 99 | 99 |
|
| 100 |
- console = getenv("_LIBCONTAINER_CONSOLE_PATH");
|
|
| 101 |
- if (console != NULL) {
|
|
| 102 |
- consolefd = open(console, O_RDWR); |
|
| 103 |
- if (consolefd < 0) {
|
|
| 104 |
- pr_perror("Failed to open console %s", console);
|
|
| 105 |
- exit(1); |
|
| 100 |
+ char nlbuf[NLMSG_HDRLEN]; |
|
| 101 |
+ struct nlmsghdr *nh; |
|
| 102 |
+ if ((n = read(pipenum, nlbuf, NLMSG_HDRLEN)) != NLMSG_HDRLEN) {
|
|
| 103 |
+ pr_perror("Failed to read netlink header, got %d", n);
|
|
| 104 |
+ exit(1); |
|
| 105 |
+ } |
|
| 106 |
+ |
|
| 107 |
+ nh = (struct nlmsghdr *)nlbuf; |
|
| 108 |
+ if (nh->nlmsg_type == NLMSG_ERROR) {
|
|
| 109 |
+ pr_perror("Invalid netlink header message");
|
|
| 110 |
+ exit(1); |
|
| 111 |
+ } |
|
| 112 |
+ if (nh->nlmsg_type != INIT_MSG) {
|
|
| 113 |
+ pr_perror("Unexpected netlink message type %d", nh->nlmsg_type);
|
|
| 114 |
+ exit(1); |
|
| 115 |
+ } |
|
| 116 |
+ // read the netlink payload |
|
| 117 |
+ len = NLMSG_PAYLOAD(nh, 0); |
|
| 118 |
+ char data[len]; |
|
| 119 |
+ if ((n = read(pipenum, data, len)) != len) {
|
|
| 120 |
+ pr_perror("Failed to read netlink payload, got %d", n);
|
|
| 121 |
+ exit(1); |
|
| 122 |
+ } |
|
| 123 |
+ |
|
| 124 |
+ int start = 0; |
|
| 125 |
+ struct nlattr *attr; |
|
| 126 |
+ while (start < len) {
|
|
| 127 |
+ int payload_len; |
|
| 128 |
+ attr = (struct nlattr *)((void *)data + start); |
|
| 129 |
+ start += NLA_HDRLEN; |
|
| 130 |
+ payload_len = attr->nla_len - NLA_HDRLEN; |
|
| 131 |
+ switch (attr->nla_type) {
|
|
| 132 |
+ case PID_ATTR: |
|
| 133 |
+ pid = (pid_t) readint32(data + start); |
|
| 134 |
+ break; |
|
| 135 |
+ case CONSOLE_PATH_ATTR: |
|
| 136 |
+ consolefd = open((char *)data + start, O_RDWR); |
|
| 137 |
+ if (consolefd < 0) {
|
|
| 138 |
+ pr_perror("Failed to open console %s", (char *)data + start);
|
|
| 139 |
+ exit(1); |
|
| 140 |
+ } |
|
| 141 |
+ break; |
|
| 106 | 142 |
} |
| 143 |
+ start += NLA_ALIGN(payload_len); |
|
| 144 |
+ } |
|
| 145 |
+ |
|
| 146 |
+ // required pid to be passed |
|
| 147 |
+ if (pid == 0) {
|
|
| 148 |
+ pr_perror("missing pid");
|
|
| 149 |
+ exit(1); |
|
| 107 | 150 |
} |
| 108 | 151 |
|
| 109 | 152 |
/* Check that the specified process exists */ |
| ... | ... |
@@ -133,15 +189,13 @@ void nsexec() |
| 133 | 133 |
} |
| 134 | 134 |
|
| 135 | 135 |
/* Skip namespaces we're already part of */ |
| 136 |
- if (fstatat(self_tfd, namespaces[i], &self_st, 0) != -1 && |
|
| 137 |
- st.st_ino == self_st.st_ino) {
|
|
| 136 |
+ if (fstatat(self_tfd, namespaces[i], &self_st, 0) != -1 && st.st_ino == self_st.st_ino) {
|
|
| 138 | 137 |
continue; |
| 139 | 138 |
} |
| 140 | 139 |
|
| 141 | 140 |
fd = openat(tfd, namespaces[i], O_RDONLY); |
| 142 | 141 |
if (fd == -1) {
|
| 143 |
- pr_perror("Failed to open ns file %s for ns %s", buf,
|
|
| 144 |
- namespaces[i]); |
|
| 142 |
+ pr_perror("Failed to open ns file %s for ns %s", buf, namespaces[i]);
|
|
| 145 | 143 |
exit(1); |
| 146 | 144 |
} |
| 147 | 145 |
// Set the namespace. |
| ... | ... |
@@ -80,10 +80,19 @@ func (p Process) Signal(sig os.Signal) error {
|
| 80 | 80 |
|
| 81 | 81 |
// NewConsole creates new console for process and returns it |
| 82 | 82 |
func (p *Process) NewConsole(rootuid int) (Console, error) {
|
| 83 |
- console, err := newConsole(rootuid, rootuid) |
|
| 83 |
+ console, err := NewConsole(rootuid, rootuid) |
|
| 84 | 84 |
if err != nil {
|
| 85 | 85 |
return nil, err |
| 86 | 86 |
} |
| 87 | 87 |
p.consolePath = console.Path() |
| 88 | 88 |
return console, nil |
| 89 | 89 |
} |
| 90 |
+ |
|
| 91 |
+// ConsoleFromPath sets the process's console with the path provided |
|
| 92 |
+func (p *Process) ConsoleFromPath(path string) error {
|
|
| 93 |
+ if p.consolePath != "" {
|
|
| 94 |
+ return newGenericError(fmt.Errorf("console path already exists for process"), ConsoleExists)
|
|
| 95 |
+ } |
|
| 96 |
+ p.consolePath = path |
|
| 97 |
+ return nil |
|
| 98 |
+} |
| ... | ... |
@@ -41,13 +41,14 @@ type parentProcess interface {
|
| 41 | 41 |
} |
| 42 | 42 |
|
| 43 | 43 |
type setnsProcess struct {
|
| 44 |
- cmd *exec.Cmd |
|
| 45 |
- parentPipe *os.File |
|
| 46 |
- childPipe *os.File |
|
| 47 |
- cgroupPaths map[string]string |
|
| 48 |
- config *initConfig |
|
| 49 |
- fds []string |
|
| 50 |
- process *Process |
|
| 44 |
+ cmd *exec.Cmd |
|
| 45 |
+ parentPipe *os.File |
|
| 46 |
+ childPipe *os.File |
|
| 47 |
+ cgroupPaths map[string]string |
|
| 48 |
+ config *initConfig |
|
| 49 |
+ fds []string |
|
| 50 |
+ process *Process |
|
| 51 |
+ bootstrapData io.Reader |
|
| 51 | 52 |
} |
| 52 | 53 |
|
| 53 | 54 |
func (p *setnsProcess) startTime() (string, error) {
|
| ... | ... |
@@ -64,6 +65,16 @@ func (p *setnsProcess) signal(sig os.Signal) error {
|
| 64 | 64 |
|
| 65 | 65 |
func (p *setnsProcess) start() (err error) {
|
| 66 | 66 |
defer p.parentPipe.Close() |
| 67 |
+ err = p.cmd.Start() |
|
| 68 |
+ p.childPipe.Close() |
|
| 69 |
+ if err != nil {
|
|
| 70 |
+ return newSystemError(err) |
|
| 71 |
+ } |
|
| 72 |
+ if p.bootstrapData != nil {
|
|
| 73 |
+ if _, err := io.Copy(p.parentPipe, p.bootstrapData); err != nil {
|
|
| 74 |
+ return newSystemError(err) |
|
| 75 |
+ } |
|
| 76 |
+ } |
|
| 67 | 77 |
if err = p.execSetns(); err != nil {
|
| 68 | 78 |
return newSystemError(err) |
| 69 | 79 |
} |
| ... | ... |
@@ -96,11 +107,6 @@ func (p *setnsProcess) start() (err error) {
|
| 96 | 96 |
// before the go runtime boots, we wait on the process to die and receive the child's pid |
| 97 | 97 |
// over the provided pipe. |
| 98 | 98 |
func (p *setnsProcess) execSetns() error {
|
| 99 |
- err := p.cmd.Start() |
|
| 100 |
- p.childPipe.Close() |
|
| 101 |
- if err != nil {
|
|
| 102 |
- return newSystemError(err) |
|
| 103 |
- } |
|
| 104 | 99 |
status, err := p.cmd.Process.Wait() |
| 105 | 100 |
if err != nil {
|
| 106 | 101 |
p.cmd.Wait() |
| ... | ... |
@@ -10,7 +10,7 @@ Topics listed in the roadmap do not mean that they will be implemented or added |
| 10 | 10 |
|
| 11 | 11 |
### Digest and Hashing |
| 12 | 12 |
|
| 13 |
-A bundle is designed to be moved between hosts. |
|
| 13 |
+A bundle is designed to be moved between hosts. |
|
| 14 | 14 |
Although OCI doesn't define a transport method we should have a cryptographic digest of the on-disk bundle that can be used to verify that a bundle is not corrupted and in an expected configuration. |
| 15 | 15 |
|
| 16 | 16 |
*Owner:* philips |
| ... | ... |
@@ -20,11 +20,11 @@ Although OCI doesn't define a transport method we should have a cryptographic di |
| 20 | 20 |
There are some discussions about having `runtime.json` being optional for containers and specifying defaults. |
| 21 | 21 |
Runtimes would use this standard set of defaults for containers and `runtime.json` would provide overrides for fine tuning of these extra host or platform specific settings. |
| 22 | 22 |
|
| 23 |
-*Owner:* |
|
| 23 |
+*Owner:* |
|
| 24 | 24 |
|
| 25 | 25 |
### Define Container Lifecycle |
| 26 | 26 |
|
| 27 |
-Containers have a lifecycle and being able to identify and document the lifecycle of a container is very helpful for implementations of the spec. |
|
| 27 |
+Containers have a lifecycle and being able to identify and document the lifecycle of a container is very helpful for implementations of the spec. |
|
| 28 | 28 |
The lifecycle events of a container also help identify areas to implement hooks that are portable across various implementations and platforms. |
| 29 | 29 |
|
| 30 | 30 |
*Owner:* mrunalp |
| ... | ... |
@@ -33,27 +33,27 @@ The lifecycle events of a container also help identify areas to implement hooks |
| 33 | 33 |
|
| 34 | 34 |
Define what type of actions a runtime can perform on a container without imposing hardships on authors of platforms that do not support advanced options. |
| 35 | 35 |
|
| 36 |
-*Owner:* |
|
| 36 |
+*Owner:* |
|
| 37 | 37 |
|
| 38 | 38 |
### Clarify rootfs requirement in base spec |
| 39 | 39 |
|
| 40 | 40 |
Is the rootfs needed or should it just be expected in the bundle without having a field in the spec? |
| 41 | 41 |
|
| 42 |
-*Owner:* |
|
| 42 |
+*Owner:* |
|
| 43 | 43 |
|
| 44 | 44 |
### Container Definition |
| 45 | 45 |
|
| 46 | 46 |
Define what a software container is and its attributes in a cross platform way. |
| 47 | 47 |
|
| 48 |
-*Owner:* |
|
| 48 |
+*Owner:* |
|
| 49 | 49 |
|
| 50 | 50 |
### Live Container Updates |
| 51 | 51 |
|
| 52 |
-Should we allow dynamic container updates to runtime options? |
|
| 52 |
+Should we allow dynamic container updates to runtime options? |
|
| 53 | 53 |
|
| 54 | 54 |
*Owner:* vishh |
| 55 | 55 |
|
| 56 |
-### Protobuf Config |
|
| 56 |
+### Protobuf Config |
|
| 57 | 57 |
|
| 58 | 58 |
We currently have only one language binding for the spec and that is Go. |
| 59 | 59 |
If we change the specs format in the respository to be something like protobuf then the generation for multiple language bindings become effortless. |
| ... | ... |
@@ -62,7 +62,7 @@ If we change the specs format in the respository to be something like protobuf t |
| 62 | 62 |
|
| 63 | 63 |
### Validation Tooling |
| 64 | 64 |
|
| 65 |
-Provide validation tooling for compliance with OCI spec and runtime environment. |
|
| 65 |
+Provide validation tooling for compliance with OCI spec and runtime environment. |
|
| 66 | 66 |
|
| 67 | 67 |
*Owner:* mrunalp |
| 68 | 68 |
|
| ... | ... |
@@ -70,27 +70,27 @@ Provide validation tooling for compliance with OCI spec and runtime environment. |
| 70 | 70 |
|
| 71 | 71 |
Decide on a robust versioning schema for the spec as it evolves. |
| 72 | 72 |
|
| 73 |
-*Owner:* |
|
| 73 |
+*Owner:* |
|
| 74 | 74 |
|
| 75 | 75 |
### Printable/Compiled Spec |
| 76 | 76 |
|
| 77 | 77 |
Reguardless of how the spec is written, ensure that it is easy to read and follow for first time users. |
| 78 | 78 |
|
| 79 |
-*Owner:* vbatts |
|
| 79 |
+*Owner:* vbatts |
|
| 80 | 80 |
|
| 81 | 81 |
### Base Config Compatibility |
| 82 | 82 |
|
| 83 | 83 |
Ensure that the base configuration format is viable for various platforms. |
| 84 | 84 |
|
| 85 |
-Systems: |
|
| 85 |
+Systems: |
|
| 86 | 86 |
|
| 87 | 87 |
* Solaris |
| 88 |
-* Windows |
|
| 88 |
+* Windows |
|
| 89 | 89 |
* Linux |
| 90 | 90 |
|
| 91 |
-*Owner:* |
|
| 91 |
+*Owner:* |
|
| 92 | 92 |
|
| 93 | 93 |
### Full Lifecycle Hooks |
| 94 | 94 |
Ensure that we have lifecycle hooks in the correct places with full coverage over the container lifecycle. |
| 95 | 95 |
|
| 96 |
-*Owner:* |
|
| 96 |
+*Owner:* |
| ... | ... |
@@ -30,7 +30,7 @@ The OpenContainers team reserves the right to deny participation any individual |
| 30 | 30 |
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. |
| 31 | 31 |
By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. |
| 32 | 32 |
|
| 33 |
-## Thanks |
|
| 33 |
+## Thanks |
|
| 34 | 34 |
|
| 35 | 35 |
Thanks to the [Fedora Code of Conduct](https://getfedora.org/code-of-conduct) and [Contributor Covenant](http://contributor-covenant.org) for inspiration and ideas. |
| 36 | 36 |
|