1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2011 Lennart Poettering
11 #include <sys/inotify.h>
16 #include "alloc-util.h"
17 #include "cgroup-util.h"
18 #include "dirent-util.h"
22 #include "format-util.h"
24 #include "hostname-util.h"
26 #include "login-util.h"
28 #include "parse-util.h"
29 #include "path-util.h"
30 #include "socket-util.h"
31 #include "string-util.h"
33 #include "user-util.h"
38 * invalid input parameters → -EINVAL
40 * process does not exist → -ESRCH
41 * cgroup does not exist → -ENOENT
42 * machine, session does not exist → -ENXIO
43 * requested metadata on object is missing → -ENODATA
46 _public_
int sd_pid_get_session(pid_t pid
, char **session
) {
49 assert_return(pid
>= 0, -EINVAL
);
50 assert_return(session
, -EINVAL
);
52 r
= cg_pid_get_session(pid
, session
);
53 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
56 _public_
int sd_pid_get_unit(pid_t pid
, char **unit
) {
59 assert_return(pid
>= 0, -EINVAL
);
60 assert_return(unit
, -EINVAL
);
62 r
= cg_pid_get_unit(pid
, unit
);
63 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
66 _public_
int sd_pid_get_user_unit(pid_t pid
, char **unit
) {
69 assert_return(pid
>= 0, -EINVAL
);
70 assert_return(unit
, -EINVAL
);
72 r
= cg_pid_get_user_unit(pid
, unit
);
73 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
76 _public_
int sd_pid_get_machine_name(pid_t pid
, char **name
) {
79 assert_return(pid
>= 0, -EINVAL
);
80 assert_return(name
, -EINVAL
);
82 r
= cg_pid_get_machine_name(pid
, name
);
83 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
86 _public_
int sd_pid_get_slice(pid_t pid
, char **slice
) {
89 assert_return(pid
>= 0, -EINVAL
);
90 assert_return(slice
, -EINVAL
);
92 r
= cg_pid_get_slice(pid
, slice
);
93 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
96 _public_
int sd_pid_get_user_slice(pid_t pid
, char **slice
) {
99 assert_return(pid
>= 0, -EINVAL
);
100 assert_return(slice
, -EINVAL
);
102 r
= cg_pid_get_user_slice(pid
, slice
);
103 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
106 _public_
int sd_pid_get_owner_uid(pid_t pid
, uid_t
*uid
) {
109 assert_return(pid
>= 0, -EINVAL
);
110 assert_return(uid
, -EINVAL
);
112 r
= cg_pid_get_owner_uid(pid
, uid
);
113 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
116 _public_
int sd_pid_get_cgroup(pid_t pid
, char **cgroup
) {
120 assert_return(pid
>= 0, -EINVAL
);
121 assert_return(cgroup
, -EINVAL
);
123 r
= cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER
, pid
, &c
);
127 /* The internal APIs return the empty string for the root
128 * cgroup, let's return the "/" in the public APIs instead, as
129 * that's easier and less ambiguous for people to grok. */
142 _public_
int sd_peer_get_session(int fd
, char **session
) {
143 struct ucred ucred
= {};
146 assert_return(fd
>= 0, -EBADF
);
147 assert_return(session
, -EINVAL
);
149 r
= getpeercred(fd
, &ucred
);
153 return cg_pid_get_session(ucred
.pid
, session
);
156 _public_
int sd_peer_get_owner_uid(int fd
, uid_t
*uid
) {
160 assert_return(fd
>= 0, -EBADF
);
161 assert_return(uid
, -EINVAL
);
163 r
= getpeercred(fd
, &ucred
);
167 return cg_pid_get_owner_uid(ucred
.pid
, uid
);
170 _public_
int sd_peer_get_unit(int fd
, char **unit
) {
174 assert_return(fd
>= 0, -EBADF
);
175 assert_return(unit
, -EINVAL
);
177 r
= getpeercred(fd
, &ucred
);
181 return cg_pid_get_unit(ucred
.pid
, unit
);
184 _public_
int sd_peer_get_user_unit(int fd
, char **unit
) {
188 assert_return(fd
>= 0, -EBADF
);
189 assert_return(unit
, -EINVAL
);
191 r
= getpeercred(fd
, &ucred
);
195 return cg_pid_get_user_unit(ucred
.pid
, unit
);
198 _public_
int sd_peer_get_machine_name(int fd
, char **machine
) {
202 assert_return(fd
>= 0, -EBADF
);
203 assert_return(machine
, -EINVAL
);
205 r
= getpeercred(fd
, &ucred
);
209 return cg_pid_get_machine_name(ucred
.pid
, machine
);
212 _public_
int sd_peer_get_slice(int fd
, char **slice
) {
216 assert_return(fd
>= 0, -EBADF
);
217 assert_return(slice
, -EINVAL
);
219 r
= getpeercred(fd
, &ucred
);
223 return cg_pid_get_slice(ucred
.pid
, slice
);
226 _public_
int sd_peer_get_user_slice(int fd
, char **slice
) {
230 assert_return(fd
>= 0, -EBADF
);
231 assert_return(slice
, -EINVAL
);
233 r
= getpeercred(fd
, &ucred
);
237 return cg_pid_get_user_slice(ucred
.pid
, slice
);
240 _public_
int sd_peer_get_cgroup(int fd
, char **cgroup
) {
244 assert_return(fd
>= 0, -EBADF
);
245 assert_return(cgroup
, -EINVAL
);
247 r
= getpeercred(fd
, &ucred
);
251 return sd_pid_get_cgroup(ucred
.pid
, cgroup
);
254 static int file_of_uid(uid_t uid
, char **p
) {
256 assert_return(uid_is_valid(uid
), -EINVAL
);
259 if (asprintf(p
, "/run/systemd/users/" UID_FMT
, uid
) < 0)
265 _public_
int sd_uid_get_state(uid_t uid
, char**state
) {
266 _cleanup_free_
char *p
= NULL
;
270 assert_return(state
, -EINVAL
);
272 r
= file_of_uid(uid
, &p
);
276 r
= parse_env_file(p
, NEWLINE
, "STATE", &s
, NULL
);
279 s
= strdup("offline");
297 _public_
int sd_uid_get_display(uid_t uid
, char **session
) {
298 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
301 assert_return(session
, -EINVAL
);
303 r
= file_of_uid(uid
, &p
);
307 r
= parse_env_file(p
, NEWLINE
, "DISPLAY", &s
, NULL
);
315 *session
= TAKE_PTR(s
);
320 static int file_of_seat(const char *seat
, char **_p
) {
327 if (!filename_is_valid(seat
))
330 p
= strappend("/run/systemd/seats/", seat
);
332 _cleanup_free_
char *buf
= NULL
;
334 r
= sd_session_get_seat(NULL
, &buf
);
338 p
= strappend("/run/systemd/seats/", buf
);
348 _public_
int sd_uid_is_on_seat(uid_t uid
, int require_active
, const char *seat
) {
349 _cleanup_free_
char *t
= NULL
, *s
= NULL
, *p
= NULL
;
352 const char *word
, *variable
, *state
;
354 assert_return(uid_is_valid(uid
), -EINVAL
);
356 r
= file_of_seat(seat
, &p
);
360 variable
= require_active
? "ACTIVE_UID" : "UIDS";
362 r
= parse_env_file(p
, NEWLINE
, variable
, &s
, NULL
);
370 if (asprintf(&t
, UID_FMT
, uid
) < 0)
373 FOREACH_WORD(word
, l
, s
, state
)
374 if (strneq(t
, word
, l
))
380 static int uid_get_array(uid_t uid
, const char *variable
, char ***array
) {
381 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
387 r
= file_of_uid(uid
, &p
);
391 r
= parse_env_file(p
, NEWLINE
, variable
, &s
, NULL
);
392 if (r
== -ENOENT
|| (r
>= 0 && isempty(s
))) {
400 a
= strv_split(s
, " ");
405 r
= (int) strv_length(a
);
415 _public_
int sd_uid_get_sessions(uid_t uid
, int require_active
, char ***sessions
) {
416 return uid_get_array(
418 require_active
== 0 ? "ONLINE_SESSIONS" :
419 require_active
> 0 ? "ACTIVE_SESSIONS" :
424 _public_
int sd_uid_get_seats(uid_t uid
, int require_active
, char ***seats
) {
425 return uid_get_array(
427 require_active
== 0 ? "ONLINE_SEATS" :
428 require_active
> 0 ? "ACTIVE_SEATS" :
433 static int file_of_session(const char *session
, char **_p
) {
440 if (!session_id_valid(session
))
443 p
= strappend("/run/systemd/sessions/", session
);
445 _cleanup_free_
char *buf
= NULL
;
447 r
= sd_pid_get_session(0, &buf
);
451 p
= strappend("/run/systemd/sessions/", buf
);
461 _public_
int sd_session_is_active(const char *session
) {
462 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
465 r
= file_of_session(session
, &p
);
469 r
= parse_env_file(p
, NEWLINE
, "ACTIVE", &s
, NULL
);
477 return parse_boolean(s
);
480 _public_
int sd_session_is_remote(const char *session
) {
481 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
484 r
= file_of_session(session
, &p
);
488 r
= parse_env_file(p
, NEWLINE
, "REMOTE", &s
, NULL
);
496 return parse_boolean(s
);
499 _public_
int sd_session_get_state(const char *session
, char **state
) {
500 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
503 assert_return(state
, -EINVAL
);
505 r
= file_of_session(session
, &p
);
509 r
= parse_env_file(p
, NEWLINE
, "STATE", &s
, NULL
);
517 *state
= TAKE_PTR(s
);
522 _public_
int sd_session_get_uid(const char *session
, uid_t
*uid
) {
524 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
526 assert_return(uid
, -EINVAL
);
528 r
= file_of_session(session
, &p
);
532 r
= parse_env_file(p
, NEWLINE
, "UID", &s
, NULL
);
540 return parse_uid(s
, uid
);
543 static int session_get_string(const char *session
, const char *field
, char **value
) {
544 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
547 assert_return(value
, -EINVAL
);
550 r
= file_of_session(session
, &p
);
554 r
= parse_env_file(p
, NEWLINE
, field
, &s
, NULL
);
562 *value
= TAKE_PTR(s
);
566 _public_
int sd_session_get_seat(const char *session
, char **seat
) {
567 return session_get_string(session
, "SEAT", seat
);
570 _public_
int sd_session_get_tty(const char *session
, char **tty
) {
571 return session_get_string(session
, "TTY", tty
);
574 _public_
int sd_session_get_vt(const char *session
, unsigned *vtnr
) {
575 _cleanup_free_
char *vtnr_string
= NULL
;
579 assert_return(vtnr
, -EINVAL
);
581 r
= session_get_string(session
, "VTNR", &vtnr_string
);
585 r
= safe_atou(vtnr_string
, &u
);
593 _public_
int sd_session_get_service(const char *session
, char **service
) {
594 return session_get_string(session
, "SERVICE", service
);
597 _public_
int sd_session_get_type(const char *session
, char **type
) {
598 return session_get_string(session
, "TYPE", type
);
601 _public_
int sd_session_get_class(const char *session
, char **class) {
602 return session_get_string(session
, "CLASS", class);
605 _public_
int sd_session_get_desktop(const char *session
, char **desktop
) {
606 _cleanup_free_
char *escaped
= NULL
;
610 assert_return(desktop
, -EINVAL
);
612 r
= session_get_string(session
, "DESKTOP", &escaped
);
616 r
= cunescape(escaped
, 0, &t
);
624 _public_
int sd_session_get_display(const char *session
, char **display
) {
625 return session_get_string(session
, "DISPLAY", display
);
628 _public_
int sd_session_get_remote_user(const char *session
, char **remote_user
) {
629 return session_get_string(session
, "REMOTE_USER", remote_user
);
632 _public_
int sd_session_get_remote_host(const char *session
, char **remote_host
) {
633 return session_get_string(session
, "REMOTE_HOST", remote_host
);
636 _public_
int sd_seat_get_active(const char *seat
, char **session
, uid_t
*uid
) {
637 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *t
= NULL
;
640 assert_return(session
|| uid
, -EINVAL
);
642 r
= file_of_seat(seat
, &p
);
646 r
= parse_env_file(p
, NEWLINE
,
662 r
= parse_uid(t
, uid
);
668 *session
= TAKE_PTR(s
);
673 _public_
int sd_seat_get_sessions(const char *seat
, char ***sessions
, uid_t
**uids
, unsigned *n_uids
) {
674 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *t
= NULL
;
675 _cleanup_strv_free_
char **a
= NULL
;
676 _cleanup_free_ uid_t
*b
= NULL
;
680 r
= file_of_seat(seat
, &p
);
684 r
= parse_env_file(p
, NEWLINE
,
694 a
= strv_split(s
, " ");
700 const char *word
, *state
;
703 FOREACH_WORD(word
, l
, t
, state
)
713 FOREACH_WORD(word
, l
, t
, state
) {
714 _cleanup_free_
char *k
= NULL
;
716 k
= strndup(word
, l
);
720 r
= parse_uid(k
, b
+ i
);
729 r
= (int) strv_length(a
);
732 *sessions
= TAKE_PTR(a
);
743 static int seat_get_can(const char *seat
, const char *variable
) {
744 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
749 r
= file_of_seat(seat
, &p
);
753 r
= parse_env_file(p
, NEWLINE
,
763 return parse_boolean(s
);
766 _public_
int sd_seat_can_multi_session(const char *seat
) {
767 return seat_get_can(seat
, "CAN_MULTI_SESSION");
770 _public_
int sd_seat_can_tty(const char *seat
) {
771 return seat_get_can(seat
, "CAN_TTY");
774 _public_
int sd_seat_can_graphical(const char *seat
) {
775 return seat_get_can(seat
, "CAN_GRAPHICAL");
778 _public_
int sd_get_seats(char ***seats
) {
781 r
= get_files_in_directory("/run/systemd/seats/", seats
);
790 _public_
int sd_get_sessions(char ***sessions
) {
793 r
= get_files_in_directory("/run/systemd/sessions/", sessions
);
802 _public_
int sd_get_uids(uid_t
**users
) {
803 _cleanup_closedir_
DIR *d
;
807 _cleanup_free_ uid_t
*l
= NULL
;
809 d
= opendir("/run/systemd/users/");
811 if (errno
== ENOENT
) {
819 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
823 dirent_ensure_type(d
, de
);
825 if (!dirent_is_file(de
))
828 k
= parse_uid(de
->d_name
, &uid
);
833 if ((unsigned) r
>= n
) {
837 t
= realloc(l
, sizeof(uid_t
) * n
);
844 assert((unsigned) r
< n
);
851 *users
= TAKE_PTR(l
);
856 _public_
int sd_get_machine_names(char ***machines
) {
857 _cleanup_strv_free_
char **l
= NULL
;
861 r
= get_files_in_directory("/run/systemd/machines/", &l
);
873 /* Filter out the unit: symlinks */
874 for (a
= b
= l
; *a
; a
++) {
875 if (startswith(*a
, "unit:") || !machine_name_is_valid(*a
))
888 *machines
= TAKE_PTR(l
);
893 _public_
int sd_machine_get_class(const char *machine
, char **class) {
894 _cleanup_free_
char *c
= NULL
;
898 assert_return(machine_name_is_valid(machine
), -EINVAL
);
899 assert_return(class, -EINVAL
);
901 p
= strjoina("/run/systemd/machines/", machine
);
902 r
= parse_env_file(p
, NEWLINE
, "CLASS", &c
, NULL
);
910 *class = TAKE_PTR(c
);
915 _public_
int sd_machine_get_ifindices(const char *machine
, int **ifindices
) {
916 _cleanup_free_
char *netif
= NULL
;
917 size_t l
, allocated
= 0, nr
= 0;
919 const char *p
, *word
, *state
;
922 assert_return(machine_name_is_valid(machine
), -EINVAL
);
923 assert_return(ifindices
, -EINVAL
);
925 p
= strjoina("/run/systemd/machines/", machine
);
926 r
= parse_env_file(p
, NEWLINE
, "NETIF", &netif
, NULL
);
936 FOREACH_WORD(word
, l
, netif
, state
) {
940 *(char*) (mempcpy(buf
, word
, l
)) = 0;
942 if (parse_ifindex(buf
, &ifi
) < 0)
945 if (!GREEDY_REALLOC(ni
, allocated
, nr
+1)) {
957 static inline int MONITOR_TO_FD(sd_login_monitor
*m
) {
958 return (int) (unsigned long) m
- 1;
961 static inline sd_login_monitor
* FD_TO_MONITOR(int fd
) {
962 return (sd_login_monitor
*) (unsigned long) (fd
+ 1);
965 _public_
int sd_login_monitor_new(const char *category
, sd_login_monitor
**m
) {
966 _cleanup_close_
int fd
= -1;
970 assert_return(m
, -EINVAL
);
972 fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
976 if (!category
|| streq(category
, "seat")) {
977 k
= inotify_add_watch(fd
, "/run/systemd/seats/", IN_MOVED_TO
|IN_DELETE
);
984 if (!category
|| streq(category
, "session")) {
985 k
= inotify_add_watch(fd
, "/run/systemd/sessions/", IN_MOVED_TO
|IN_DELETE
);
992 if (!category
|| streq(category
, "uid")) {
993 k
= inotify_add_watch(fd
, "/run/systemd/users/", IN_MOVED_TO
|IN_DELETE
);
1000 if (!category
|| streq(category
, "machine")) {
1001 k
= inotify_add_watch(fd
, "/run/systemd/machines/", IN_MOVED_TO
|IN_DELETE
);
1011 *m
= FD_TO_MONITOR(fd
);
1017 _public_ sd_login_monitor
* sd_login_monitor_unref(sd_login_monitor
*m
) {
1023 fd
= MONITOR_TO_FD(m
);
1029 _public_
int sd_login_monitor_flush(sd_login_monitor
*m
) {
1032 assert_return(m
, -EINVAL
);
1034 r
= flush_fd(MONITOR_TO_FD(m
));
1041 _public_
int sd_login_monitor_get_fd(sd_login_monitor
*m
) {
1043 assert_return(m
, -EINVAL
);
1045 return MONITOR_TO_FD(m
);
1048 _public_
int sd_login_monitor_get_events(sd_login_monitor
*m
) {
1050 assert_return(m
, -EINVAL
);
1052 /* For now we will only return POLLIN here, since we don't
1053 * need anything else ever for inotify. However, let's have
1054 * this API to keep our options open should we later on need
1059 _public_
int sd_login_monitor_get_timeout(sd_login_monitor
*m
, uint64_t *timeout_usec
) {
1061 assert_return(m
, -EINVAL
);
1062 assert_return(timeout_usec
, -EINVAL
);
1064 /* For now we will only return (uint64_t) -1, since we don't
1065 * need any timeout. However, let's have this API to keep our
1066 * options open should we later on need it. */
1067 *timeout_usec
= (uint64_t) -1;