sd-login: Add get_{user_}unit_cgroup helpers

This functionality is generally useful to external clients too: given a
pid/pidfd/peer, it's useful to find the cgroup path of the unit.
Especially since we're going to start tagging information about the unit
onto the cgroup xattrs
This commit is contained in:
Adrian Vovk committed 2026-11-04 20:20:27 -05:00
1 parent 478287a5e6
commit e61b656a9f
6 files changed
+246 -14

No files matched your search

+7 -1
View File
@@ -925,23 +925,29 @@ manpages = [
'sd_peer_get_session',
'sd_peer_get_slice',
'sd_peer_get_unit',
'sd_peer_get_unit_cgroup',
'sd_peer_get_user_slice',
'sd_peer_get_user_unit',
'sd_peer_get_user_unit_cgroup',
'sd_pid_get_cgroup',
'sd_pid_get_machine_name',
'sd_pid_get_session',
'sd_pid_get_slice',
'sd_pid_get_unit',
'sd_pid_get_unit_cgroup',
'sd_pid_get_user_slice',
'sd_pid_get_user_unit',
'sd_pid_get_user_unit_cgroup',
'sd_pidfd_get_cgroup',
'sd_pidfd_get_machine_name',
'sd_pidfd_get_owner_uid',
'sd_pidfd_get_session',
'sd_pidfd_get_slice',
'sd_pidfd_get_unit',
'sd_pidfd_get_unit_cgroup',
'sd_pidfd_get_user_slice',
'sd_pidfd_get_user_unit'],
'sd_pidfd_get_user_unit',
'sd_pidfd_get_user_unit_cgroup'],
'HAVE_PAM'],
['sd_pidfd_get_inode_id', '3', [], ''],
['sd_seat_get_active',
+73 -11
View File
@@ -25,6 +25,8 @@
<refname>sd_pid_get_slice</refname>
<refname>sd_pid_get_user_slice</refname>
<refname>sd_pid_get_cgroup</refname>
<refname>sd_pid_get_unit_cgroup</refname>
<refname>sd_pid_get_user_unit_cgroup</refname>
<refname>sd_pidfd_get_owner_uid</refname>
<refname>sd_pidfd_get_session</refname>
<refname>sd_pidfd_get_user_unit</refname>
@@ -33,6 +35,8 @@
<refname>sd_pidfd_get_slice</refname>
<refname>sd_pidfd_get_user_slice</refname>
<refname>sd_pidfd_get_cgroup</refname>
<refname>sd_pidfd_get_unit_cgroup</refname>
<refname>sd_pidfd_get_user_unit_cgroup</refname>
<refname>sd_peer_get_owner_uid</refname>
<refname>sd_peer_get_session</refname>
<refname>sd_peer_get_user_unit</refname>
@@ -41,6 +45,8 @@
<refname>sd_peer_get_slice</refname>
<refname>sd_peer_get_user_slice</refname>
<refname>sd_peer_get_cgroup</refname>
<refname>sd_peer_get_unit_cgroup</refname>
<refname>sd_peer_get_user_unit_cgroup</refname>
<refpurpose>Determine the owner uid of the user unit or session,
or the session, user unit, system unit, container/VM or slice that
a specific PID or socket peer belongs to</refpurpose>
@@ -98,6 +104,18 @@
<paramdef>char **<parameter>cgroup</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_pid_get_unit_cgroup</function></funcdef>
<paramdef>pid_t <parameter>pid</parameter></paramdef>
<paramdef>char **<parameter>cgroup</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_pid_get_user_unit_cgroup</function></funcdef>
<paramdef>pid_t <parameter>pid</parameter></paramdef>
<paramdef>char **<parameter>cgroup</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_pidfd_get_owner_uid</function></funcdef>
<paramdef>int <parameter>pidfd</parameter></paramdef>
@@ -146,6 +164,18 @@
<paramdef>char **<parameter>cgroup</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_pidfd_get_unit_cgroup</function></funcdef>
<paramdef>int <parameter>pidfd</parameter></paramdef>
<paramdef>char **<parameter>cgroup</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_pidfd_get_user_unit_cgroup</function></funcdef>
<paramdef>int <parameter>pidfd</parameter></paramdef>
<paramdef>char **<parameter>cgroup</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_peer_get_owner_uid</function></funcdef>
<paramdef>int <parameter>fd</parameter></paramdef>
@@ -193,6 +223,18 @@
<paramdef>int <parameter>fd</parameter></paramdef>
<paramdef>char **<parameter>cgroup</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_peer_get_unit_cgroup</function></funcdef>
<paramdef>int <parameter>fd</parameter></paramdef>
<paramdef>char **<parameter>cgroup</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_peer_get_user_unit_cgroup</function></funcdef>
<paramdef>int <parameter>fd</parameter></paramdef>
<paramdef>char **<parameter>cgroup</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
@@ -266,7 +308,17 @@
<para><function>sd_pid_get_cgroup()</function> returns the control group path of the specified process,
relative to the root of the hierarchy. Returns the path without trailing slash, except for processes
located in the root control group, where "/" is returned. To find the actual control group path
in the file system, the returned path needs to be prefixed with <filename>/sys/fs/cgroup/</filename>.</para>
in the file system, the returned path needs to be prefixed with <filename>/sys/fs/cgroup/</filename>.
The returned string needs to be freed with the libc
<citerefentry project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
call after use.</para>
<para><function>sd_pid_get_unit_cgroup()</function> is like <function>sd_pid_get_cgroup()</function>,
but returns the control group path of the systemd system unit that contains the process. If the process
is not part of a systemd unit, then this function fails with <constant>-ENODATA</constant>.</para>
<para><function>sd_pid_get_user_unit_cgroup()</function> is like <function>sd_pid_get_unit_cgroup()</function>,
but operates on systemd user units (as managed by the user's systemd instance).</para>
<para>If the <varname>pid</varname> parameter of any of these
functions is passed as 0, the operation is executed for the
@@ -278,12 +330,14 @@
<function>sd_pidfd_get_unit()</function>,
<function>sd_pidfd_get_machine_name()</function>,
<function>sd_pidfd_get_slice()</function>,
<function>sd_pidfd_get_user_slice()</function> and
<function>sd_pidfd_get_cgroup()</function> calls operate similarly to their PID counterparts, but accept a
<constant>PIDFD</constant> instead of a <constant>PID</constant>, which means they are not subject to recycle
race conditions as the process is pinned by the file descriptor during the whole duration of the invocation.
Note that these require a kernel that supports <constant>PIDFD</constant>. A suitable file descriptor may be
acquired via
<function>sd_pidfd_get_user_slice()</function>,
<function>sd_pidfd_get_cgroup()</function>,
<function>sd_pidfd_get_unit_cgroup()</function>, and
<function>sd_pidfd_get_user_unit_cgroup()</function>, calls operate similarly to their PID counterparts,
but accept a <constant>PIDFD</constant> instead of a <constant>PID</constant>, which means they are not
subject to recycle race conditions as the process is pinned by the file descriptor during the whole
duration of the invocation. Note that these require a kernel that supports <constant>PIDFD</constant>.
A suitable file descriptor may be acquired via
<citerefentry project='man-pages'><refentrytitle>pidfd_open</refentrytitle><manvolnum>2</manvolnum></citerefentry>.</para>
<para>The <function>sd_peer_get_owner_uid()</function>,
@@ -292,10 +346,12 @@
<function>sd_peer_get_unit()</function>,
<function>sd_peer_get_machine_name()</function>,
<function>sd_peer_get_slice()</function>,
<function>sd_peer_get_user_slice()</function> and
<function>sd_peer_get_cgroup()</function> calls operate similarly to their PID counterparts, but accept a
connected <constant>AF_UNIX</constant> socket and retrieve information about the connected peer process.
Note that these fields are retrieved via <filename>/proc/</filename>, and hence are not suitable for
<function>sd_peer_get_user_slice()</function>,
<function>sd_peer_get_cgroup()</function>,
<function>sd_peer_get_unit_cgroup()</function>, and
<function>sd_peer_get_user_unit_cgroup()</function> calls operate similarly to their PID counterparts, but
accept a connected <constant>AF_UNIX</constant> socket and retrieve information about the connected peer
process. Note that these fields are retrieved via <filename>/proc/</filename>, and hence are not suitable for
authorization purposes, as they are subject to races.</para>
</refsect1>
@@ -386,6 +442,12 @@
<function>sd_pidfd_get_slice()</function>,
<function>sd_pidfd_get_user_slice()</function>, and
<function>sd_pidfd_get_cgroup()</function> were added in version 253.</para>
<para><function>sd_pid_get_unit_cgroup()</function>,
<function>sd_pid_get_user_unit_cgroup()</function>,
<function>sd_pidfd_get_unit_cgroup()</function>,
<function>sd_pidfd_get_user_unit_cgroup()</function>,
<function>sd_peer_get_unit_cgroup()</function>, and
<function>sd_peer_get_user_unit_cgroup()</function> were added in version 263.</para>
</refsect1>
<refsect1>
+6
View File
@@ -1121,4 +1121,10 @@ global:
sd_varlink_invoke_and_upgrade;
sd_varlink_invoke_and_upgradeb;
sd_varlink_wants_reply;
sd_pid_get_unit_cgroup;
sd_pid_get_user_unit_cgroup;
sd_pidfd_get_unit_cgroup;
sd_pidfd_get_user_unit_cgroup;
sd_peer_get_unit_cgroup;
sd_peer_get_user_unit_cgroup;
} LIBSYSTEMD_262;
+120
View File
@@ -124,6 +124,38 @@ _public_ int sd_pid_get_cgroup(pid_t pid, char **ret_cgroup) {
return 0;
}
_public_ int sd_pid_get_unit_cgroup(pid_t pid, char **ret_cgroup) {
int r;
assert_return(pid >= 0, -EINVAL);
_cleanup_free_ char *c = NULL;
r = cg_pid_get_unit_path(pid, &c);
if (r < 0)
return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r;
if (ret_cgroup)
*ret_cgroup = TAKE_PTR(c);
return 0;
}
_public_ int sd_pid_get_user_unit_cgroup(pid_t pid, char **ret_cgroup) {
int r;
assert_return(pid >= 0, -EINVAL);
_cleanup_free_ char *c = NULL;
r = cg_pid_get_user_unit_path(pid, &c);
if (r < 0)
return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r;
if (ret_cgroup)
*ret_cgroup = TAKE_PTR(c);
return 0;
}
_public_ int sd_pidfd_get_session(int pidfd, char **ret_session) {
_cleanup_free_ char *session = NULL;
pid_t pid;
@@ -316,6 +348,54 @@ _public_ int sd_pidfd_get_cgroup(int pidfd, char **ret_cgroup) {
return 0;
}
_public_ int sd_pidfd_get_unit_cgroup(int pidfd, char **ret_cgroup) {
_cleanup_free_ char *cgroup = NULL;
pid_t pid;
int r;
assert_return(pidfd >= 0, -EBADF);
r = pidfd_get_pid(pidfd, &pid);
if (r < 0)
return r;
r = sd_pid_get_unit_cgroup(pid, &cgroup);
if (r < 0)
return r;
r = pidfd_verify_pid(pidfd, pid);
if (r < 0)
return r;
if (ret_cgroup)
*ret_cgroup = TAKE_PTR(cgroup);
return 0;
}
_public_ int sd_pidfd_get_user_unit_cgroup(int pidfd, char **ret_cgroup) {
_cleanup_free_ char *cgroup = NULL;
pid_t pid;
int r;
assert_return(pidfd >= 0, -EBADF);
r = pidfd_get_pid(pidfd, &pid);
if (r < 0)
return r;
r = sd_pid_get_user_unit_cgroup(pid, &cgroup);
if (r < 0)
return r;
r = pidfd_verify_pid(pidfd, pid);
if (r < 0)
return r;
if (ret_cgroup)
*ret_cgroup = TAKE_PTR(cgroup);
return 0;
}
_public_ int sd_peer_get_session(int fd, char **ret_session) {
int r;
@@ -437,6 +517,46 @@ _public_ int sd_peer_get_cgroup(int fd, char **ret_cgroup) {
return 0;
}
_public_ int sd_peer_get_unit_cgroup(int fd, char **ret_cgroup) {
int r;
assert_return(fd >= 0, -EBADF);
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
r = getpeerpidref(fd, &pidref);
if (r < 0)
return r;
_cleanup_free_ char *c = NULL;
r = cg_pidref_get_unit_path(&pidref, &c);
if (r < 0)
return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r;
if (ret_cgroup)
*ret_cgroup = TAKE_PTR(c);
return 0;
}
_public_ int sd_peer_get_user_unit_cgroup(int fd, char **ret_cgroup) {
int r;
assert_return(fd >= 0, -EBADF);
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
r = getpeerpidref(fd, &pidref);
if (r < 0)
return r;
_cleanup_free_ char *c = NULL;
r = cg_pidref_get_user_unit_path(&pidref, &c);
if (r < 0)
return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r;
if (ret_cgroup)
*ret_cgroup = TAKE_PTR(c);
return 0;
}
static int file_of_uid(uid_t uid, char **ret) {
assert_return(uid_is_valid(uid), -EINVAL);
+20 -2
View File
@@ -45,7 +45,8 @@ TEST(login) {
*display = NULL, *remote_user = NULL, *remote_host = NULL,
*type = NULL, *class = NULL, *state = NULL, *state2 = NULL,
*seat = NULL, *session = NULL,
*unit = NULL, *user_unit = NULL, *slice = NULL;
*unit = NULL, *user_unit = NULL, *slice = NULL,
*unit_cgroup = NULL, *user_unit_cgroup = NULL;
_cleanup_close_ int pidfd = -EBADF;
int r;
uid_t u, u2 = UID_INVALID;
@@ -55,10 +56,18 @@ TEST(login) {
log_info("sd_pid_get_unit(0, …) → %s / \"%s\"", e(r), strnull(unit));
ASSERT_TRUE(IN_SET(r, 0, -ENODATA));
r = sd_pid_get_unit_cgroup(/* pid= */ 0, &unit_cgroup);
log_info("sd_pid_get_unit_cgroup(0, …) → %s / \"%s\"", e(r), strnull(unit_cgroup));
assert_se(IN_SET(r, 0, -ENODATA));
r = sd_pid_get_user_unit(/* pid= */ 0, &user_unit);
log_info("sd_pid_get_user_unit(0, …) → %s / \"%s\"", e(r), strnull(user_unit));
ASSERT_TRUE(IN_SET(r, 0, -ENODATA));
r = sd_pid_get_user_unit_cgroup(/* pid= */ 0, &user_unit_cgroup);
log_info("sd_pid_get_user_unit_cgroup(0, …) → %s / \"%s\"", e(r), strnull(user_unit_cgroup));
assert_se(IN_SET(r, 0, -ENODATA));
/* Coverage for https://github.com/systemd/systemd/issues/39949 */
ASSERT_TRUE(!unit || !user_unit || !streq(unit, user_unit));
@@ -80,16 +89,25 @@ TEST(login) {
pidfd = pidfd_open(getpid_cached(), 0);
if (pidfd >= 0) {
_cleanup_free_ char *cgroup2 = NULL, *session2 = NULL,
*unit2 = NULL, *user_unit2 = NULL, *slice2 = NULL;
*unit2 = NULL, *user_unit2 = NULL, *slice2 = NULL,
*unit_cgroup2 = NULL, *user_unit_cgroup2 = NULL;
r = sd_pidfd_get_unit(pidfd, &unit2);
log_info("sd_pidfd_get_unit(pidfd, …) → %s / \"%s\"", e(r), strnull(unit2));
ASSERT_TRUE(IN_SET(r, 0, -ENODATA));
r = sd_pidfd_get_unit_cgroup(pidfd, &unit_cgroup2);
log_info("sd_pidfd_get_unit_cgroup(pidfd, …) → %s / \"%s\"", e(r), strnull(unit_cgroup2));
assert_se(IN_SET(r, 0, -ENODATA));
r = sd_pidfd_get_user_unit(pidfd, &user_unit2);
log_info("sd_pidfd_get_user_unit(pidfd, …) → %s / \"%s\"", e(r), strnull(user_unit2));
ASSERT_TRUE(IN_SET(r, 0, -ENODATA));
r = sd_pidfd_get_user_unit_cgroup(pidfd, &user_unit_cgroup2);
log_info("sd_pidfd_get_user_unit_cgroup(pidfd, …) → %s / \"%s\"", e(r), strnull(user_unit_cgroup2));
assert_se(IN_SET(r, 0, -ENODATA));
r = sd_pidfd_get_slice(pidfd, &slice2);
log_info("sd_pidfd_get_slice(pidfd, …) → %s / \"%s\"", e(r), strnull(slice2));
ASSERT_TRUE(IN_SET(r, 0, -ENODATA));
+20
View File
@@ -77,6 +77,16 @@ int sd_pid_get_machine_name(pid_t pid, char **ret_machine);
* hierarchy. */
int sd_pid_get_cgroup(pid_t pid, char **ret_cgroup);
/* Get systemd non-slice unit (i.e. service) control group from PID,
* for system services. This will return an error for non-service
* processes. */
int sd_pid_get_unit_cgroup(pid_t pid, char **ret_cgroup);
/* Get systemd non-slice unit (i.e. service) control group from PID,
* for user services. This will return an error for non-service
* processes. */
int sd_pid_get_user_unit_cgroup(pid_t pid, char **ret_cgroup);
/* Equivalent to the corresponding sd_pid_get* functions, but take a
* PIDFD instead of a PID, to ensure there can be no possible PID
* recycle issues before/after the calls. */
@@ -88,6 +98,8 @@ int sd_pidfd_get_slice(int pidfd, char **ret_slice);
int sd_pidfd_get_user_slice(int pidfd, char **ret_slice);
int sd_pidfd_get_machine_name(int pidfd, char **ret_machine);
int sd_pidfd_get_cgroup(int pidfd, char **ret_cgroup);
int sd_pidfd_get_unit_cgroup(int pidfd, char **ret_cgroup);
int sd_pidfd_get_user_unit_cgroup(int pidfd, char **ret_cgroup);
/* Similar to sd_pid_get_session(), but retrieves data about the peer
* of a connected AF_UNIX socket */
@@ -121,6 +133,14 @@ int sd_peer_get_machine_name(int fd, char **ret_machine);
* of a connected AF_UNIX socket. */
int sd_peer_get_cgroup(int fd, char **ret_cgroup);
/* Similar to sd_pid_get_unit_cgroup(), but retrieves data about the peer
* of a connected AF_UNIX socket. */
int sd_peer_get_unit_cgroup(int fd, char **ret_cgroup);
/* Similar to sd_pid_get_user_unit_cgroup(), but retrieves data about the peer
* of a connected AF_UNIX socket. */
int sd_peer_get_user_unit_cgroup(int fd, char **ret_cgroup);
/* Get state from UID. Possible states: offline, lingering, online, active, closing */
int sd_uid_get_state(uid_t uid, char **ret_state);