1 /* SPDX-License-Identifier: LGPL-2.1+ */
6 #include <sys/inotify.h>
11 #include "alloc-util.h"
12 #include "cgroup-util.h"
13 #include "dirent-util.h"
17 #include "format-util.h"
19 #include "hostname-util.h"
21 #include "login-util.h"
23 #include "parse-util.h"
24 #include "path-util.h"
25 #include "socket-util.h"
26 #include "string-util.h"
28 #include "user-util.h"
33 * invalid input parameters → -EINVAL
35 * process does not exist → -ESRCH
36 * cgroup does not exist → -ENOENT
37 * machine, session does not exist → -ENXIO
38 * requested metadata on object is missing → -ENODATA
41 _public_
int sd_pid_get_session(pid_t pid
, char **session
) {
44 assert_return(pid
>= 0, -EINVAL
);
45 assert_return(session
, -EINVAL
);
47 r
= cg_pid_get_session(pid
, session
);
48 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
51 _public_
int sd_pid_get_unit(pid_t pid
, char **unit
) {
54 assert_return(pid
>= 0, -EINVAL
);
55 assert_return(unit
, -EINVAL
);
57 r
= cg_pid_get_unit(pid
, unit
);
58 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
61 _public_
int sd_pid_get_user_unit(pid_t pid
, char **unit
) {
64 assert_return(pid
>= 0, -EINVAL
);
65 assert_return(unit
, -EINVAL
);
67 r
= cg_pid_get_user_unit(pid
, unit
);
68 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
71 _public_
int sd_pid_get_machine_name(pid_t pid
, char **name
) {
74 assert_return(pid
>= 0, -EINVAL
);
75 assert_return(name
, -EINVAL
);
77 r
= cg_pid_get_machine_name(pid
, name
);
78 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
81 _public_
int sd_pid_get_slice(pid_t pid
, char **slice
) {
84 assert_return(pid
>= 0, -EINVAL
);
85 assert_return(slice
, -EINVAL
);
87 r
= cg_pid_get_slice(pid
, slice
);
88 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
91 _public_
int sd_pid_get_user_slice(pid_t pid
, char **slice
) {
94 assert_return(pid
>= 0, -EINVAL
);
95 assert_return(slice
, -EINVAL
);
97 r
= cg_pid_get_user_slice(pid
, slice
);
98 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
101 _public_
int sd_pid_get_owner_uid(pid_t pid
, uid_t
*uid
) {
104 assert_return(pid
>= 0, -EINVAL
);
105 assert_return(uid
, -EINVAL
);
107 r
= cg_pid_get_owner_uid(pid
, uid
);
108 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
111 _public_
int sd_pid_get_cgroup(pid_t pid
, char **cgroup
) {
115 assert_return(pid
>= 0, -EINVAL
);
116 assert_return(cgroup
, -EINVAL
);
118 r
= cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER
, pid
, &c
);
122 /* The internal APIs return the empty string for the root
123 * cgroup, let's return the "/" in the public APIs instead, as
124 * that's easier and less ambiguous for people to grok. */
137 _public_
int sd_peer_get_session(int fd
, char **session
) {
138 struct ucred ucred
= {};
141 assert_return(fd
>= 0, -EBADF
);
142 assert_return(session
, -EINVAL
);
144 r
= getpeercred(fd
, &ucred
);
148 return cg_pid_get_session(ucred
.pid
, session
);
151 _public_
int sd_peer_get_owner_uid(int fd
, uid_t
*uid
) {
155 assert_return(fd
>= 0, -EBADF
);
156 assert_return(uid
, -EINVAL
);
158 r
= getpeercred(fd
, &ucred
);
162 return cg_pid_get_owner_uid(ucred
.pid
, uid
);
165 _public_
int sd_peer_get_unit(int fd
, char **unit
) {
169 assert_return(fd
>= 0, -EBADF
);
170 assert_return(unit
, -EINVAL
);
172 r
= getpeercred(fd
, &ucred
);
176 return cg_pid_get_unit(ucred
.pid
, unit
);
179 _public_
int sd_peer_get_user_unit(int fd
, char **unit
) {
183 assert_return(fd
>= 0, -EBADF
);
184 assert_return(unit
, -EINVAL
);
186 r
= getpeercred(fd
, &ucred
);
190 return cg_pid_get_user_unit(ucred
.pid
, unit
);
193 _public_
int sd_peer_get_machine_name(int fd
, char **machine
) {
197 assert_return(fd
>= 0, -EBADF
);
198 assert_return(machine
, -EINVAL
);
200 r
= getpeercred(fd
, &ucred
);
204 return cg_pid_get_machine_name(ucred
.pid
, machine
);
207 _public_
int sd_peer_get_slice(int fd
, char **slice
) {
211 assert_return(fd
>= 0, -EBADF
);
212 assert_return(slice
, -EINVAL
);
214 r
= getpeercred(fd
, &ucred
);
218 return cg_pid_get_slice(ucred
.pid
, slice
);
221 _public_
int sd_peer_get_user_slice(int fd
, char **slice
) {
225 assert_return(fd
>= 0, -EBADF
);
226 assert_return(slice
, -EINVAL
);
228 r
= getpeercred(fd
, &ucred
);
232 return cg_pid_get_user_slice(ucred
.pid
, slice
);
235 _public_
int sd_peer_get_cgroup(int fd
, char **cgroup
) {
239 assert_return(fd
>= 0, -EBADF
);
240 assert_return(cgroup
, -EINVAL
);
242 r
= getpeercred(fd
, &ucred
);
246 return sd_pid_get_cgroup(ucred
.pid
, cgroup
);
249 static int file_of_uid(uid_t uid
, char **p
) {
251 assert_return(uid_is_valid(uid
), -EINVAL
);
254 if (asprintf(p
, "/run/systemd/users/" UID_FMT
, uid
) < 0)
260 _public_
int sd_uid_get_state(uid_t uid
, char**state
) {
261 _cleanup_free_
char *p
= NULL
;
265 assert_return(state
, -EINVAL
);
267 r
= file_of_uid(uid
, &p
);
271 r
= parse_env_file(NULL
, p
, NEWLINE
, "STATE", &s
, NULL
);
274 s
= strdup("offline");
292 _public_
int sd_uid_get_display(uid_t uid
, char **session
) {
293 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
296 assert_return(session
, -EINVAL
);
298 r
= file_of_uid(uid
, &p
);
302 r
= parse_env_file(NULL
, p
, NEWLINE
, "DISPLAY", &s
, NULL
);
310 *session
= TAKE_PTR(s
);
315 static int file_of_seat(const char *seat
, char **_p
) {
322 if (!filename_is_valid(seat
))
325 p
= strappend("/run/systemd/seats/", seat
);
327 _cleanup_free_
char *buf
= NULL
;
329 r
= sd_session_get_seat(NULL
, &buf
);
333 p
= strappend("/run/systemd/seats/", buf
);
343 _public_
int sd_uid_is_on_seat(uid_t uid
, int require_active
, const char *seat
) {
344 _cleanup_free_
char *t
= NULL
, *s
= NULL
, *p
= NULL
;
347 const char *word
, *variable
, *state
;
349 assert_return(uid_is_valid(uid
), -EINVAL
);
351 r
= file_of_seat(seat
, &p
);
355 variable
= require_active
? "ACTIVE_UID" : "UIDS";
357 r
= parse_env_file(NULL
, p
, NEWLINE
, variable
, &s
, NULL
);
365 if (asprintf(&t
, UID_FMT
, uid
) < 0)
368 FOREACH_WORD(word
, l
, s
, state
)
369 if (strneq(t
, word
, l
))
375 static int uid_get_array(uid_t uid
, const char *variable
, char ***array
) {
376 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
382 r
= file_of_uid(uid
, &p
);
386 r
= parse_env_file(NULL
, p
, NEWLINE
, variable
, &s
, NULL
);
387 if (r
== -ENOENT
|| (r
>= 0 && isempty(s
))) {
395 a
= strv_split(s
, " ");
400 r
= (int) strv_length(a
);
410 _public_
int sd_uid_get_sessions(uid_t uid
, int require_active
, char ***sessions
) {
411 return uid_get_array(
413 require_active
== 0 ? "ONLINE_SESSIONS" :
414 require_active
> 0 ? "ACTIVE_SESSIONS" :
419 _public_
int sd_uid_get_seats(uid_t uid
, int require_active
, char ***seats
) {
420 return uid_get_array(
422 require_active
== 0 ? "ONLINE_SEATS" :
423 require_active
> 0 ? "ACTIVE_SEATS" :
428 static int file_of_session(const char *session
, char **_p
) {
435 if (!session_id_valid(session
))
438 p
= strappend("/run/systemd/sessions/", session
);
440 _cleanup_free_
char *buf
= NULL
;
442 r
= sd_pid_get_session(0, &buf
);
446 p
= strappend("/run/systemd/sessions/", buf
);
456 _public_
int sd_session_is_active(const char *session
) {
457 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
460 r
= file_of_session(session
, &p
);
464 r
= parse_env_file(NULL
, p
, NEWLINE
, "ACTIVE", &s
, NULL
);
472 return parse_boolean(s
);
475 _public_
int sd_session_is_remote(const char *session
) {
476 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
479 r
= file_of_session(session
, &p
);
483 r
= parse_env_file(NULL
, p
, NEWLINE
, "REMOTE", &s
, NULL
);
491 return parse_boolean(s
);
494 _public_
int sd_session_get_state(const char *session
, char **state
) {
495 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
498 assert_return(state
, -EINVAL
);
500 r
= file_of_session(session
, &p
);
504 r
= parse_env_file(NULL
, p
, NEWLINE
, "STATE", &s
, NULL
);
512 *state
= TAKE_PTR(s
);
517 _public_
int sd_session_get_uid(const char *session
, uid_t
*uid
) {
519 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
521 assert_return(uid
, -EINVAL
);
523 r
= file_of_session(session
, &p
);
527 r
= parse_env_file(NULL
, p
, NEWLINE
, "UID", &s
, NULL
);
535 return parse_uid(s
, uid
);
538 static int session_get_string(const char *session
, const char *field
, char **value
) {
539 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
542 assert_return(value
, -EINVAL
);
545 r
= file_of_session(session
, &p
);
549 r
= parse_env_file(NULL
, p
, NEWLINE
, field
, &s
, NULL
);
557 *value
= TAKE_PTR(s
);
561 _public_
int sd_session_get_seat(const char *session
, char **seat
) {
562 return session_get_string(session
, "SEAT", seat
);
565 _public_
int sd_session_get_tty(const char *session
, char **tty
) {
566 return session_get_string(session
, "TTY", tty
);
569 _public_
int sd_session_get_vt(const char *session
, unsigned *vtnr
) {
570 _cleanup_free_
char *vtnr_string
= NULL
;
574 assert_return(vtnr
, -EINVAL
);
576 r
= session_get_string(session
, "VTNR", &vtnr_string
);
580 r
= safe_atou(vtnr_string
, &u
);
588 _public_
int sd_session_get_service(const char *session
, char **service
) {
589 return session_get_string(session
, "SERVICE", service
);
592 _public_
int sd_session_get_type(const char *session
, char **type
) {
593 return session_get_string(session
, "TYPE", type
);
596 _public_
int sd_session_get_class(const char *session
, char **class) {
597 return session_get_string(session
, "CLASS", class);
600 _public_
int sd_session_get_desktop(const char *session
, char **desktop
) {
601 _cleanup_free_
char *escaped
= NULL
;
605 assert_return(desktop
, -EINVAL
);
607 r
= session_get_string(session
, "DESKTOP", &escaped
);
611 r
= cunescape(escaped
, 0, &t
);
619 _public_
int sd_session_get_display(const char *session
, char **display
) {
620 return session_get_string(session
, "DISPLAY", display
);
623 _public_
int sd_session_get_remote_user(const char *session
, char **remote_user
) {
624 return session_get_string(session
, "REMOTE_USER", remote_user
);
627 _public_
int sd_session_get_remote_host(const char *session
, char **remote_host
) {
628 return session_get_string(session
, "REMOTE_HOST", remote_host
);
631 _public_
int sd_seat_get_active(const char *seat
, char **session
, uid_t
*uid
) {
632 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *t
= NULL
;
635 assert_return(session
|| uid
, -EINVAL
);
637 r
= file_of_seat(seat
, &p
);
641 r
= parse_env_file(NULL
, p
, NEWLINE
,
657 r
= parse_uid(t
, uid
);
663 *session
= TAKE_PTR(s
);
668 _public_
int sd_seat_get_sessions(const char *seat
, char ***sessions
, uid_t
**uids
, unsigned *n_uids
) {
669 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *t
= NULL
;
670 _cleanup_strv_free_
char **a
= NULL
;
671 _cleanup_free_ uid_t
*b
= NULL
;
675 r
= file_of_seat(seat
, &p
);
679 r
= parse_env_file(NULL
, p
, NEWLINE
,
689 a
= strv_split(s
, " ");
695 const char *word
, *state
;
698 FOREACH_WORD(word
, l
, t
, state
)
708 FOREACH_WORD(word
, l
, t
, state
) {
709 _cleanup_free_
char *k
= NULL
;
711 k
= strndup(word
, l
);
715 r
= parse_uid(k
, b
+ i
);
724 r
= (int) strv_length(a
);
727 *sessions
= TAKE_PTR(a
);
738 static int seat_get_can(const char *seat
, const char *variable
) {
739 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
744 r
= file_of_seat(seat
, &p
);
748 r
= parse_env_file(NULL
, p
, NEWLINE
,
758 return parse_boolean(s
);
761 _public_
int sd_seat_can_multi_session(const char *seat
) {
762 return seat_get_can(seat
, "CAN_MULTI_SESSION");
765 _public_
int sd_seat_can_tty(const char *seat
) {
766 return seat_get_can(seat
, "CAN_TTY");
769 _public_
int sd_seat_can_graphical(const char *seat
) {
770 return seat_get_can(seat
, "CAN_GRAPHICAL");
773 _public_
int sd_get_seats(char ***seats
) {
776 r
= get_files_in_directory("/run/systemd/seats/", seats
);
785 _public_
int sd_get_sessions(char ***sessions
) {
788 r
= get_files_in_directory("/run/systemd/sessions/", sessions
);
797 _public_
int sd_get_uids(uid_t
**users
) {
798 _cleanup_closedir_
DIR *d
;
802 _cleanup_free_ uid_t
*l
= NULL
;
804 d
= opendir("/run/systemd/users/");
806 if (errno
== ENOENT
) {
814 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
818 dirent_ensure_type(d
, de
);
820 if (!dirent_is_file(de
))
823 k
= parse_uid(de
->d_name
, &uid
);
828 if ((unsigned) r
>= n
) {
832 t
= realloc(l
, sizeof(uid_t
) * n
);
839 assert((unsigned) r
< n
);
846 *users
= TAKE_PTR(l
);
851 _public_
int sd_get_machine_names(char ***machines
) {
852 _cleanup_strv_free_
char **l
= NULL
;
856 r
= get_files_in_directory("/run/systemd/machines/", &l
);
868 /* Filter out the unit: symlinks */
869 for (a
= b
= l
; *a
; a
++) {
870 if (startswith(*a
, "unit:") || !machine_name_is_valid(*a
))
883 *machines
= TAKE_PTR(l
);
888 _public_
int sd_machine_get_class(const char *machine
, char **class) {
889 _cleanup_free_
char *c
= NULL
;
893 assert_return(class, -EINVAL
);
895 if (streq(machine
, ".host")) {
900 if (!machine_name_is_valid(machine
))
903 p
= strjoina("/run/systemd/machines/", machine
);
904 r
= parse_env_file(NULL
, p
, NEWLINE
, "CLASS", &c
, NULL
);
913 *class = TAKE_PTR(c
);
917 _public_
int sd_machine_get_ifindices(const char *machine
, int **ifindices
) {
918 _cleanup_free_
char *netif
= NULL
;
919 size_t l
, allocated
= 0, nr
= 0;
921 const char *p
, *word
, *state
;
924 assert_return(machine_name_is_valid(machine
), -EINVAL
);
925 assert_return(ifindices
, -EINVAL
);
927 p
= strjoina("/run/systemd/machines/", machine
);
928 r
= parse_env_file(NULL
, p
, NEWLINE
, "NETIF", &netif
, NULL
);
938 FOREACH_WORD(word
, l
, netif
, state
) {
942 *(char*) (mempcpy(buf
, word
, l
)) = 0;
944 if (parse_ifindex(buf
, &ifi
) < 0)
947 if (!GREEDY_REALLOC(ni
, allocated
, nr
+1)) {
959 static inline int MONITOR_TO_FD(sd_login_monitor
*m
) {
960 return (int) (unsigned long) m
- 1;
963 static inline sd_login_monitor
* FD_TO_MONITOR(int fd
) {
964 return (sd_login_monitor
*) (unsigned long) (fd
+ 1);
967 _public_
int sd_login_monitor_new(const char *category
, sd_login_monitor
**m
) {
968 _cleanup_close_
int fd
= -1;
972 assert_return(m
, -EINVAL
);
974 fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
978 if (!category
|| streq(category
, "seat")) {
979 k
= inotify_add_watch(fd
, "/run/systemd/seats/", IN_MOVED_TO
|IN_DELETE
);
986 if (!category
|| streq(category
, "session")) {
987 k
= inotify_add_watch(fd
, "/run/systemd/sessions/", IN_MOVED_TO
|IN_DELETE
);
994 if (!category
|| streq(category
, "uid")) {
995 k
= inotify_add_watch(fd
, "/run/systemd/users/", IN_MOVED_TO
|IN_DELETE
);
1002 if (!category
|| streq(category
, "machine")) {
1003 k
= inotify_add_watch(fd
, "/run/systemd/machines/", IN_MOVED_TO
|IN_DELETE
);
1013 *m
= FD_TO_MONITOR(fd
);
1019 _public_ sd_login_monitor
* sd_login_monitor_unref(sd_login_monitor
*m
) {
1025 fd
= MONITOR_TO_FD(m
);
1031 _public_
int sd_login_monitor_flush(sd_login_monitor
*m
) {
1034 assert_return(m
, -EINVAL
);
1036 r
= flush_fd(MONITOR_TO_FD(m
));
1043 _public_
int sd_login_monitor_get_fd(sd_login_monitor
*m
) {
1045 assert_return(m
, -EINVAL
);
1047 return MONITOR_TO_FD(m
);
1050 _public_
int sd_login_monitor_get_events(sd_login_monitor
*m
) {
1052 assert_return(m
, -EINVAL
);
1054 /* For now we will only return POLLIN here, since we don't
1055 * need anything else ever for inotify. However, let's have
1056 * this API to keep our options open should we later on need
1061 _public_
int sd_login_monitor_get_timeout(sd_login_monitor
*m
, uint64_t *timeout_usec
) {
1063 assert_return(m
, -EINVAL
);
1064 assert_return(timeout_usec
, -EINVAL
);
1066 /* For now we will only return (uint64_t) -1, since we don't
1067 * need any timeout. However, let's have this API to keep our
1068 * options open should we later on need it. */
1069 *timeout_usec
= (uint64_t) -1;