Commit 041ab17a authored by Jimmi Dyson's avatar Jimmi Dyson

Bump cadvisor to fix interface stats bugs & improve performance

Includes necessary godep upgrades for docker & systemd packages as well as migrating from docker/libcontainer to opencontainers/runc/libcontainer.
parent 837a070d
......@@ -271,23 +271,28 @@
},
{
"ImportPath": "github.com/coreos/go-systemd/daemon",
"Comment": "v2-27-g97e243d",
"Rev": "97e243d21a8e232e9d8af38ba2366dfcfceebeba"
"Comment": "v4",
"Rev": "b4a58d95188dd092ae20072bac14cece0e67c388"
},
{
"ImportPath": "github.com/coreos/go-systemd/dbus",
"Comment": "v2-27-g97e243d",
"Rev": "97e243d21a8e232e9d8af38ba2366dfcfceebeba"
"Comment": "v4",
"Rev": "b4a58d95188dd092ae20072bac14cece0e67c388"
},
{
"ImportPath": "github.com/coreos/go-systemd/journal",
"Comment": "v2-27-g97e243d",
"Rev": "97e243d21a8e232e9d8af38ba2366dfcfceebeba"
"Comment": "v4",
"Rev": "b4a58d95188dd092ae20072bac14cece0e67c388"
},
{
"ImportPath": "github.com/coreos/go-systemd/unit",
"Comment": "v2-27-g97e243d",
"Rev": "97e243d21a8e232e9d8af38ba2366dfcfceebeba"
"Comment": "v4",
"Rev": "b4a58d95188dd092ae20072bac14cece0e67c388"
},
{
"ImportPath": "github.com/coreos/go-systemd/util",
"Comment": "v4",
"Rev": "b4a58d95188dd092ae20072bac14cece0e67c388"
},
{
"ImportPath": "github.com/coreos/pkg/capnslog",
......@@ -330,43 +335,38 @@
},
{
"ImportPath": "github.com/docker/docker/pkg/jsonmessage",
"Comment": "v1.4.1-4045-g2b27fe1",
"Rev": "2b27fe17a1b3fb8472fde96d768fa70996adf201"
"Comment": "v1.4.1-4831-g0f5c9d3",
"Rev": "0f5c9d301b9b1cca66b3ea0f9dec3b5317d3686d"
},
{
"ImportPath": "github.com/docker/docker/pkg/mount",
"Comment": "v1.4.1-4045-g2b27fe1",
"Rev": "2b27fe17a1b3fb8472fde96d768fa70996adf201"
"Comment": "v1.4.1-4831-g0f5c9d3",
"Rev": "0f5c9d301b9b1cca66b3ea0f9dec3b5317d3686d"
},
{
"ImportPath": "github.com/docker/docker/pkg/parsers",
"Comment": "v1.4.1-4045-g2b27fe1",
"Rev": "2b27fe17a1b3fb8472fde96d768fa70996adf201"
"Comment": "v1.4.1-4831-g0f5c9d3",
"Rev": "0f5c9d301b9b1cca66b3ea0f9dec3b5317d3686d"
},
{
"ImportPath": "github.com/docker/docker/pkg/symlink",
"Comment": "v1.4.1-4045-g2b27fe1",
"Rev": "2b27fe17a1b3fb8472fde96d768fa70996adf201"
"Comment": "v1.4.1-4831-g0f5c9d3",
"Rev": "0f5c9d301b9b1cca66b3ea0f9dec3b5317d3686d"
},
{
"ImportPath": "github.com/docker/docker/pkg/term",
"Comment": "v1.4.1-4045-g2b27fe1",
"Rev": "2b27fe17a1b3fb8472fde96d768fa70996adf201"
"Comment": "v1.4.1-4831-g0f5c9d3",
"Rev": "0f5c9d301b9b1cca66b3ea0f9dec3b5317d3686d"
},
{
"ImportPath": "github.com/docker/docker/pkg/timeutils",
"Comment": "v1.4.1-4045-g2b27fe1",
"Rev": "2b27fe17a1b3fb8472fde96d768fa70996adf201"
"Comment": "v1.4.1-4831-g0f5c9d3",
"Rev": "0f5c9d301b9b1cca66b3ea0f9dec3b5317d3686d"
},
{
"ImportPath": "github.com/docker/docker/pkg/units",
"Comment": "v1.4.1-4045-g2b27fe1",
"Rev": "2b27fe17a1b3fb8472fde96d768fa70996adf201"
},
{
"ImportPath": "github.com/docker/libcontainer",
"Comment": "v2.2.1",
"Rev": "5dc7ba0f24332273461e45bc49edcb4d5aa6c44c"
"Comment": "v1.4.1-4831-g0f5c9d3",
"Rev": "0f5c9d301b9b1cca66b3ea0f9dec3b5317d3686d"
},
{
"ImportPath": "github.com/docker/spdystream",
......@@ -392,6 +392,7 @@
},
{
"ImportPath": "github.com/fsouza/go-dockerclient",
"Comment": "0.2.1-728-g1399676",
"Rev": "1399676f53e6ccf46e0bf00751b21bed329bc60e"
},
{
......@@ -408,8 +409,8 @@
},
{
"ImportPath": "github.com/godbus/dbus",
"Comment": "0-7-g939230d",
"Rev": "939230d2086a4f1870e04c52e0a376c25bae0ec4"
"Comment": "v3",
"Rev": "c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f"
},
{
"ImportPath": "github.com/gogo/protobuf/proto",
......@@ -434,93 +435,93 @@
},
{
"ImportPath": "github.com/google/cadvisor/api",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/cache/memory",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/collector",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/container",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/events",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/fs",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/healthz",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/http",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/info/v1",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/info/v2",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/manager",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/metrics",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/pages",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/storage",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/summary",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/utils",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/validate",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/cadvisor/version",
"Comment": "v0.19.3.2",
"Rev": "a8085bf9276c22f16dbcd7aa56f0d4d0626a0b2e"
"Comment": "v0.20.1",
"Rev": "634965abc45557ed03c268bb193a00cfcbedbd32"
},
{
"ImportPath": "github.com/google/gofuzz",
......@@ -632,6 +633,11 @@
"Rev": "8adf9e1730c55cdc590de7d49766cb2acc88d8f2"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer",
"Comment": "v0.0.5",
"Rev": "97bc9a7faf3dd660d9be90a2880b2e37f3cdbf38"
},
{
"ImportPath": "github.com/pborman/uuid",
"Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4"
},
......@@ -724,6 +730,10 @@
"Rev": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1"
},
{
"ImportPath": "github.com/vishvananda/netlink",
"Rev": "1e2e08e8a2dcdacaae3f14ac44c5cfa31361f270"
},
{
"ImportPath": "github.com/xiang90/probing",
"Rev": "6a0cc1ae81b4cc11db5e491e030e4b98fba79c19"
},
......
/*
Copyright 2013 CoreOS Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Integration with the systemd D-Bus API. See http://www.freedesktop.org/wiki/Software/systemd/dbus/
package dbus
......@@ -64,10 +62,16 @@ func PathBusEscape(path string) string {
// Conn is a connection to systemd's dbus endpoint.
type Conn struct {
sysconn *dbus.Conn
sysobj *dbus.Object
// sysconn/sysobj are only used to call dbus methods
sysconn *dbus.Conn
sysobj dbus.BusObject
// sigconn/sigobj are only used to receive dbus signals
sigconn *dbus.Conn
sigobj dbus.BusObject
jobListener struct {
jobs map[dbus.ObjectPath]chan string
jobs map[dbus.ObjectPath]chan<- string
sync.Mutex
}
subscriber struct {
......@@ -77,26 +81,77 @@ type Conn struct {
ignore map[dbus.ObjectPath]int64
cleanIgnore int64
}
dispatch map[string]func(dbus.Signal)
}
// New() establishes a connection to the system bus and authenticates.
// New establishes a connection to the system bus and authenticates.
// Callers should call Close() when done with the connection.
func New() (*Conn, error) {
c := new(Conn)
return newConnection(func() (*dbus.Conn, error) {
return dbusAuthHelloConnection(dbus.SystemBusPrivate)
})
}
// NewUserConnection establishes a connection to the session bus and
// authenticates. This can be used to connect to systemd user instances.
// Callers should call Close() when done with the connection.
func NewUserConnection() (*Conn, error) {
return newConnection(func() (*dbus.Conn, error) {
return dbusAuthHelloConnection(dbus.SessionBusPrivate)
})
}
if err := c.initConnection(); err != nil {
// NewSystemdConnection establishes a private, direct connection to systemd.
// This can be used for communicating with systemd without a dbus daemon.
// Callers should call Close() when done with the connection.
func NewSystemdConnection() (*Conn, error) {
return newConnection(func() (*dbus.Conn, error) {
// We skip Hello when talking directly to systemd.
return dbusAuthConnection(func() (*dbus.Conn, error) {
return dbus.Dial("unix:path=/run/systemd/private")
})
})
}
// Close closes an established connection
func (c *Conn) Close() {
c.sysconn.Close()
c.sigconn.Close()
}
func newConnection(createBus func() (*dbus.Conn, error)) (*Conn, error) {
sysconn, err := createBus()
if err != nil {
return nil, err
}
sigconn, err := createBus()
if err != nil {
sysconn.Close()
return nil, err
}
c.initJobs()
c := &Conn{
sysconn: sysconn,
sysobj: systemdObject(sysconn),
sigconn: sigconn,
sigobj: systemdObject(sigconn),
}
c.subscriber.ignore = make(map[dbus.ObjectPath]int64)
c.jobListener.jobs = make(map[dbus.ObjectPath]chan<- string)
// Setup the listeners on jobs so that we can get completions
c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
"type='signal', interface='org.freedesktop.systemd1.Manager', member='JobRemoved'")
c.dispatch()
return c, nil
}
func (c *Conn) initConnection() error {
var err error
c.sysconn, err = dbus.SystemBusPrivate()
func dbusAuthConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
conn, err := createBus()
if err != nil {
return err
return nil, err
}
// Only use EXTERNAL method, and hardcode the uid (not username)
......@@ -104,25 +159,29 @@ func (c *Conn) initConnection() error {
// libc)
methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))}
err = c.sysconn.Auth(methods)
err = conn.Auth(methods)
if err != nil {
c.sysconn.Close()
return err
conn.Close()
return nil, err
}
err = c.sysconn.Hello()
return conn, nil
}
func dbusAuthHelloConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
conn, err := dbusAuthConnection(createBus)
if err != nil {
c.sysconn.Close()
return err
return nil, err
}
c.sysobj = c.sysconn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1"))
if err = conn.Hello(); err != nil {
conn.Close()
return nil, err
}
// Setup the listeners on jobs so that we can get completions
c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
"type='signal', interface='org.freedesktop.systemd1.Manager', member='JobRemoved'")
c.initSubscription()
c.initDispatch()
return conn, nil
}
return nil
func systemdObject(conn *dbus.Conn) dbus.BusObject {
return conn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1"))
}
/*
Copyright 2013 CoreOS Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dbus
import (
"errors"
"path"
"strconv"
"github.com/godbus/dbus"
)
func (c *Conn) initJobs() {
c.jobListener.jobs = make(map[dbus.ObjectPath]chan string)
}
func (c *Conn) jobComplete(signal *dbus.Signal) {
var id uint32
var job dbus.ObjectPath
......@@ -41,26 +37,26 @@ func (c *Conn) jobComplete(signal *dbus.Signal) {
c.jobListener.Unlock()
}
func (c *Conn) startJob(job string, args ...interface{}) (<-chan string, error) {
c.jobListener.Lock()
defer c.jobListener.Unlock()
func (c *Conn) startJob(ch chan<- string, job string, args ...interface{}) (int, error) {
if ch != nil {
c.jobListener.Lock()
defer c.jobListener.Unlock()
}
ch := make(chan string, 1)
var path dbus.ObjectPath
err := c.sysobj.Call(job, 0, args...).Store(&path)
var p dbus.ObjectPath
err := c.sysobj.Call(job, 0, args...).Store(&p)
if err != nil {
return nil, err
return 0, err
}
c.jobListener.jobs[path] = ch
return ch, nil
}
func (c *Conn) runJob(job string, args ...interface{}) (string, error) {
respCh, err := c.startJob(job, args...)
if err != nil {
return "", err
if ch != nil {
c.jobListener.jobs[p] = ch
}
return <-respCh, nil
// ignore error since 0 is fine if conversion fails
jobID, _ := strconv.Atoi(path.Base(string(p)))
return jobID, nil
}
// StartUnit enqueues a start job and depending jobs, if any (unless otherwise
......@@ -78,50 +74,58 @@ func (c *Conn) runJob(job string, args ...interface{}) (string, error) {
// requirement dependencies. It is not recommended to make use of the latter
// two options.
//
// Result string: one of done, canceled, timeout, failed, dependency, skipped.
// If the provided channel is non-nil, a result string will be sent to it upon
// job completion: one of done, canceled, timeout, failed, dependency, skipped.
// done indicates successful execution of a job. canceled indicates that a job
// has been canceled before it finished execution. timeout indicates that the
// job timeout was reached. failed indicates that the job failed. dependency
// indicates that a job this job has been depending on failed and the job hence
// has been removed too. skipped indicates that a job was skipped because it
// didn't apply to the units current state.
func (c *Conn) StartUnit(name string, mode string) (string, error) {
return c.runJob("org.freedesktop.systemd1.Manager.StartUnit", name, mode)
//
// If no error occurs, the ID of the underlying systemd job will be returned. There
// does exist the possibility for no error to be returned, but for the returned job
// ID to be 0. In this case, the actual underlying ID is not 0 and this datapoint
// should not be considered authoritative.
//
// If an error does occur, it will be returned to the user alongside a job ID of 0.
func (c *Conn) StartUnit(name string, mode string, ch chan<- string) (int, error) {
return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartUnit", name, mode)
}
// StopUnit is similar to StartUnit but stops the specified unit rather
// than starting it.
func (c *Conn) StopUnit(name string, mode string) (string, error) {
return c.runJob("org.freedesktop.systemd1.Manager.StopUnit", name, mode)
func (c *Conn) StopUnit(name string, mode string, ch chan<- string) (int, error) {
return c.startJob(ch, "org.freedesktop.systemd1.Manager.StopUnit", name, mode)
}
// ReloadUnit reloads a unit. Reloading is done only if the unit is already running and fails otherwise.
func (c *Conn) ReloadUnit(name string, mode string) (string, error) {
return c.runJob("org.freedesktop.systemd1.Manager.ReloadUnit", name, mode)
func (c *Conn) ReloadUnit(name string, mode string, ch chan<- string) (int, error) {
return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadUnit", name, mode)
}
// RestartUnit restarts a service. If a service is restarted that isn't
// running it will be started.
func (c *Conn) RestartUnit(name string, mode string) (string, error) {
return c.runJob("org.freedesktop.systemd1.Manager.RestartUnit", name, mode)
func (c *Conn) RestartUnit(name string, mode string, ch chan<- string) (int, error) {
return c.startJob(ch, "org.freedesktop.systemd1.Manager.RestartUnit", name, mode)
}
// TryRestartUnit is like RestartUnit, except that a service that isn't running
// is not affected by the restart.
func (c *Conn) TryRestartUnit(name string, mode string) (string, error) {
return c.runJob("org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode)
func (c *Conn) TryRestartUnit(name string, mode string, ch chan<- string) (int, error) {
return c.startJob(ch, "org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode)
}
// ReloadOrRestart attempts a reload if the unit supports it and use a restart
// otherwise.
func (c *Conn) ReloadOrRestartUnit(name string, mode string) (string, error) {
return c.runJob("org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode)
func (c *Conn) ReloadOrRestartUnit(name string, mode string, ch chan<- string) (int, error) {
return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode)
}
// ReloadOrTryRestart attempts a reload if the unit supports it and use a "Try"
// flavored restart otherwise.
func (c *Conn) ReloadOrTryRestartUnit(name string, mode string) (string, error) {
return c.runJob("org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode)
func (c *Conn) ReloadOrTryRestartUnit(name string, mode string, ch chan<- string) (int, error) {
return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode)
}
// StartTransientUnit() may be used to create and start a transient unit, which
......@@ -129,8 +133,8 @@ func (c *Conn) ReloadOrTryRestartUnit(name string, mode string) (string, error)
// system is rebooted. name is the unit name including suffix, and must be
// unique. mode is the same as in StartUnit(), properties contains properties
// of the unit.
func (c *Conn) StartTransientUnit(name string, mode string, properties ...Property) (string, error) {
return c.runJob("org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0))
func (c *Conn) StartTransientUnit(name string, mode string, properties []Property, ch chan<- string) (int, error) {
return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0))
}
// KillUnit takes the unit name and a UNIX signal number to send. All of the unit's
......
/*
Copyright 2013 CoreOS Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dbus
......
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dbus
type set struct {
......@@ -17,17 +31,17 @@ func (s *set) Contains(value string) (exists bool) {
return
}
func (s *set) Length() (int) {
func (s *set) Length() int {
return len(s.data)
}
func (s *set) Values() (values []string) {
for val, _ := range s.data {
for val, _ := range s.data {
values = append(values, val)
}
return
}
return
}
func newSet() (*set) {
return &set{make(map[string] bool)}
func newSet() *set {
return &set{make(map[string]bool)}
}
/*
Copyright 2013 CoreOS Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dbus
......@@ -33,12 +31,12 @@ const (
// systemd will automatically stop sending signals so there is no need to
// explicitly call Unsubscribe().
func (c *Conn) Subscribe() error {
c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
"type='signal',interface='org.freedesktop.systemd1.Manager',member='UnitNew'")
c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
"type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'")
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
if err != nil {
return err
}
......@@ -48,7 +46,7 @@ func (c *Conn) Subscribe() error {
// Unsubscribe this connection from systemd dbus events.
func (c *Conn) Unsubscribe() error {
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store()
err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store()
if err != nil {
return err
}
......@@ -56,14 +54,10 @@ func (c *Conn) Unsubscribe() error {
return nil
}
func (c *Conn) initSubscription() {
c.subscriber.ignore = make(map[dbus.ObjectPath]int64)
}
func (c *Conn) initDispatch() {
func (c *Conn) dispatch() {
ch := make(chan *dbus.Signal, signalBuffer)
c.sysconn.Signal(ch)
c.sigconn.Signal(ch)
go func() {
for {
......@@ -72,24 +66,32 @@ func (c *Conn) initDispatch() {
return
}
switch signal.Name {
case "org.freedesktop.systemd1.Manager.JobRemoved":
if signal.Name == "org.freedesktop.systemd1.Manager.JobRemoved" {
c.jobComplete(signal)
}
if c.subscriber.updateCh == nil {
continue
}
var unitPath dbus.ObjectPath
switch signal.Name {
case "org.freedesktop.systemd1.Manager.JobRemoved":
unitName := signal.Body[2].(string)
var unitPath dbus.ObjectPath
c.sysobj.Call("org.freedesktop.systemd1.Manager.GetUnit", 0, unitName).Store(&unitPath)
if unitPath != dbus.ObjectPath("") {
c.sendSubStateUpdate(unitPath)
}
case "org.freedesktop.systemd1.Manager.UnitNew":
c.sendSubStateUpdate(signal.Body[1].(dbus.ObjectPath))
unitPath = signal.Body[1].(dbus.ObjectPath)
case "org.freedesktop.DBus.Properties.PropertiesChanged":
if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" {
// we only care about SubState updates, which are a Unit property
c.sendSubStateUpdate(signal.Path)
unitPath = signal.Path
}
}
if unitPath == dbus.ObjectPath("") {
continue
}
c.sendSubStateUpdate(unitPath)
}
}()
}
......@@ -103,7 +105,7 @@ func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitSt
// SubscribeUnitsCustom is like SubscribeUnits but lets you specify the buffer
// size of the channels, the comparison function for detecting changes and a filter
// function for cutting down on the noise that your channel receives.
func (c *Conn) SubscribeUnitsCustom(interval time.Duration, buffer int, isChanged func(*UnitStatus, *UnitStatus) bool, filterUnit func (string) bool) (<-chan map[string]*UnitStatus, <-chan error) {
func (c *Conn) SubscribeUnitsCustom(interval time.Duration, buffer int, isChanged func(*UnitStatus, *UnitStatus) bool, filterUnit func(string) bool) (<-chan map[string]*UnitStatus, <-chan error) {
old := make(map[string]*UnitStatus)
statusChan := make(chan map[string]*UnitStatus, buffer)
errChan := make(chan error, buffer)
......@@ -176,9 +178,6 @@ func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan
func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) {
c.subscriber.Lock()
defer c.subscriber.Unlock()
if c.subscriber.updateCh == nil {
return
}
if c.shouldIgnore(path) {
return
......
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dbus
import (
......
/*
Copyright 2013 CoreOS Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package journal provides write bindings to the systemd journal
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package journal provides write bindings to the local systemd journal.
// It is implemented in pure Go and connects to the journal directly over its
// unix socket.
//
// To read from the journal, see the "sdjournal" package, which wraps the
// sd-journal a C API.
//
// http://www.freedesktop.org/software/systemd/man/systemd-journald.service.html
package journal
import (
......@@ -55,14 +60,14 @@ func init() {
}
}
// Enabled returns true iff the systemd journal is available for logging
// Enabled returns true if the local systemd journal is available for logging
func Enabled() bool {
return conn != nil
}
// Send a message to the systemd journal. vars is a map of journald fields to
// values. Fields must be composed of uppercase letters, numbers, and
// underscores, but must not start with an underscore. Within these
// Send a message to the local systemd journal. vars is a map of journald
// fields to values. Fields must be composed of uppercase letters, numbers,
// and underscores, but must not start with an underscore. Within these
// restrictions, any arbitrary field name may be used. Some names have special
// significance: see the journalctl documentation
// (http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html)
......@@ -104,6 +109,11 @@ func Send(message string, priority Priority, vars map[string]string) error {
return nil
}
// Print prints a message to the local systemd journal using Send().
func Print(priority Priority, format string, a ...interface{}) error {
return Send(fmt.Sprintf(format, a...), priority, nil)
}
func appendVariable(w io.Writer, name, value string) {
if !validVarName(name) {
journalError("variable name contains invalid character, ignoring")
......
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package unit
import (
......@@ -10,6 +24,23 @@ import (
"unicode"
)
const (
// SYSTEMD_LINE_MAX mimics the maximum line length that systemd can use.
// On typical systemd platforms (i.e. modern Linux), this will most
// commonly be 2048, so let's use that as a sanity check.
// Technically, we should probably pull this at runtime:
// SYSTEMD_LINE_MAX = int(C.sysconf(C.__SC_LINE_MAX))
// but this would introduce an (unfortunate) dependency on cgo
SYSTEMD_LINE_MAX = 2048
// characters that systemd considers indicate a newline
SYSTEMD_NEWLINE = "\r\n"
)
var (
ErrLineTooLong = fmt.Errorf("line too long (max %d bytes)", SYSTEMD_LINE_MAX)
)
// Deserialize parses a systemd unit file into a list of UnitOption objects.
func Deserialize(f io.Reader) (opts []*UnitOption, err error) {
lexer, optchan, errchan := newLexer(f)
......@@ -40,17 +71,34 @@ type lexer struct {
func (l *lexer) lex() {
var err error
defer func() {
close(l.optchan)
close(l.errchan)
}()
next := l.lexNextSection
for next != nil {
if l.buf.Buffered() >= SYSTEMD_LINE_MAX {
// systemd truncates lines longer than LINE_MAX
// https://bugs.freedesktop.org/show_bug.cgi?id=85308
// Rather than allowing this to pass silently, let's
// explicitly gate people from encountering this
line, err := l.buf.Peek(SYSTEMD_LINE_MAX)
if err != nil {
l.errchan <- err
return
}
if bytes.IndexAny(line, SYSTEMD_NEWLINE) == -1 {
l.errchan <- ErrLineTooLong
return
}
}
next, err = next()
if err != nil {
l.errchan <- err
break
return
}
}
close(l.optchan)
close(l.errchan)
}
type lexStep func() (lexStep, error)
......@@ -66,7 +114,7 @@ func (l *lexer) lexSectionName() (lexStep, error) {
func (l *lexer) lexSectionSuffixFunc(section string) lexStep {
return func() (lexStep, error) {
garbage, err := l.toEOL()
garbage, _, err := l.toEOL()
if err != nil {
return nil, err
}
......@@ -83,7 +131,7 @@ func (l *lexer) lexSectionSuffixFunc(section string) lexStep {
func (l *lexer) ignoreLineFunc(next lexStep) lexStep {
return func() (lexStep, error) {
for {
line, err := l.toEOL()
line, _, err := l.toEOL()
if err != nil {
return nil, err
}
......@@ -163,49 +211,64 @@ func (l *lexer) lexOptionNameFunc(section string) lexStep {
}
name := strings.TrimSpace(partial.String())
return l.lexOptionValueFunc(section, name), nil
return l.lexOptionValueFunc(section, name, bytes.Buffer{}), nil
}
}
func (l *lexer) lexOptionValueFunc(section, name string) lexStep {
func (l *lexer) lexOptionValueFunc(section, name string, partial bytes.Buffer) lexStep {
return func() (lexStep, error) {
var partial bytes.Buffer
for {
line, err := l.toEOL()
line, eof, err := l.toEOL()
if err != nil {
return nil, err
}
if len(bytes.TrimSpace(line)) == 0 {
break
}
partial.Write(line)
// lack of continuation means this value has been exhausted
idx := bytes.LastIndex(line, []byte{'\\'})
if idx == -1 || idx != (len(line)-1) {
partial.Write(line)
break
}
partial.Write(line[0:idx])
partial.WriteRune(' ')
if !eof {
partial.WriteRune('\n')
}
return l.lexOptionValueFunc(section, name, partial), nil
}
val := strings.TrimSpace(partial.String())
val := partial.String()
if strings.HasSuffix(val, "\n") {
// A newline was added to the end, so the file didn't end with a backslash.
// => Keep the newline
val = strings.TrimSpace(val) + "\n"
} else {
val = strings.TrimSpace(val)
}
l.optchan <- &UnitOption{Section: section, Name: name, Value: val}
return l.lexNextSectionOrOptionFunc(section), nil
}
}
func (l *lexer) toEOL() ([]byte, error) {
// toEOL reads until the end-of-line or end-of-file.
// Returns (data, EOFfound, error)
func (l *lexer) toEOL() ([]byte, bool, error) {
line, err := l.buf.ReadBytes('\n')
// ignore EOF here since it's roughly equivalent to EOL
if err != nil && err != io.EOF {
return nil, err
return nil, false, err
}
line = bytes.TrimSuffix(line, []byte{'\r'})
line = bytes.TrimSuffix(line, []byte{'\n'})
return line, nil
return line, err == io.EOF, nil
}
func isComment(r rune) bool {
......
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Implements systemd-escape [--unescape] [--path]
package unit
import (
"fmt"
"strconv"
"strings"
)
const (
allowed = `:_.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`
)
// If isPath is true:
// We remove redundant '/'s, the leading '/', and trailing '/'.
// If the result is empty, a '/' is inserted.
//
// We always:
// Replace the following characters with `\x%x`:
// Leading `.`
// `-`, `\`, and anything not in this set: `:-_.\[0-9a-zA-Z]`
// Replace '/' with '-'.
func escape(unescaped string, isPath bool) string {
e := []byte{}
inSlashes := false
start := true
for i := 0; i < len(unescaped); i++ {
c := unescaped[i]
if isPath {
if c == '/' {
inSlashes = true
continue
} else if inSlashes {
inSlashes = false
if !start {
e = append(e, '-')
}
}
}
if c == '/' {
e = append(e, '-')
} else if start && c == '.' || strings.IndexByte(allowed, c) == -1 {
e = append(e, []byte(fmt.Sprintf(`\x%x`, c))...)
} else {
e = append(e, c)
}
start = false
}
if isPath && len(e) == 0 {
e = append(e, '-')
}
return string(e)
}
// If isPath is true:
// We always return a string beginning with '/'.
//
// We always:
// Replace '-' with '/'.
// Replace `\x%x` with the value represented in hex.
func unescape(escaped string, isPath bool) string {
u := []byte{}
for i := 0; i < len(escaped); i++ {
c := escaped[i]
if c == '-' {
c = '/'
} else if c == '\\' && len(escaped)-i >= 4 && escaped[i+1] == 'x' {
n, err := strconv.ParseInt(escaped[i+2:i+4], 16, 8)
if err == nil {
c = byte(n)
i += 3
}
}
u = append(u, c)
}
if isPath && (len(u) == 0 || u[0] != '/') {
u = append([]byte("/"), u...)
}
return string(u)
}
// UnitNameEscape escapes a string as `systemd-escape` would
func UnitNameEscape(unescaped string) string {
return escape(unescaped, false)
}
// UnitNameUnescape unescapes a string as `systemd-escape --unescape` would
func UnitNameUnescape(escaped string) string {
return unescape(escaped, false)
}
// UnitNamePathEscape escapes a string as `systemd-escape --path` would
func UnitNamePathEscape(unescaped string) string {
return escape(unescaped, true)
}
// UnitNamePathUnescape unescapes a string as `systemd-escape --path --unescape` would
func UnitNamePathUnescape(escaped string) string {
return unescape(escaped, true)
}
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package unit
import (
......@@ -10,6 +24,10 @@ type UnitOption struct {
Value string
}
func NewUnitOption(section, name, value string) *UnitOption {
return &UnitOption{Section: section, Name: name, Value: value}
}
func (uo *UnitOption) String() string {
return fmt.Sprintf("{Section: %q, Name: %q, Value: %q}", uo.Section, uo.Name, uo.Value)
}
......
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package unit
import (
......@@ -5,7 +19,9 @@ import (
"io"
)
// Serialize encodes all of the given UnitOption objects into a unit file
// Serialize encodes all of the given UnitOption objects into a
// unit file. When serialized the options are sorted in their
// supplied order but grouped by section.
func Serialize(opts []*UnitOption) io.Reader {
var buf bytes.Buffer
......@@ -13,22 +29,30 @@ func Serialize(opts []*UnitOption) io.Reader {
return &buf
}
curSection := opts[0].Section
writeSectionHeader(&buf, curSection)
writeNewline(&buf)
// Index of sections -> ordered options
idx := map[string][]*UnitOption{}
// Separately preserve order in which sections were seen
sections := []string{}
for _, opt := range opts {
if opt.Section != curSection {
curSection = opt.Section
sec := opt.Section
if _, ok := idx[sec]; !ok {
sections = append(sections, sec)
}
idx[sec] = append(idx[sec], opt)
}
for i, sect := range sections {
writeSectionHeader(&buf, sect)
writeNewline(&buf)
opts := idx[sect]
for _, opt := range opts {
writeOption(&buf, opt)
writeNewline(&buf)
writeSectionHeader(&buf, curSection)
}
if i < len(sections)-1 {
writeNewline(&buf)
}
writeOption(&buf, opt)
writeNewline(&buf)
}
return &buf
......
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package util contains utility functions related to systemd that applications
// can use to check things like whether systemd is running.
package util
import (
"os"
)
// IsRunningSystemd checks whether the host was booted with systemd as its init
// system. This functions similar to systemd's `sd_booted(3)`: internally, it
// checks whether /run/systemd/system/ exists and is a directory.
// http://www.freedesktop.org/software/systemd/man/sd_booted.html
func IsRunningSystemd() bool {
fi, err := os.Lstat("/run/systemd/system")
if err != nil {
return false
}
return fi.IsDir()
}
......@@ -5,7 +5,7 @@ import (
)
// GetMounts retrieves a list of mounts for the current running process.
func GetMounts() ([]*MountInfo, error) {
func GetMounts() ([]*Info, error) {
return parseMountTable()
}
......
package mount
// MountInfo reveals information about a particular mounted filesystem. This
// Info reveals information about a particular mounted filesystem. This
// struct is populated from the content in the /proc/<pid>/mountinfo file.
type MountInfo struct {
// Id is a unique identifier of the mount (may be reused after umount).
Id int
type Info struct {
// ID is a unique identifier of the mount (may be reused after umount).
ID int
// Parent indicates the ID of the mount parent (or of self for the top of the
// mount tree).
......
......@@ -15,7 +15,7 @@ import (
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
// bind mounts.
func parseMountTable() ([]*MountInfo, error) {
func parseMountTable() ([]*Info, error) {
var rawEntries *C.struct_statfs
count := int(C.getmntinfo(&rawEntries, C.MNT_WAIT))
......@@ -29,9 +29,9 @@ func parseMountTable() ([]*MountInfo, error) {
header.Len = count
header.Data = uintptr(unsafe.Pointer(rawEntries))
var out []*MountInfo
var out []*Info
for _, entry := range entries {
var mountinfo MountInfo
var mountinfo Info
mountinfo.Mountpoint = C.GoString(&entry.f_mntonname[0])
mountinfo.Source = C.GoString(&entry.f_mntfromname[0])
mountinfo.Fstype = C.GoString(&entry.f_fstypename[0])
......
......@@ -30,7 +30,7 @@ const (
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
// bind mounts
func parseMountTable() ([]*MountInfo, error) {
func parseMountTable() ([]*Info, error) {
f, err := os.Open("/proc/self/mountinfo")
if err != nil {
return nil, err
......@@ -40,10 +40,10 @@ func parseMountTable() ([]*MountInfo, error) {
return parseInfoFile(f)
}
func parseInfoFile(r io.Reader) ([]*MountInfo, error) {
func parseInfoFile(r io.Reader) ([]*Info, error) {
var (
s = bufio.NewScanner(r)
out = []*MountInfo{}
out = []*Info{}
)
for s.Scan() {
......@@ -52,13 +52,13 @@ func parseInfoFile(r io.Reader) ([]*MountInfo, error) {
}
var (
p = &MountInfo{}
p = &Info{}
text = s.Text()
optionalFields string
)
if _, err := fmt.Sscanf(text, mountinfoFormat,
&p.Id, &p.Parent, &p.Major, &p.Minor,
&p.ID, &p.Parent, &p.Major, &p.Minor,
&p.Root, &p.Mountpoint, &p.Opts, &optionalFields); err != nil {
return nil, fmt.Errorf("Scanning '%s' failed: %s", text, err)
}
......@@ -84,7 +84,7 @@ func parseInfoFile(r io.Reader) ([]*MountInfo, error) {
// PidMountInfo collects the mounts for a specific process ID. If the process
// ID is unknown, it is better to use `GetMounts` which will inspect
// "/proc/self/mountinfo" instead.
func PidMountInfo(pid int) ([]*MountInfo, error) {
func PidMountInfo(pid int) ([]*Info, error) {
f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
if err != nil {
return nil, err
......
......@@ -7,6 +7,6 @@ import (
"runtime"
)
func parseMountTable() ([]*MountInfo, error) {
func parseMountTable() ([]*Info, error) {
return nil, fmt.Errorf("mount.parseMountTable is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}
......@@ -2,6 +2,8 @@ package parsers
import (
"fmt"
"net/url"
"path"
"runtime"
"strconv"
"strings"
......@@ -52,7 +54,11 @@ func ParseTCPAddr(addr string, defaultAddr string) (string, error) {
return "", fmt.Errorf("Invalid proto, expected tcp: %s", addr)
}
hostParts := strings.Split(addr, ":")
u, err := url.Parse("tcp://" + addr)
if err != nil {
return "", err
}
hostParts := strings.Split(u.Host, ":")
if len(hostParts) != 2 {
return "", fmt.Errorf("Invalid bind address format: %s", addr)
}
......@@ -65,7 +71,7 @@ func ParseTCPAddr(addr string, defaultAddr string) (string, error) {
if err != nil && p == 0 {
return "", fmt.Errorf("Invalid bind address format: %s", addr)
}
return fmt.Sprintf("tcp://%s:%d", host, p), nil
return fmt.Sprintf("tcp://%s:%d%s", host, p, u.Path), nil
}
// Get a repos name and returns the right reposName + tag|digest
......@@ -153,5 +159,12 @@ func ParseLink(val string) (string, string, error) {
if len(arr) == 1 {
return val, val, nil
}
// This is kept because we can actually get an HostConfig with links
// from an already created container and the format is not `foo:bar`
// but `/foo:/c1/bar`
if strings.HasPrefix(arr[0], "/") {
_, alias := path.Split(arr[1])
return arr[0][1:], alias, nil
}
return arr[0], arr[1], nil
}
Copyright (c) 2009 The oauth2 Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
......@@ -51,7 +51,7 @@ func CustomSize(format string, size float64, base float64, _map []string) string
// HumanSize returns a human-readable approximation of a size
// using SI standard (eg. "44kB", "17MB")
func HumanSize(size float64) string {
return CustomSize("%.4g %s", float64(size), 1000.0, decimapAbbrs)
return CustomSize("%.4g %s", size, 1000.0, decimapAbbrs)
}
func BytesSize(size float64) string {
......
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
Copyright 2012 SocialCode
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
The MIT License (MIT)
Copyright (c) 2014 The AUTHORS
Copyright (c) 2013 Armon Dadgar
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
......
......@@ -176,7 +176,7 @@
END OF TERMS AND CONDITIONS
Copyright 2014 Docker, Inc.
Copyright 2014-2015 Docker, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......
Go support for Protocol Buffers - Google's data interchange format
Copyright 2010 The Go Authors. All rights reserved.
https://github.com/golang/protobuf
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The MIT License (MIT)
Copyright (c) 2014 Simon Eskildsen
Copyright (c) 2014 Yasuhiro Matsumoto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
......@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
The MIT License (MIT)
Copyright (c) 2015 Microsoft
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
The MIT License (MIT)
Copyright (c) 2013 npipe authors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
Copyright 2013 Suryandaru Triandana <syndtr@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# The libcontainer Contributors' Guide
Want to hack on libcontainer? Awesome! Here are instructions to get you
started. They are probably not perfect, please let us know if anything
feels wrong or incomplete.
## Reporting Issues
When reporting [issues](https://github.com/docker/libcontainer/issues)
on GitHub please include your host OS (Ubuntu 12.04, Fedora 19, etc),
the output of `uname -a`. Please include the steps required to reproduce
the problem if possible and applicable.
This information will help us review and fix your issue faster.
## Development Environment
### Requirements
For best results, use a Linux development environment.
The following packages are required to compile libcontainer natively.
- Golang 1.3
- GCC
- git
- cgutils
You can develop on OSX, but you are limited to Dockerfile-based builds only.
### Building libcontainer from Dockerfile
make all
This is the easiest way of building libcontainer.
As this build is done using Docker, you can even run this from [OSX](https://github.com/boot2docker/boot2docker)
### Testing changes with "nsinit"
make sh
This will create an container that runs `nsinit exec sh` on a busybox rootfs with the configuration from ['minimal.json'](https://github.com/docker/libcontainer/blob/master/sample_configs/minimal.json).
Like the previous command, you can run this on OSX too!
### Building libcontainer directly
> Note: You should add the `vendor` directory to your GOPATH to use the vendored libraries
./update-vendor.sh
go get -d ./...
make direct-build
# Run the tests
make direct-test-short | egrep --color 'FAIL|$'
# Run all the test
make direct-test | egrep --color 'FAIL|$'
### Testing Changes with "nsinit" directly
To test a change:
# Install nsinit
make direct-install
# Optional, add a docker0 bridge
ip link add docker0 type bridge
ifconfig docker0 172.17.0.1/16 up
mkdir testfs
curl -sSL https://github.com/jpetazzo/docker-busybox/raw/buildroot-2014.02/rootfs.tar | tar -xC testfs
cd testfs
cp <your-sample-config.json> container.json
nsinit exec sh
## Contribution Guidelines
### Pull requests are always welcome
We are always thrilled to receive pull requests, and do our best to
process them as fast as possible. Not sure if that typo is worth a pull
request? Do it! We will appreciate it.
If your pull request is not accepted on the first try, don't be
discouraged! If there's a problem with the implementation, hopefully you
received feedback on what to improve.
We're trying very hard to keep libcontainer lean and focused. We don't want it
to do everything for everybody. This means that we might decide against
incorporating a new feature. However, there might be a way to implement
that feature *on top of* libcontainer.
### Discuss your design on the mailing list
We recommend discussing your plans [on the mailing
list](https://groups.google.com/forum/?fromgroups#!forum/libcontainer)
before starting to code - especially for more ambitious contributions.
This gives other contributors a chance to point you in the right
direction, give feedback on your design, and maybe point out if someone
else is working on the same thing.
### Create issues...
Any significant improvement should be documented as [a GitHub
issue](https://github.com/docker/libcontainer/issues) before anybody
starts working on it.
### ...but check for existing issues first!
Please take a moment to check that an issue doesn't already exist
documenting your bug report or improvement proposal. If it does, it
never hurts to add a quick "+1" or "I have this problem too". This will
help prioritize the most common problems and requests.
### Conventions
Fork the repo and make changes on your fork in a feature branch:
- If it's a bugfix branch, name it XXX-something where XXX is the number of the
issue
- If it's a feature branch, create an enhancement issue to announce your
intentions, and name it XXX-something where XXX is the number of the issue.
Submit unit tests for your changes. Go has a great test framework built in; use
it! Take a look at existing tests for inspiration. Run the full test suite on
your branch before submitting a pull request.
Update the documentation when creating or modifying features. Test
your documentation changes for clarity, concision, and correctness, as
well as a clean documentation build. See ``docs/README.md`` for more
information on building the docs and how docs get released.
Write clean code. Universally formatted code promotes ease of writing, reading,
and maintenance. Always run `gofmt -s -w file.go` on each changed file before
committing your changes. Most editors have plugins that do this automatically.
Pull requests descriptions should be as clear as possible and include a
reference to all the issues that they address.
Pull requests must not contain commits from other users or branches.
Commit messages must start with a capitalized and short summary (max. 50
chars) written in the imperative, followed by an optional, more detailed
explanatory text which is separated from the summary by an empty line.
Code review comments may be added to your pull request. Discuss, then make the
suggested modifications and push additional commits to your feature branch. Be
sure to post a comment after pushing. The new commits will show up in the pull
request automatically, but the reviewers will not be notified unless you
comment.
Before the pull request is merged, make sure that you squash your commits into
logical units of work using `git rebase -i` and `git push -f`. After every
commit the test suite should be passing. Include documentation changes in the
same commit so that a revert would remove all traces of the feature or fix.
Commits that fix or close an issue should include a reference like `Closes #XXX`
or `Fixes #XXX`, which will automatically close the issue when merged.
### Testing
Make sure you include suitable tests, preferably unit tests, in your pull request
and that all the tests pass.
*Instructions for running tests to be added.*
### Merge approval
libcontainer maintainers use LGTM (looks good to me) in comments on the code review
to indicate acceptance.
A change requires LGTMs from at lease two maintainers. One of those must come from
a maintainer of the component affected. For example, if a change affects `netlink/`
and `security`, it needs at least one LGTM from a maintainer of each. Maintainers
only need one LGTM as presumably they LGTM their own change.
For more details see [MAINTAINERS.md](MAINTAINERS.md)
### Sign your work
The sign-off is a simple line at the end of the explanation for the
patch, which certifies that you wrote it or otherwise have the right to
pass it on as an open-source patch. The rules are pretty simple: if you
can certify the below (from
[developercertificate.org](http://developercertificate.org/)):
```
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
```
then you just add a line to every git commit message:
Docker-DCO-1.1-Signed-off-by: Joe Smith <joe.smith@email.com> (github: github_handle)
using your real name (sorry, no pseudonyms or anonymous contributions.)
One way to automate this, is customise your get ``commit.template`` by adding
a ``prepare-commit-msg`` hook to your libcontainer checkout:
```
curl -o .git/hooks/prepare-commit-msg https://raw.githubusercontent.com/docker/docker/master/contrib/prepare-commit-msg.hook && chmod +x .git/hooks/prepare-commit-msg
```
* Note: the above script expects to find your GitHub user name in ``git config --get github.user``
#### Small patch exception
There are several exceptions to the signing requirement. Currently these are:
* Your patch fixes spelling or grammar errors.
* Your patch is a single line change to documentation contained in the
`docs` directory.
* Your patch fixes Markdown formatting or syntax errors in the
documentation contained in the `docs` directory.
If you have any questions, please refer to the FAQ in the [docs](to be written)
### How can I become a maintainer?
* Step 1: learn the component inside out
* Step 2: make yourself useful by contributing code, bugfixes, support etc.
* Step 3: volunteer on the irc channel (#libcontainer@freenode)
Don't forget: being a maintainer is a time investment. Make sure you will have time to make yourself available.
You don't have to be a maintainer to make a difference on the project!
FROM golang:1.4
RUN echo "deb http://ftp.us.debian.org/debian testing main contrib" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y iptables criu=1.5.2-1 && rm -rf /var/lib/apt/lists/*
RUN go get golang.org/x/tools/cmd/cover
ENV GOPATH $GOPATH:/go/src/github.com/docker/libcontainer/vendor
RUN go get github.com/docker/docker/pkg/term
# setup a playground for us to spawn containers in
RUN mkdir /busybox && \
curl -sSL 'https://github.com/jpetazzo/docker-busybox/raw/buildroot-2014.11/rootfs.tar' | tar -xC /busybox
RUN curl -sSL https://raw.githubusercontent.com/docker/docker/master/hack/dind -o /dind && \
chmod +x /dind
COPY . /go/src/github.com/docker/libcontainer
WORKDIR /go/src/github.com/docker/libcontainer
RUN cp sample_configs/minimal.json /busybox/container.json
RUN make direct-install
ENTRYPOINT ["/dind"]
CMD ["make", "direct-test"]
Michael Crosby <michael@docker.com> (@crosbymichael)
Rohit Jnagal <jnagal@google.com> (@rjnagal)
Victor Marmol <vmarmol@google.com> (@vmarmol)
Mrunal Patel <mpatel@redhat.com> (@mrunalp)
Alexandr Morozov <lk4d4@docker.com> (@LK4D4)
Daniel, Dao Quang Minh <dqminh89@gmail.com> (@dqminh)
update-vendor.sh: Tianon Gravi <admwiggin@gmail.com> (@tianon)
# The libcontainer Maintainers' Guide
## Introduction
Dear maintainer. Thank you for investing the time and energy to help
make libcontainer as useful as possible. Maintaining a project is difficult,
sometimes unrewarding work. Sure, you will get to contribute cool
features to the project. But most of your time will be spent reviewing,
cleaning up, documenting, answering questions, justifying design
decisions - while everyone has all the fun! But remember - the quality
of the maintainers work is what distinguishes the good projects from the
great. So please be proud of your work, even the unglamourous parts,
and encourage a culture of appreciation and respect for *every* aspect
of improving the project - not just the hot new features.
This document is a manual for maintainers old and new. It explains what
is expected of maintainers, how they should work, and what tools are
available to them.
This is a living document - if you see something out of date or missing,
speak up!
## What are a maintainer's responsibility?
It is every maintainer's responsibility to:
* 1) Expose a clear roadmap for improving their component.
* 2) Deliver prompt feedback and decisions on pull requests.
* 3) Be available to anyone with questions, bug reports, criticism etc.
on their component. This includes IRC, GitHub requests and the mailing
list.
* 4) Make sure their component respects the philosophy, design and
roadmap of the project.
## How are decisions made?
Short answer: with pull requests to the libcontainer repository.
libcontainer is an open-source project with an open design philosophy. This
means that the repository is the source of truth for EVERY aspect of the
project, including its philosophy, design, roadmap and APIs. *If it's
part of the project, it's in the repo. It's in the repo, it's part of
the project.*
As a result, all decisions can be expressed as changes to the
repository. An implementation change is a change to the source code. An
API change is a change to the API specification. A philosophy change is
a change to the philosophy manifesto. And so on.
All decisions affecting libcontainer, big and small, follow the same 3 steps:
* Step 1: Open a pull request. Anyone can do this.
* Step 2: Discuss the pull request. Anyone can do this.
* Step 3: Accept (`LGTM`) or refuse a pull request. The relevant maintainers do
this (see below "Who decides what?")
## Who decides what?
All decisions are pull requests, and the relevant maintainers make
decisions by accepting or refusing the pull request. Review and acceptance
by anyone is denoted by adding a comment in the pull request: `LGTM`.
However, only currently listed `MAINTAINERS` are counted towards the required
two LGTMs.
libcontainer follows the timeless, highly efficient and totally unfair system
known as [Benevolent dictator for life](http://en.wikipedia.org/wiki/Benevolent_Dictator_for_Life), with Michael Crosby in the role of BDFL.
This means that all decisions are made by default by Michael. Since making
every decision himself would be highly un-scalable, in practice decisions
are spread across multiple maintainers.
The relevant maintainers for a pull request can be worked out in two steps:
* Step 1: Determine the subdirectories affected by the pull request. This
might be `netlink/` and `security/`, or any other part of the repo.
* Step 2: Find the `MAINTAINERS` file which affects this directory. If the
directory itself does not have a `MAINTAINERS` file, work your way up
the repo hierarchy until you find one.
### I'm a maintainer, and I'm going on holiday
Please let your co-maintainers and other contributors know by raising a pull
request that comments out your `MAINTAINERS` file entry using a `#`.
### I'm a maintainer, should I make pull requests too?
Yes. Nobody should ever push to master directly. All changes should be
made through a pull request.
### Who assigns maintainers?
Michael has final `LGTM` approval for all pull requests to `MAINTAINERS` files.
### How is this process changed?
Just like everything else: by making a pull request :)
all:
docker build -t dockercore/libcontainer .
test:
# we need NET_ADMIN for the netlink tests and SYS_ADMIN for mounting
docker run --rm -it --privileged dockercore/libcontainer
sh:
docker run --rm -it --privileged -w /busybox dockercore/libcontainer nsinit exec sh
GO_PACKAGES = $(shell find . -not \( -wholename ./vendor -prune -o -wholename ./.git -prune \) -name '*.go' -print0 | xargs -0n1 dirname | sort -u)
direct-test:
go test $(TEST_TAGS) -cover -v $(GO_PACKAGES)
direct-test-short:
go test $(TEST_TAGS) -cover -test.short -v $(GO_PACKAGES)
direct-build:
go build -v $(GO_PACKAGES)
direct-install:
go install -v $(GO_PACKAGES)
local:
go test -v
validate:
hack/validate.sh
binary: all
docker run --rm --privileged -v $(CURDIR)/bundles:/go/bin dockercore/libcontainer make direct-install
libcontainer
Copyright 2012-2015 Docker, Inc.
This product includes software developed at Docker, Inc. (http://www.docker.com).
The following is courtesy of our legal counsel:
Use and transfer of Docker may be subject to certain restrictions by the
United States and other governments.
It is your responsibility to ensure that your use and/or transfer does not
violate applicable laws.
For more information, please see http://www.bis.doc.gov
See also http://www.apache.org/dev/crypto.html and/or seek legal counsel.
# libcontainer Principles
In the design and development of libcontainer we try to follow these principles:
(Work in progress)
* Don't try to replace every tool. Instead, be an ingredient to improve them.
* Less code is better.
* Fewer components are better. Do you really need to add one more class?
* 50 lines of straightforward, readable code is better than 10 lines of magic that nobody can understand.
* Don't do later what you can do now. "//TODO: refactor" is not acceptable in new code.
* When hesitating between two options, choose the one that is easier to reverse.
* "No" is temporary; "Yes" is forever. If you're not sure about a new feature, say no. You can change your mind later.
* Containers must be portable to the greatest possible number of machines. Be suspicious of any change which makes machines less interchangeable.
* The fewer moving parts in a container, the better.
* Don't merge it unless you document it.
* Don't document it unless you can keep it up-to-date.
* Don't merge it unless you test it!
* Everyone's problem is slightly different. Focus on the part that is the same for everyone, and solve that.
# libcontainer: what's next?
This document is a high-level overview of where we want to take libcontainer next.
It is a curated selection of planned improvements which are either important, difficult, or both.
For a more complete view of planned and requested improvements, see [the Github issues](https://github.com/docker/libcontainer/issues).
To suggest changes to the roadmap, including additions, please write the change as if it were already in effect, and make a pull request.
## Broader kernel support
Our goal is to make libcontainer run everywhere, but currently libcontainer requires Linux version 3.8 or higher. If you’re deploying new machines for the purpose of running libcontainer, this is a fairly easy requirement to meet. However, if you’re adding libcontainer to an existing deployment, you may not have the flexibility to update and patch the kernel.
## Cross-architecture support
Our goal is to make libcontainer run everywhere. Recently libcontainer has
expanded from its initial support for x86_64 systems to include POWER (ppc64
little and big endian variants), IBM System z (s390x 64-bit), and ARM. We plan
to continue expanding architecture support such that libcontainer containers
can be created and used on more architectures.
// +build linux
package apparmor
import (
"io"
"os"
"text/template"
)
type data struct {
Name string
Imports []string
InnerImports []string
}
const baseTemplate = `
{{range $value := .Imports}}
{{$value}}
{{end}}
profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
{{range $value := .InnerImports}}
{{$value}}
{{end}}
network,
capability,
file,
umount,
deny @{PROC}/sys/fs/** wklx,
deny @{PROC}/sysrq-trigger rwklx,
deny @{PROC}/mem rwklx,
deny @{PROC}/kmem rwklx,
deny @{PROC}/sys/kernel/[^s][^h][^m]* wklx,
deny @{PROC}/sys/kernel/*/** wklx,
deny mount,
deny /sys/[^f]*/** wklx,
deny /sys/f[^s]*/** wklx,
deny /sys/fs/[^c]*/** wklx,
deny /sys/fs/c[^g]*/** wklx,
deny /sys/fs/cg[^r]*/** wklx,
deny /sys/firmware/efi/efivars/** rwklx,
deny /sys/kernel/security/** rwklx,
}
`
func generateProfile(out io.Writer) error {
compiled, err := template.New("apparmor_profile").Parse(baseTemplate)
if err != nil {
return err
}
data := &data{
Name: "docker-default",
}
if tunablesExists() {
data.Imports = append(data.Imports, "#include <tunables/global>")
} else {
data.Imports = append(data.Imports, "@{PROC}=/proc/")
}
if abstractionsExists() {
data.InnerImports = append(data.InnerImports, "#include <abstractions/base>")
}
if err := compiled.Execute(out, data); err != nil {
return err
}
return nil
}
// check if the tunables/global exist
func tunablesExists() bool {
_, err := os.Stat("/etc/apparmor.d/tunables/global")
return err == nil
}
// check if abstractions/base exist
func abstractionsExists() bool {
_, err := os.Stat("/etc/apparmor.d/abstractions/base")
return err == nil
}
// +build linux
package apparmor
import (
"fmt"
"os"
"os/exec"
"path"
)
const (
DefaultProfilePath = "/etc/apparmor.d/docker"
)
func InstallDefaultProfile() error {
if !IsEnabled() {
return nil
}
// Make sure /etc/apparmor.d exists
if err := os.MkdirAll(path.Dir(DefaultProfilePath), 0755); err != nil {
return err
}
f, err := os.OpenFile(DefaultProfilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return err
}
if err := generateProfile(f); err != nil {
f.Close()
return err
}
f.Close()
cmd := exec.Command("/sbin/apparmor_parser", "-r", "-W", "docker")
// to use the parser directly we have to make sure we are in the correct
// dir with the profile
cmd.Dir = "/etc/apparmor.d"
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("Error loading docker apparmor profile: %s (%s)", err, output)
}
return nil
}
package devices
import (
"github.com/docker/libcontainer/configs"
)
// TODO Windows. This can be factored out further - Devices are not supported
// by Windows Containers.
func DeviceFromPath(path, permissions string) (*configs.Device, error) {
return nil, nil
}
func HostDevices() ([]*configs.Device, error) {
return nil, nil
}
% nsinit User Manual
% docker/libcontainer
% JAN 2015
NAME:
nsinit - A low-level utility for managing containers.
It is used to spawn new containers or join existing containers.
USAGE:
nsinit [global options] command [command options] [arguments...]
VERSION:
0.1
COMMANDS:
config display the container configuration
exec execute a new command inside a container
init runs the init process inside the namespace
oom display oom notifications for a container
pause pause the container's processes
stats display statistics for the container
unpause unpause the container's processes
help, h shows a list of commands or help for one command
EXAMPLES:
Get the <container_id> of an already running docker container.
`sudo docker ps` will return the list of all the running containers.
take the <container_id> (e.g. 4addb0b2d307) and go to its config directory
`/var/lib/docker/execdriver/native/4addb0b2d307` and here you can run the nsinit
command line utility.
e.g. `nsinit exec /bin/bash` will start a shell on the already running container.
# HISTORY
Jan 2015, Originally compiled by Shishir Mahajan (shishir dot mahajan at redhat dot com)
based on nsinit source material and internal work.
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
Guillaume J. Charmes <guillaume@docker.com> (@creack)
// Packet netlink provide access to low level Netlink sockets and messages.
//
// Actual implementations are in:
// netlink_linux.go
// netlink_darwin.go
package netlink
import (
"errors"
"net"
)
var (
ErrWrongSockType = errors.New("Wrong socket type")
ErrShortResponse = errors.New("Got short response from netlink")
ErrInterfaceExists = errors.New("Network interface already exists")
)
// A Route is a subnet associated with the interface to reach it.
type Route struct {
*net.IPNet
Iface *net.Interface
Default bool
}
// An IfAddr defines IP network settings for a given network interface
type IfAddr struct {
Iface *net.Interface
IP net.IP
IPNet *net.IPNet
}
// +build arm ppc64 ppc64le
package netlink
func ifrDataByte(b byte) uint8 {
return uint8(b)
}
// +build !arm,!ppc64,!ppc64le
package netlink
func ifrDataByte(b byte) int8 {
return int8(b)
}
## 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.
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)
}
},
}
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)
}
}
}
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")
},
}
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)
}
}
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")
}
},
}
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)
}
},
}
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)))
},
}
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,
},
},
},
},
},
}
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)
},
}
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)
},
}
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)
}
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)
}
These configuration files can be used with `nsinit` to quickly develop, test,
and experiment with features of libcontainer.
When consuming these configuration files, copy them into your rootfs and rename
the file to `container.json` for use with `nsinit`.
{
"no_pivot_root": false,
"parent_death_signal": 0,
"pivot_dir": "",
"rootfs": "/rootfs/jessie",
"readonlyfs": false,
"mounts": [
{
"source": "shm",
"destination": "/dev/shm",
"device": "tmpfs",
"flags": 14,
"data": "mode=1777,size=65536k",
"relabel": ""
},
{
"source": "mqueue",
"destination": "/dev/mqueue",
"device": "mqueue",
"flags": 14,
"data": "",
"relabel": ""
},
{
"source": "sysfs",
"destination": "/sys",
"device": "sysfs",
"flags": 15,
"data": "",
"relabel": ""
}
],
"devices": [
{
"type": 99,
"path": "/dev/fuse",
"major": 10,
"minor": 229,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/null",
"major": 1,
"minor": 3,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/zero",
"major": 1,
"minor": 5,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/full",
"major": 1,
"minor": 7,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/tty",
"major": 5,
"minor": 0,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/urandom",
"major": 1,
"minor": 9,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/random",
"major": 1,
"minor": 8,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
}
],
"mount_label": "",
"hostname": "nsinit",
"namespaces": [
{
"type": "NEWNS",
"path": ""
},
{
"type": "NEWUTS",
"path": ""
},
{
"type": "NEWIPC",
"path": ""
},
{
"type": "NEWPID",
"path": ""
},
{
"type": "NEWNET",
"path": ""
}
],
"capabilities": [
"CHOWN",
"DAC_OVERRIDE",
"FSETID",
"FOWNER",
"MKNOD",
"NET_RAW",
"SETGID",
"SETUID",
"SETFCAP",
"SETPCAP",
"NET_BIND_SERVICE",
"SYS_CHROOT",
"KILL",
"AUDIT_WRITE"
],
"networks": [
{
"type": "loopback",
"name": "",
"bridge": "",
"mac_address": "",
"address": "127.0.0.1/0",
"gateway": "localhost",
"ipv6_address": "",
"ipv6_gateway": "",
"mtu": 0,
"txqueuelen": 0,
"host_interface_name": ""
}
],
"routes": null,
"cgroups": {
"name": "libcontainer",
"parent": "nsinit",
"allow_all_devices": false,
"allowed_devices": [
{
"type": 99,
"path": "",
"major": -1,
"minor": -1,
"permissions": "m",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 98,
"path": "",
"major": -1,
"minor": -1,
"permissions": "m",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/console",
"major": 5,
"minor": 1,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/tty0",
"major": 4,
"minor": 0,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/tty1",
"major": 4,
"minor": 1,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "",
"major": 136,
"minor": -1,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "",
"major": 5,
"minor": 2,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "",
"major": 10,
"minor": 200,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/null",
"major": 1,
"minor": 3,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/zero",
"major": 1,
"minor": 5,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/full",
"major": 1,
"minor": 7,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/tty",
"major": 5,
"minor": 0,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/urandom",
"major": 1,
"minor": 9,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/random",
"major": 1,
"minor": 8,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
}
],
"memory": 0,
"memory_reservation": 0,
"memory_swap": 0,
"cpu_shares": 0,
"cpu_quota": 0,
"cpu_period": 0,
"cpuset_cpus": "",
"cpuset_mems": "",
"blkio_weight": 0,
"freezer": "",
"slice": ""
},
"apparmor_profile": "docker-default",
"process_label": "",
"rlimits": [
{
"type": 7,
"hard": 1024,
"soft": 1024
}
],
"additional_groups": null,
"uid_mappings": null,
"gid_mappings": null,
"mask_paths": [
"/proc/kcore"
],
"readonly_paths": [
"/proc/sys",
"/proc/sysrq-trigger",
"/proc/irq",
"/proc/bus"
]
}
{
"no_pivot_root": false,
"parent_death_signal": 0,
"pivot_dir": "",
"rootfs": "/rootfs/jessie",
"readonlyfs": false,
"mounts": [
{
"source": "shm",
"destination": "/dev/shm",
"device": "tmpfs",
"flags": 14,
"data": "mode=1777,size=65536k",
"relabel": ""
},
{
"source": "mqueue",
"destination": "/dev/mqueue",
"device": "mqueue",
"flags": 14,
"data": "",
"relabel": ""
},
{
"source": "sysfs",
"destination": "/sys",
"device": "sysfs",
"flags": 15,
"data": "",
"relabel": ""
}
],
"devices": [
{
"type": 99,
"path": "/dev/fuse",
"major": 10,
"minor": 229,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/null",
"major": 1,
"minor": 3,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/zero",
"major": 1,
"minor": 5,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/full",
"major": 1,
"minor": 7,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/tty",
"major": 5,
"minor": 0,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/urandom",
"major": 1,
"minor": 9,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/random",
"major": 1,
"minor": 8,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
}
],
"mount_label": "",
"hostname": "koye",
"namespaces": [
{
"type": "NEWNS",
"path": ""
},
{
"type": "NEWUTS",
"path": ""
},
{
"type": "NEWIPC",
"path": ""
},
{
"type": "NEWPID",
"path": ""
},
{
"type": "NEWNET",
"path": ""
}
],
"capabilities": [
"CHOWN",
"DAC_OVERRIDE",
"FSETID",
"FOWNER",
"MKNOD",
"NET_RAW",
"SETGID",
"SETUID",
"SETFCAP",
"SETPCAP",
"NET_BIND_SERVICE",
"SYS_CHROOT",
"KILL",
"AUDIT_WRITE"
],
"networks": [
{
"type": "loopback",
"name": "",
"bridge": "",
"mac_address": "",
"address": "127.0.0.1/0",
"gateway": "localhost",
"ipv6_address": "",
"ipv6_gateway": "",
"mtu": 0,
"txqueuelen": 0,
"host_interface_name": ""
},
{
"type": "veth",
"name": "eth0",
"bridge": "docker0",
"mac_address": "",
"address": "172.17.0.101/16",
"gateway": "172.17.42.1",
"ipv6_address": "",
"ipv6_gateway": "",
"mtu": 1500,
"txqueuelen": 0,
"host_interface_name": "vethnsinit"
}
],
"routes": null,
"cgroups": {
"name": "libcontainer",
"parent": "nsinit",
"allow_all_devices": false,
"allowed_devices": [
{
"type": 99,
"path": "",
"major": -1,
"minor": -1,
"permissions": "m",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 98,
"path": "",
"major": -1,
"minor": -1,
"permissions": "m",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/console",
"major": 5,
"minor": 1,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/tty0",
"major": 4,
"minor": 0,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/tty1",
"major": 4,
"minor": 1,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "",
"major": 136,
"minor": -1,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "",
"major": 5,
"minor": 2,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "",
"major": 10,
"minor": 200,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/null",
"major": 1,
"minor": 3,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/zero",
"major": 1,
"minor": 5,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/full",
"major": 1,
"minor": 7,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/tty",
"major": 5,
"minor": 0,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/urandom",
"major": 1,
"minor": 9,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/random",
"major": 1,
"minor": 8,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
}
],
"memory": 0,
"memory_reservation": 0,
"memory_swap": 0,
"cpu_shares": 0,
"cpu_quota": 0,
"cpu_period": 0,
"cpuset_cpus": "",
"cpuset_mems": "",
"blkio_weight": 0,
"freezer": "",
"slice": ""
},
"apparmor_profile": "",
"process_label": "",
"rlimits": [
{
"type": 7,
"hard": 1024,
"soft": 1024
}
],
"additional_groups": null,
"uid_mappings": null,
"gid_mappings": null,
"mask_paths": [
"/proc/kcore"
],
"readonly_paths": [
"/proc/sys",
"/proc/sysrq-trigger",
"/proc/irq",
"/proc/bus"
]
}
{
"no_pivot_root": false,
"parent_death_signal": 0,
"pivot_dir": "",
"rootfs": "/rootfs/jessie",
"readonlyfs": false,
"mounts": [
{
"source": "shm",
"destination": "/dev/shm",
"device": "tmpfs",
"flags": 14,
"data": "mode=1777,size=65536k",
"relabel": ""
},
{
"source": "mqueue",
"destination": "/dev/mqueue",
"device": "mqueue",
"flags": 14,
"data": "",
"relabel": ""
},
{
"source": "sysfs",
"destination": "/sys",
"device": "sysfs",
"flags": 15,
"data": "",
"relabel": ""
}
],
"devices": [
{
"type": 99,
"path": "/dev/fuse",
"major": 10,
"minor": 229,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/null",
"major": 1,
"minor": 3,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/zero",
"major": 1,
"minor": 5,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/full",
"major": 1,
"minor": 7,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/tty",
"major": 5,
"minor": 0,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/urandom",
"major": 1,
"minor": 9,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/random",
"major": 1,
"minor": 8,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
}
],
"mount_label": "",
"hostname": "nsinit",
"namespaces": [
{
"type": "NEWNS",
"path": ""
},
{
"type": "NEWUTS",
"path": ""
},
{
"type": "NEWIPC",
"path": ""
},
{
"type": "NEWNET",
"path": ""
}
],
"capabilities": [
"CHOWN",
"DAC_OVERRIDE",
"FSETID",
"FOWNER",
"MKNOD",
"NET_RAW",
"SETGID",
"SETUID",
"SETFCAP",
"SETPCAP",
"NET_BIND_SERVICE",
"SYS_CHROOT",
"KILL",
"AUDIT_WRITE"
],
"networks": [
{
"type": "loopback",
"name": "",
"bridge": "",
"mac_address": "",
"address": "127.0.0.1/0",
"gateway": "localhost",
"ipv6_address": "",
"ipv6_gateway": "",
"mtu": 0,
"txqueuelen": 0,
"host_interface_name": ""
}
],
"routes": null,
"cgroups": {
"name": "libcontainer",
"parent": "nsinit",
"allow_all_devices": false,
"allowed_devices": [
{
"type": 99,
"path": "",
"major": -1,
"minor": -1,
"permissions": "m",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 98,
"path": "",
"major": -1,
"minor": -1,
"permissions": "m",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/console",
"major": 5,
"minor": 1,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/tty0",
"major": 4,
"minor": 0,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/tty1",
"major": 4,
"minor": 1,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "",
"major": 136,
"minor": -1,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "",
"major": 5,
"minor": 2,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "",
"major": 10,
"minor": 200,
"permissions": "rwm",
"file_mode": 0,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/null",
"major": 1,
"minor": 3,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/zero",
"major": 1,
"minor": 5,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/full",
"major": 1,
"minor": 7,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/tty",
"major": 5,
"minor": 0,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/urandom",
"major": 1,
"minor": 9,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
},
{
"type": 99,
"path": "/dev/random",
"major": 1,
"minor": 8,
"permissions": "rwm",
"file_mode": 438,
"uid": 0,
"gid": 0
}
],
"memory": 0,
"memory_reservation": 0,
"memory_swap": 0,
"cpu_shares": 0,
"cpu_quota": 0,
"cpu_period": 0,
"cpuset_cpus": "",
"cpuset_mems": "",
"blkio_weight": 0,
"freezer": "",
"slice": ""
},
"apparmor_profile": "",
"process_label": "",
"rlimits": [
{
"type": 7,
"hard": 1024,
"soft": 1024
}
],
"additional_groups": null,
"uid_mappings": null,
"gid_mappings": null,
"mask_paths": [
"/proc/kcore"
],
"readonly_paths": [
"/proc/sys",
"/proc/sysrq-trigger",
"/proc/irq",
"/proc/bus"
]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment