]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/libsystemd/sd-login/sd-login.c
tree-wide: Add more socket units (#37991)
[thirdparty/systemd.git] / src / libsystemd / sd-login / sd-login.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <poll.h>
4#include <sys/inotify.h>
5#include <unistd.h>
6
7#include "sd-login.h"
8
9#include "alloc-util.h"
10#include "cgroup-util.h"
11#include "dirent-util.h"
12#include "env-file.h"
13#include "extract-word.h"
14#include "fd-util.h"
15#include "format-util.h"
16#include "fs-util.h"
17#include "hostname-util.h"
18#include "io-util.h"
19#include "login-util.h"
20#include "parse-util.h"
21#include "path-util.h"
22#include "pidfd-util.h"
23#include "pidref.h"
24#include "socket-util.h"
25#include "string-util.h"
26#include "strv.h"
27#include "user-util.h"
28
29/* Error codes:
30 *
31 * invalid input parameters → -EINVAL
32 * invalid fd → -EBADF
33 * process does not exist → -ESRCH
34 * cgroup does not exist → -ENOENT
35 * machine, session does not exist → -ENXIO
36 * requested metadata on object is missing → -ENODATA
37 */
38
39_public_ int sd_pid_get_session(pid_t pid, char **ret_session) {
40 int r;
41
42 assert_return(pid >= 0, -EINVAL);
43
44 r = cg_pid_get_session(pid, ret_session);
45 return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r;
46}
47
48_public_ int sd_pid_get_unit(pid_t pid, char **ret_unit) {
49 int r;
50
51 assert_return(pid >= 0, -EINVAL);
52
53 r = cg_pid_get_unit(pid, ret_unit);
54 return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r;
55}
56
57_public_ int sd_pid_get_user_unit(pid_t pid, char **ret_unit) {
58 int r;
59
60 assert_return(pid >= 0, -EINVAL);
61
62 r = cg_pid_get_user_unit(pid, ret_unit);
63 return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r;
64}
65
66_public_ int sd_pid_get_machine_name(pid_t pid, char **ret_name) {
67 int r;
68
69 assert_return(pid >= 0, -EINVAL);
70
71 r = cg_pid_get_machine_name(pid, ret_name);
72 return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r;
73}
74
75_public_ int sd_pid_get_slice(pid_t pid, char **ret_slice) {
76 int r;
77
78 assert_return(pid >= 0, -EINVAL);
79
80 r = cg_pid_get_slice(pid, ret_slice);
81 return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r;
82}
83
84_public_ int sd_pid_get_user_slice(pid_t pid, char **ret_slice) {
85 int r;
86
87 assert_return(pid >= 0, -EINVAL);
88
89 r = cg_pid_get_user_slice(pid, ret_slice);
90 return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r;
91}
92
93_public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *ret_uid) {
94 int r;
95
96 assert_return(pid >= 0, -EINVAL);
97
98 r = cg_pid_get_owner_uid(pid, ret_uid);
99 return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r;
100}
101
102_public_ int sd_pid_get_cgroup(pid_t pid, char **ret_cgroup) {
103 int r;
104
105 assert_return(pid >= 0, -EINVAL);
106
107 _cleanup_free_ char *c = NULL;
108 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &c);
109 if (r < 0)
110 return r;
111
112 if (ret_cgroup) {
113 /* The internal APIs return the empty string for the root cgroup, let's return the "/" in the
114 * public APIs instead, as that's easier and less ambiguous for people to grok. */
115 if (isempty(c)) {
116 r = free_and_strdup(&c, "/");
117 if (r < 0)
118 return r;
119 }
120
121 *ret_cgroup = TAKE_PTR(c);
122 }
123
124 return 0;
125}
126
127_public_ int sd_pidfd_get_session(int pidfd, char **ret_session) {
128 _cleanup_free_ char *session = NULL;
129 pid_t pid;
130 int r;
131
132 assert_return(pidfd >= 0, -EBADF);
133
134 r = pidfd_get_pid(pidfd, &pid);
135 if (r < 0)
136 return r;
137
138 r = sd_pid_get_session(pid, &session);
139 if (r < 0)
140 return r;
141
142 r = pidfd_verify_pid(pidfd, pid);
143 if (r < 0)
144 return r;
145
146 if (ret_session)
147 *ret_session = TAKE_PTR(session);
148 return 0;
149}
150
151_public_ int sd_pidfd_get_unit(int pidfd, char **ret_unit) {
152 _cleanup_free_ char *unit = NULL;
153 pid_t pid;
154 int r;
155
156 assert_return(pidfd >= 0, -EBADF);
157
158 r = pidfd_get_pid(pidfd, &pid);
159 if (r < 0)
160 return r;
161
162 r = sd_pid_get_unit(pid, &unit);
163 if (r < 0)
164 return r;
165
166 r = pidfd_verify_pid(pidfd, pid);
167 if (r < 0)
168 return r;
169
170 if (ret_unit)
171 *ret_unit = TAKE_PTR(unit);
172 return 0;
173}
174
175_public_ int sd_pidfd_get_user_unit(int pidfd, char **ret_unit) {
176 _cleanup_free_ char *unit = NULL;
177 pid_t pid;
178 int r;
179
180 assert_return(pidfd >= 0, -EBADF);
181
182 r = pidfd_get_pid(pidfd, &pid);
183 if (r < 0)
184 return r;
185
186 r = sd_pid_get_user_unit(pid, &unit);
187 if (r < 0)
188 return r;
189
190 r = pidfd_verify_pid(pidfd, pid);
191 if (r < 0)
192 return r;
193
194 if (ret_unit)
195 *ret_unit = TAKE_PTR(unit);
196 return 0;
197}
198
199_public_ int sd_pidfd_get_machine_name(int pidfd, char **ret_name) {
200 _cleanup_free_ char *name = NULL;
201 pid_t pid;
202 int r;
203
204 assert_return(pidfd >= 0, -EBADF);
205
206 r = pidfd_get_pid(pidfd, &pid);
207 if (r < 0)
208 return r;
209
210 r = sd_pid_get_machine_name(pid, &name);
211 if (r < 0)
212 return r;
213
214 r = pidfd_verify_pid(pidfd, pid);
215 if (r < 0)
216 return r;
217
218 if (ret_name)
219 *ret_name = TAKE_PTR(name);
220 return 0;
221}
222
223_public_ int sd_pidfd_get_slice(int pidfd, char **ret_slice) {
224 _cleanup_free_ char *slice = NULL;
225 pid_t pid;
226 int r;
227
228 assert_return(pidfd >= 0, -EBADF);
229
230 r = pidfd_get_pid(pidfd, &pid);
231 if (r < 0)
232 return r;
233
234 r = sd_pid_get_slice(pid, &slice);
235 if (r < 0)
236 return r;
237
238 r = pidfd_verify_pid(pidfd, pid);
239 if (r < 0)
240 return r;
241
242 if (ret_slice)
243 *ret_slice = TAKE_PTR(slice);
244 return 0;
245}
246
247_public_ int sd_pidfd_get_user_slice(int pidfd, char **ret_slice) {
248 _cleanup_free_ char *slice = NULL;
249 pid_t pid;
250 int r;
251
252 assert_return(pidfd >= 0, -EBADF);
253
254 r = pidfd_get_pid(pidfd, &pid);
255 if (r < 0)
256 return r;
257
258 r = sd_pid_get_user_slice(pid, &slice);
259 if (r < 0)
260 return r;
261
262 r = pidfd_verify_pid(pidfd, pid);
263 if (r < 0)
264 return r;
265
266 if (ret_slice)
267 *ret_slice = TAKE_PTR(slice);
268 return 0;
269}
270
271_public_ int sd_pidfd_get_owner_uid(int pidfd, uid_t *ret_uid) {
272 uid_t uid;
273 pid_t pid;
274 int r;
275
276 assert_return(pidfd >= 0, -EBADF);
277
278 r = pidfd_get_pid(pidfd, &pid);
279 if (r < 0)
280 return r;
281
282 r = sd_pid_get_owner_uid(pid, &uid);
283 if (r < 0)
284 return r;
285
286 r = pidfd_verify_pid(pidfd, pid);
287 if (r < 0)
288 return r;
289
290 if (ret_uid)
291 *ret_uid = uid;
292 return 0;
293}
294
295_public_ int sd_pidfd_get_cgroup(int pidfd, char **ret_cgroup) {
296 _cleanup_free_ char *cgroup = NULL;
297 pid_t pid;
298 int r;
299
300 assert_return(pidfd >= 0, -EBADF);
301
302 r = pidfd_get_pid(pidfd, &pid);
303 if (r < 0)
304 return r;
305
306 r = sd_pid_get_cgroup(pid, &cgroup);
307 if (r < 0)
308 return r;
309
310 r = pidfd_verify_pid(pidfd, pid);
311 if (r < 0)
312 return r;
313
314 if (ret_cgroup)
315 *ret_cgroup = TAKE_PTR(cgroup);
316 return 0;
317}
318
319_public_ int sd_peer_get_session(int fd, char **ret_session) {
320 int r;
321
322 assert_return(fd >= 0, -EBADF);
323
324 _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
325 r = getpeerpidref(fd, &pidref);
326 if (r < 0)
327 return r;
328
329 return cg_pidref_get_session(&pidref, ret_session);
330}
331
332_public_ int sd_peer_get_owner_uid(int fd, uid_t *ret_uid) {
333 int r;
334
335 assert_return(fd >= 0, -EBADF);
336
337 _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
338 r = getpeerpidref(fd, &pidref);
339 if (r < 0)
340 return r;
341
342 return cg_pidref_get_owner_uid(&pidref, ret_uid);
343}
344
345_public_ int sd_peer_get_unit(int fd, char **ret_unit) {
346 int r;
347
348 assert_return(fd >= 0, -EBADF);
349
350 _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
351 r = getpeerpidref(fd, &pidref);
352 if (r < 0)
353 return r;
354
355 return cg_pidref_get_unit(&pidref, ret_unit);
356}
357
358_public_ int sd_peer_get_user_unit(int fd, char **ret_unit) {
359 struct ucred ucred;
360 int r;
361
362 assert_return(fd >= 0, -EBADF);
363
364 r = getpeercred(fd, &ucred);
365 if (r < 0)
366 return r;
367
368 return cg_pid_get_user_unit(ucred.pid, ret_unit);
369}
370
371_public_ int sd_peer_get_machine_name(int fd, char **ret_machine) {
372 struct ucred ucred;
373 int r;
374
375 assert_return(fd >= 0, -EBADF);
376
377 r = getpeercred(fd, &ucred);
378 if (r < 0)
379 return r;
380
381 return cg_pid_get_machine_name(ucred.pid, ret_machine);
382}
383
384_public_ int sd_peer_get_slice(int fd, char **ret_slice) {
385 struct ucred ucred;
386 int r;
387
388 assert_return(fd >= 0, -EBADF);
389
390 r = getpeercred(fd, &ucred);
391 if (r < 0)
392 return r;
393
394 return cg_pid_get_slice(ucred.pid, ret_slice);
395}
396
397_public_ int sd_peer_get_user_slice(int fd, char **ret_slice) {
398 struct ucred ucred;
399 int r;
400
401 assert_return(fd >= 0, -EBADF);
402
403 r = getpeercred(fd, &ucred);
404 if (r < 0)
405 return r;
406
407 return cg_pid_get_user_slice(ucred.pid, ret_slice);
408}
409
410_public_ int sd_peer_get_cgroup(int fd, char **ret_cgroup) {
411 int r;
412
413 assert_return(fd >= 0, -EBADF);
414
415 _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
416 r = getpeerpidref(fd, &pidref);
417 if (r < 0)
418 return r;
419
420 _cleanup_free_ char *c = NULL;
421 r = cg_pidref_get_path(SYSTEMD_CGROUP_CONTROLLER, &pidref, &c);
422 if (r < 0)
423 return r;
424
425 if (ret_cgroup) {
426 /* The internal APIs return the empty string for the root cgroup, let's return the "/" in the
427 * public APIs instead, as that's easier and less ambiguous for people to grok. */
428 if (isempty(c)) {
429 r = free_and_strdup(&c, "/");
430 if (r < 0)
431 return r;
432 }
433
434 *ret_cgroup = TAKE_PTR(c);
435 }
436
437 return 0;
438}
439
440static int file_of_uid(uid_t uid, char **ret) {
441
442 assert_return(uid_is_valid(uid), -EINVAL);
443 assert(ret);
444
445 if (asprintf(ret, "/run/systemd/users/" UID_FMT, uid) < 0)
446 return -ENOMEM;
447
448 return 0;
449}
450
451_public_ int sd_uid_get_state(uid_t uid, char **ret_state) {
452 _cleanup_free_ char *p = NULL, *s = NULL;
453 int r;
454
455 r = file_of_uid(uid, &p);
456 if (r < 0)
457 return r;
458
459 r = parse_env_file(/* f= */ NULL, p, "STATE", &s);
460 if (r == -ENOENT)
461 r = free_and_strdup(&s, "offline");
462 if (r < 0)
463 return r;
464 if (isempty(s))
465 return -EIO;
466
467 if (ret_state)
468 *ret_state = TAKE_PTR(s);
469 return 0;
470}
471
472_public_ int sd_uid_get_display(uid_t uid, char **ret_session) {
473 _cleanup_free_ char *p = NULL, *s = NULL;
474 int r;
475
476 r = file_of_uid(uid, &p);
477 if (r < 0)
478 return r;
479
480 r = parse_env_file(/* f= */ NULL, p, "DISPLAY", &s);
481 if (r == -ENOENT)
482 return -ENODATA;
483 if (r < 0)
484 return r;
485 if (isempty(s))
486 return -ENODATA;
487
488 if (ret_session)
489 *ret_session = TAKE_PTR(s);
490 return 0;
491}
492
493_public_ int sd_uid_get_login_time(uid_t uid, uint64_t *ret_usec) {
494 _cleanup_free_ char *p = NULL, *s = NULL, *rt = NULL;
495 int r;
496
497 r = file_of_uid(uid, &p);
498 if (r < 0)
499 return r;
500
501 r = parse_env_file(/* f= */ NULL, p, "STATE", &s, "REALTIME", &rt);
502 if (r == -ENOENT)
503 return -ENXIO;
504 if (r < 0)
505 return r;
506 if (isempty(s) || isempty(rt))
507 return -EIO;
508
509 if (!STR_IN_SET(s, "active", "online"))
510 return -ENXIO;
511
512 return safe_atou64(rt, ret_usec);
513}
514
515static int file_of_seat(const char *seat, char **ret) {
516 char *p;
517 int r;
518
519 assert(ret);
520
521 if (seat) {
522 if (!filename_is_valid(seat))
523 return -EINVAL;
524
525 p = path_join("/run/systemd/seats", seat);
526 } else {
527 _cleanup_free_ char *buf = NULL;
528
529 r = sd_session_get_seat(NULL, &buf);
530 if (r < 0)
531 return r;
532
533 p = path_join("/run/systemd/seats", buf);
534 }
535 if (!p)
536 return -ENOMEM;
537
538 *ret = TAKE_PTR(p);
539 return 0;
540}
541
542_public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) {
543 _cleanup_free_ char *filename = NULL, *content = NULL;
544 int r;
545
546 assert_return(uid_is_valid(uid), -EINVAL);
547
548 r = file_of_seat(seat, &filename);
549 if (r < 0)
550 return r;
551
552 r = parse_env_file(/* f= */ NULL, filename,
553 require_active ? "ACTIVE_UID" : "UIDS",
554 &content);
555 if (r == -ENOENT)
556 return 0;
557 if (r < 0)
558 return r;
559 if (isempty(content))
560 return 0;
561
562 return string_contains_word(content, NULL, FORMAT_UID(uid));
563}
564
565static int uid_get_array(uid_t uid, const char *variable, char ***ret_array) {
566 _cleanup_free_ char *p = NULL, *s = NULL;
567 int r;
568
569 assert(variable);
570
571 r = file_of_uid(uid, &p);
572 if (r < 0)
573 return r;
574
575 r = parse_env_file(/* f= */ NULL, p, variable, &s);
576 if (r == -ENOENT || (r >= 0 && isempty(s))) {
577 if (ret_array)
578 *ret_array = NULL;
579 return 0;
580 }
581 if (r < 0)
582 return r;
583
584 _cleanup_strv_free_ char **a = strv_split(s, NULL);
585 if (!a)
586 return -ENOMEM;
587
588 strv_uniq(a);
589 r = (int) strv_length(a);
590
591 if (ret_array)
592 *ret_array = TAKE_PTR(a);
593
594 return r;
595}
596
597_public_ int sd_uid_get_sessions(uid_t uid, int require_active, char ***ret_sessions) {
598 return uid_get_array(
599 uid,
600 require_active == 0 ? "ONLINE_SESSIONS" :
601 require_active > 0 ? "ACTIVE_SESSIONS" :
602 "SESSIONS",
603 ret_sessions);
604}
605
606_public_ int sd_uid_get_seats(uid_t uid, int require_active, char ***ret_seats) {
607 return uid_get_array(
608 uid,
609 require_active == 0 ? "ONLINE_SEATS" :
610 require_active > 0 ? "ACTIVE_SEATS" :
611 "SEATS",
612 ret_seats);
613}
614
615static int file_of_session(const char *session, char **ret) {
616 char *p;
617 int r;
618
619 assert(ret);
620
621 if (session) {
622 if (!session_id_valid(session))
623 return -EINVAL;
624
625 p = path_join("/run/systemd/sessions", session);
626 } else {
627 _cleanup_free_ char *buf = NULL;
628
629 r = sd_pid_get_session(0, &buf);
630 if (r < 0)
631 return r;
632
633 p = path_join("/run/systemd/sessions", buf);
634 }
635 if (!p)
636 return -ENOMEM;
637
638 *ret = p;
639 return 0;
640}
641
642_public_ int sd_session_is_active(const char *session) {
643 _cleanup_free_ char *p = NULL, *s = NULL;
644 int r;
645
646 r = file_of_session(session, &p);
647 if (r < 0)
648 return r;
649
650 r = parse_env_file(/* f= */ NULL, p, "ACTIVE", &s);
651 if (r == -ENOENT)
652 return -ENXIO;
653 if (r < 0)
654 return r;
655 if (isempty(s))
656 return -EIO;
657
658 return parse_boolean(s);
659}
660
661_public_ int sd_session_is_remote(const char *session) {
662 _cleanup_free_ char *p = NULL, *s = NULL;
663 int r;
664
665 r = file_of_session(session, &p);
666 if (r < 0)
667 return r;
668
669 r = parse_env_file(/* f= */ NULL, p, "REMOTE", &s);
670 if (r == -ENOENT)
671 return -ENXIO;
672 if (r < 0)
673 return r;
674 if (isempty(s))
675 return -ENODATA;
676
677 return parse_boolean(s);
678}
679
680_public_ int sd_session_get_state(const char *session, char **ret_state) {
681 _cleanup_free_ char *p = NULL, *s = NULL;
682 int r;
683
684 r = file_of_session(session, &p);
685 if (r < 0)
686 return r;
687
688 r = parse_env_file(/* f= */ NULL, p, "STATE", &s);
689 if (r == -ENOENT)
690 return -ENXIO;
691 if (r < 0)
692 return r;
693 if (isempty(s))
694 return -EIO;
695
696 if (ret_state)
697 *ret_state = TAKE_PTR(s);
698 return 0;
699}
700
701_public_ int sd_session_get_uid(const char *session, uid_t *ret_uid) {
702 int r;
703 _cleanup_free_ char *p = NULL, *s = NULL;
704
705 r = file_of_session(session, &p);
706 if (r < 0)
707 return r;
708
709 r = parse_env_file(/* f= */ NULL, p, "UID", &s);
710 if (r == -ENOENT)
711 return -ENXIO;
712 if (r < 0)
713 return r;
714 if (isempty(s))
715 return -EIO;
716
717 return parse_uid(s, ret_uid);
718}
719
720static int session_get_string(const char *session, const char *field, char **ret_value) {
721 _cleanup_free_ char *p = NULL, *s = NULL;
722 int r;
723
724 assert(field);
725
726 r = file_of_session(session, &p);
727 if (r < 0)
728 return r;
729
730 r = parse_env_file(/* f= */ NULL, p, field, &s);
731 if (r == -ENOENT)
732 return -ENXIO;
733 if (r < 0)
734 return r;
735 if (isempty(s))
736 return -ENODATA;
737
738 if (ret_value)
739 *ret_value = TAKE_PTR(s);
740 return 0;
741}
742
743_public_ int sd_session_get_username(const char *session, char **ret_username) {
744 return session_get_string(session, "USER", ret_username);
745}
746
747_public_ int sd_session_get_seat(const char *session, char **ret_seat) {
748 return session_get_string(session, "SEAT", ret_seat);
749}
750
751_public_ int sd_session_get_start_time(const char *session, uint64_t *ret_usec) {
752 _cleanup_free_ char *p = NULL, *s = NULL;
753 int r;
754
755 r = file_of_session(session, &p);
756 if (r < 0)
757 return r;
758
759 r = parse_env_file(/* f= */ NULL, p, "REALTIME", &s);
760 if (r == -ENOENT)
761 return -ENXIO;
762 if (r < 0)
763 return r;
764 if (isempty(s))
765 return -EIO;
766
767 return safe_atou64(s, ret_usec);
768}
769
770_public_ int sd_session_get_tty(const char *session, char **ret_tty) {
771 return session_get_string(session, "TTY", ret_tty);
772}
773
774_public_ int sd_session_get_vt(const char *session, unsigned *ret_vtnr) {
775 _cleanup_free_ char *vtnr_string = NULL;
776 int r;
777
778 r = session_get_string(session, "VTNR", &vtnr_string);
779 if (r < 0)
780 return r;
781
782 return safe_atou(vtnr_string, ret_vtnr);
783}
784
785_public_ int sd_session_get_service(const char *session, char **ret_service) {
786 return session_get_string(session, "SERVICE", ret_service);
787}
788
789_public_ int sd_session_get_type(const char *session, char **ret_type) {
790 return session_get_string(session, "TYPE", ret_type);
791}
792
793_public_ int sd_session_get_class(const char *session, char **ret_class) {
794 return session_get_string(session, "CLASS", ret_class);
795}
796
797_public_ int sd_session_get_desktop(const char *session, char **ret_desktop) {
798 return session_get_string(session, "DESKTOP", ret_desktop);
799}
800
801_public_ int sd_session_get_display(const char *session, char **ret_display) {
802 return session_get_string(session, "DISPLAY", ret_display);
803}
804
805_public_ int sd_session_get_remote_user(const char *session, char **ret_remote_user) {
806 return session_get_string(session, "REMOTE_USER", ret_remote_user);
807}
808
809_public_ int sd_session_get_remote_host(const char *session, char **ret_remote_host) {
810 return session_get_string(session, "REMOTE_HOST", ret_remote_host);
811}
812
813_public_ int sd_session_get_leader(const char *session, pid_t *ret_leader) {
814 _cleanup_free_ char *leader_string = NULL;
815 int r;
816
817 r = session_get_string(session, "LEADER", &leader_string);
818 if (r < 0)
819 return r;
820
821 return parse_pid(leader_string, ret_leader);
822}
823
824_public_ int sd_seat_get_active(const char *seat, char **ret_session, uid_t *ret_uid) {
825 _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
826 int r;
827
828 r = file_of_seat(seat, &p);
829 if (r < 0)
830 return r;
831
832 r = parse_env_file(/* f= */ NULL, p,
833 "ACTIVE", &s,
834 "ACTIVE_UID", &t);
835 if (r == -ENOENT)
836 return -ENXIO;
837 if (r < 0)
838 return r;
839
840 if (ret_session && !s)
841 return -ENODATA;
842
843 if (ret_uid && !t)
844 return -ENODATA;
845
846 if (ret_uid && t) {
847 r = parse_uid(t, ret_uid);
848 if (r < 0)
849 return r;
850 }
851
852 if (ret_session && s)
853 *ret_session = TAKE_PTR(s);
854
855 return 0;
856}
857
858_public_ int sd_seat_get_sessions(
859 const char *seat,
860 char ***ret_sessions,
861 uid_t **ret_uids,
862 unsigned *ret_n_uids) {
863
864 _cleanup_free_ char *fname = NULL, *session_line = NULL, *uid_line = NULL;
865 _cleanup_strv_free_ char **sessions = NULL;
866 _cleanup_free_ uid_t *uids = NULL;
867 unsigned n_sessions = 0;
868 int r;
869
870 r = file_of_seat(seat, &fname);
871 if (r < 0)
872 return r;
873
874 r = parse_env_file(/* f= */ NULL, fname,
875 "SESSIONS", &session_line,
876 "UIDS", &uid_line);
877 if (r == -ENOENT)
878 return -ENXIO;
879 if (r < 0)
880 return r;
881
882 if (session_line) {
883 sessions = strv_split(session_line, NULL);
884 if (!sessions)
885 return -ENOMEM;
886
887 n_sessions = strv_length(sessions);
888 };
889
890 if (ret_uids && uid_line) {
891 uids = new(uid_t, n_sessions);
892 if (!uids)
893 return -ENOMEM;
894
895 size_t n = 0;
896 for (const char *p = uid_line;;) {
897 _cleanup_free_ char *word = NULL;
898
899 r = extract_first_word(&p, &word, NULL, 0);
900 if (r < 0)
901 return r;
902 if (r == 0)
903 break;
904
905 r = parse_uid(word, &uids[n++]);
906 if (r < 0)
907 return r;
908 }
909
910 if (n != n_sessions)
911 return -EUCLEAN;
912 }
913
914 if (ret_sessions)
915 *ret_sessions = TAKE_PTR(sessions);
916 if (ret_uids)
917 *ret_uids = TAKE_PTR(uids);
918 if (ret_n_uids)
919 *ret_n_uids = n_sessions;
920
921 return n_sessions;
922}
923
924static int seat_get_can(const char *seat, const char *variable) {
925 _cleanup_free_ char *p = NULL, *s = NULL;
926 int r;
927
928 assert(variable);
929
930 r = file_of_seat(seat, &p);
931 if (r < 0)
932 return r;
933
934 r = parse_env_file(/* f= */ NULL, p,
935 variable, &s);
936 if (r == -ENOENT)
937 return -ENXIO;
938 if (r < 0)
939 return r;
940 if (isempty(s))
941 return -ENODATA;
942
943 return parse_boolean(s);
944}
945
946_public_ int sd_seat_can_multi_session(const char *seat) {
947 return true;
948}
949
950_public_ int sd_seat_can_tty(const char *seat) {
951 return seat_get_can(seat, "CAN_TTY");
952}
953
954_public_ int sd_seat_can_graphical(const char *seat) {
955 return seat_get_can(seat, "CAN_GRAPHICAL");
956}
957
958_public_ int sd_get_seats(char ***ret_seats) {
959 int r;
960
961 r = get_files_in_directory("/run/systemd/seats/", ret_seats);
962 if (r == -ENOENT) {
963 if (ret_seats)
964 *ret_seats = NULL;
965 return 0;
966 }
967 return r;
968}
969
970_public_ int sd_get_sessions(char ***ret_sessions) {
971 int r;
972
973 r = get_files_in_directory("/run/systemd/sessions/", ret_sessions);
974 if (r == -ENOENT) {
975 if (ret_sessions)
976 *ret_sessions = NULL;
977 return 0;
978 }
979 return r;
980}
981
982_public_ int sd_get_uids(uid_t **ret_users) {
983 _cleanup_closedir_ DIR *d = NULL;
984 _cleanup_free_ uid_t *l = NULL;
985 size_t n = 0;
986
987 d = opendir("/run/systemd/users/");
988 if (!d) {
989 if (errno == ENOENT) {
990 if (ret_users)
991 *ret_users = NULL;
992 return 0;
993 }
994 return -errno;
995 }
996
997 FOREACH_DIRENT_ALL(de, d, return -errno) {
998 uid_t uid;
999
1000 if (!dirent_is_file(de))
1001 continue;
1002
1003 if (parse_uid(de->d_name, &uid) < 0)
1004 continue;
1005
1006 if (ret_users) {
1007 if (!GREEDY_REALLOC(l, n + 1))
1008 return -ENOMEM;
1009
1010 l[n] = uid;
1011 }
1012
1013 n++;
1014 }
1015
1016 if (n > INT_MAX)
1017 return -EOVERFLOW;
1018
1019 if (ret_users)
1020 *ret_users = TAKE_PTR(l);
1021
1022 return (int) n;
1023}
1024
1025_public_ int sd_get_machine_names(char ***ret_machines) {
1026 _cleanup_strv_free_ char **l = NULL;
1027 char **a, **b;
1028 int r;
1029
1030 r = get_files_in_directory("/run/systemd/machines/", &l);
1031 if (r == -ENOENT) {
1032 if (ret_machines)
1033 *ret_machines = NULL;
1034 return 0;
1035 }
1036 if (r < 0)
1037 return r;
1038
1039 if (l) {
1040 r = 0;
1041
1042 /* Filter out the unit: symlinks */
1043 for (a = b = l; *a; a++) {
1044 if (startswith(*a, "unit:") || !hostname_is_valid(*a, 0))
1045 free(*a);
1046 else {
1047 *b = *a;
1048 b++;
1049 r++;
1050 }
1051 }
1052
1053 *b = NULL;
1054 }
1055
1056 if (ret_machines)
1057 *ret_machines = TAKE_PTR(l);
1058
1059 return r;
1060}
1061
1062_public_ int sd_machine_get_class(const char *machine, char **ret_class) {
1063 _cleanup_free_ char *c = NULL;
1064 int r;
1065
1066 if (streq_ptr(machine, ".host")) {
1067 c = strdup("host");
1068 if (!c)
1069 return -ENOMEM;
1070 } else {
1071 if (!hostname_is_valid(machine, 0))
1072 return -EINVAL;
1073
1074 _cleanup_free_ char *p = path_join("/run/systemd/machines/", machine);
1075 if (!p)
1076 return -ENOMEM;
1077
1078 r = parse_env_file(/* f= */ NULL, p, "CLASS", &c);
1079 if (r == -ENOENT)
1080 return -ENXIO;
1081 if (r < 0)
1082 return r;
1083 if (!c)
1084 return -EIO;
1085 }
1086
1087 if (ret_class)
1088 *ret_class = TAKE_PTR(c);
1089
1090 return 0;
1091}
1092
1093_public_ int sd_machine_get_ifindices(const char *machine, int **ret_ifindices) {
1094 _cleanup_free_ char *netif_line = NULL, *p = NULL;
1095 int r;
1096
1097 assert_return(hostname_is_valid(machine, 0), -EINVAL);
1098
1099 p = path_join("/run/systemd/machines/", machine);
1100 if (!p)
1101 return -ENOMEM;
1102
1103 r = parse_env_file(/* f= */ NULL, p, "NETIF", &netif_line);
1104 if (r == -ENOENT)
1105 return -ENXIO;
1106 if (r < 0)
1107 return r;
1108 if (!netif_line) {
1109 if (ret_ifindices)
1110 *ret_ifindices = NULL;
1111 return 0;
1112 }
1113
1114 _cleanup_strv_free_ char **tt = strv_split(netif_line, NULL);
1115 if (!tt)
1116 return -ENOMEM;
1117
1118 _cleanup_free_ int *ifindices = NULL;
1119 if (ret_ifindices) {
1120 ifindices = new(int, strv_length(tt));
1121 if (!ifindices)
1122 return -ENOMEM;
1123 }
1124
1125 size_t n = 0;
1126 for (size_t i = 0; tt[i]; i++) {
1127 int ind;
1128
1129 ind = parse_ifindex(tt[i]);
1130 if (ind < 0)
1131 /* Return -EUCLEAN to distinguish from -EINVAL for invalid args */
1132 return ind == -EINVAL ? -EUCLEAN : ind;
1133
1134 if (ret_ifindices)
1135 ifindices[n] = ind;
1136 n++;
1137 }
1138
1139 if (ret_ifindices)
1140 *ret_ifindices = TAKE_PTR(ifindices);
1141
1142 return n;
1143}
1144
1145static int MONITOR_TO_FD(sd_login_monitor *m) {
1146 return (int) (unsigned long) m - 1;
1147}
1148
1149static sd_login_monitor* FD_TO_MONITOR(int fd) {
1150 return (sd_login_monitor*) (unsigned long) (fd + 1);
1151}
1152
1153_public_ int sd_login_monitor_new(const char *category, sd_login_monitor **ret) {
1154 _cleanup_close_ int fd = -EBADF;
1155
1156 assert_return(ret, -EINVAL);
1157
1158 fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1159 if (fd < 0)
1160 return -errno;
1161
1162 static const struct {
1163 const char *name;
1164 const char *path;
1165 } categories[] = {
1166 { "seat", "/run/systemd/seats/" },
1167 { "session", "/run/systemd/sessions/" },
1168 { "uid", "/run/systemd/users/" },
1169 { "machine", "/run/systemd/machines/" },
1170 };
1171
1172 bool good = false;
1173 FOREACH_ELEMENT(c, categories) {
1174 if (category && !streq(category, c->name))
1175 continue;
1176
1177 if (inotify_add_watch(fd, c->path, IN_MOVED_TO|IN_DELETE) < 0)
1178 return -errno;
1179
1180 good = true;
1181 }
1182
1183 if (!good)
1184 return -EINVAL;
1185
1186 *ret = FD_TO_MONITOR(TAKE_FD(fd));
1187 return 0;
1188}
1189
1190_public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
1191 if (m)
1192 (void) close(MONITOR_TO_FD(m));
1193
1194 return NULL;
1195}
1196
1197_public_ int sd_login_monitor_flush(sd_login_monitor *m) {
1198 int r;
1199
1200 assert_return(m, -EINVAL);
1201
1202 r = flush_fd(MONITOR_TO_FD(m));
1203 if (r < 0)
1204 return r;
1205
1206 return 0;
1207}
1208
1209_public_ int sd_login_monitor_get_fd(sd_login_monitor *m) {
1210
1211 assert_return(m, -EINVAL);
1212
1213 return MONITOR_TO_FD(m);
1214}
1215
1216_public_ int sd_login_monitor_get_events(sd_login_monitor *m) {
1217
1218 assert_return(m, -EINVAL);
1219
1220 /* For now we will only return POLLIN here, since we don't
1221 * need anything else ever for inotify. However, let's have
1222 * this API to keep our options open should we later on need
1223 * it. */
1224 return POLLIN;
1225}
1226
1227_public_ int sd_login_monitor_get_timeout(sd_login_monitor *m, uint64_t *ret_timeout_usec) {
1228
1229 assert_return(m, -EINVAL);
1230 assert_return(ret_timeout_usec, -EINVAL);
1231
1232 /* For now we will only return UINT64_MAX, since we don't
1233 * need any timeout. However, let's have this API to keep our
1234 * options open should we later on need it. */
1235 *ret_timeout_usec = UINT64_MAX;
1236 return 0;
1237}