1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include <sys/inotify.h>
10 #include "alloc-util.h"
11 #include "cgroup-util.h"
12 #include "dirent-util.h"
15 #include "extract-word.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 "stdio-util.h"
27 #include "string-util.h"
29 #include "user-util.h"
34 * invalid input parameters → -EINVAL
36 * process does not exist → -ESRCH
37 * cgroup does not exist → -ENOENT
38 * machine, session does not exist → -ENXIO
39 * requested metadata on object is missing → -ENODATA
42 _public_
int sd_pid_get_session(pid_t pid
, char **session
) {
45 assert_return(pid
>= 0, -EINVAL
);
46 assert_return(session
, -EINVAL
);
48 r
= cg_pid_get_session(pid
, session
);
49 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
52 _public_
int sd_pid_get_unit(pid_t pid
, char **unit
) {
55 assert_return(pid
>= 0, -EINVAL
);
56 assert_return(unit
, -EINVAL
);
58 r
= cg_pid_get_unit(pid
, unit
);
59 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
62 _public_
int sd_pid_get_user_unit(pid_t pid
, char **unit
) {
65 assert_return(pid
>= 0, -EINVAL
);
66 assert_return(unit
, -EINVAL
);
68 r
= cg_pid_get_user_unit(pid
, unit
);
69 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
72 _public_
int sd_pid_get_machine_name(pid_t pid
, char **name
) {
75 assert_return(pid
>= 0, -EINVAL
);
76 assert_return(name
, -EINVAL
);
78 r
= cg_pid_get_machine_name(pid
, name
);
79 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
82 _public_
int sd_pid_get_slice(pid_t pid
, char **slice
) {
85 assert_return(pid
>= 0, -EINVAL
);
86 assert_return(slice
, -EINVAL
);
88 r
= cg_pid_get_slice(pid
, slice
);
89 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
92 _public_
int sd_pid_get_user_slice(pid_t pid
, char **slice
) {
95 assert_return(pid
>= 0, -EINVAL
);
96 assert_return(slice
, -EINVAL
);
98 r
= cg_pid_get_user_slice(pid
, slice
);
99 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
102 _public_
int sd_pid_get_owner_uid(pid_t pid
, uid_t
*uid
) {
105 assert_return(pid
>= 0, -EINVAL
);
106 assert_return(uid
, -EINVAL
);
108 r
= cg_pid_get_owner_uid(pid
, uid
);
109 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
112 _public_
int sd_pid_get_cgroup(pid_t pid
, char **cgroup
) {
116 assert_return(pid
>= 0, -EINVAL
);
117 assert_return(cgroup
, -EINVAL
);
119 r
= cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER
, pid
, &c
);
123 /* The internal APIs return the empty string for the root
124 * cgroup, let's return the "/" in the public APIs instead, as
125 * that's easier and less ambiguous for people to grok. */
138 _public_
int sd_peer_get_session(int fd
, char **session
) {
139 struct ucred ucred
= {};
142 assert_return(fd
>= 0, -EBADF
);
143 assert_return(session
, -EINVAL
);
145 r
= getpeercred(fd
, &ucred
);
149 return cg_pid_get_session(ucred
.pid
, session
);
152 _public_
int sd_peer_get_owner_uid(int fd
, uid_t
*uid
) {
156 assert_return(fd
>= 0, -EBADF
);
157 assert_return(uid
, -EINVAL
);
159 r
= getpeercred(fd
, &ucred
);
163 return cg_pid_get_owner_uid(ucred
.pid
, uid
);
166 _public_
int sd_peer_get_unit(int fd
, char **unit
) {
170 assert_return(fd
>= 0, -EBADF
);
171 assert_return(unit
, -EINVAL
);
173 r
= getpeercred(fd
, &ucred
);
177 return cg_pid_get_unit(ucred
.pid
, unit
);
180 _public_
int sd_peer_get_user_unit(int fd
, char **unit
) {
184 assert_return(fd
>= 0, -EBADF
);
185 assert_return(unit
, -EINVAL
);
187 r
= getpeercred(fd
, &ucred
);
191 return cg_pid_get_user_unit(ucred
.pid
, unit
);
194 _public_
int sd_peer_get_machine_name(int fd
, char **machine
) {
198 assert_return(fd
>= 0, -EBADF
);
199 assert_return(machine
, -EINVAL
);
201 r
= getpeercred(fd
, &ucred
);
205 return cg_pid_get_machine_name(ucred
.pid
, machine
);
208 _public_
int sd_peer_get_slice(int fd
, char **slice
) {
212 assert_return(fd
>= 0, -EBADF
);
213 assert_return(slice
, -EINVAL
);
215 r
= getpeercred(fd
, &ucred
);
219 return cg_pid_get_slice(ucred
.pid
, slice
);
222 _public_
int sd_peer_get_user_slice(int fd
, char **slice
) {
226 assert_return(fd
>= 0, -EBADF
);
227 assert_return(slice
, -EINVAL
);
229 r
= getpeercred(fd
, &ucred
);
233 return cg_pid_get_user_slice(ucred
.pid
, slice
);
236 _public_
int sd_peer_get_cgroup(int fd
, char **cgroup
) {
240 assert_return(fd
>= 0, -EBADF
);
241 assert_return(cgroup
, -EINVAL
);
243 r
= getpeercred(fd
, &ucred
);
247 return sd_pid_get_cgroup(ucred
.pid
, cgroup
);
250 static int file_of_uid(uid_t uid
, char **p
) {
252 assert_return(uid_is_valid(uid
), -EINVAL
);
255 if (asprintf(p
, "/run/systemd/users/" UID_FMT
, uid
) < 0)
261 _public_
int sd_uid_get_state(uid_t uid
, char**state
) {
262 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
265 assert_return(state
, -EINVAL
);
267 r
= file_of_uid(uid
, &p
);
271 r
= parse_env_file(NULL
, p
, "STATE", &s
);
273 r
= free_and_strdup(&s
, "offline");
281 *state
= TAKE_PTR(s
);
285 _public_
int sd_uid_get_display(uid_t uid
, char **session
) {
286 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
289 assert_return(session
, -EINVAL
);
291 r
= file_of_uid(uid
, &p
);
295 r
= parse_env_file(NULL
, p
, "DISPLAY", &s
);
303 *session
= TAKE_PTR(s
);
308 static int file_of_seat(const char *seat
, char **_p
) {
315 if (!filename_is_valid(seat
))
318 p
= path_join("/run/systemd/seats", seat
);
320 _cleanup_free_
char *buf
= NULL
;
322 r
= sd_session_get_seat(NULL
, &buf
);
326 p
= path_join("/run/systemd/seats", buf
);
335 _public_
int sd_uid_is_on_seat(uid_t uid
, int require_active
, const char *seat
) {
336 _cleanup_free_
char *filename
= NULL
, *content
= NULL
;
339 assert_return(uid_is_valid(uid
), -EINVAL
);
341 r
= file_of_seat(seat
, &filename
);
345 r
= parse_env_file(NULL
, filename
,
346 require_active
? "ACTIVE_UID" : "UIDS",
352 if (isempty(content
))
355 char t
[DECIMAL_STR_MAX(uid_t
)];
356 xsprintf(t
, UID_FMT
, uid
);
358 return string_contains_word(content
, NULL
, t
);
361 static int uid_get_array(uid_t uid
, const char *variable
, char ***array
) {
362 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
368 r
= file_of_uid(uid
, &p
);
372 r
= parse_env_file(NULL
, p
, variable
, &s
);
373 if (r
== -ENOENT
|| (r
>= 0 && isempty(s
))) {
381 a
= strv_split(s
, NULL
);
386 r
= (int) strv_length(a
);
396 _public_
int sd_uid_get_sessions(uid_t uid
, int require_active
, char ***sessions
) {
397 return uid_get_array(
399 require_active
== 0 ? "ONLINE_SESSIONS" :
400 require_active
> 0 ? "ACTIVE_SESSIONS" :
405 _public_
int sd_uid_get_seats(uid_t uid
, int require_active
, char ***seats
) {
406 return uid_get_array(
408 require_active
== 0 ? "ONLINE_SEATS" :
409 require_active
> 0 ? "ACTIVE_SEATS" :
414 static int file_of_session(const char *session
, char **_p
) {
421 if (!session_id_valid(session
))
424 p
= path_join("/run/systemd/sessions", session
);
426 _cleanup_free_
char *buf
= NULL
;
428 r
= sd_pid_get_session(0, &buf
);
432 p
= path_join("/run/systemd/sessions", buf
);
442 _public_
int sd_session_is_active(const char *session
) {
443 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
446 r
= file_of_session(session
, &p
);
450 r
= parse_env_file(NULL
, p
, "ACTIVE", &s
);
458 return parse_boolean(s
);
461 _public_
int sd_session_is_remote(const char *session
) {
462 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
465 r
= file_of_session(session
, &p
);
469 r
= parse_env_file(NULL
, p
, "REMOTE", &s
);
477 return parse_boolean(s
);
480 _public_
int sd_session_get_state(const char *session
, char **state
) {
481 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
484 assert_return(state
, -EINVAL
);
486 r
= file_of_session(session
, &p
);
490 r
= parse_env_file(NULL
, p
, "STATE", &s
);
498 *state
= TAKE_PTR(s
);
503 _public_
int sd_session_get_uid(const char *session
, uid_t
*uid
) {
505 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
507 assert_return(uid
, -EINVAL
);
509 r
= file_of_session(session
, &p
);
513 r
= parse_env_file(NULL
, p
, "UID", &s
);
521 return parse_uid(s
, uid
);
524 static int session_get_string(const char *session
, const char *field
, char **value
) {
525 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
528 assert_return(value
, -EINVAL
);
531 r
= file_of_session(session
, &p
);
535 r
= parse_env_file(NULL
, p
, field
, &s
);
543 *value
= TAKE_PTR(s
);
547 _public_
int sd_session_get_seat(const char *session
, char **seat
) {
548 return session_get_string(session
, "SEAT", seat
);
551 _public_
int sd_session_get_tty(const char *session
, char **tty
) {
552 return session_get_string(session
, "TTY", tty
);
555 _public_
int sd_session_get_vt(const char *session
, unsigned *vtnr
) {
556 _cleanup_free_
char *vtnr_string
= NULL
;
560 assert_return(vtnr
, -EINVAL
);
562 r
= session_get_string(session
, "VTNR", &vtnr_string
);
566 r
= safe_atou(vtnr_string
, &u
);
574 _public_
int sd_session_get_service(const char *session
, char **service
) {
575 return session_get_string(session
, "SERVICE", service
);
578 _public_
int sd_session_get_type(const char *session
, char **type
) {
579 return session_get_string(session
, "TYPE", type
);
582 _public_
int sd_session_get_class(const char *session
, char **class) {
583 return session_get_string(session
, "CLASS", class);
586 _public_
int sd_session_get_desktop(const char *session
, char **desktop
) {
587 _cleanup_free_
char *escaped
= NULL
;
591 assert_return(desktop
, -EINVAL
);
593 r
= session_get_string(session
, "DESKTOP", &escaped
);
597 r
= cunescape(escaped
, 0, &t
);
605 _public_
int sd_session_get_display(const char *session
, char **display
) {
606 return session_get_string(session
, "DISPLAY", display
);
609 _public_
int sd_session_get_remote_user(const char *session
, char **remote_user
) {
610 return session_get_string(session
, "REMOTE_USER", remote_user
);
613 _public_
int sd_session_get_remote_host(const char *session
, char **remote_host
) {
614 return session_get_string(session
, "REMOTE_HOST", remote_host
);
617 _public_
int sd_seat_get_active(const char *seat
, char **session
, uid_t
*uid
) {
618 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *t
= NULL
;
621 assert_return(session
|| uid
, -EINVAL
);
623 r
= file_of_seat(seat
, &p
);
627 r
= parse_env_file(NULL
, p
,
642 r
= parse_uid(t
, uid
);
648 *session
= TAKE_PTR(s
);
653 _public_
int sd_seat_get_sessions(
655 char ***ret_sessions
,
657 unsigned *ret_n_uids
) {
659 _cleanup_free_
char *fname
= NULL
, *session_line
= NULL
, *uid_line
= NULL
;
660 _cleanup_strv_free_
char **sessions
= NULL
;
661 _cleanup_free_ uid_t
*uids
= NULL
;
662 unsigned n_sessions
= 0;
665 r
= file_of_seat(seat
, &fname
);
669 r
= parse_env_file(NULL
, fname
,
670 "SESSIONS", &session_line
,
678 sessions
= strv_split(session_line
, NULL
);
682 n_sessions
= strv_length(sessions
);
685 if (ret_uids
&& uid_line
) {
686 uids
= new(uid_t
, n_sessions
);
691 for (const char *p
= uid_line
;;) {
692 _cleanup_free_
char *word
= NULL
;
694 r
= extract_first_word(&p
, &word
, NULL
, 0);
700 r
= parse_uid(word
, &uids
[n
++]);
710 *ret_sessions
= TAKE_PTR(sessions
);
712 *ret_uids
= TAKE_PTR(uids
);
714 *ret_n_uids
= n_sessions
;
719 static int seat_get_can(const char *seat
, const char *variable
) {
720 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
725 r
= file_of_seat(seat
, &p
);
729 r
= parse_env_file(NULL
, p
,
738 return parse_boolean(s
);
741 _public_
int sd_seat_can_multi_session(const char *seat
) {
745 _public_
int sd_seat_can_tty(const char *seat
) {
746 return seat_get_can(seat
, "CAN_TTY");
749 _public_
int sd_seat_can_graphical(const char *seat
) {
750 return seat_get_can(seat
, "CAN_GRAPHICAL");
753 _public_
int sd_get_seats(char ***seats
) {
756 r
= get_files_in_directory("/run/systemd/seats/", seats
);
765 _public_
int sd_get_sessions(char ***sessions
) {
768 r
= get_files_in_directory("/run/systemd/sessions/", sessions
);
777 _public_
int sd_get_uids(uid_t
**users
) {
778 _cleanup_closedir_
DIR *d
;
782 _cleanup_free_ uid_t
*l
= NULL
;
784 d
= opendir("/run/systemd/users/");
786 if (errno
== ENOENT
) {
794 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
798 dirent_ensure_type(d
, de
);
800 if (!dirent_is_file(de
))
803 k
= parse_uid(de
->d_name
, &uid
);
808 if ((unsigned) r
>= n
) {
812 t
= reallocarray(l
, sizeof(uid_t
), n
);
819 assert((unsigned) r
< n
);
826 *users
= TAKE_PTR(l
);
831 _public_
int sd_get_machine_names(char ***machines
) {
832 _cleanup_strv_free_
char **l
= NULL
;
836 r
= get_files_in_directory("/run/systemd/machines/", &l
);
848 /* Filter out the unit: symlinks */
849 for (a
= b
= l
; *a
; a
++) {
850 if (startswith(*a
, "unit:") || !machine_name_is_valid(*a
))
863 *machines
= TAKE_PTR(l
);
868 _public_
int sd_machine_get_class(const char *machine
, char **class) {
869 _cleanup_free_
char *c
= NULL
;
873 assert_return(class, -EINVAL
);
875 if (streq(machine
, ".host")) {
880 if (!machine_name_is_valid(machine
))
883 p
= strjoina("/run/systemd/machines/", machine
);
884 r
= parse_env_file(NULL
, p
, "CLASS", &c
);
893 *class = TAKE_PTR(c
);
897 _public_
int sd_machine_get_ifindices(const char *machine
, int **ret_ifindices
) {
898 _cleanup_free_
char *netif_line
= NULL
;
902 assert_return(machine_name_is_valid(machine
), -EINVAL
);
904 p
= strjoina("/run/systemd/machines/", machine
);
905 r
= parse_env_file(NULL
, p
, "NETIF", &netif_line
);
911 *ret_ifindices
= NULL
;
915 _cleanup_strv_free_
char **tt
= strv_split(netif_line
, NULL
);
919 _cleanup_free_
int *ifindices
= NULL
;
921 ifindices
= new(int, strv_length(tt
));
927 for (size_t i
= 0; tt
[i
]; i
++) {
930 ind
= parse_ifindex(tt
[i
]);
932 /* Return -EUCLEAN to distinguish from -EINVAL for invalid args */
933 return ind
== -EINVAL
? -EUCLEAN
: ind
;
941 *ret_ifindices
= TAKE_PTR(ifindices
);
946 static int MONITOR_TO_FD(sd_login_monitor
*m
) {
947 return (int) (unsigned long) m
- 1;
950 static sd_login_monitor
* FD_TO_MONITOR(int fd
) {
951 return (sd_login_monitor
*) (unsigned long) (fd
+ 1);
954 _public_
int sd_login_monitor_new(const char *category
, sd_login_monitor
**m
) {
955 _cleanup_close_
int fd
= -1;
959 assert_return(m
, -EINVAL
);
961 fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
965 if (!category
|| streq(category
, "seat")) {
966 k
= inotify_add_watch(fd
, "/run/systemd/seats/", IN_MOVED_TO
|IN_DELETE
);
973 if (!category
|| streq(category
, "session")) {
974 k
= inotify_add_watch(fd
, "/run/systemd/sessions/", IN_MOVED_TO
|IN_DELETE
);
981 if (!category
|| streq(category
, "uid")) {
982 k
= inotify_add_watch(fd
, "/run/systemd/users/", IN_MOVED_TO
|IN_DELETE
);
989 if (!category
|| streq(category
, "machine")) {
990 k
= inotify_add_watch(fd
, "/run/systemd/machines/", IN_MOVED_TO
|IN_DELETE
);
1000 *m
= FD_TO_MONITOR(TAKE_FD(fd
));
1004 _public_ sd_login_monitor
* sd_login_monitor_unref(sd_login_monitor
*m
) {
1006 close_nointr(MONITOR_TO_FD(m
));
1011 _public_
int sd_login_monitor_flush(sd_login_monitor
*m
) {
1014 assert_return(m
, -EINVAL
);
1016 r
= flush_fd(MONITOR_TO_FD(m
));
1023 _public_
int sd_login_monitor_get_fd(sd_login_monitor
*m
) {
1025 assert_return(m
, -EINVAL
);
1027 return MONITOR_TO_FD(m
);
1030 _public_
int sd_login_monitor_get_events(sd_login_monitor
*m
) {
1032 assert_return(m
, -EINVAL
);
1034 /* For now we will only return POLLIN here, since we don't
1035 * need anything else ever for inotify. However, let's have
1036 * this API to keep our options open should we later on need
1041 _public_
int sd_login_monitor_get_timeout(sd_login_monitor
*m
, uint64_t *timeout_usec
) {
1043 assert_return(m
, -EINVAL
);
1044 assert_return(timeout_usec
, -EINVAL
);
1046 /* For now we will only return (uint64_t) -1, since we don't
1047 * need any timeout. However, let's have this API to keep our
1048 * options open should we later on need it. */
1049 *timeout_usec
= (uint64_t) -1;