From: u2386 Date: Sun, 11 Jun 2023 16:16:57 +0000 (+0000) Subject: nsenter: add option `-c` to join the cgroup of target process X-Git-Tag: v2.40-rc1~344^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b40650b71a742c188d0986a1162a5730ecb95510;p=thirdparty%2Futil-linux.git nsenter: add option `-c` to join the cgroup of target process This commit adds support for the -c or --join-cgroup option in nsenter, allowing a new process to join the cgroup of target process. Example: Setup the target process: $ podman run --rm -d docker.io/golang sleep 10000 51a89deb6baf6d $ podman inspect --format '{{ .State.Pid }}' 51a89deb6baf6d6 216054 Enter the cgroup namespace of target process without option -c: $ sudo ./nsenter -C -U -t 216054 sh -c "cat /proc/self/cgroup" 0::/../../../../session-899.scope Enter the cgroup namespace of target process with option -c: $ sudo ./nsenter -c -C -U -t 216054 sh -c "cat /proc/self/cgroup" 0::/ Reviewed-by: Karel Zak Reviewed-by: Thomas Weißschuh Signed-off-by: u2386 --- diff --git a/bash-completion/nsenter b/bash-completion/nsenter index 3ffd813714..19f18e7c15 100644 --- a/bash-completion/nsenter +++ b/bash-completion/nsenter @@ -53,6 +53,7 @@ _nsenter_module() --wdns= --env --no-fork + --join-cgroup --help --version " diff --git a/include/pathnames.h b/include/pathnames.h index 56f64c3870..9b75338de4 100644 --- a/include/pathnames.h +++ b/include/pathnames.h @@ -226,5 +226,7 @@ #define _PATH_DEV_RFKILL "/dev/rfkill" #define _PATH_SYS_RFKILL "/sys/class/rfkill" +/* cgroup path */ +#define _PATH_SYS_CGROUP "/sys/fs/cgroup" #endif /* PATHNAMES_H */ diff --git a/sys-utils/nsenter.1.adoc b/sys-utils/nsenter.1.adoc index b7e92d6b19..58dd125482 100644 --- a/sys-utils/nsenter.1.adoc +++ b/sys-utils/nsenter.1.adoc @@ -141,6 +141,9 @@ Do not fork before exec'ing the specified program. By default, when entering a P *-Z*, *--follow-context*:: Set the SELinux security context used for executing a new process according to already running process specified by *--target* PID. (The util-linux has to be compiled with SELinux support otherwise the option is unavailable.) +*-c*, *--join-cgroup*:: +Add the initiated process to the cgroup of the target process. + include::man-common/help-version.adoc[] == NOTES diff --git a/sys-utils/nsenter.c b/sys-utils/nsenter.c index 6f9558052c..cdbdb482f7 100644 --- a/sys-utils/nsenter.c +++ b/sys-utils/nsenter.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #ifdef HAVE_LINUX_NSFS_H @@ -54,6 +55,8 @@ #include "all-io.h" #include "env.h" #include "caputils.h" +#include "statfs_magic.h" +#include "pathnames.h" static struct namespace_file { int nstype; @@ -111,6 +114,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" -W, --wdns set the working directory in namespace\n"), out); fputs(_(" -e, --env inherit environment variables from target process\n"), out); fputs(_(" -F, --no-fork do not fork before exec'ing \n"), out); + fputs(_(" -c, --join-cgroup join the cgroup of the target process\n"), out); #ifdef HAVE_LIBSELINUX fputs(_(" -Z, --follow-context set SELinux context according to --target PID\n"), out); #endif @@ -127,6 +131,7 @@ static int root_fd = -1; static int wd_fd = -1; static int env_fd = -1; static int uid_gid_fd = -1; +static int cgroup_procs_fd = -1; static void set_parent_user_ns_fd(void) { @@ -202,6 +207,50 @@ static int get_ns_ino(const char *path, ino_t *ino) return 0; } +static void open_cgroup_procs(void) +{ + char *buf = NULL, *path = NULL; + int cgroup_fd; + char fdpath[PATH_MAX]; + + open_target_fd(&cgroup_fd, "cgroup", optarg); + + if (read_all_alloc(cgroup_fd, &buf) < 1) + err(EXIT_FAILURE, _("failed to get cgroup path")); + + path = strrchr(strtok(buf, "\n"), ':'); + if (!path) + err(EXIT_FAILURE, _("failed to get cgroup path")); + path++; + + snprintf(fdpath, sizeof(fdpath), _PATH_SYS_CGROUP "/%s/cgroup.procs", path); + if ((cgroup_procs_fd = open(fdpath, O_WRONLY | O_APPEND)) < 0) + err(EXIT_FAILURE, _("failed to open cgroup.procs")); +} + +static int is_cgroup2(void) +{ + struct statfs fs_stat; + int rc; + + rc = statfs(_PATH_SYS_CGROUP, &fs_stat); + if (rc) + err(EXIT_FAILURE, _("statfs %s failed"), _PATH_SYS_CGROUP); + return F_TYPE_EQUAL(fs_stat.f_type, STATFS_CGROUP2_MAGIC); +} + +static void join_into_cgroup(void) +{ + pid_t pid; + char buf[ sizeof(stringify_value(UINT32_MAX)) ]; + int len; + + pid = getpid(); + len = snprintf(buf, sizeof(buf), "%zu", (size_t) pid); + if (write_all(cgroup_procs_fd, buf, len)) + err(EXIT_FAILURE, _("write cgroup.procs failed")); +} + static int is_usable_namespace(pid_t target, const struct namespace_file *nsfile) { char path[PATH_MAX]; @@ -294,6 +343,7 @@ int main(int argc, char *argv[]) { "wdns", optional_argument, NULL, 'W' }, { "env", no_argument, NULL, 'e' }, { "no-fork", no_argument, NULL, 'F' }, + { "join-cgroup", no_argument, NULL, 'c'}, { "preserve-credentials", no_argument, NULL, OPT_PRESERVE_CRED }, { "keep-caps", no_argument, NULL, OPT_KEEPCAPS }, { "user-parent", no_argument, NULL, OPT_USER_PARENT}, @@ -312,7 +362,7 @@ int main(int argc, char *argv[]) int c, pass, namespaces = 0, setgroups_nerrs = 0, preserve_cred = 0; bool do_rd = false, do_wd = false, do_uid = false, force_uid = false, do_gid = false, force_gid = false, do_env = false, do_all = false, - do_user_parent = false; + do_join_cgroup = false, do_user_parent = false; int do_fork = -1; /* unknown yet */ char *wdns = NULL; uid_t uid = 0; @@ -329,7 +379,7 @@ int main(int argc, char *argv[]) close_stdout_atexit(); while ((c = - getopt_long(argc, argv, "+ahVt:m::u::i::n::p::C::U::T::S:G:r::w::W::eFZ", + getopt_long(argc, argv, "+ahVt:m::u::i::n::p::C::U::T::S:G:r::w::W::ecFZ", longopts, NULL)) != -1) { err_exclusive_options(c, longopts, excl, excl_st); @@ -407,6 +457,9 @@ int main(int argc, char *argv[]) case 'F': do_fork = 0; break; + case 'c': + do_join_cgroup = true; + break; case 'r': if (optarg) open_target_fd(&root_fd, "root", optarg); @@ -491,6 +544,11 @@ int main(int argc, char *argv[]) open_target_fd(&env_fd, "environ", NULL); if (do_uid || do_gid) open_target_fd(&uid_gid_fd, "", NULL); + if (do_join_cgroup) { + if (!is_cgroup2()) + errx(EXIT_FAILURE, _("--join-cgroup is only supported in cgroup v2")); + open_cgroup_procs(); + } /* * Get parent userns from any available ns. @@ -598,6 +656,10 @@ int main(int argc, char *argv[]) close(env_fd); } + // Join into the target cgroup + if (cgroup_procs_fd >= 0) + join_into_cgroup(); + if (uid_gid_fd >= 0) { struct stat st;