1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2011 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/inotify.h>
29 #include "alloc-util.h"
30 #include "cgroup-util.h"
31 #include "dirent-util.h"
35 #include "format-util.h"
37 #include "hostname-util.h"
39 #include "login-util.h"
41 #include "parse-util.h"
42 #include "path-util.h"
43 #include "socket-util.h"
44 #include "string-util.h"
46 #include "user-util.h"
51 * invalid input parameters → -EINVAL
53 * process does not exist → -ESRCH
54 * cgroup does not exist → -ENOENT
55 * machine, session does not exist → -ENXIO
56 * requested metadata on object is missing → -ENODATA
59 _public_
int sd_pid_get_session(pid_t pid
, char **session
) {
62 assert_return(pid
>= 0, -EINVAL
);
63 assert_return(session
, -EINVAL
);
65 r
= cg_pid_get_session(pid
, session
);
66 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
69 _public_
int sd_pid_get_unit(pid_t pid
, char **unit
) {
72 assert_return(pid
>= 0, -EINVAL
);
73 assert_return(unit
, -EINVAL
);
75 r
= cg_pid_get_unit(pid
, unit
);
76 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
79 _public_
int sd_pid_get_user_unit(pid_t pid
, char **unit
) {
82 assert_return(pid
>= 0, -EINVAL
);
83 assert_return(unit
, -EINVAL
);
85 r
= cg_pid_get_user_unit(pid
, unit
);
86 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
89 _public_
int sd_pid_get_machine_name(pid_t pid
, char **name
) {
92 assert_return(pid
>= 0, -EINVAL
);
93 assert_return(name
, -EINVAL
);
95 r
= cg_pid_get_machine_name(pid
, name
);
96 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
99 _public_
int sd_pid_get_slice(pid_t pid
, char **slice
) {
102 assert_return(pid
>= 0, -EINVAL
);
103 assert_return(slice
, -EINVAL
);
105 r
= cg_pid_get_slice(pid
, slice
);
106 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
109 _public_
int sd_pid_get_user_slice(pid_t pid
, char **slice
) {
112 assert_return(pid
>= 0, -EINVAL
);
113 assert_return(slice
, -EINVAL
);
115 r
= cg_pid_get_user_slice(pid
, slice
);
116 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
119 _public_
int sd_pid_get_owner_uid(pid_t pid
, uid_t
*uid
) {
122 assert_return(pid
>= 0, -EINVAL
);
123 assert_return(uid
, -EINVAL
);
125 r
= cg_pid_get_owner_uid(pid
, uid
);
126 return IN_SET(r
, -ENXIO
, -ENOMEDIUM
) ? -ENODATA
: r
;
129 _public_
int sd_pid_get_cgroup(pid_t pid
, char **cgroup
) {
133 assert_return(pid
>= 0, -EINVAL
);
134 assert_return(cgroup
, -EINVAL
);
136 r
= cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER
, pid
, &c
);
140 /* The internal APIs return the empty string for the root
141 * cgroup, let's return the "/" in the public APIs instead, as
142 * that's easier and less ambiguous for people to grok. */
155 _public_
int sd_peer_get_session(int fd
, char **session
) {
156 struct ucred ucred
= {};
159 assert_return(fd
>= 0, -EBADF
);
160 assert_return(session
, -EINVAL
);
162 r
= getpeercred(fd
, &ucred
);
166 return cg_pid_get_session(ucred
.pid
, session
);
169 _public_
int sd_peer_get_owner_uid(int fd
, uid_t
*uid
) {
173 assert_return(fd
>= 0, -EBADF
);
174 assert_return(uid
, -EINVAL
);
176 r
= getpeercred(fd
, &ucred
);
180 return cg_pid_get_owner_uid(ucred
.pid
, uid
);
183 _public_
int sd_peer_get_unit(int fd
, char **unit
) {
187 assert_return(fd
>= 0, -EBADF
);
188 assert_return(unit
, -EINVAL
);
190 r
= getpeercred(fd
, &ucred
);
194 return cg_pid_get_unit(ucred
.pid
, unit
);
197 _public_
int sd_peer_get_user_unit(int fd
, char **unit
) {
201 assert_return(fd
>= 0, -EBADF
);
202 assert_return(unit
, -EINVAL
);
204 r
= getpeercred(fd
, &ucred
);
208 return cg_pid_get_user_unit(ucred
.pid
, unit
);
211 _public_
int sd_peer_get_machine_name(int fd
, char **machine
) {
215 assert_return(fd
>= 0, -EBADF
);
216 assert_return(machine
, -EINVAL
);
218 r
= getpeercred(fd
, &ucred
);
222 return cg_pid_get_machine_name(ucred
.pid
, machine
);
225 _public_
int sd_peer_get_slice(int fd
, char **slice
) {
229 assert_return(fd
>= 0, -EBADF
);
230 assert_return(slice
, -EINVAL
);
232 r
= getpeercred(fd
, &ucred
);
236 return cg_pid_get_slice(ucred
.pid
, slice
);
239 _public_
int sd_peer_get_user_slice(int fd
, char **slice
) {
243 assert_return(fd
>= 0, -EBADF
);
244 assert_return(slice
, -EINVAL
);
246 r
= getpeercred(fd
, &ucred
);
250 return cg_pid_get_user_slice(ucred
.pid
, slice
);
253 _public_
int sd_peer_get_cgroup(int fd
, char **cgroup
) {
257 assert_return(fd
>= 0, -EBADF
);
258 assert_return(cgroup
, -EINVAL
);
260 r
= getpeercred(fd
, &ucred
);
264 return sd_pid_get_cgroup(ucred
.pid
, cgroup
);
267 static int file_of_uid(uid_t uid
, char **p
) {
269 assert_return(uid_is_valid(uid
), -EINVAL
);
272 if (asprintf(p
, "/run/systemd/users/" UID_FMT
, uid
) < 0)
278 _public_
int sd_uid_get_state(uid_t uid
, char**state
) {
279 _cleanup_free_
char *p
= NULL
;
283 assert_return(state
, -EINVAL
);
285 r
= file_of_uid(uid
, &p
);
289 r
= parse_env_file(p
, NEWLINE
, "STATE", &s
, NULL
);
292 s
= strdup("offline");
310 _public_
int sd_uid_get_display(uid_t uid
, char **session
) {
311 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
314 assert_return(session
, -EINVAL
);
316 r
= file_of_uid(uid
, &p
);
320 r
= parse_env_file(p
, NEWLINE
, "DISPLAY", &s
, NULL
);
334 static int file_of_seat(const char *seat
, char **_p
) {
341 if (!filename_is_valid(seat
))
344 p
= strappend("/run/systemd/seats/", seat
);
346 _cleanup_free_
char *buf
= NULL
;
348 r
= sd_session_get_seat(NULL
, &buf
);
352 p
= strappend("/run/systemd/seats/", buf
);
363 _public_
int sd_uid_is_on_seat(uid_t uid
, int require_active
, const char *seat
) {
364 _cleanup_free_
char *t
= NULL
, *s
= NULL
, *p
= NULL
;
367 const char *word
, *variable
, *state
;
369 assert_return(uid_is_valid(uid
), -EINVAL
);
371 r
= file_of_seat(seat
, &p
);
375 variable
= require_active
? "ACTIVE_UID" : "UIDS";
377 r
= parse_env_file(p
, NEWLINE
, variable
, &s
, NULL
);
385 if (asprintf(&t
, UID_FMT
, uid
) < 0)
388 FOREACH_WORD(word
, l
, s
, state
)
389 if (strneq(t
, word
, l
))
395 static int uid_get_array(uid_t uid
, const char *variable
, char ***array
) {
396 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
402 r
= file_of_uid(uid
, &p
);
406 r
= parse_env_file(p
, NEWLINE
, variable
, &s
, NULL
);
407 if (r
== -ENOENT
|| (r
>= 0 && isempty(s
))) {
415 a
= strv_split(s
, " ");
430 _public_
int sd_uid_get_sessions(uid_t uid
, int require_active
, char ***sessions
) {
431 return uid_get_array(
433 require_active
== 0 ? "ONLINE_SESSIONS" :
434 require_active
> 0 ? "ACTIVE_SESSIONS" :
439 _public_
int sd_uid_get_seats(uid_t uid
, int require_active
, char ***seats
) {
440 return uid_get_array(
442 require_active
== 0 ? "ONLINE_SEATS" :
443 require_active
> 0 ? "ACTIVE_SEATS" :
448 static int file_of_session(const char *session
, char **_p
) {
455 if (!session_id_valid(session
))
458 p
= strappend("/run/systemd/sessions/", session
);
460 _cleanup_free_
char *buf
= NULL
;
462 r
= sd_pid_get_session(0, &buf
);
466 p
= strappend("/run/systemd/sessions/", buf
);
476 _public_
int sd_session_is_active(const char *session
) {
477 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
480 r
= file_of_session(session
, &p
);
484 r
= parse_env_file(p
, NEWLINE
, "ACTIVE", &s
, NULL
);
492 return parse_boolean(s
);
495 _public_
int sd_session_is_remote(const char *session
) {
496 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
499 r
= file_of_session(session
, &p
);
503 r
= parse_env_file(p
, NEWLINE
, "REMOTE", &s
, NULL
);
511 return parse_boolean(s
);
514 _public_
int sd_session_get_state(const char *session
, char **state
) {
515 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
518 assert_return(state
, -EINVAL
);
520 r
= file_of_session(session
, &p
);
524 r
= parse_env_file(p
, NEWLINE
, "STATE", &s
, NULL
);
538 _public_
int sd_session_get_uid(const char *session
, uid_t
*uid
) {
540 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
542 assert_return(uid
, -EINVAL
);
544 r
= file_of_session(session
, &p
);
548 r
= parse_env_file(p
, NEWLINE
, "UID", &s
, NULL
);
556 return parse_uid(s
, uid
);
559 static int session_get_string(const char *session
, const char *field
, char **value
) {
560 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
563 assert_return(value
, -EINVAL
);
566 r
= file_of_session(session
, &p
);
570 r
= parse_env_file(p
, NEWLINE
, field
, &s
, NULL
);
583 _public_
int sd_session_get_seat(const char *session
, char **seat
) {
584 return session_get_string(session
, "SEAT", seat
);
587 _public_
int sd_session_get_tty(const char *session
, char **tty
) {
588 return session_get_string(session
, "TTY", tty
);
591 _public_
int sd_session_get_vt(const char *session
, unsigned *vtnr
) {
592 _cleanup_free_
char *vtnr_string
= NULL
;
596 assert_return(vtnr
, -EINVAL
);
598 r
= session_get_string(session
, "VTNR", &vtnr_string
);
602 r
= safe_atou(vtnr_string
, &u
);
610 _public_
int sd_session_get_service(const char *session
, char **service
) {
611 return session_get_string(session
, "SERVICE", service
);
614 _public_
int sd_session_get_type(const char *session
, char **type
) {
615 return session_get_string(session
, "TYPE", type
);
618 _public_
int sd_session_get_class(const char *session
, char **class) {
619 return session_get_string(session
, "CLASS", class);
622 _public_
int sd_session_get_desktop(const char *session
, char **desktop
) {
623 _cleanup_free_
char *escaped
= NULL
;
627 assert_return(desktop
, -EINVAL
);
629 r
= session_get_string(session
, "DESKTOP", &escaped
);
633 r
= cunescape(escaped
, 0, &t
);
641 _public_
int sd_session_get_display(const char *session
, char **display
) {
642 return session_get_string(session
, "DISPLAY", display
);
645 _public_
int sd_session_get_remote_user(const char *session
, char **remote_user
) {
646 return session_get_string(session
, "REMOTE_USER", remote_user
);
649 _public_
int sd_session_get_remote_host(const char *session
, char **remote_host
) {
650 return session_get_string(session
, "REMOTE_HOST", remote_host
);
653 _public_
int sd_seat_get_active(const char *seat
, char **session
, uid_t
*uid
) {
654 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *t
= NULL
;
657 assert_return(session
|| uid
, -EINVAL
);
659 r
= file_of_seat(seat
, &p
);
663 r
= parse_env_file(p
, NEWLINE
,
679 r
= parse_uid(t
, uid
);
692 _public_
int sd_seat_get_sessions(const char *seat
, char ***sessions
, uid_t
**uids
, unsigned *n_uids
) {
693 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *t
= NULL
;
694 _cleanup_strv_free_
char **a
= NULL
;
695 _cleanup_free_ uid_t
*b
= NULL
;
699 r
= file_of_seat(seat
, &p
);
703 r
= parse_env_file(p
, NEWLINE
,
713 a
= strv_split(s
, " ");
719 const char *word
, *state
;
722 FOREACH_WORD(word
, l
, t
, state
)
732 FOREACH_WORD(word
, l
, t
, state
) {
733 _cleanup_free_
char *k
= NULL
;
735 k
= strndup(word
, l
);
739 r
= parse_uid(k
, b
+ i
);
766 static int seat_get_can(const char *seat
, const char *variable
) {
767 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
772 r
= file_of_seat(seat
, &p
);
776 r
= parse_env_file(p
, NEWLINE
,
786 return parse_boolean(s
);
789 _public_
int sd_seat_can_multi_session(const char *seat
) {
790 return seat_get_can(seat
, "CAN_MULTI_SESSION");
793 _public_
int sd_seat_can_tty(const char *seat
) {
794 return seat_get_can(seat
, "CAN_TTY");
797 _public_
int sd_seat_can_graphical(const char *seat
) {
798 return seat_get_can(seat
, "CAN_GRAPHICAL");
801 _public_
int sd_get_seats(char ***seats
) {
804 r
= get_files_in_directory("/run/systemd/seats/", seats
);
813 _public_
int sd_get_sessions(char ***sessions
) {
816 r
= get_files_in_directory("/run/systemd/sessions/", sessions
);
825 _public_
int sd_get_uids(uid_t
**users
) {
826 _cleanup_closedir_
DIR *d
;
830 _cleanup_free_ uid_t
*l
= NULL
;
832 d
= opendir("/run/systemd/users/");
834 if (errno
== ENOENT
) {
842 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
846 dirent_ensure_type(d
, de
);
848 if (!dirent_is_file(de
))
851 k
= parse_uid(de
->d_name
, &uid
);
856 if ((unsigned) r
>= n
) {
860 t
= realloc(l
, sizeof(uid_t
) * n
);
867 assert((unsigned) r
< n
);
881 _public_
int sd_get_machine_names(char ***machines
) {
882 _cleanup_strv_free_
char **l
= NULL
;
886 r
= get_files_in_directory("/run/systemd/machines/", &l
);
898 /* Filter out the unit: symlinks */
899 for (a
= b
= l
; *a
; a
++) {
900 if (startswith(*a
, "unit:") || !machine_name_is_valid(*a
))
919 _public_
int sd_machine_get_class(const char *machine
, char **class) {
920 _cleanup_free_
char *c
= NULL
;
924 assert_return(machine_name_is_valid(machine
), -EINVAL
);
925 assert_return(class, -EINVAL
);
927 p
= strjoina("/run/systemd/machines/", machine
);
928 r
= parse_env_file(p
, NEWLINE
, "CLASS", &c
, NULL
);
942 _public_
int sd_machine_get_ifindices(const char *machine
, int **ifindices
) {
943 _cleanup_free_
char *netif
= NULL
;
944 size_t l
, allocated
= 0, nr
= 0;
946 const char *p
, *word
, *state
;
949 assert_return(machine_name_is_valid(machine
), -EINVAL
);
950 assert_return(ifindices
, -EINVAL
);
952 p
= strjoina("/run/systemd/machines/", machine
);
953 r
= parse_env_file(p
, NEWLINE
, "NETIF", &netif
, NULL
);
963 FOREACH_WORD(word
, l
, netif
, state
) {
967 *(char*) (mempcpy(buf
, word
, l
)) = 0;
969 if (parse_ifindex(buf
, &ifi
) < 0)
972 if (!GREEDY_REALLOC(ni
, allocated
, nr
+1)) {
984 static inline int MONITOR_TO_FD(sd_login_monitor
*m
) {
985 return (int) (unsigned long) m
- 1;
988 static inline sd_login_monitor
* FD_TO_MONITOR(int fd
) {
989 return (sd_login_monitor
*) (unsigned long) (fd
+ 1);
992 _public_
int sd_login_monitor_new(const char *category
, sd_login_monitor
**m
) {
996 assert_return(m
, -EINVAL
);
998 fd
= inotify_init1(IN_NONBLOCK
|IN_CLOEXEC
);
1002 if (!category
|| streq(category
, "seat")) {
1003 k
= inotify_add_watch(fd
, "/run/systemd/seats/", IN_MOVED_TO
|IN_DELETE
);
1012 if (!category
|| streq(category
, "session")) {
1013 k
= inotify_add_watch(fd
, "/run/systemd/sessions/", IN_MOVED_TO
|IN_DELETE
);
1022 if (!category
|| streq(category
, "uid")) {
1023 k
= inotify_add_watch(fd
, "/run/systemd/users/", IN_MOVED_TO
|IN_DELETE
);
1032 if (!category
|| streq(category
, "machine")) {
1033 k
= inotify_add_watch(fd
, "/run/systemd/machines/", IN_MOVED_TO
|IN_DELETE
);
1047 *m
= FD_TO_MONITOR(fd
);
1051 _public_ sd_login_monitor
* sd_login_monitor_unref(sd_login_monitor
*m
) {
1057 fd
= MONITOR_TO_FD(m
);
1063 _public_
int sd_login_monitor_flush(sd_login_monitor
*m
) {
1066 assert_return(m
, -EINVAL
);
1068 r
= flush_fd(MONITOR_TO_FD(m
));
1075 _public_
int sd_login_monitor_get_fd(sd_login_monitor
*m
) {
1077 assert_return(m
, -EINVAL
);
1079 return MONITOR_TO_FD(m
);
1082 _public_
int sd_login_monitor_get_events(sd_login_monitor
*m
) {
1084 assert_return(m
, -EINVAL
);
1086 /* For now we will only return POLLIN here, since we don't
1087 * need anything else ever for inotify. However, let's have
1088 * this API to keep our options open should we later on need
1093 _public_
int sd_login_monitor_get_timeout(sd_login_monitor
*m
, uint64_t *timeout_usec
) {
1095 assert_return(m
, -EINVAL
);
1096 assert_return(timeout_usec
, -EINVAL
);
1098 /* For now we will only return (uint64_t) -1, since we don't
1099 * need any timeout. However, let's have this API to keep our
1100 * options open should we later on need it. */
1101 *timeout_usec
= (uint64_t) -1;