diff --git a/init.d/cgroups.in b/init.d/cgroups.in index c478098fa..25a5da071 100644 --- a/init.d/cgroups.in +++ b/init.d/cgroups.in @@ -21,6 +21,7 @@ depend() cgroup1_base() { + eerror "cgroups v1 is deprecated. Please unset rc_cgroup_mode." grep -qw cgroup /proc/filesystems || return 0 if ! mountinfo -q /sys/fs/cgroup; then ebegin "Mounting cgroup filesystem" @@ -44,24 +45,22 @@ cgroup1_controllers() { yesno "${rc_controller_cgroups:-YES}" && [ -e /proc/cgroups ] && grep -qw cgroup /proc/filesystems || return 0 + while read -r name _ _ enabled _; do - case "${enabled}" in - 1) mountinfo -q "/sys/fs/cgroup/${name}" && continue - local x - for x in $rc_cgroup_controllers; do - [ "${name}" = "blkio" ] && [ "${x}" = "io" ] && - continue 2 - [ "${name}" = "${x}" ] && - continue 2 - done - mkdir "/sys/fs/cgroup/${name}" - mount -n -t cgroup -o "${cgroup_opts},${name}" \ - "${name}" "/sys/fs/cgroup/${name}" - yesno "${rc_cgroup_memory_use_hierarchy:-no}" && - [ "${name}" = memory ] && - echo 1 > /sys/fs/cgroup/memory/memory.use_hierarchy - ;; - esac + [ "$enabled" = 1 ] || continue; + mountinfo -q "/sys/fs/cgroup/${name}" && continue + + local x + for x in $rc_cgroup_controllers; do + [ "${name}" = "blkio" ] && [ "${x}" = "io" ] && continue 2 + [ "${name}" = "${x}" ] && continue 2 + done + + mkdir "/sys/fs/cgroup/${name}" + mount -n -t cgroup -o "${cgroup_opts},${name}" "${name}" "/sys/fs/cgroup/${name}" + if [ yesno "${rc_cgroup_memory_use_hierarchy:-no}" && [ "${name}" = memory ]; then + echo 1 > /sys/fs/cgroup/memory/memory.use_hierarchy + fi done < /proc/cgroups return 0 } @@ -69,38 +68,42 @@ cgroup1_controllers() cgroup2_base() { grep -qw cgroup2 /proc/filesystems || return 0 - local base - base="$(cgroup2_find_path)" - mkdir -p "${base}" - mount -t cgroup2 cgroup2 -o "${cgroup_opts},nsdelegate" "${base}" 2> /dev/null || - mount -t cgroup2 cgroup2 -o "${cgroup_opts}" "${base}" + mkdir -p "${cgroup_root}" + mount -t cgroup2 cgroup2 -o "${cgroup_opts},nsdelegate" "${cgroup_root}" 2> /dev/null || + mount -t cgroup2 cgroup2 -o "${cgroup_opts}" "${cgroup_root}" + mkdir -p "${cgroup_root}/rc" return 0 } -cgroup2_controllers() +setup_controller() { - grep -qw cgroup2 /proc/filesystems || return 0 - local active cgroup_path x y - cgroup_path="$(cgroup2_find_path)" - [ -z "${cgroup_path}" ] && return 0 - [ ! -e "${cgroup_path}/cgroup.controllers" ] && return 0 - [ ! -e "${cgroup_path}/cgroup.subtree_control" ]&& return 0 - read -r active < "${cgroup_path}/cgroup.controllers" - for x in ${active}; do - case "${rc_cgroup_mode:-unified}" in - unified) - echo "+${x}" > "${cgroup_path}/cgroup.subtree_control" - ;; + local root="$1" controller allowed + shift + + [ ! -e "${root}/cgroup.controllers" ] && return 0 + [ ! -e "${root}/cgroup.subtree_control" ] && return 0 + + for controller; do + case "${rc_cgroup_mode:-unified}" in + unified) echo "+${controller}" > "${root}/cgroup.subtree_control" ;; hybrid) - for y in ${rc_cgroup_controllers}; do - if [ "$x" = "$y" ]; then - echo "+${x}" > "${cgroup_path}/cgroup.subtree_control" + for allowed in ${rc_cgroup_controllers}; do + if [ "$controller" = "$allowed" ]; then + echo "+${controller}" > "${root}/cgroup.subtree_control" fi done ;; esac done - return 0 +} + +cgroup2_controllers() +{ + grep -qw cgroup2 /proc/filesystems || return 0 + local active + read -r active < "${cgroup_root}/cgroup.controllers" + setup_controller "${cgroup_root}" $active + setup_controller "${cgroup_root}/rc" $active } cgroups_hybrid() @@ -109,21 +112,18 @@ cgroups_hybrid() cgroup2_base cgroup2_controllers cgroup1_controllers - return 0 } cgroups_legacy() { cgroup1_base cgroup1_controllers - return 0 } cgroups_unified() { cgroup2_base cgroup2_controllers - return 0 } mount_cgroups() @@ -133,7 +133,6 @@ mount_cgroups() legacy) cgroups_legacy ;; unified) cgroups_unified ;; esac - return 0 } restorecon_cgroups() @@ -143,7 +142,6 @@ restorecon_cgroups() restorecon -rF /sys/fs/cgroup >/dev/null 2>&1 eend $? fi - return 0 } start() @@ -153,5 +151,6 @@ start() mount_cgroups restorecon_cgroups fi + return 0 } diff --git a/init.d/user.in b/init.d/user.in index ea416e5ce..1526599a4 100644 --- a/init.d/user.in +++ b/init.d/user.in @@ -28,4 +28,13 @@ start_pre() { eerror "symbolic links to it for the users you want to start" return 1 fi + + if [ -d "$cgroup_path" ]; then + local user_cgroup=${cgroup_path}/daemons + + mkdir "$user_cgroup" + chown "$user:$user" "$user_cgroup" + + cgroup_path="${cgroup_path}/system" + fi } diff --git a/sh/rc-cgroup.sh b/sh/cgroups.sh similarity index 62% rename from sh/rc-cgroup.sh rename to sh/cgroups.sh index f10e328c1..854581d50 100644 --- a/sh/rc-cgroup.sh +++ b/sh/cgroups.sh @@ -11,17 +11,97 @@ extra_stopped_commands="${extra_stopped_commands} cgroup_cleanup" description_cgroup_cleanup="Kill all processes in the cgroup" -cgroup_find_path() +cgroup_root="/sys/fs/cgroup" +if [ "${rc_cgroup_mode:-unified}" = hybrid ]; then + cgroup_root="/sys/fs/cgroup/unified" +fi + +# use 'rc' for the supervision root cgroup to avoid issues with our old cgroup v1 style +cgroup_path="${cgroup_root}/rc/${RC_SVCNAME}" + +if [ -d "/sys/fs/cgroup/openrc.${RC_SVCNAME}" ]; then + cgroup_path="${cgroup_root}/openrc.${RC_SVCNAME}" +fi + +if yesno "$RC_USER_SERVICES" && [ -d "${cgroup_path}/daemons" ]; then + cgroup_path="${cgroup_path}/daemons" +fi + +cgroup_running() { - local OIFS name dir result - [ -n "$1" ] || return 0 - OIFS="$IFS" - IFS=":" - while read -r _ name dir; do - [ "$name" = "$1" ] && result="$dir" - done < /proc/1/cgroup - IFS="$OIFS" - printf "%s" "${result}" + [ -d "${cgroup_path}" ] || + [ -d "/sys/fs/cgroup/openrc.${RC_SVCNAME}" ] || + [ -d "/sys/fs/cgroup/unified/openrc.${RC_SVCNAME}" ] || + [ -d "/sys/fs/cgroup/openrc/${RC_SVCNAME}" ] # cgroup v1, deperecated +} + +cgroup_add_service() +{ + if [ "${rc_cgroup_mode:-unified}" != unified ]; then + eerror "cgroups v1 is deprecated. Please unset rc_cgroup_mode and reboot." + + if [ ! -w /sys/fs/cgroup ]; then + eerror "No permission to apply cgroup settings" + return 1 + fi + + # relocate starting process to the top of the cgroup + # it prevents from unwanted inheriting of the user + # cgroups. But may lead to a problems where that inheriting + # is needed. + for d in /sys/fs/cgroup/* ; do + [ -w "${d}"/tasks ] && printf "%d" 0 > "${d}"/tasks + done + + openrc_cgroup=/sys/fs/cgroup/openrc + if [ -d "$openrc_cgroup" ]; then + cgroup="$openrc_cgroup/$RC_SVCNAME" + mkdir -p "$cgroup" + [ -w "$cgroup/tasks" ] && printf "%d" 0 > "$cgroup/tasks" + fi + + if [ "${rc_cgroup_mode}" != hybrid ]; then + return 0 + fi + fi + + mountinfo -q "${cgroup_root}" || return 0 + + if [ ! -w "${cgroup_root}" ]; then + eerror "No permission to apply cgroup settings" + return 1 + fi + + [ ! -d "${cgroup_path}" ] && mkdir -p "${cgroup_path}" + [ -f "${cgroup_path}"/cgroup.procs ] && printf 0 > "${cgroup_path}"/cgroup.procs +} + +cgroup_remove() +{ + [ ! -d "${cgroup_path}" ] || [ ! -e "${cgroup_path}"/cgroup.events ] && return 0 + + local key populated value + + while read -r key value; do + [ "$key" = populated ] && [ "$value" = 1 ] && return 0 + done < "${cgroup_path}/cgroup.events" + + rmdir "${cgroup_path}" +} + +cgroup_set_limits() +{ + [ "${rc_cgroup_mode:-unified}" != unified ] && cgroup1_set_limits + mountinfo -q "${cgroup_root}" || return 0 + + [ -z "${rc_cgroup_settings}" ] && return 0 + + echo "${rc_cgroup_settings}" | while read -r key value; do + [ -z "${key}" ] || [ -z "${value}" ] && continue + [ ! -f "${cgroup_path}/${key}" ] && continue + veinfo "${RC_SVCNAME}: cgroups: setting ${key} to ${value}" + printf "%s" "${value}" > "${cgroup_path}/${key}" + done } # This extracts all pids in a cgroup and puts them in the cgroup_pids @@ -33,13 +113,14 @@ cgroup_get_pids() { local cgroup_procs p cgroup_pids= - cgroup_procs="$(cgroup2_find_path)" - if [ -n "${cgroup_procs}" ]; then - cgroup_procs="${cgroup_procs}/openrc.${RC_SVCNAME}/cgroup.procs" + + if [ "${rc_cgroup_mode:-unified}" != legacy ]; then + cgroup_procs="${cgroup_path}/cgroup.procs" else cgroup_procs="/sys/fs/cgroup/openrc/${RC_SVCNAME}/tasks" fi [ -f "${cgroup_procs}" ] || return 0 + while read -r p; do [ "$p" -eq $$ ] && continue cgroup_pids="${cgroup_pids} ${p}" @@ -47,14 +128,65 @@ cgroup_get_pids() return 0 } -cgroup_running() +cgroup_fallback_cleanup() { + ebegin "Starting fallback cgroups cleanup" + local loops=0 + cgroup_get_pids + if [ -n "${cgroup_pids}" ]; then + kill -s CONT ${cgroup_pids} 2> /dev/null + kill -s "${stopsig:-TERM}" ${cgroup_pids} 2> /dev/null + yesno "${rc_send_sighup:-no}" && kill -s HUP ${cgroup_pids} 2> /dev/null + kill -s "${stopsig:-TERM}" ${cgroup_pids} 2> /dev/null + cgroup_get_pids + while [ -n "${cgroup_pids}" ] && + [ "${loops}" -lt "${rc_timeout_stopsec:-90}" ]; do + loops=$((loops+1)) + sleep 1 + cgroup_get_pids + done + if [ -n "${cgroup_pids}" ] && yesno "${rc_send_sigkill:-yes}"; then + kill -s KILL ${cgroup_pids} 2> /dev/null + fi + fi + eend $? +} + +cgroup_cleanup() +{ + cgroup_running || return 0 + ebegin "Starting cgroups cleanup" + + if [ -f "${cgroup_path}"/cgroup.kill ]; then + # move ourselves to the root cgroup before killing. + printf "%d" 0 > "${cgroup_root}/cgroup.procs" + printf "%d" 1 > "${cgroup_path}"/cgroup.kill + else + cgroup_fallback_cleanup + fi + + cgroup_remove + cgroup_get_pids + [ -z "${cgroup_pids}" ] + eend $? "Unable to stop all processes" + return 0 +} + +## deprecated + +cgroup1_find_path() { - [ -d "/sys/fs/cgroup/unified/${RC_SVCNAME}" ] || - [ -d "/sys/fs/cgroup/${RC_SVCNAME}" ] || - [ -d "/sys/fs/cgroup/openrc/${RC_SVCNAME}" ] + local OIFS name dir result + [ -n "$1" ] || return 0 + OIFS="$IFS" + IFS=":" + while read -r _ name dir; do + [ "$name" = "$1" ] && result="$dir" + done < /proc/1/cgroup + IFS="$OIFS" + printf "%s" "${result}" } -cgroup_set_values() +cgroup1_set_values() { [ -n "$1" ] && [ -n "$2" ] && [ -d "/sys/fs/cgroup/$1" ] || return 0 @@ -98,25 +230,7 @@ cgroup_set_values() return 0 } -cgroup_add_service() -{ - # relocate starting process to the top of the cgroup - # it prevents from unwanted inheriting of the user - # cgroups. But may lead to a problems where that inheriting - # is needed. - for d in /sys/fs/cgroup/* ; do - [ -w "${d}"/tasks ] && printf "%d" 0 > "${d}"/tasks - done - - openrc_cgroup=/sys/fs/cgroup/openrc - if [ -d "$openrc_cgroup" ]; then - cgroup="$openrc_cgroup/$RC_SVCNAME" - mkdir -p "$cgroup" - [ -w "$cgroup/tasks" ] && printf "%d" 0 > "$cgroup/tasks" - fi -} - -cgroup_set_limits() +cgroup1_set_limits() { local blkio="${rc_cgroup_blkio:-$RC_CGROUP_BLKIO}" [ -n "$blkio" ] && cgroup_set_values blkio "$blkio" @@ -151,104 +265,3 @@ cgroup_set_limits() return 0 } -cgroup2_find_path() -{ - if grep -qw cgroup2 /proc/filesystems; then - case "${rc_cgroup_mode:-unified}" in - hybrid) printf "/sys/fs/cgroup/unified" ;; - unified) printf "/sys/fs/cgroup" ;; - esac - fi - return 0 -} - -cgroup2_remove() -{ - local cgroup_path rc_cgroup_path - cgroup_path="$(cgroup2_find_path)" - [ -z "${cgroup_path}" ] && return 0 - rc_cgroup_path="${cgroup_path}/openrc.${RC_SVCNAME}" - [ ! -d "${rc_cgroup_path}" ] || - [ ! -e "${rc_cgroup_path}"/cgroup.events ] && - return 0 - grep -qx "$$" "${rc_cgroup_path}/cgroup.procs" && - printf "%d" 0 > "${cgroup_path}/cgroup.procs" - local key populated vvalue - while read -r key value; do - case "${key}" in - populated) populated=${value} ;; - *) ;; - esac - done < "${rc_cgroup_path}/cgroup.events" - [ "${populated}" = 1 ] && return 0 - rmdir "${rc_cgroup_path}" - return 0 -} - -cgroup2_set_limits() -{ - local cgroup_path - cgroup_path="$(cgroup2_find_path)" - [ -z "${cgroup_path}" ] && return 0 - mountinfo -q "${cgroup_path}"|| return 0 - rc_cgroup_path="${cgroup_path}/openrc.${RC_SVCNAME}" - [ ! -d "${rc_cgroup_path}" ] && mkdir "${rc_cgroup_path}" - [ -f "${rc_cgroup_path}"/cgroup.procs ] && - printf 0 > "${rc_cgroup_path}"/cgroup.procs - [ -z "${rc_cgroup_settings}" ] && return 0 - echo "${rc_cgroup_settings}" | while read -r key value; do - [ -z "${key}" ] && continue - [ -z "${value}" ] && continue - [ ! -f "${rc_cgroup_path}/${key}" ] && continue - veinfo "${RC_SVCNAME}: cgroups: setting ${key} to ${value}" - printf "%s" "${value}" > "${rc_cgroup_path}/${key}" - done - return 0 -} - -cgroup2_kill_cgroup() { - local cgroup_path - cgroup_path="$(cgroup2_find_path)" - [ -z "${cgroup_path}" ] && return 1 - rc_cgroup_path="${cgroup_path}/openrc.${RC_SVCNAME}" - if [ -f "${rc_cgroup_path}"/cgroup.kill ]; then - printf "%d" 1 > "${rc_cgroup_path}"/cgroup.kill - fi - return -} - -cgroup_fallback_cleanup() { - ebegin "Starting fallback cgroups cleanup" - local loops=0 - cgroup_get_pids - if [ -n "${cgroup_pids}" ]; then - kill -s CONT ${cgroup_pids} 2> /dev/null - kill -s "${stopsig:-TERM}" ${cgroup_pids} 2> /dev/null - yesno "${rc_send_sighup:-no}" && - kill -s HUP ${cgroup_pids} 2> /dev/null - kill -s "${stopsig:-TERM}" ${cgroup_pids} 2> /dev/null - cgroup_get_pids - while [ -n "${cgroup_pids}" ] && - [ "${loops}" -lt "${rc_timeout_stopsec:-90}" ]; do - loops=$((loops+1)) - sleep 1 - cgroup_get_pids - done - if [ -n "${cgroup_pids}" ] && yesno "${rc_send_sigkill:-yes}"; then - kill -s KILL ${cgroup_pids} 2> /dev/null - fi - fi - eend $? -} - -cgroup_cleanup() -{ - cgroup_running || return 0 - ebegin "Starting cgroups cleanup" - cgroup2_kill_cgroup || cgroup_fallback_cleanup - cgroup2_remove - cgroup_get_pids - [ -z "${cgroup_pids}" ] - eend $? "Unable to stop all processes" - return 0 -} diff --git a/sh/meson.build b/sh/meson.build index a229600fa..95334570a 100644 --- a/sh/meson.build +++ b/sh/meson.build @@ -29,7 +29,7 @@ scripts_config = [ if os == 'linux' sh += [ - 'rc-cgroup.sh', + 'cgroups.sh', ] scripts_config += [ 'binfmt.sh.in', diff --git a/sh/openrc-run.sh.in b/sh/openrc-run.sh.in index f2a2b1717..b6ccd8f31 100644 --- a/sh/openrc-run.sh.in +++ b/sh/openrc-run.sh.in @@ -57,7 +57,7 @@ sourcex "@LIBEXECDIR@/sh/functions.sh" sourcex "@LIBEXECDIR@/sh/rc-functions.sh" case $RC_SYS in PREFIX|SYSTEMD-NSPAWN) ;; - *) yesno "$RC_USER_SERVICES" || sourcex -e "@LIBEXECDIR@/sh/rc-cgroup.sh";; + *) sourcex -e "@LIBEXECDIR@/sh/cgroups.sh";; esac # Support LiveCD foo @@ -280,20 +280,12 @@ status|describe) ;; [ -n "${rc_ulimit:-$RC_ULIMIT}" ] && apply_ulimits ${rc_ulimit:-$RC_ULIMIT} # Apply cgroups settings if defined - if [ "$(command -v cgroup_add_service)" = "cgroup_add_service" ]; then - if grep -qs /sys/fs/cgroup /proc/1/mountinfo - then - if [ -d /sys/fs/cgroup -a ! -w /sys/fs/cgroup ]; then - eerror "No permission to apply cgroup settings" - break - fi - fi - cgroup_add_service - fi + [ -n "$cgroup_path" ] || break + + cgroup_add_service + cgroup_set_limits - [ "$(command -v cgroup_set_limits)" = "cgroup_set_limits" ] && cgroup_set_limits - [ "$(command -v cgroup2_set_limits)" = "cgroup2_set_limits" ] && cgroup2_set_limits - ;; + break esac eval "printf '%s\n' $required_dirs" | while read _d; do