1 /* SPDX-License-Identifier: LGPL-2.1+ */
8 #include <sys/inotify.h>
13 #include "alloc-util.h"
14 #include "cgroup-util.h"
15 #include "dirent-util.h"
19 #include "format-util.h"
21 #include "hostname-util.h"
23 #include "login-util.h"
25 #include "parse-util.h"
26 #include "path-util.h"
27 #include "socket-util.h"
28 #include "string-util.h"
30 #include "user-util.h"
35 * invalid input parameters → -EINVAL
37 * process does not exist → -ESRCH
38 * cgroup does not exist → -ENOENT
39 * machine, session does not exist → -ENXIO
40 * requested metadata on object is missing → -ENODATA
43 _public_
int sd_pid_get_session(pid_t pid
, char **session
) {
46 assert_return(pid
>= 0, -EINVAL
);
47 assert_return(session
, -EINVAL
);
49 r
= cg_pid_get_session(pid
, session
);
50 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
53 _public_
int sd_pid_get_unit(pid_t pid
, char **unit
) {
56 assert_return(pid
>= 0, -EINVAL
);
57 assert_return(unit
, -EINVAL
);
59 r
= cg_pid_get_unit(pid
, unit
);
60 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
63 _public_
int sd_pid_get_user_unit(pid_t pid
, char **unit
) {
66 assert_return(pid
>= 0, -EINVAL
);
67 assert_return(unit
, -EINVAL
);
69 r
= cg_pid_get_user_unit(pid
, unit
);
70 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
73 _public_
int sd_pid_get_machine_name(pid_t pid
, char **name
) {
76 assert_return(pid
>= 0, -EINVAL
);
77 assert_return(name
, -EINVAL
);
79 r
= cg_pid_get_machine_name(pid
, name
);
80 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
83 _public_
int sd_pid_get_slice(pid_t pid
, char **slice
) {
86 assert_return(pid
>= 0, -EINVAL
);
87 assert_return(slice
, -EINVAL
);
89 r
= cg_pid_get_slice(pid
, slice
);
90 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
93 _public_
int sd_pid_get_user_slice(pid_t pid
, char **slice
) {
96 assert_return(pid
>= 0, -EINVAL
);
97 assert_return(slice
, -EINVAL
);
99 r
= cg_pid_get_user_slice(pid
, slice
);
100 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
103 _public_
int sd_pid_get_owner_uid(pid_t pid
, uid_t
*uid
) {
106 assert_return(pid
>= 0, -EINVAL
);
107 assert_return(uid
, -EINVAL
);
109 r
= cg_pid_get_owner_uid(pid
, uid
);
110 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
113 _public_
int sd_pid_get_cgroup(pid_t pid
, char **cgroup
) {
117 assert_return(pid
>= 0, -EINVAL
);
118 assert_return(cgroup
, -EINVAL
);
120 r
= cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER
, pid
, &c
);
124 /* The internal APIs return the empty string for the root
125 * cgroup, let's return the "/" in the public APIs instead, as
126 * that's easier and less ambiguous for people to grok. */
139 _public_
int sd_peer_get_session(int fd
, char **session
) {
140 struct ucred ucred
= {};
143 assert_return(fd
>= 0, -EBADF
);
144 assert_return(session
, -EINVAL
);
146 r
= getpeercred(fd
, &ucred
);
150 return cg_pid_get_session(ucred
.pid
, session
);
153 _public_
int sd_peer_get_owner_uid(int fd
, uid_t
*uid
) {
157 assert_return(fd
>= 0, -EBADF
);
158 assert_return(uid
, -EINVAL
);
160 r
= getpeercred(fd
, &ucred
);
164 return cg_pid_get_owner_uid(ucred
.pid
, uid
);
167 _public_
int sd_peer_get_unit(int fd
, char **unit
) {
171 assert_return(fd
>= 0, -EBADF
);
172 assert_return(unit
, -EINVAL
);
174 r
= getpeercred(fd
, &ucred
);
178 return cg_pid_get_unit(ucred
.pid
, unit
);
181 _public_
int sd_peer_get_user_unit(int fd
, char **unit
) {
185 assert_return(fd
>= 0, -EBADF
);
186 assert_return(unit
, -EINVAL
);
188 r
= getpeercred(fd
, &ucred
);
192 return cg_pid_get_user_unit(ucred
.pid
, unit
);
195 _public_
int sd_peer_get_machine_name(int fd
, char **machine
) {
199 assert_return(fd
>= 0, -EBADF
);
200 assert_return(machine
, -EINVAL
);
202 r
= getpeercred(fd
, &ucred
);
206 return cg_pid_get_machine_name(ucred
.pid
, machine
);
209 _public_
int sd_peer_get_slice(int fd
, char **slice
) {
213 assert_return(fd
>= 0, -EBADF
);
214 assert_return(slice
, -EINVAL
);
216 r
= getpeercred(fd
, &ucred
);
220 return cg_pid_get_slice(ucred
.pid
, slice
);
223 _public_
int sd_peer_get_user_slice(int fd
, char **slice
) {
227 assert_return(fd
>= 0, -EBADF
);
228 assert_return(slice
, -EINVAL
);
230 r
= getpeercred(fd
, &ucred
);
234 return cg_pid_get_user_slice(ucred
.pid
, slice
);
237 _public_
int sd_peer_get_cgroup(int fd
, char **cgroup
) {
241 assert_return(fd
>= 0, -EBADF
);
242 assert_return(cgroup
, -EINVAL
);
244 r
= getpeercred(fd
, &ucred
);
248 return sd_pid_get_cgroup(ucred
.pid
, cgroup
);
251 static int file_of_uid(uid_t uid
, char **p
) {
253 assert_return(uid_is_valid(uid
), -EINVAL
);
256 if (asprintf(p
, "/run/systemd/users/" UID_FMT
, uid
) < 0)
262 _public_
int sd_uid_get_state(uid_t uid
, char**state
) {
263 _cleanup_free_
char *p
= NULL
;
267 assert_return(state
, -EINVAL
);
269 r
= file_of_uid(uid
, &p
);
273 r
= parse_env_file(NULL
, p
, NEWLINE
, "STATE", &s
, NULL
);
276 s
= strdup("offline");
294 _public_
int sd_uid_get_display(uid_t uid
, char **session
) {
295 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
298 assert_return(session
, -EINVAL
);
300 r
= file_of_uid(uid
, &p
);
304 r
= parse_env_file(NULL
, p
, NEWLINE
, "DISPLAY", &s
, NULL
);
312 *session
= TAKE_PTR(s
);
317 static int file_of_seat(const char *seat
, char **_p
) {
324 if (!filename_is_valid(seat
))
327 p
= strappend("/run/systemd/seats/", seat
);
329 _cleanup_free_
char *buf
= NULL
;
331 r
= sd_session_get_seat(NULL
, &buf
);
335 p
= strappend("/run/systemd/seats/", buf
);
345 _public_
int sd_uid_is_on_seat(uid_t uid
, int require_active
, const char *seat
) {
346 _cleanup_free_
char *t
= NULL
, *s
= NULL
, *p
= NULL
;
349 const char *word
, *variable
, *state
;
351 assert_return(uid_is_valid(uid
), -EINVAL
);
353 r
= file_of_seat(seat
, &p
);
357 variable
= require_active
? "ACTIVE_UID" : "UIDS";
359 r
= parse_env_file(NULL
, p
, NEWLINE
, variable
, &s
, NULL
);
367 if (asprintf(&t
, UID_FMT
, uid
) < 0)
370 FOREACH_WORD(word
, l
, s
, state
)
371 if (strneq(t
, word
, l
))
377 static int uid_get_array(uid_t uid
, const char *variable
, char ***array
) {
378 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
384 r
= file_of_uid(uid
, &p
);
388 r
= parse_env_file(NULL
, p
, NEWLINE
, variable
, &s
, NULL
);
389 if (r
== -ENOENT
|| (r
>= 0 && isempty(s
))) {
397 a
= strv_split(s
, " ");
402 r
= (int) strv_length(a
);
412 _public_
int sd_uid_get_sessions(uid_t uid
, int require_active
, char ***sessions
) {
413 return uid_get_array(
415 require_active
== 0 ? "ONLINE_SESSIONS" :
416 require_active
> 0 ? "ACTIVE_SESSIONS" :
421 _public_
int sd_uid_get_seats(uid_t uid
, int require_active
, char ***seats
) {
422 return uid_get_array(
424 require_active
== 0 ? "ONLINE_SEATS" :
425 require_active
> 0 ? "ACTIVE_SEATS" :
430 static int file_of_session(const char *session
, char **_p
) {
437 if (!session_id_valid(session
))
440 p
= strappend("/run/systemd/sessions/", session
);
442 _cleanup_free_
char *buf
= NULL
;
444 r
= sd_pid_get_session(0, &buf
);
448 p
= strappend("/run/systemd/sessions/", buf
);
458 _public_
int sd_session_is_active(const char *session
) {
459 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
462 r
= file_of_session(session
, &p
);
466 r
= parse_env_file(NULL
, p
, NEWLINE
, "ACTIVE", &s
, NULL
);
474 return parse_boolean(s
);
477 _public_
int sd_session_is_remote(const char *session
) {
478 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
481 r
= file_of_session(session
, &p
);
485 r
= parse_env_file(NULL
, p
, NEWLINE
, "REMOTE", &s
, NULL
);
493 return parse_boolean(s
);
496 _public_
int sd_session_get_state(const char *session
, char **state
) {
497 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
500 assert_return(state
, -EINVAL
);
502 r
= file_of_session(session
, &p
);
506 r
= parse_env_file(NULL
, p
, NEWLINE
, "STATE", &s
, NULL
);
514 *state
= TAKE_PTR(s
);
519 _public_
int sd_session_get_uid(const char *session
, uid_t
*uid
) {
521 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
523 assert_return(uid
, -EINVAL
);
525 r
= file_of_session(session
, &p
);
529 r
= parse_env_file(NULL
, p
, NEWLINE
, "UID", &s
, NULL
);
537 return parse_uid(s
, uid
);
540 static int session_get_string(const char *session
, const char *field
, char **value
) {
541 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
544 assert_return(value
, -EINVAL
);
547 r
= file_of_session(session
, &p
);
551 r
= parse_env_file(NULL
, p
, NEWLINE
, field
, &s
, NULL
);
559 *value
= TAKE_PTR(s
);
563 _public_
int sd_session_get_seat(const char *session
, char **seat
) {
564 return session_get_string(session
, "SEAT", seat
);
567 _public_
int sd_session_get_tty(const char *session
, char **tty
) {
568 return session_get_string(session
, "TTY", tty
);
571 _public_
int sd_session_get_vt(const char *session
, unsigned *vtnr
) {
572 _cleanup_free_
char *vtnr_string
= NULL
;
576 assert_return(vtnr
, -EINVAL
);
578 r
= session_get_string(session
, "VTNR", &vtnr_string
);
582 r
= safe_atou(vtnr_string
, &u
);
590 _public_
int sd_session_get_service(const char *session
, char **service
) {
591 return session_get_string(session
, "SERVICE", service
);
594 _public_
int sd_session_get_type(const char *session
, char **type
) {
595 return session_get_string(session
, "TYPE", type
);
598 _public_
int sd_session_get_class(const char *session
, char **class) {
599 return session_get_string(session
, "CLASS", class);
602 _public_
int sd_session_get_desktop(const char *session
, char **desktop
) {
603 _cleanup_free_
char *escaped
= NULL
;
607 assert_return(desktop
, -EINVAL
);
609 r
= session_get_string(session
, "DESKTOP", &escaped
);
613 r
= cunescape(escaped
, 0, &t
);
621 _public_
int sd_session_get_display(const char *session
, char **display
) {
622 return session_get_string(session
, "DISPLAY", display
);
625 _public_
int sd_session_get_remote_user(const char *session
, char **remote_user
) {
626 return session_get_string(session
, "REMOTE_USER", remote_user
);
629 _public_
int sd_session_get_remote_host(const char *session
, char **remote_host
) {
630 return session_get_string(session
, "REMOTE_HOST", remote_host
);
633 _public_
int sd_seat_get_active(const char *seat
, char **session
, uid_t
*uid
) {
634 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *t
= NULL
;
637 assert_return(session
|| uid
, -EINVAL
);
639 r
= file_of_seat(seat
, &p
);
643 r
= parse_env_file(NULL
, p
, NEWLINE
,
659 r
= parse_uid(t
, uid
);
665 *session
= TAKE_PTR(s
);
670 _public_
int sd_seat_get_sessions(const char *seat
, char ***sessions
, uid_t
**uids
, unsigned *n_uids
) {
671 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *t
= NULL
;
672 _cleanup_strv_free_
char **a
= NULL
;
673 _cleanup_free_ uid_t
*b
= NULL
;
677 r
= file_of_seat(seat
, &p
);
681 r
= parse_env_file(NULL
, p
, NEWLINE
,
691 a
= strv_split(s
, " ");
697 const char *word
, *state
;
700 FOREACH_WORD(word
, l
, t
, state
)
710 FOREACH_WORD(word
, l
, t
, state
) {
711 _cleanup_free_
char *k
= NULL
;
713 k
= strndup(word
, l
);
717 r
= parse_uid(k
, b
+ i
);
726 r
= (int) strv_length(a
);
729 *sessions
= TAKE_PTR(a
);
740 static int seat_get_can(const char *seat
, const char *variable
) {
741 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
746 r
= file_of_seat(seat
, &p
);
750 r
= parse_env_file(NULL
, p
, NEWLINE
,
760 return parse_boolean(s
);
763 _public_
int sd_seat_can_multi_session(const char *seat
) {
764 return seat_get_can(seat
, "CAN_MULTI_SESSION");
767 _public_
int sd_seat_can_tty(const char *seat
) {
768 return seat_get_can(seat
, "CAN_TTY");
771 _public_
int sd_seat_can_graphical(const char *seat
) {
772 return seat_get_can(seat
, "CAN_GRAPHICAL");
775 _public_
int sd_get_seats(char ***seats
) {
778 r
= get_files_in_directory("/run/systemd/seats/", seats
);
787 _public_
int sd_get_sessions(char ***sessions
) {
790 r
= get_files_in_directory("/run/systemd/sessions/", sessions
);
799 _public_
int sd_get_uids(uid_t
**users
) {
800 _cleanup_closedir_
DIR *d
;
804 _cleanup_free_ uid_t
*l
= NULL
;
806 d
= opendir("/run/systemd/users/");
808 if (errno
== ENOENT
) {
816 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
820 dirent_ensure_type(d
, de
);
822 if (!dirent_is_file(de
))
825 k
= parse_uid(de
->d_name
, &uid
);
830 if ((unsigned) r
>= n
) {
834 t
= realloc(l
, sizeof(uid_t
) * n
);
841 assert((unsigned) r
< n
);
848 *users
= TAKE_PTR(l
);
853 _public_
int sd_get_machine_names(char ***machines
) {
854 _cleanup_strv_free_
char **l
= NULL
;
858 r
= get_files_in_directory("/run/systemd/machines/", &l
);
870 /* Filter out the unit: symlinks */
871 for (a
= b
= l
; *a
; a
++) {
872 if (startswith(*a
, "unit:") || !machine_name_is_valid(*a
))
885 *machines
= TAKE_PTR(l
);
890 _public_
int sd_machine_get_class(const char *machine
, char **class) {
891 _cleanup_free_
char *c
= NULL
;
895 assert_return(machine_name_is_valid(machine
), -EINVAL
);
896 assert_return(class, -EINVAL
);
898 p
= strjoina("/run/systemd/machines/", machine
);
899 r
= parse_env_file(NULL
, p
, NEWLINE
, "CLASS", &c
, NULL
);
907 *class = TAKE_PTR(c
);
912 _public_
int sd_machine_get_ifindices(const char *machine
, int **ifindices
) {
913 _cleanup_free_
char *netif
= NULL
;
914 size_t l
, allocated
= 0, nr
= 0;
916 const char *p
, *word
, *state
;
919 assert_return(machine_name_is_valid(machine
), -EINVAL
);
920 assert_return(ifindices
, -EINVAL
);
922 p
= strjoina("/run/systemd/machines/", machine
);
923 r
= parse_env_file(NULL
, p
, NEWLINE
, "NETIF", &netif
, NULL
);
933 FOREACH_WORD(word
, l
, netif
, state
) {
937 *(char*) (mempcpy(buf
, word
, l
)) = 0;
939 if (parse_ifindex(buf
, &ifi
) < 0)
942 if (!GREEDY_REALLOC(ni
, allocated
, nr
+1)) {
954 static inline int MONITOR_TO_FD(sd_login_monitor
*m
) {
955 return (int) (unsigned long) m
- 1;
958 static inline sd_login_monitor
* FD_TO_MONITOR(int fd
) {
959 return (sd_login_monitor
*) (unsigned long) (fd
+ 1);
962 _public_
int sd_login_monitor_new(const char *category
, sd_login_monitor
**m
) {
963 _cleanup_close_
int fd
= -1;
967 assert_return(m
, -EINVAL
);
969 fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
973 if (!category
|| streq(category
, "seat")) {
974 k
= inotify_add_watch(fd
, "/run/systemd/seats/", IN_MOVED_TO
|IN_DELETE
);
981 if (!category
|| streq(category
, "session")) {
982 k
= inotify_add_watch(fd
, "/run/systemd/sessions/", IN_MOVED_TO
|IN_DELETE
);
989 if (!category
|| streq(category
, "uid")) {
990 k
= inotify_add_watch(fd
, "/run/systemd/users/", IN_MOVED_TO
|IN_DELETE
);
997 if (!category
|| streq(category
, "machine")) {
998 k
= inotify_add_watch(fd
, "/run/systemd/machines/", IN_MOVED_TO
|IN_DELETE
);
1008 *m
= FD_TO_MONITOR(fd
);
1014 _public_ sd_login_monitor
* sd_login_monitor_unref(sd_login_monitor
*m
) {
1020 fd
= MONITOR_TO_FD(m
);
1026 _public_
int sd_login_monitor_flush(sd_login_monitor
*m
) {
1029 assert_return(m
, -EINVAL
);
1031 r
= flush_fd(MONITOR_TO_FD(m
));
1038 _public_
int sd_login_monitor_get_fd(sd_login_monitor
*m
) {
1040 assert_return(m
, -EINVAL
);
1042 return MONITOR_TO_FD(m
);
1045 _public_
int sd_login_monitor_get_events(sd_login_monitor
*m
) {
1047 assert_return(m
, -EINVAL
);
1049 /* For now we will only return POLLIN here, since we don't
1050 * need anything else ever for inotify. However, let's have
1051 * this API to keep our options open should we later on need
1056 _public_
int sd_login_monitor_get_timeout(sd_login_monitor
*m
, uint64_t *timeout_usec
) {
1058 assert_return(m
, -EINVAL
);
1059 assert_return(timeout_usec
, -EINVAL
);
1061 /* For now we will only return (uint64_t) -1, since we don't
1062 * need any timeout. However, let's have this API to keep our
1063 * options open should we later on need it. */
1064 *timeout_usec
= (uint64_t) -1;