summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common/specgen.go14
-rw-r--r--docs/source/markdown/podman-network-connect.1.md2
-rw-r--r--docs/source/markdown/podman-network-disconnect.1.md2
-rw-r--r--docs/source/markdown/podman-network-inspect.1.md2
-rw-r--r--docs/source/markdown/podman-network-ls.1.md2
-rw-r--r--docs/source/markdown/podman-network.1.md2
-rw-r--r--docs/source/markdown/podman-run.1.md25
-rw-r--r--pkg/bindings/connection.go2
-rw-r--r--pkg/specgen/generate/config_linux.go53
-rw-r--r--pkg/specgen/generate/namespaces.go2
-rw-r--r--pkg/specgen/generate/oci.go2
-rw-r--r--pkg/specgen/namespaces.go16
-rw-r--r--pkg/specgen/specgen.go7
-rw-r--r--test/e2e/run_networking_test.go23
-rw-r--r--test/e2e/run_test.go33
-rw-r--r--test/system/120-load.bats30
-rw-r--r--test/system/400-unprivileged-access.bats2
-rw-r--r--utils/utils_supported.go45
18 files changed, 205 insertions, 59 deletions
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index 0bb6e79e5..e0da142ad 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -517,18 +517,22 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
}
switch con[0] {
- case "proc-opts":
- s.ProcOpts = strings.Split(con[1], ",")
+ case "apparmor":
+ s.ContainerSecurityConfig.ApparmorProfile = con[1]
+ s.Annotations[define.InspectAnnotationApparmor] = con[1]
case "label":
// TODO selinux opts and label opts are the same thing
s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1])
s.Annotations[define.InspectAnnotationLabel] = strings.Join(s.ContainerSecurityConfig.SelinuxOpts, ",label=")
- case "apparmor":
- s.ContainerSecurityConfig.ApparmorProfile = con[1]
- s.Annotations[define.InspectAnnotationApparmor] = con[1]
+ case "mask":
+ s.ContainerSecurityConfig.Mask = append(s.ContainerSecurityConfig.Mask, strings.Split(con[1], ":")...)
+ case "proc-opts":
+ s.ProcOpts = strings.Split(con[1], ",")
case "seccomp":
s.SeccompProfilePath = con[1]
s.Annotations[define.InspectAnnotationSeccomp] = con[1]
+ case "unmask":
+ s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, strings.Split(con[1], ":")...)
default:
return fmt.Errorf("invalid --security-opt 2: %q", opt)
}
diff --git a/docs/source/markdown/podman-network-connect.1.md b/docs/source/markdown/podman-network-connect.1.md
index 58b6e5c44..a31a415dc 100644
--- a/docs/source/markdown/podman-network-connect.1.md
+++ b/docs/source/markdown/podman-network-connect.1.md
@@ -10,6 +10,8 @@ podman\-network\-connect - Connect a container to a network
Connects a container to a network. A container can be connected to a network by name or by ID.
Once connected, the container can communicate with other containers in the same network.
+This command is not available for rootless users.
+
## OPTIONS
#### **--alias**
Add network-scoped alias for the container. If the network is using the `dnsname` CNI plugin, these aliases
diff --git a/docs/source/markdown/podman-network-disconnect.1.md b/docs/source/markdown/podman-network-disconnect.1.md
index 95c7018a8..8b7125282 100644
--- a/docs/source/markdown/podman-network-disconnect.1.md
+++ b/docs/source/markdown/podman-network-disconnect.1.md
@@ -9,6 +9,8 @@ podman\-network\-disconnect - Disconnect a container from a network
## DESCRIPTION
Disconnects a container from a network.
+This command is not available for rootless users.
+
## OPTIONS
#### **--force**, **-f**
diff --git a/docs/source/markdown/podman-network-inspect.1.md b/docs/source/markdown/podman-network-inspect.1.md
index 47d647b3f..56515d0c1 100644
--- a/docs/source/markdown/podman-network-inspect.1.md
+++ b/docs/source/markdown/podman-network-inspect.1.md
@@ -7,7 +7,7 @@ podman\-network\-inspect - Displays the raw CNI network configuration for one or
**podman network inspect** [*options*] [*network* ...]
## DESCRIPTION
-Display the raw (JSON format) network configuration. This command is not available for rootless users.
+Display the raw (JSON format) network configuration.
## OPTIONS
#### **--format**, **-f**
diff --git a/docs/source/markdown/podman-network-ls.1.md b/docs/source/markdown/podman-network-ls.1.md
index a964c97e8..9d2dd52a8 100644
--- a/docs/source/markdown/podman-network-ls.1.md
+++ b/docs/source/markdown/podman-network-ls.1.md
@@ -7,7 +7,7 @@ podman\-network\-ls - Display a summary of CNI networks
**podman network ls** [*options*]
## DESCRIPTION
-Displays a list of existing podman networks. This command is not available for rootless users.
+Displays a list of existing podman networks.
## OPTIONS
#### **--filter**, **-f**
diff --git a/docs/source/markdown/podman-network.1.md b/docs/source/markdown/podman-network.1.md
index d21b200d9..bc161659a 100644
--- a/docs/source/markdown/podman-network.1.md
+++ b/docs/source/markdown/podman-network.1.md
@@ -7,7 +7,7 @@ podman\-network - Manage Podman CNI networks
**podman network** *subcommand*
## DESCRIPTION
-The network command manages CNI networks for Podman. It is not supported for rootless users.
+The network command manages CNI networks for Podman.
## COMMANDS
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index 83aaa33e8..1038906c0 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -885,11 +885,16 @@ Security Options
- **label=level:**_LEVEL_: Set the label level for the container processes
- **label=filetype:**TYPE_: Set the label file type for the container files
- **label=disable**: Turn off label separation for the container
+- **mask**=_/path/1:/path/2_: The paths to mask separated by a colon. A masked path
+ cannot be accessed inside the container.
- **no-new-privileges**: Disable container processes from gaining additional privileges
- **seccomp=unconfined**: Turn off seccomp confinement for the container
- **seccomp**=_profile.json_: Allowed syscall list seccomp JSON file to be used as a seccomp filter
- **proc-opts**=_OPTIONS_ : Comma separated list of options to use for the /proc mount. More details
for the possible mount options are specified at **proc(5)** man page.
+- **unmask**=_ALL_ or _/path/1:/path/2_: Paths to unmask separated by a colon. If set to **ALL**, it will
+ unmask all the paths that are masked by default.
+ The default masked paths are **/proc/acpi, /proc/kcore, /proc/keys, /proc/latency_stats, /proc/sched_debug, /proc/scsi, /proc/timer_list, /proc/timer_stats, /sys/firmware, and /sys/fs/selinux.**
Note: Labeling can be disabled for all containers by setting **label=false** in the **containers.conf**(5) file.
@@ -1479,6 +1484,26 @@ $ podman run --security-opt label=type:svirt_apache_t -i -t centos bash
Note you would have to write policy defining a **svirt_apache_t** type.
+To mask additional specific paths in the container, specify the paths
+separated by a colon using the **mask** option with the **--security-opt**
+flag.
+
+```
+$ podman run --security-opt mask=/foo/bar:/second/path fedora bash
+```
+
+To unmask all the paths that are masked by default, set the **unmask** option to
+**ALL**. Or to only unmask specific paths, specify the paths as shown above with
+the **mask** option.
+
+```
+$ podman run --security-opt unmask=ALL fedora bash
+```
+
+```
+$ podman run --security-opt unmask=/foo/bar:/sys/firmware fedora bash
+```
+
### Setting device weight
If you want to set _/dev/sda_ device weight to **200**, you can specify the device
diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go
index 31435ae91..a5683796a 100644
--- a/pkg/bindings/connection.go
+++ b/pkg/bindings/connection.go
@@ -152,7 +152,7 @@ func pingNewConnection(ctx context.Context) error {
return err
}
// the ping endpoint sits at / in this case
- response, err := client.DoRequest(nil, http.MethodGet, "../../../_ping", nil, nil)
+ response, err := client.DoRequest(nil, http.MethodGet, "/_ping", nil, nil)
if err != nil {
return err
}
diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go
index 2d40dba8f..1808f99b8 100644
--- a/pkg/specgen/generate/config_linux.go
+++ b/pkg/specgen/generate/config_linux.go
@@ -4,13 +4,16 @@ import (
"fmt"
"io/ioutil"
"os"
+ "path"
"path/filepath"
"strings"
"github.com/containers/podman/v2/pkg/rootless"
+ "github.com/containers/podman/v2/pkg/util"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
@@ -137,22 +140,33 @@ func DevicesFromPath(g *generate.Generator, devicePath string) error {
return addDevice(g, strings.Join(append([]string{resolvedDevicePath}, devs[1:]...), ":"))
}
-func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, g *generate.Generator) {
+func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, mask, unmask []string, g *generate.Generator) {
+ defaultMaskPaths := []string{"/proc/acpi",
+ "/proc/kcore",
+ "/proc/keys",
+ "/proc/latency_stats",
+ "/proc/timer_list",
+ "/proc/timer_stats",
+ "/proc/sched_debug",
+ "/proc/scsi",
+ "/sys/firmware",
+ "/sys/fs/selinux",
+ "/sys/dev/block",
+ }
+
+ unmaskAll := false
+ if unmask != nil && unmask[0] == "ALL" {
+ unmaskAll = true
+ }
+
if !privileged {
- for _, mp := range []string{
- "/proc/acpi",
- "/proc/kcore",
- "/proc/keys",
- "/proc/latency_stats",
- "/proc/timer_list",
- "/proc/timer_stats",
- "/proc/sched_debug",
- "/proc/scsi",
- "/sys/firmware",
- "/sys/fs/selinux",
- "/sys/dev",
- } {
- g.AddLinuxMaskedPaths(mp)
+ if !unmaskAll {
+ for _, mp := range defaultMaskPaths {
+ // check that the path to mask is not in the list of paths to unmask
+ if !util.StringInSlice(mp, unmask) {
+ g.AddLinuxMaskedPaths(mp)
+ }
+ }
}
if pidModeIsHost && rootless.IsRootless() {
@@ -170,6 +184,15 @@ func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, g *generate.
g.AddLinuxReadonlyPaths(rp)
}
}
+
+ // mask the paths provided by the user
+ for _, mp := range mask {
+ if !path.IsAbs(mp) && mp != "" {
+ logrus.Errorf("Path %q is not an absolute path, skipping...", mp)
+ continue
+ }
+ g.AddLinuxMaskedPaths(mp)
+ }
}
// based on getDevices from runc (libcontainer/devices/devices.go)
diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go
index ddc73ca61..036c7b7a1 100644
--- a/pkg/specgen/generate/namespaces.go
+++ b/pkg/specgen/generate/namespaces.go
@@ -233,6 +233,8 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.
val = fmt.Sprintf("slirp4netns:%s", s.NetNS.Value)
}
toReturn = append(toReturn, libpod.WithNetNS(portMappings, postConfigureNetNS, val, nil))
+ case specgen.Private:
+ fallthrough
case specgen.Bridge:
portMappings, err := createPortMappings(ctx, s, img)
if err != nil {
diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go
index 8454458a8..0368ab205 100644
--- a/pkg/specgen/generate/oci.go
+++ b/pkg/specgen/generate/oci.go
@@ -298,7 +298,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
}
}
- BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), &g)
+ BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), s.Mask, s.Unmask, &g)
for name, val := range s.Env {
g.AddProcessEnv(name, val)
diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go
index d15745fa0..9d78a0210 100644
--- a/pkg/specgen/namespaces.go
+++ b/pkg/specgen/namespaces.go
@@ -258,24 +258,22 @@ func ParseNetworkNamespace(ns string) (Namespace, []string, error) {
var cniNetworks []string
// Net defaults to Slirp on rootless
switch {
- case ns == "slirp4netns", strings.HasPrefix(ns, "slirp4netns:"):
+ case ns == string(Slirp), strings.HasPrefix(ns, string(Slirp)+":"):
toReturn.NSMode = Slirp
- case ns == "pod":
+ case ns == string(FromPod):
toReturn.NSMode = FromPod
- case ns == "":
+ case ns == "" || ns == string(Default) || ns == string(Private):
if rootless.IsRootless() {
toReturn.NSMode = Slirp
} else {
toReturn.NSMode = Bridge
}
- case ns == "bridge":
+ case ns == string(Bridge):
toReturn.NSMode = Bridge
- case ns == "none":
+ case ns == string(NoNetwork):
toReturn.NSMode = NoNetwork
- case ns == "host":
+ case ns == string(Host):
toReturn.NSMode = Host
- case ns == "private":
- toReturn.NSMode = Private
case strings.HasPrefix(ns, "ns:"):
split := strings.SplitN(ns, ":", 2)
if len(split) != 2 {
@@ -283,7 +281,7 @@ func ParseNetworkNamespace(ns string) (Namespace, []string, error) {
}
toReturn.NSMode = Path
toReturn.Value = split[1]
- case strings.HasPrefix(ns, "container:"):
+ case strings.HasPrefix(ns, string(FromContainer)+":"):
split := strings.SplitN(ns, ":", 2)
if len(split) != 2 {
return toReturn, nil, errors.Errorf("must provide name or ID or a container when specifying container:")
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index fad2406e5..964b89fa4 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -307,6 +307,13 @@ type ContainerSecurityConfig struct {
Umask string `json:"umask,omitempty"`
// ProcOpts are the options used for the proc mount.
ProcOpts []string `json:"procfs_opts,omitempty"`
+ // Mask is the path we want to mask in the container. This masks the paths
+ // given in addition to the default list.
+ // Optional
+ Mask []string `json:"mask,omitempty"`
+ // Unmask is the path we want to unmask in the container. To override
+ // all the default paths that are masked, set unmask=ALL.
+ Unmask []string `json:"unmask,omitempty"`
}
// ContainerCgroupConfig contains configuration information about a container's
diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go
index 3e80e953e..3fb00a28b 100644
--- a/test/e2e/run_networking_test.go
+++ b/test/e2e/run_networking_test.go
@@ -49,9 +49,28 @@ var _ = Describe("Podman run networking", func() {
Expect(session.ExitCode()).To(Equal(0))
})
+ It("podman run network connection with default", func() {
+ session := podmanTest.Podman([]string{"run", "--network", "default", ALPINE, "wget", "www.podman.io"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
+
+ It("podman run network connection with none", func() {
+ session := podmanTest.Podman([]string{"run", "--network", "none", ALPINE, "wget", "www.podman.io"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(1))
+ Expect(session.ErrorToString()).To(ContainSubstring("wget: bad address 'www.podman.io'"))
+ })
+
+ It("podman run network connection with private", func() {
+ session := podmanTest.Podman([]string{"run", "--network", "private", ALPINE, "wget", "www.podman.io"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
+
It("podman run network connection with loopback", func() {
- session := podmanTest.Podman([]string{"run", "-dt", "--network", "host", ALPINE, "wget", "www.podman.io"})
- session.Wait(90)
+ session := podmanTest.Podman([]string{"run", "--network", "host", ALPINE, "wget", "www.podman.io"})
+ session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
})
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 0d65a3e59..efc125d2b 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -233,6 +233,39 @@ var _ = Describe("Podman run", func() {
return jsonFile
}
+ It("podman run mask and unmask path test", func() {
+ session := podmanTest.Podman([]string{"run", "-d", "--name=maskCtr1", "--security-opt", "unmask=ALL", "--security-opt", "mask=/proc/acpi", ALPINE, "sleep", "200"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"exec", "maskCtr1", "ls", "/sys/firmware"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.OutputToString()).To(Not(BeEmpty()))
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"exec", "maskCtr1", "ls", "/proc/acpi"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.OutputToString()).To(BeEmpty())
+
+ session = podmanTest.Podman([]string{"run", "-d", "--name=maskCtr2", "--security-opt", "unmask=/proc/acpi:/sys/firmware", ALPINE, "sleep", "200"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"exec", "maskCtr2", "ls", "/sys/firmware"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.OutputToString()).To(Not(BeEmpty()))
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"exec", "maskCtr2", "ls", "/proc/acpi"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.OutputToString()).To(Not(BeEmpty()))
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"run", "-d", "--name=maskCtr3", "--security-opt", "mask=/sys/power/disk", ALPINE, "sleep", "200"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"exec", "maskCtr3", "cat", "/sys/power/disk"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.OutputToString()).To(BeEmpty())
+ Expect(session.ExitCode()).To(Equal(0))
+ })
+
It("podman run seccomp test", func() {
session := podmanTest.Podman([]string{"run", "-it", "--security-opt", strings.Join([]string{"seccomp=", forbidGetCWDSeccompProfile()}, ""), ALPINE, "pwd"})
session.WaitWithDefaultTimeout()
diff --git a/test/system/120-load.bats b/test/system/120-load.bats
index 8ea9b1c69..272e2ae93 100644
--- a/test/system/120-load.bats
+++ b/test/system/120-load.bats
@@ -28,12 +28,15 @@ verify_iid_and_name() {
@test "podman save to pipe and load" {
# Generate a random name and tag (must be lower-case)
- local random_name=x$(random_string 12 | tr A-Z a-z)
- local random_tag=t$(random_string 7 | tr A-Z a-z)
+ local random_name=x0$(random_string 12 | tr A-Z a-z)
+ local random_tag=t0$(random_string 7 | tr A-Z a-z)
local fqin=localhost/$random_name:$random_tag
run_podman tag $IMAGE $fqin
- archive=$PODMAN_TMPDIR/myimage-$(random_string 8).tar
+ # Believe it or not, 'podman load' would barf if any path element
+ # included a capital letter
+ archive=$PODMAN_TMPDIR/MySubDirWithCaps/MyImage-$(random_string 8).tar
+ mkdir -p $(dirname $archive)
# We can't use run_podman because that uses the BATS 'run' function
# which redirects stdout and stderr. Here we need to guarantee
@@ -51,19 +54,20 @@ verify_iid_and_name() {
run_podman images $fqin --format '{{.Repository}}:{{.Tag}}'
is "$output" "$fqin" "image preserves name across save/load"
- # FIXME: when/if 7337 gets fixed, load with a new tag
- if false; then
- local new_name=x$(random_string 14 | tr A-Z a-z)
- local new_tag=t$(random_string 6 | tr A-Z a-z)
+ # Load with a new tag
+ local new_name=x1$(random_string 14 | tr A-Z a-z)
+ local new_tag=t1$(random_string 6 | tr A-Z a-z)
run_podman rmi $fqin
- fqin=localhost/$new_name:$new_tag
- run_podman load -i $archive $fqin
- run_podman images $fqin --format '{{.Repository}}:{{.Tag}}'
- is "$output" "$fqin" "image can be loaded with new name:tag"
- fi
+
+ new_fqin=localhost/$new_name:$new_tag
+ run_podman load -i $archive $new_fqin
+ run_podman images --format '{{.Repository}}:{{.Tag}}' --sort tag
+ is "${lines[0]}" "$IMAGE" "image is preserved"
+ is "${lines[1]}" "$fqin" "image is reloaded with old fqin"
+ is "${lines[2]}" "$new_fqin" "image is reloaded with new fqin too"
# Clean up
- run_podman rmi $fqin
+ run_podman rmi $fqin $new_fqin
}
diff --git a/test/system/400-unprivileged-access.bats b/test/system/400-unprivileged-access.bats
index 142d7dcd9..20fdd068f 100644
--- a/test/system/400-unprivileged-access.bats
+++ b/test/system/400-unprivileged-access.bats
@@ -118,7 +118,7 @@ EOF
/proc/scsi
/sys/firmware
/sys/fs/selinux
- /sys/dev
+ /sys/dev/block
)
# Some of the above may not exist on our host. Find only the ones that do.
diff --git a/utils/utils_supported.go b/utils/utils_supported.go
index bcaa2c61a..e6978ca6f 100644
--- a/utils/utils_supported.go
+++ b/utils/utils_supported.go
@@ -43,6 +43,15 @@ func RunUnderSystemdScope(pid int, slice string, unitName string) error {
ch := make(chan string)
_, err = conn.StartTransientUnit(unitName, "replace", properties, ch)
if err != nil {
+ // On errors check if the cgroup already exists, if it does move the process there
+ if props, err := conn.GetUnitTypeProperties(unitName, "Scope"); err == nil {
+ if cgroup, ok := props["ControlGroup"].(string); ok && cgroup != "" {
+ if err := moveUnderCgroup(cgroup, "", []uint32{uint32(pid)}); err != nil {
+ return err
+ }
+ return nil
+ }
+ }
return err
}
defer conn.Close()
@@ -101,6 +110,13 @@ func GetCgroupProcess(pid int) (string, error) {
// MoveUnderCgroupSubtree moves the PID under a cgroup subtree.
func MoveUnderCgroupSubtree(subtree string) error {
+ return moveUnderCgroup("", subtree, nil)
+}
+
+// moveUnderCgroup moves a group of processes to a new cgroup.
+// If cgroup is the empty string, then the current calling process cgroup is used.
+// If processes is empty, then the processes from the current cgroup are moved.
+func moveUnderCgroup(cgroup, subtree string, processes []uint32) error {
procFile := "/proc/self/cgroup"
f, err := os.Open(procFile)
if err != nil {
@@ -140,13 +156,12 @@ func MoveUnderCgroupSubtree(subtree string) error {
cgroupRoot = filepath.Join(cgroupRoot, controller)
}
- processes, err := ioutil.ReadFile(filepath.Join(cgroupRoot, parts[2], "cgroup.procs"))
- if err != nil {
- return err
+ parentCgroup := cgroup
+ if parentCgroup == "" {
+ parentCgroup = parts[2]
}
-
- newCgroup := filepath.Join(cgroupRoot, parts[2], subtree)
- if err := os.Mkdir(newCgroup, 0755); err != nil {
+ newCgroup := filepath.Join(cgroupRoot, parentCgroup, subtree)
+ if err := os.Mkdir(newCgroup, 0755); err != nil && !os.IsExist(err) {
return err
}
@@ -156,9 +171,21 @@ func MoveUnderCgroupSubtree(subtree string) error {
}
defer f.Close()
- for _, pid := range bytes.Split(processes, []byte("\n")) {
- if _, err := f.Write(pid); err != nil {
- logrus.Warnf("Cannot move process %s to cgroup %q", pid, newCgroup)
+ if len(processes) > 0 {
+ for _, pid := range processes {
+ if _, err := f.Write([]byte(fmt.Sprintf("%d\n", pid))); err != nil {
+ logrus.Warnf("Cannot move process %d to cgroup %q", pid, newCgroup)
+ }
+ }
+ } else {
+ processesData, err := ioutil.ReadFile(filepath.Join(cgroupRoot, parts[2], "cgroup.procs"))
+ if err != nil {
+ return err
+ }
+ for _, pid := range bytes.Split(processesData, []byte("\n")) {
+ if _, err := f.Write(pid); err != nil {
+ logrus.Warnf("Cannot move process %d to cgroup %q", pid, newCgroup)
+ }
}
}
}