cgroup-util: Add get_user_unit_path helpers

This is similar to get_user_unit_name, but instead of returning just the
unit name it returns the full cgroup path up to the user unit.

This also refactors the existing get_unit_path helper, so that the bulk
of the logic can be shared. Additionally, the refactor means that we
only duplicate exactly as many bytes as we need, rather than duplicating
the entire cgroup path (which may be quite long!) and putting a
null-terminator in the middle of the memory allocation.
This commit is contained in:
Adrian Vovk committed 2026-11-04 20:20:27 -05:00
1 parent db446d004f
commit 478287a5e6
3 files changed
+189 -18

No files matched your search

+156 -17
View File
@@ -765,21 +765,37 @@ int cg_pidref_get_path_shifted(const PidRef *pidref, const char *cached_root, ch
return strdup_to(ret_cgroup, c);
}
int cg_path_decode_unit(const char *cgroup, char **ret_unit) {
static const char* skip_unit(const char *cgroup) {
assert(cgroup);
size_t n = strcspn(cgroup, "/");
if (n < 3)
return -ENXIO;
return NULL;
char *c = strndupa_safe(cgroup, n);
c = cg_unescape(c);
if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
return NULL;
return cgroup + n;
}
int cg_path_decode_unit(const char *cgroup, char **ret_unit) {
assert(cgroup);
const char *end = skip_unit(cgroup);
if (!end)
return -ENXIO;
if (ret_unit)
return strdup_to(ret_unit, c);
if (ret_unit) {
const char *start = cg_unescape(cgroup);
_cleanup_free_ char *unit = strndup(start, end - start);
if (!unit)
return -ENOMEM;
*ret_unit = TAKE_PTR(unit);
}
return 0;
}
@@ -855,25 +871,24 @@ int cg_path_get_unit_full(const char *path, char **ret_unit, char **ret_subgroup
return 0;
}
int cg_path_get_unit_path(const char *path, char **ret) {
_cleanup_free_ char *path_copy = NULL;
char *unit_name;
static const char* skip_unit_path(const char *cgroup) {
const char *end = skip_slices(cgroup);
return skip_unit(end);
}
int cg_path_get_unit_path(const char *path, char **ret_path) {
assert(path);
assert(ret);
assert(ret_path);
path_copy = strdup(path);
if (!path_copy)
return -ENOMEM;
unit_name = (char*) skip_slices(path_copy);
unit_name[strcspn(unit_name, "/")] = 0;
if (!unit_name_is_valid(cg_unescape(unit_name), UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
const char *end = skip_unit_path(path);
if (!end)
return -ENXIO;
*ret = TAKE_PTR(path_copy);
_cleanup_free_ char *unit_path = strndup(path, end - path);
if (!unit_path)
return -ENOMEM;
*ret_path = TAKE_PTR(unit_path);
return 0;
}
@@ -888,6 +903,34 @@ int cg_pid_get_unit_full(pid_t pid, char **ret_unit, char **ret_subgroup) {
return cg_path_get_unit_full(cgroup, ret_unit, ret_subgroup);
}
int cg_pid_get_unit_path(pid_t pid, char **ret_path) {
int r;
assert(pid >= 0);
assert(ret_path);
_cleanup_free_ char *cgroup = NULL;
r = cg_pid_get_path(pid, &cgroup);
if (r < 0)
return r;
const char *end;
r = cg_shift_path(cgroup, /* cached_root= */ NULL, &end);
if (r < 0)
return r;
end = skip_unit_path(end);
if (!end)
return -ENXIO;
_cleanup_free_ char *unit_path = strndup(cgroup, end - cgroup);
if (!unit_path)
return -ENOMEM;
*ret_path = TAKE_PTR(unit_path);
return 0;
}
int cg_pidref_get_unit_full(const PidRef *pidref, char **ret_unit, char **ret_subgroup) {
int r;
@@ -912,6 +955,28 @@ int cg_pidref_get_unit_full(const PidRef *pidref, char **ret_unit, char **ret_su
return 0;
}
int cg_pidref_get_unit_path(const PidRef *pidref, char **ret_path) {
int r;
if (!pidref_is_set(pidref))
return -ESRCH;
if (pidref_is_remote(pidref))
return -EREMOTE;
_cleanup_free_ char *unit_path = NULL;
r = cg_pid_get_unit_path(pidref->pid, &unit_path);
if (r < 0)
return r;
r = pidref_verify(pidref);
if (r < 0)
return r;
if (ret_path)
*ret_path = TAKE_PTR(unit_path);
return 0;
}
static const char* skip_session(const char *p) {
size_t n;
@@ -1011,6 +1076,30 @@ int cg_path_get_user_unit_full(const char *path, char **ret_unit, char **ret_sub
return cg_path_get_unit_full(t, ret_unit, ret_subgroup);
}
static const char* skip_user_unit_path(const char *cgroup) {
const char *end = skip_user_prefix(cgroup);
if (!end)
return NULL;
return skip_unit_path(end);
}
int cg_path_get_user_unit_path(const char *path, char **ret_path) {
assert(path);
assert(ret_path);
const char *end = skip_user_unit_path(path);
if (!end)
return -ENXIO;
_cleanup_free_ char *unit_path = strndup(path, end - path);
if (!unit_path)
return -ENOMEM;
*ret_path = TAKE_PTR(unit_path);
return 0;
}
int cg_pid_get_user_unit_full(pid_t pid, char **ret_unit, char **ret_subgroup) {
int r;
@@ -1022,6 +1111,34 @@ int cg_pid_get_user_unit_full(pid_t pid, char **ret_unit, char **ret_subgroup) {
return cg_path_get_user_unit_full(cgroup, ret_unit, ret_subgroup);
}
int cg_pid_get_user_unit_path(pid_t pid, char **ret_path) {
int r;
assert(pid >= 0);
assert(ret_path);
_cleanup_free_ char *cgroup = NULL;
r = cg_pid_get_path(pid, &cgroup);
if (r < 0)
return r;
const char *end;
r = cg_shift_path(cgroup, /* cached_root= */ NULL, &end);
if (r < 0)
return r;
end = skip_user_unit_path(end);
if (!end)
return -ENXIO;
_cleanup_free_ char *unit_path = strndup(cgroup, end - cgroup);
if (!unit_path)
return -ENOMEM;
*ret_path = TAKE_PTR(unit_path);
return 0;
}
int cg_pidref_get_user_unit_full(const PidRef *pidref, char **ret_unit, char **ret_subgroup) {
int r;
@@ -1046,6 +1163,28 @@ int cg_pidref_get_user_unit_full(const PidRef *pidref, char **ret_unit, char **r
return 0;
}
int cg_pidref_get_user_unit_path(const PidRef *pidref, char **ret_path) {
int r;
if (!pidref_is_set(pidref))
return -ESRCH;
if (pidref_is_remote(pidref))
return -EREMOTE;
_cleanup_free_ char *unit_path = NULL;
r = cg_pid_get_user_unit_path(pidref->pid, &unit_path);
if (r < 0)
return r;
r = pidref_verify(pidref);
if (r < 0)
return r;
if (ret_path)
*ret_path = TAKE_PTR(unit_path);
return 0;
}
int cg_path_get_machine_name(const char *path, char **ret_machine) {
_cleanup_free_ char *u = NULL;
const char *sl;
+6 -1
View File
@@ -185,7 +185,8 @@ int cg_path_get_unit_full(const char *path, char **ret_unit, char **ret_subgroup
static inline int cg_path_get_unit(const char *path, char **ret_unit) {
return cg_path_get_unit_full(path, ret_unit, /* ret_subgroup= */ NULL);
}
int cg_path_get_unit_path(const char *path, char **ret_unit);
int cg_path_get_unit_path(const char *path, char **ret_path);
int cg_path_get_user_unit_path(const char *path, char **ret_path);
int cg_path_get_user_unit_full(const char *path, char **ret_unit, char **ret_subgroup);
static inline int cg_path_get_user_unit(const char *path, char **ret_unit) {
return cg_path_get_user_unit_full(path, ret_unit, /* ret_subgroup= */ NULL);
@@ -206,18 +207,22 @@ int cg_pid_get_unit_full(pid_t pid, char **ret_unit, char **ret_subgroup);
static inline int cg_pid_get_unit(pid_t pid, char **ret_unit) {
return cg_pid_get_unit_full(pid, ret_unit, /* ret_subgroup= */ NULL);
}
int cg_pid_get_unit_path(pid_t pid, char **ret_path);
int cg_pidref_get_unit_full(const PidRef *pidref, char **ret_unit, char **ret_subgroup);
static inline int cg_pidref_get_unit(const PidRef *pidref, char **ret_unit) {
return cg_pidref_get_unit_full(pidref, ret_unit, /* ret_subgroup= */ NULL);
}
int cg_pidref_get_unit_path(const PidRef *pidref, char **ret_path);
int cg_pid_get_user_unit_full(pid_t pid, char **ret_unit, char **ret_subgroup);
static inline int cg_pid_get_user_unit(pid_t pid, char **ret_unit) {
return cg_pid_get_user_unit_full(pid, ret_unit, /* ret_subgroup= */ NULL);
}
int cg_pid_get_user_unit_path(pid_t pid, char **ret_path);
int cg_pidref_get_user_unit_full(const PidRef *pidref, char **ret_unit, char **ret_subgroup);
static inline int cg_pidref_get_user_unit(const PidRef *pidref, char **ret_unit) {
return cg_pidref_get_user_unit_full(pidref, ret_unit, /* ret_subgroup= */ NULL);
}
int cg_pidref_get_user_unit_path(const PidRef *pidref, char **ret_path);
int cg_pid_get_machine_name(pid_t pid, char **ret_machine);
int cg_pid_get_slice(pid_t pid, char **ret_slice);
int cg_pid_get_user_slice(pid_t pid, char **ret_slice);
+27
View File
@@ -148,6 +148,33 @@ TEST(path_get_user_unit) {
check_p_g_u_u("/capsule.slice/capsule@#.service/foo.slice/foo-bar.slice/run-p9-i1.service", -ENXIO, /* result= */ NULL);
}
static void check_p_g_u_u_p(const char *path, int code, const char *result) {
_cleanup_free_ char *unit_path = NULL;
int r;
r = cg_path_get_user_unit_path(path, &unit_path);
printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit_path, r, strnull(result), code);
ASSERT_EQ(r, code);
ASSERT_STREQ(unit_path, result);
}
TEST(path_get_user_unit_path) {
check_p_g_u_u_p("/user.slice/user-1000.slice/user@1000.service/app.slice/foobar.service", 0, "/user.slice/user-1000.slice/user@1000.service/app.slice/foobar.service");
check_p_g_u_u_p("/user.slice/user-1000.slice/user@1000.service/app.slice/foobar.service/", 0, "/user.slice/user-1000.slice/user@1000.service/app.slice/foobar.service");
check_p_g_u_u_p("/user.slice/user-1000.slice/user@1000.service/app.slice/foobar.service/asdf", 0, "/user.slice/user-1000.slice/user@1000.service/app.slice/foobar.service");
check_p_g_u_u_p("/user.slice/user-1000.slice/user@1000.service/app.slice/foobar@baz.service", 0, "/user.slice/user-1000.slice/user@1000.service/app.slice/foobar@baz.service");
check_p_g_u_u_p("/user.slice/user-1000.slice/user@1000.service/app.slice/foobar@baz.service/asdf", 0, "/user.slice/user-1000.slice/user@1000.service/app.slice/foobar@baz.service");
check_p_g_u_u_p("/user.slice/user-1000.slice/user@1000.service/app.slice/_cpu.service", 0, "/user.slice/user-1000.slice/user@1000.service/app.slice/_cpu.service");
check_p_g_u_u_p("/user.slice/user-1000.slice/user@1000.service/app.slice/_cpu.service/asdf", 0, "/user.slice/user-1000.slice/user@1000.service/app.slice/_cpu.service");
check_p_g_u_u_p("/user.slice/user-1000.slice/user@1000.service/app.slice/example.slice/foobar.service", 0, "/user.slice/user-1000.slice/user@1000.service/app.slice/example.slice/foobar.service");
check_p_g_u_u_p("/capsule.slice/capsule@test.service/app.slice/foobar.service", 0, "/capsule.slice/capsule@test.service/app.slice/foobar.service");
check_p_g_u_u_p("/user.slice/user-1000.slice/session-2.scope/foobar.service", 0, "/user.slice/user-1000.slice/session-2.scope/foobar.service");
check_p_g_u_u_p("sadfdsafsda", -ENXIO, NULL);
check_p_g_u_u_p("/user.slice/user-1000.slice/user@1000.service/app.slice/foo###bar.service", -ENXIO, NULL);
check_p_g_u_u_p("/user.slice/user-1000.slice/user@1000.service/app.slice/foobar@.service", -ENXIO, NULL);
check_p_g_u_u_p("/system.slice/foobar.service", -ENXIO, NULL);
}
static void check_p_g_s(const char *path, int code, const char *result) {
_cleanup_free_ char *s = NULL;