summaryrefslogtreecommitdiffstats
path: root/src/basic/cgroup-util.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 03:50:42 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 03:50:42 +0000
commit78e9bb837c258ac0ec7712b3d612cc2f407e731e (patch)
treef515d16b6efd858a9aeb5b0ef5d6f90bf288283d /src/basic/cgroup-util.c
parentAdding debian version 255.5-1. (diff)
downloadsystemd-78e9bb837c258ac0ec7712b3d612cc2f407e731e.tar.xz
systemd-78e9bb837c258ac0ec7712b3d612cc2f407e731e.zip
Merging upstream version 256.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/basic/cgroup-util.c')
-rw-r--r--src/basic/cgroup-util.c239
1 files changed, 129 insertions, 110 deletions
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 18b16ec..553ee60 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -22,6 +22,7 @@
#include "log.h"
#include "login-util.h"
#include "macro.h"
+#include "missing_fs.h"
#include "missing_magic.h"
#include "missing_threads.h"
#include "mkdir.h"
@@ -39,6 +40,38 @@
#include "user-util.h"
#include "xattr-util.h"
+int cg_path_open(const char *controller, const char *path) {
+ _cleanup_free_ char *fs = NULL;
+ int r;
+
+ r = cg_get_path(controller, path, /* item=*/ NULL, &fs);
+ if (r < 0)
+ return r;
+
+ return RET_NERRNO(open(fs, O_DIRECTORY|O_CLOEXEC));
+}
+
+int cg_cgroupid_open(int cgroupfs_fd, uint64_t id) {
+ _cleanup_close_ int fsfd = -EBADF;
+
+ if (cgroupfs_fd < 0) {
+ fsfd = open("/sys/fs/cgroup", O_CLOEXEC|O_DIRECTORY);
+ if (fsfd < 0)
+ return -errno;
+
+ cgroupfs_fd = fsfd;
+ }
+
+ cg_file_handle fh = CG_FILE_HANDLE_INIT;
+ CG_FILE_HANDLE_CGROUPID(fh) = id;
+
+ int fd = open_by_handle_at(cgroupfs_fd, &fh.file_handle, O_DIRECTORY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+
static int cg_enumerate_items(const char *controller, const char *path, FILE **ret, const char *item) {
_cleanup_free_ char *fs = NULL;
FILE *f;
@@ -62,7 +95,7 @@ int cg_enumerate_processes(const char *controller, const char *path, FILE **ret)
return cg_enumerate_items(controller, path, ret, "cgroup.procs");
}
-int cg_read_pid(FILE *f, pid_t *ret) {
+int cg_read_pid(FILE *f, pid_t *ret, CGroupFlags flags) {
unsigned long ul;
/* Note that the cgroup.procs might contain duplicates! See cgroups.txt for details. */
@@ -70,27 +103,33 @@ int cg_read_pid(FILE *f, pid_t *ret) {
assert(f);
assert(ret);
- errno = 0;
- if (fscanf(f, "%lu", &ul) != 1) {
+ for (;;) {
+ errno = 0;
+ if (fscanf(f, "%lu", &ul) != 1) {
- if (feof(f)) {
- *ret = 0;
- return 0;
+ if (feof(f)) {
+ *ret = 0;
+ return 0;
+ }
+
+ return errno_or_else(EIO);
}
- return errno_or_else(EIO);
- }
+ if (ul > PID_T_MAX)
+ return -EIO;
- if (ul <= 0)
- return -EIO;
- if (ul > PID_T_MAX)
- return -EIO;
+ /* In some circumstances (e.g. WSL), cgroups might contain unmappable PIDs from other
+ * contexts. These show up as zeros, and depending on the caller, can either be plain
+ * skipped over, or returned as-is. */
+ if (ul == 0 && !FLAGS_SET(flags, CGROUP_DONT_SKIP_UNMAPPED))
+ continue;
- *ret = (pid_t) ul;
- return 1;
+ *ret = (pid_t) ul;
+ return 1;
+ }
}
-int cg_read_pidref(FILE *f, PidRef *ret) {
+int cg_read_pidref(FILE *f, PidRef *ret, CGroupFlags flags) {
int r;
assert(f);
@@ -99,14 +138,22 @@ int cg_read_pidref(FILE *f, PidRef *ret) {
for (;;) {
pid_t pid;
- r = cg_read_pid(f, &pid);
+ r = cg_read_pid(f, &pid, flags);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to read pid from cgroup item: %m");
if (r == 0) {
*ret = PIDREF_NULL;
return 0;
}
+ if (pid == 0)
+ return -EREMOTE;
+
+ if (FLAGS_SET(flags, CGROUP_NO_PIDFD)) {
+ *ret = PIDREF_MAKE_FROM_PID(pid);
+ return 1;
+ }
+
r = pidref_set_pid(ret, pid);
if (r >= 0)
return 1;
@@ -135,7 +182,7 @@ int cg_read_event(
return r;
for (const char *p = content;;) {
- _cleanup_free_ char *line = NULL, *key = NULL, *val = NULL;
+ _cleanup_free_ char *line = NULL, *key = NULL;
const char *q;
r = extract_first_word(&p, &line, "\n", 0);
@@ -154,12 +201,7 @@ int cg_read_event(
if (!streq(key, event))
continue;
- val = strdup(q);
- if (!val)
- return -ENOMEM;
-
- *ret = TAKE_PTR(val);
- return 0;
+ return strdup_to(ret, q);
}
}
@@ -234,20 +276,13 @@ int cg_read_subgroup(DIR *d, char **ret) {
assert(ret);
FOREACH_DIRENT_ALL(de, d, return -errno) {
- char *b;
-
if (de->d_type != DT_DIR)
continue;
if (dot_or_dot_dot(de->d_name))
continue;
- b = strdup(de->d_name);
- if (!b)
- return -ENOMEM;
-
- *ret = b;
- return 1;
+ return strdup_to_full(ret, de->d_name);
}
*ret = NULL;
@@ -317,14 +352,14 @@ static int cg_kill_items(
if (r == -ENOENT)
break;
if (r < 0)
- return RET_GATHER(ret, r);
+ return RET_GATHER(ret, log_debug_errno(r, "Failed to enumerate cgroup items: %m"));
for (;;) {
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
- r = cg_read_pidref(f, &pidref);
+ r = cg_read_pidref(f, &pidref, flags);
if (r < 0)
- return RET_GATHER(ret, r);
+ return RET_GATHER(ret, log_debug_errno(r, "Failed to read pidref from cgroup '%s': %m", path));
if (r == 0)
break;
@@ -340,7 +375,7 @@ static int cg_kill_items(
/* If we haven't killed this process yet, kill it */
r = pidref_kill(&pidref, sig);
if (r < 0 && r != -ESRCH)
- RET_GATHER(ret, r);
+ RET_GATHER(ret, log_debug_errno(r, "Failed to kill process with pid " PID_FMT " from cgroup '%s': %m", pidref.pid, path));
if (r >= 0) {
if (flags & CGROUP_SIGCONT)
(void) pidref_kill(&pidref, SIGCONT);
@@ -379,6 +414,8 @@ int cg_kill(
int r, ret;
r = cg_kill_items(path, sig, flags, s, log_kill, userdata, "cgroup.procs");
+ if (r < 0)
+ log_debug_errno(r, "Failed to kill processes in cgroup '%s' item cgroup.procs: %m", path);
if (r < 0 || sig != SIGKILL)
return r;
@@ -393,9 +430,13 @@ int cg_kill(
if (r == 0)
return ret;
- r = cg_kill_items(path, sig, flags, s, log_kill, userdata, "cgroup.threads");
+ /* Opening pidfds for non thread group leaders only works from 6.9 onwards with PIDFD_THREAD. On
+ * older kernels or without PIDFD_THREAD pidfd_open() fails with EINVAL. Since we might read non
+ * thread group leader IDs from cgroup.threads, we set CGROUP_NO_PIDFD to avoid trying open pidfd's
+ * for them and instead use the regular pid. */
+ r = cg_kill_items(path, sig, flags|CGROUP_NO_PIDFD, s, log_kill, userdata, "cgroup.threads");
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to kill processes in cgroup '%s' item cgroup.threads: %m", path);
return r > 0 || ret > 0;
}
@@ -418,7 +459,7 @@ int cg_kill_kernel_sigkill(const char *path) {
r = write_string_file(killfile, "1", WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to write to cgroup.kill for cgroup '%s': %m", path);
return 0;
}
@@ -455,7 +496,7 @@ int cg_kill_recursive(
r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, path, &d);
if (r < 0) {
if (r != -ENOENT)
- RET_GATHER(ret, r);
+ RET_GATHER(ret, log_debug_errno(r, "Failed to enumerate cgroup '%s' subgroups: %m", path));
return ret;
}
@@ -465,7 +506,7 @@ int cg_kill_recursive(
r = cg_read_subgroup(d, &fn);
if (r < 0) {
- RET_GATHER(ret, r);
+ RET_GATHER(ret, log_debug_errno(r, "Failed to read subgroup from cgroup '%s': %m", path));
break;
}
if (r == 0)
@@ -476,6 +517,8 @@ int cg_kill_recursive(
return -ENOMEM;
r = cg_kill_recursive(p, sig, flags, s, log_kill, userdata);
+ if (r < 0)
+ log_debug_errno(r, "Failed to recursively kill processes in cgroup '%s': %m", p);
if (r != 0 && ret >= 0)
ret = r;
}
@@ -484,7 +527,7 @@ int cg_kill_recursive(
if (FLAGS_SET(flags, CGROUP_REMOVE)) {
r = cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, path);
if (!IN_SET(r, -ENOENT, -EBUSY))
- RET_GATHER(ret, r);
+ RET_GATHER(ret, log_debug_errno(r, "Failed to remove cgroup '%s': %m", path));
}
return ret;
@@ -917,7 +960,7 @@ int cg_is_empty(const char *controller, const char *path) {
if (r < 0)
return r;
- r = cg_read_pid(f, &pid);
+ r = cg_read_pid(f, &pid, CGROUP_DONT_SKIP_UNMAPPED);
if (r < 0)
return r;
@@ -1125,44 +1168,29 @@ int cg_pid_get_path_shifted(pid_t pid, const char *root, char **ret_cgroup) {
if (r < 0)
return r;
- if (c == raw)
+ if (c == raw) {
*ret_cgroup = TAKE_PTR(raw);
- else {
- char *n;
-
- n = strdup(c);
- if (!n)
- return -ENOMEM;
-
- *ret_cgroup = n;
+ return 0;
}
- return 0;
+ return strdup_to(ret_cgroup, c);
}
int cg_path_decode_unit(const char *cgroup, char **ret_unit) {
- char *c, *s;
- size_t n;
-
assert(cgroup);
assert(ret_unit);
- n = strcspn(cgroup, "/");
+ size_t n = strcspn(cgroup, "/");
if (n < 3)
return -ENXIO;
- c = strndupa_safe(cgroup, n);
+ char *c = strndupa_safe(cgroup, n);
c = cg_unescape(c);
if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
return -ENXIO;
- s = strdup(c);
- if (!s)
- return -ENOMEM;
-
- *ret_unit = s;
- return 0;
+ return strdup_to(ret_unit, c);
}
static bool valid_slice_name(const char *p, size_t n) {
@@ -1431,7 +1459,7 @@ int cg_pid_get_machine_name(pid_t pid, char **ret_machine) {
int cg_path_get_cgroupid(const char *path, uint64_t *ret) {
cg_file_handle fh = CG_FILE_HANDLE_INIT;
- int mnt_id = -1;
+ int mnt_id;
assert(path);
assert(ret);
@@ -1445,6 +1473,20 @@ int cg_path_get_cgroupid(const char *path, uint64_t *ret) {
return 0;
}
+int cg_fd_get_cgroupid(int fd, uint64_t *ret) {
+ cg_file_handle fh = CG_FILE_HANDLE_INIT;
+ int mnt_id = -1;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ if (name_to_handle_at(fd, "", &fh.file_handle, &mnt_id, AT_EMPTY_PATH) < 0)
+ return -errno;
+
+ *ret = CG_FILE_HANDLE_CGROUPID(fh);
+ return 0;
+}
+
int cg_path_get_session(const char *path, char **ret_session) {
_cleanup_free_ char *unit = NULL;
char *start, *end;
@@ -1467,17 +1509,10 @@ int cg_path_get_session(const char *path, char **ret_session) {
if (!session_id_valid(start))
return -ENXIO;
- if (ret_session) {
- char *rr;
-
- rr = strdup(start);
- if (!rr)
- return -ENOMEM;
-
- *ret_session = rr;
- }
+ if (!ret_session)
+ return 0;
- return 0;
+ return strdup_to(ret_session, start);
}
int cg_pid_get_session(pid_t pid, char **ret_session) {
@@ -1534,34 +1569,26 @@ int cg_path_get_slice(const char *p, char **ret_slice) {
assert(p);
assert(ret_slice);
- /* Finds the right-most slice unit from the beginning, but
- * stops before we come to the first non-slice unit. */
+ /* Finds the right-most slice unit from the beginning, but stops before we come to
+ * the first non-slice unit. */
for (;;) {
- size_t n;
-
- p += strspn(p, "/");
-
- n = strcspn(p, "/");
- if (!valid_slice_name(p, n)) {
-
- if (!e) {
- char *s;
+ const char *s;
+ int n;
- s = strdup(SPECIAL_ROOT_SLICE);
- if (!s)
- return -ENOMEM;
+ n = path_find_first_component(&p, /* accept_dot_dot = */ false, &s);
+ if (n < 0)
+ return n;
+ if (!valid_slice_name(s, n))
+ break;
- *ret_slice = s;
- return 0;
- }
+ e = s;
+ }
- return cg_path_decode_unit(e, ret_slice);
- }
+ if (e)
+ return cg_path_decode_unit(e, ret_slice);
- e = p;
- p += n;
- }
+ return strdup_to(ret_slice, SPECIAL_ROOT_SLICE);
}
int cg_pid_get_slice(pid_t pid, char **ret_slice) {
@@ -1714,15 +1741,8 @@ int cg_slice_to_path(const char *unit, char **ret) {
assert(unit);
assert(ret);
- if (streq(unit, SPECIAL_ROOT_SLICE)) {
- char *x;
-
- x = strdup("");
- if (!x)
- return -ENOMEM;
- *ret = x;
- return 0;
- }
+ if (streq(unit, SPECIAL_ROOT_SLICE))
+ return strdup_to(ret, "");
if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
return -EINVAL;
@@ -2141,15 +2161,14 @@ int cg_kernel_controllers(Set **ret) {
_cleanup_free_ char *controller = NULL;
int enabled = 0;
- errno = 0;
if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
+ if (ferror(f))
+ return -errno;
+
if (feof(f))
break;
- if (ferror(f))
- return errno_or_else(EIO);
-
return -EBADMSG;
}