1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include <sys/inotify.h>
30 #include "alloc-util.h"
31 #include "cgroup-util.h"
32 #include "dirent-util.h"
36 #include "formats-util.h"
38 #include "hostname-util.h"
40 #include "login-util.h"
42 #include "parse-util.h"
43 #include "path-util.h"
44 #include "socket-util.h"
45 #include "string-util.h"
47 #include "user-util.h"
52 * invalid input parameters → -EINVAL
54 * process does not exist → -ESRCH
55 * cgroup does not exist → -ENOENT
56 * machine, session does not exist → -ENXIO
57 * requested metadata on object is missing → -ENODATA
60 _public_
int sd_pid_get_session(pid_t pid
, char **session
) {
62 assert_return(pid
>= 0, -EINVAL
);
63 assert_return(session
, -EINVAL
);
65 return cg_pid_get_session(pid
, session
);
68 _public_
int sd_pid_get_unit(pid_t pid
, char **unit
) {
70 assert_return(pid
>= 0, -EINVAL
);
71 assert_return(unit
, -EINVAL
);
73 return cg_pid_get_unit(pid
, unit
);
76 _public_
int sd_pid_get_user_unit(pid_t pid
, char **unit
) {
78 assert_return(pid
>= 0, -EINVAL
);
79 assert_return(unit
, -EINVAL
);
81 return cg_pid_get_user_unit(pid
, unit
);
84 _public_
int sd_pid_get_machine_name(pid_t pid
, char **name
) {
86 assert_return(pid
>= 0, -EINVAL
);
87 assert_return(name
, -EINVAL
);
89 return cg_pid_get_machine_name(pid
, name
);
92 _public_
int sd_pid_get_slice(pid_t pid
, char **slice
) {
94 assert_return(pid
>= 0, -EINVAL
);
95 assert_return(slice
, -EINVAL
);
97 return cg_pid_get_slice(pid
, slice
);
100 _public_
int sd_pid_get_user_slice(pid_t pid
, char **slice
) {
102 assert_return(pid
>= 0, -EINVAL
);
103 assert_return(slice
, -EINVAL
);
105 return cg_pid_get_user_slice(pid
, slice
);
108 _public_
int sd_pid_get_owner_uid(pid_t pid
, uid_t
*uid
) {
110 assert_return(pid
>= 0, -EINVAL
);
111 assert_return(uid
, -EINVAL
);
113 return cg_pid_get_owner_uid(pid
, uid
);
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 ambigious 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
);
321 static int file_of_seat(const char *seat
, char **_p
) {
328 if (!filename_is_valid(seat
))
331 p
= strappend("/run/systemd/seats/", seat
);
333 _cleanup_free_
char *buf
= NULL
;
335 r
= sd_session_get_seat(NULL
, &buf
);
339 p
= strappend("/run/systemd/seats/", buf
);
350 _public_
int sd_uid_is_on_seat(uid_t uid
, int require_active
, const char *seat
) {
351 _cleanup_free_
char *t
= NULL
, *s
= NULL
, *p
= NULL
;
354 const char *word
, *variable
, *state
;
356 assert_return(uid_is_valid(uid
), -EINVAL
);
358 r
= file_of_seat(seat
, &p
);
362 variable
= require_active
? "ACTIVE_UID" : "UIDS";
364 r
= parse_env_file(p
, NEWLINE
, variable
, &s
, NULL
);
372 if (asprintf(&t
, UID_FMT
, uid
) < 0)
375 FOREACH_WORD(word
, l
, s
, state
)
376 if (strneq(t
, word
, l
))
382 static int uid_get_array(uid_t uid
, const char *variable
, char ***array
) {
383 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
389 r
= file_of_uid(uid
, &p
);
393 r
= parse_env_file(p
, NEWLINE
, variable
, &s
, NULL
);
394 if (r
== -ENOENT
|| (r
>= 0 && isempty(s
))) {
402 a
= strv_split(s
, " ");
417 _public_
int sd_uid_get_sessions(uid_t uid
, int require_active
, char ***sessions
) {
418 return uid_get_array(
420 require_active
== 0 ? "ONLINE_SESSIONS" :
421 require_active
> 0 ? "ACTIVE_SESSIONS" :
426 _public_
int sd_uid_get_seats(uid_t uid
, int require_active
, char ***seats
) {
427 return uid_get_array(
429 require_active
== 0 ? "ONLINE_SEATS" :
430 require_active
> 0 ? "ACTIVE_SEATS" :
435 static int file_of_session(const char *session
, char **_p
) {
442 if (!session_id_valid(session
))
445 p
= strappend("/run/systemd/sessions/", session
);
447 _cleanup_free_
char *buf
= NULL
;
449 r
= sd_pid_get_session(0, &buf
);
453 p
= strappend("/run/systemd/sessions/", buf
);
463 _public_
int sd_session_is_active(const char *session
) {
464 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
467 r
= file_of_session(session
, &p
);
471 r
= parse_env_file(p
, NEWLINE
, "ACTIVE", &s
, NULL
);
479 return parse_boolean(s
);
482 _public_
int sd_session_is_remote(const char *session
) {
483 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
486 r
= file_of_session(session
, &p
);
490 r
= parse_env_file(p
, NEWLINE
, "REMOTE", &s
, NULL
);
498 return parse_boolean(s
);
501 _public_
int sd_session_get_state(const char *session
, char **state
) {
502 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
505 assert_return(state
, -EINVAL
);
507 r
= file_of_session(session
, &p
);
511 r
= parse_env_file(p
, NEWLINE
, "STATE", &s
, NULL
);
525 _public_
int sd_session_get_uid(const char *session
, uid_t
*uid
) {
527 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
529 assert_return(uid
, -EINVAL
);
531 r
= file_of_session(session
, &p
);
535 r
= parse_env_file(p
, NEWLINE
, "UID", &s
, NULL
);
543 return parse_uid(s
, uid
);
546 static int session_get_string(const char *session
, const char *field
, char **value
) {
547 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
550 assert_return(value
, -EINVAL
);
553 r
= file_of_session(session
, &p
);
557 r
= parse_env_file(p
, NEWLINE
, field
, &s
, NULL
);
570 _public_
int sd_session_get_seat(const char *session
, char **seat
) {
571 return session_get_string(session
, "SEAT", seat
);
574 _public_
int sd_session_get_tty(const char *session
, char **tty
) {
575 return session_get_string(session
, "TTY", tty
);
578 _public_
int sd_session_get_vt(const char *session
, unsigned *vtnr
) {
579 _cleanup_free_
char *vtnr_string
= NULL
;
583 assert_return(vtnr
, -EINVAL
);
585 r
= session_get_string(session
, "VTNR", &vtnr_string
);
589 r
= safe_atou(vtnr_string
, &u
);
597 _public_
int sd_session_get_service(const char *session
, char **service
) {
598 return session_get_string(session
, "SERVICE", service
);
601 _public_
int sd_session_get_type(const char *session
, char **type
) {
602 return session_get_string(session
, "TYPE", type
);
605 _public_
int sd_session_get_class(const char *session
, char **class) {
606 return session_get_string(session
, "CLASS", class);
609 _public_
int sd_session_get_desktop(const char *session
, char **desktop
) {
610 _cleanup_free_
char *escaped
= NULL
;
614 assert_return(desktop
, -EINVAL
);
616 r
= session_get_string(session
, "DESKTOP", &escaped
);
620 r
= cunescape(escaped
, 0, &t
);
628 _public_
int sd_session_get_display(const char *session
, char **display
) {
629 return session_get_string(session
, "DISPLAY", display
);
632 _public_
int sd_session_get_remote_user(const char *session
, char **remote_user
) {
633 return session_get_string(session
, "REMOTE_USER", remote_user
);
636 _public_
int sd_session_get_remote_host(const char *session
, char **remote_host
) {
637 return session_get_string(session
, "REMOTE_HOST", remote_host
);
640 _public_
int sd_seat_get_active(const char *seat
, char **session
, uid_t
*uid
) {
641 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *t
= NULL
;
644 assert_return(session
|| uid
, -EINVAL
);
646 r
= file_of_seat(seat
, &p
);
650 r
= parse_env_file(p
, NEWLINE
,
666 r
= parse_uid(t
, uid
);
679 _public_
int sd_seat_get_sessions(const char *seat
, char ***sessions
, uid_t
**uids
, unsigned *n_uids
) {
680 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *t
= NULL
;
681 _cleanup_strv_free_
char **a
= NULL
;
682 _cleanup_free_ uid_t
*b
= NULL
;
686 r
= file_of_seat(seat
, &p
);
690 r
= parse_env_file(p
, NEWLINE
,
692 "ACTIVE_SESSIONS", &t
,
700 a
= strv_split(s
, " ");
706 const char *word
, *state
;
709 FOREACH_WORD(word
, l
, t
, state
)
719 FOREACH_WORD(word
, l
, t
, state
) {
720 _cleanup_free_
char *k
= NULL
;
722 k
= strndup(word
, l
);
726 r
= parse_uid(k
, b
+ i
);
753 static int seat_get_can(const char *seat
, const char *variable
) {
754 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
759 r
= file_of_seat(seat
, &p
);
763 r
= parse_env_file(p
, NEWLINE
,
773 return parse_boolean(s
);
776 _public_
int sd_seat_can_multi_session(const char *seat
) {
777 return seat_get_can(seat
, "CAN_MULTI_SESSION");
780 _public_
int sd_seat_can_tty(const char *seat
) {
781 return seat_get_can(seat
, "CAN_TTY");
784 _public_
int sd_seat_can_graphical(const char *seat
) {
785 return seat_get_can(seat
, "CAN_GRAPHICAL");
788 _public_
int sd_get_seats(char ***seats
) {
789 return get_files_in_directory("/run/systemd/seats/", seats
);
792 _public_
int sd_get_sessions(char ***sessions
) {
793 return get_files_in_directory("/run/systemd/sessions/", sessions
);
796 _public_
int sd_get_uids(uid_t
**users
) {
797 _cleanup_closedir_
DIR *d
;
800 _cleanup_free_ uid_t
*l
= NULL
;
802 d
= opendir("/run/systemd/users/");
813 if (!de
&& errno
!= 0)
819 dirent_ensure_type(d
, de
);
821 if (!dirent_is_file(de
))
824 k
= parse_uid(de
->d_name
, &uid
);
829 if ((unsigned) r
>= n
) {
833 t
= realloc(l
, sizeof(uid_t
) * n
);
840 assert((unsigned) r
< n
);
854 _public_
int sd_get_machine_names(char ***machines
) {
855 char **l
= NULL
, **a
, **b
;
858 assert_return(machines
, -EINVAL
);
860 r
= get_files_in_directory("/run/systemd/machines/", &l
);
867 /* Filter out the unit: symlinks */
868 for (a
= l
, b
= l
; *a
; a
++) {
869 if (startswith(*a
, "unit:") || !machine_name_is_valid(*a
))
885 _public_
int sd_machine_get_class(const char *machine
, char **class) {
886 _cleanup_free_
char *c
= NULL
;
890 assert_return(machine_name_is_valid(machine
), -EINVAL
);
891 assert_return(class, -EINVAL
);
893 p
= strjoina("/run/systemd/machines/", machine
);
894 r
= parse_env_file(p
, NEWLINE
, "CLASS", &c
, NULL
);
908 _public_
int sd_machine_get_ifindices(const char *machine
, int **ifindices
) {
909 _cleanup_free_
char *netif
= NULL
;
910 size_t l
, allocated
= 0, nr
= 0;
912 const char *p
, *word
, *state
;
915 assert_return(machine_name_is_valid(machine
), -EINVAL
);
916 assert_return(ifindices
, -EINVAL
);
918 p
= strjoina("/run/systemd/machines/", machine
);
919 r
= parse_env_file(p
, NEWLINE
, "NETIF", &netif
, NULL
);
929 FOREACH_WORD(word
, l
, netif
, state
) {
933 *(char*) (mempcpy(buf
, word
, l
)) = 0;
935 if (parse_ifindex(buf
, &ifi
) < 0)
938 if (!GREEDY_REALLOC(ni
, allocated
, nr
+1)) {
950 static inline int MONITOR_TO_FD(sd_login_monitor
*m
) {
951 return (int) (unsigned long) m
- 1;
954 static inline sd_login_monitor
* FD_TO_MONITOR(int fd
) {
955 return (sd_login_monitor
*) (unsigned long) (fd
+ 1);
958 _public_
int sd_login_monitor_new(const char *category
, sd_login_monitor
**m
) {
962 assert_return(m
, -EINVAL
);
964 fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
968 if (!category
|| streq(category
, "seat")) {
969 k
= inotify_add_watch(fd
, "/run/systemd/seats/", IN_MOVED_TO
|IN_DELETE
);
978 if (!category
|| streq(category
, "session")) {
979 k
= inotify_add_watch(fd
, "/run/systemd/sessions/", IN_MOVED_TO
|IN_DELETE
);
988 if (!category
|| streq(category
, "uid")) {
989 k
= inotify_add_watch(fd
, "/run/systemd/users/", IN_MOVED_TO
|IN_DELETE
);
998 if (!category
|| streq(category
, "machine")) {
999 k
= inotify_add_watch(fd
, "/run/systemd/machines/", IN_MOVED_TO
|IN_DELETE
);
1013 *m
= FD_TO_MONITOR(fd
);
1017 _public_ sd_login_monitor
* sd_login_monitor_unref(sd_login_monitor
*m
) {
1020 assert_return(m
, NULL
);
1022 fd
= MONITOR_TO_FD(m
);
1028 _public_
int sd_login_monitor_flush(sd_login_monitor
*m
) {
1030 assert_return(m
, -EINVAL
);
1032 return flush_fd(MONITOR_TO_FD(m
));
1035 _public_
int sd_login_monitor_get_fd(sd_login_monitor
*m
) {
1037 assert_return(m
, -EINVAL
);
1039 return MONITOR_TO_FD(m
);
1042 _public_
int sd_login_monitor_get_events(sd_login_monitor
*m
) {
1044 assert_return(m
, -EINVAL
);
1046 /* For now we will only return POLLIN here, since we don't
1047 * need anything else ever for inotify. However, let's have
1048 * this API to keep our options open should we later on need
1053 _public_
int sd_login_monitor_get_timeout(sd_login_monitor
*m
, uint64_t *timeout_usec
) {
1055 assert_return(m
, -EINVAL
);
1056 assert_return(timeout_usec
, -EINVAL
);
1058 /* For now we will only return (uint64_t) -1, since we don't
1059 * need any timeout. However, let's have this API to keep our
1060 * options open should we later on need it. */
1061 *timeout_usec
= (uint64_t) -1;