+++ /dev/null
-diff --git a/doc/configuration.xml b/doc/configuration.xml
-index e03f92d..3e48568 100644
---- a/doc/configuration.xml
-+++ b/doc/configuration.xml
-@@ -317,12 +317,11 @@ configuration.
- The directory to mount the cgroup hierarchy at. The default is /dev/cgroup.
- </description>
- </scalar>
-- <scalar name="subsys">
-+ <list name="subsys">
- <description>
--Comma-separated list of subsystems to enable on the cgroup mount point.
--The default is "all".
-+List of subsystems to enable on the cgroup mount point. The default is "all".
- </description>
-- </scalar>
-+ </list>
- <list name="inherit">
- <description>
- Some subsystems start out with clean slates, making it impossible to use the
-@@ -345,6 +344,11 @@ If this file exists, all cgroups will be created underneath this directory
- in the cgroup mount point.
- </description>
- </scalar>
-+ <boolean name="per-ss">
-+ <description>
-+If this file exists, each subsystem will have its own mount point.
-+ </description>
-+ </boolean>
- </collection>
- </collection>
-
-diff --git a/scripts/vserver.functions b/scripts/vserver.functions
-index 5381d82..d35a4fe 100644
---- a/scripts/vserver.functions
-+++ b/scripts/vserver.functions
-@@ -75,9 +75,10 @@ N_CONTEXT=
- SILENT_OPT=
-
- CGROUP_MNT=/dev/cgroup
--CGROUP_SUBSYS=all
-+declare -a CGROUP_SUBSYS=()
- declare -a CGROUP_INHERIT=( cpuset.cpus cpuset.mems )
- CGROUP_BASE=""
-+CGROUP_MNT_PER_SS=""
-
- : ${VSERVER_NAME:=$(basename "$VSERVER_DIR")}
-
-@@ -1458,11 +1459,9 @@ function _generateCgroupOptions
- fi
- findFile file "$__CONFDIR/.defaults/cgroup/subsys" ""
- if test -n "$file"; then
-- read CGROUP_SUBSYS < "$file"
-- elif $_GREP -q '^ns[[:space:]]' /proc/cgroups; then
-- # Hack for the ns subsystem, with which we are incompatible
-- CGROUP_SUBSYS=$($_SED '/^#/d;/^ns[[:space:]]/d;s/[[:space:]].*//' /proc/cgroups | \
-- (s=""; while read x; do test -n "$s" && s="$s,"; s="$s$x"; done; echo "$s"))
-+ _readFileToArray CGROUP_SUBSYS "$file"
-+ else
-+ CGROUP_SUBSYS=( $($_SED '/^#/d;/^ns[[:space:]]/d;s/[[:space:]].*//' /proc/cgroups) )
- fi
- findFile file "$__CONFDIR/.defaults/cgroup/inherit" ""
- if test -n "$file"; then
-@@ -1474,6 +1473,10 @@ function _generateCgroupOptions
- test "$CGROUP_BASE" != "${CGROUP_BASE%/}" || \
- CGROUP_BASE="${CGROUP_BASE}/"
- fi
-+ findFile file "$__CONFDIR/.defaults/cgroup/per-ss" ""
-+ if test -n "$file"; then
-+ CGROUP_MNT_PER_SS=1
-+ fi
-
- return 0
- }
-@@ -1481,8 +1484,19 @@ function _generateCgroupOptions
- function useCgroup
- {
- hasCgroup || return 1
-- test -d "$CGROUP_MNT" || return 1
-- memcg=""
-+ if test -n "$CGROUP_MNT_PER_SS"; then
-+ local existing=0
-+ local ss
-+ for ss in "${CGROUP_SUBSYS[@]}"; do
-+ if test -e "$CGROUP_MNT/$ss/tasks"; then
-+ let existing=${existing}+1
-+ fi
-+ done
-+ test "$existing" -gt 0 || return 1
-+ else
-+ test -e "$CGROUP_MNT/tasks" || return 1
-+ fi
-+ local memcg=""
- if $_VSERVER_INFO - FEATURE memcg; then
- memcg=1
- fi
-@@ -1500,6 +1514,8 @@ function _handleCgroup
- local i
- local j
- local parent
-+ local -a mnts
-+ local ss
-
- useCgroup "$vdir" || return 0
-
-@@ -1511,47 +1527,63 @@ function _handleCgroup
- name="$VSERVER_NAME"
- fi
-
-- if test "$action" = "attach"; then
-- if test -n "$CGROUP_BASE"; then
-- local -a dirs=()
-- i="$CGROUP_MNT/$CGROUP_BASE"
-- while test "$CGROUP_MNT" != "$i"; do
-- dirs=( "$i" "${dirs[@]}" )
-- i="${i%/*}"
-- done
-- for i in "${dirs[@]}"; do
-- if mkdir "$i" 2>/dev/null; then
-- parent="${i%/*}"
-- for j in "${CGROUP_INHERIT[@]}"; do
-- test -f "$parent/$j" || continue
-- cat "$parent/$j" > "$i/$j"
-+ if test -n "$CGROUP_MNT_PER_SS"; then
-+ mnts=()
-+ for ss in "${CGROUP_SUBSYS[@]}"; do
-+ mnts=( "${mnts[@]}" "$CGROUP_MNT/$ss" )
-+ done
-+ else
-+ mnts=( "$CGROUP_MNT" )
-+ fi
-+ for mnt in "${mnts[@]}"; do
-+ test -d "$mnt" || continue
-+ if test "$action" = "attach"; then
-+ if test -n "$CGROUP_BASE"; then
-+ local -a dirs=()
-+ i="$mnt/$CGROUP_BASE"
-+ while test "$mnt" != "$i"; do
-+ dirs=( "$i" "${dirs[@]}" )
-+ i="${i%/*}"
-+ done
-+ for i in "${dirs[@]}"; do
-+ if mkdir "$i" 2>/dev/null; then
-+ parent="${i%/*}"
-+ for j in "${CGROUP_INHERIT[@]}"; do
-+ test -f "$parent/$j" || continue
-+ cat "$parent/$j" > "$i/$j"
-+ done
-+ fi
-+ done
-+ fi
-+ if mkdir "$mnt/$CGROUP_BASE$name" 2>/dev/null; then
-+ parent="$mnt/$CGROUP_BASE$name"
-+ parent="${parent%/*}"
-+ for i in "${CGROUP_INHERIT[@]}"; do
-+ test -f "$parent/$i" || continue
-+ cat "$parent/$i" > "$mnt/$CGROUP_BASE$name/$i"
-+ done
-+
-+ if test -n "$dir"; then
-+ shopt -s nullglob
-+ for i in "$dir"/*; do
-+ f="${i##*/}"
-+ test "$f" != mnt -a "$f" != subsys -a \
-+ "$f" != inherit -a "$f" != name -a "$f" != base -a \
-+ "$f" != per-ss \
-+ || continue
-+ if test -n "$CGROUP_MNT_PER_SS"; then
-+ ss="${f%%.*}"
-+ test "$ss" = "${mnt##*/}" || continue
-+ fi
-+ cat "$i" > "$mnt/$CGROUP_BASE$name/$f"
- done
- fi
-- done
-- fi
-- if mkdir "$CGROUP_MNT/$CGROUP_BASE$name" 2>/dev/null; then
-- parent="$CGROUP_MNT/$CGROUP_BASE$name"
-- parent="${parent%/*}"
-- for i in "${CGROUP_INHERIT[@]}"; do
-- test -f "$parent/$i" || continue
-- cat "$parent/$i" > "$CGROUP_MNT/$CGROUP_BASE$name/$i"
-- done
--
-- if test -n "$dir"; then
-- shopt -s nullglob
-- for i in "$dir"/*; do
-- f="${i##*/}"
-- test "$f" != mnt -a "$f" != subsys -a \
-- "$f" != inherit -a "$f" != name -a "$f" != base \
-- || continue
-- cat "$i" > "$CGROUP_MNT/$CGROUP_BASE$name/$f"
-- done
- fi
-+ echo "$$" > "$mnt/$CGROUP_BASE$name/tasks"
-+ elif test "$action" = "destroy"; then
-+ rmdir "$mnt/$name" 2>/dev/null || :
- fi
-- echo "$$" > "$CGROUP_MNT/$CGROUP_BASE$name/tasks"
-- elif test "$action" = "destroy"; then
-- rmdir "$CGROUP_MNT/$name" 2>/dev/null || :
-- fi
-+ done
-
- return 0
- }
-diff --git a/src/vserver-stat.c b/src/vserver-stat.c
-index 269edb0..e81c38d 100644
---- a/src/vserver-stat.c
-+++ b/src/vserver-stat.c
-@@ -309,13 +309,15 @@ registerXidCgroups(struct Vector *vec, struct process_info *process)
- char vhi_name[65],
- filename[128],
- cgroup[129],
-+ name[129],
- buf[30];
- int fd;
-- ssize_t cgroup_len;
-+ ssize_t cgroup_len, name_len;
- unsigned long long rss = 0;
- char *endptr;
- size_t len;
- uint64_t stime_total, utime_total;
-+ int per_ss = 0;
-
-
- if (vc_virt_stat(xid, &vstat) == -1) {
-@@ -341,10 +343,14 @@ registerXidCgroups(struct Vector *vec, struct process_info *process)
- perror("read(cgroup/mnt)");
- return;
- }
-- close(fd);
-- cgroup[cgroup_len] = '/';
-- cgroup_len += 1;
-- cgroup[cgroup_len] = 0;
-+ if (cgroup_len > 0) {
-+ close(fd);
-+ while (cgroup[cgroup_len - 1] == '\n' || cgroup[cgroup_len - 1] == '\r')
-+ cgroup_len--;
-+ cgroup[cgroup_len] = '/';
-+ cgroup_len += 1;
-+ cgroup[cgroup_len] = 0;
-+ }
- }
-
- if ((fd = open(DEFAULTCONFDIR "/cgroup/base", O_RDONLY)) != -1) {
-@@ -354,14 +360,21 @@ registerXidCgroups(struct Vector *vec, struct process_info *process)
- return;
- }
- close(fd);
-- cgroup_len += len;
-- if (cgroup[cgroup_len - 1] != '/') {
-- cgroup[cgroup_len] = '/';
-- cgroup_len += 1;
-+ if (len > 0) {
-+ while (cgroup[cgroup_len + len - 1] == '\n' || cgroup[cgroup_len + len - 1] == '\r')
-+ len--;
-+ cgroup_len += len;
-+ if (cgroup[cgroup_len - 1] != '/') {
-+ cgroup[cgroup_len] = '/';
-+ cgroup_len += 1;
-+ }
-+ cgroup[cgroup_len] = 0;
- }
-- cgroup[cgroup_len] = 0;
- }
-
-+ if (access(DEFAULTCONFDIR "/cgroup/per-ss", F_OK) == 0)
-+ per_ss = 1;
-+
- len = strlen(vhi_name);
- if ((len + sizeof("/cgroup/name")) >= sizeof(filename)) {
- WRITE_MSG(2, "too long context name: ");
-@@ -380,35 +393,36 @@ registerXidCgroups(struct Vector *vec, struct process_info *process)
- WRITE_MSG(2, "\n");
- return;
- }
-- len = strlen(dir);
-- if ((len + cgroup_len) >= sizeof(cgroup)) {
-+ name_len = strlen(dir);
-+ if (name_len >= sizeof(name)) {
- WRITE_MSG(2, "cgroup name too long: ");
- WRITE_STR(2, dir);
- WRITE_MSG(2, "\n");
- return;
- }
-- strcpy(cgroup + cgroup_len, dir);
-- cgroup_len += len;
-+ strcpy(name, dir);
- }
- else {
-- ssize_t ret;
-- ret = read(fd, cgroup + cgroup_len, sizeof(cgroup) - cgroup_len);
-- if (ret == -1) {
-+ name_len = read(fd, name, sizeof(name));
-+ if (name_len == -1) {
- perror("read(cgroup/name)");
- return;
- }
-- cgroup_len += ret;
-+ if (name_len > 0) {
-+ while (name[name_len - 1] == '\n' || name[name_len - 1] == '\r')
-+ name_len--;
-+ name[name_len] = '\0';
-+ }
- close(fd);
- }
-
-- if ((cgroup_len + sizeof("/memory.usage_in_bytes")) > sizeof(filename)) {
-+ if ((cgroup_len + name_len + sizeof("/memory/memory.usage_in_bytes")) > sizeof(filename)) {
- WRITE_MSG(2, "cgroup name too long: ");
- WRITE_STR(2, cgroup);
- WRITE_MSG(2, "\n");
- return;
- }
-- strcpy(filename, cgroup);
-- strcpy(filename + cgroup_len, "/memory.usage_in_bytes");
-+ snprintf(filename, sizeof(filename), "%s%s%s/memory.usage_in_bytes", cgroup, (per_ss ? "/memory" : ""), name);
-
- if ((fd = open(filename, O_RDONLY)) == -1)
- perror("open(memory.usage_in_bytes)");
-@@ -425,8 +439,7 @@ registerXidCgroups(struct Vector *vec, struct process_info *process)
- }
- }
-
-- strcpy(filename, cgroup);
-- strcpy(filename + cgroup_len, "/cpuacct.stat");
-+ snprintf(filename, sizeof(filename), "%s%s%s/cpuacct.stat", cgroup, (per_ss ? "/cpuacct" : ""), name);
-
- if ((fd = open(filename, O_RDONLY)) == -1) {
- utime_total = 0;
-diff --git a/sysv/util-vserver b/sysv/util-vserver
-index 0f5dd6c..8cc173f 100755
---- a/sysv/util-vserver
-+++ b/sysv/util-vserver
-@@ -67,14 +67,31 @@ function mount_cgroup()
- _generateCgroupOptions
- test -n "$CGROUP_MNT" || return 0
- $_MKDIR -p "$CGROUP_MNT"
-- $_MOUNT -t cgroup -o "$CGROUP_SUBSYS" vserver "$CGROUP_MNT"
-+ if test -n "$CGROUP_MNT_PER_SS"; then
-+ for ss in "${CGROUP_SUBSYS[@]}"; do
-+ $_MKDIR -p "$CGROUP_MNT/$ss"
-+ $_MOUNT -t cgroup -o "$ss" vserver "$CGROUP_MNT/$ss"
-+ done
-+ else
-+ oIFS="$IFS"
-+ IFS=,
-+ ss="${CGROUP_SUBSYS[*]}"
-+ IFS="$oIFS"
-+ $_MOUNT -t cgroup -o "$ss" vserver "$CGROUP_MNT"
-+ fi
- }
-
- function umount_cgroup()
- {
- _generateCgroupOptions
- test -n "$CGROUP_MNT" || return 0
-- $_UMOUNT "$CGROUP_MNT"
-+ if test -n "$CGROUP_MNT_PER_SS"; then
-+ for ss in "${CGROUP_SUBSYS[@]}"; do
-+ $_UMOUNT "$CGROUP_MNT/$ss"
-+ done
-+ else
-+ $_UMOUNT "$CGROUP_MNT"
-+ fi
- }
-
- function start()