]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-login/sd-login.c
util: replace close_nointr_nofail() by a more useful safe_close()
[thirdparty/systemd.git] / src / libsystemd / sd-login / sd-login.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <unistd.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <sys/inotify.h>
26 #include <sys/poll.h>
27
28 #include "util.h"
29 #include "cgroup-util.h"
30 #include "macro.h"
31 #include "strv.h"
32 #include "fileio.h"
33 #include "login-shared.h"
34 #include "sd-login.h"
35
36 _public_ int sd_pid_get_session(pid_t pid, char **session) {
37
38 assert_return(pid >= 0, -EINVAL);
39 assert_return(session, -EINVAL);
40
41 return cg_pid_get_session(pid, session);
42 }
43
44 _public_ int sd_pid_get_unit(pid_t pid, char **unit) {
45
46 assert_return(pid >= 0, -EINVAL);
47 assert_return(unit, -EINVAL);
48
49 return cg_pid_get_unit(pid, unit);
50 }
51
52 _public_ int sd_pid_get_user_unit(pid_t pid, char **unit) {
53
54 assert_return(pid >= 0, -EINVAL);
55 assert_return(unit, -EINVAL);
56
57 return cg_pid_get_user_unit(pid, unit);
58 }
59
60 _public_ int sd_pid_get_machine_name(pid_t pid, char **name) {
61
62 assert_return(pid >= 0, -EINVAL);
63 assert_return(name, -EINVAL);
64
65 return cg_pid_get_machine_name(pid, name);
66 }
67
68 _public_ int sd_pid_get_slice(pid_t pid, char **slice) {
69
70 assert_return(pid >= 0, -EINVAL);
71 assert_return(slice, -EINVAL);
72
73 return cg_pid_get_slice(pid, slice);
74 }
75
76 _public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) {
77
78 assert_return(pid >= 0, -EINVAL);
79 assert_return(uid, -EINVAL);
80
81 return cg_pid_get_owner_uid(pid, uid);
82 }
83
84 _public_ int sd_peer_get_session(int fd, char **session) {
85 struct ucred ucred;
86 int r;
87
88 assert_return(fd >= 0, -EINVAL);
89 assert_return(session, -EINVAL);
90
91 r = getpeercred(fd, &ucred);
92 if (r < 0)
93 return r;
94
95 return cg_pid_get_session(ucred.pid, session);
96 }
97
98 _public_ int sd_peer_get_owner_uid(int fd, uid_t *uid) {
99 struct ucred ucred;
100 int r;
101
102 assert_return(fd >= 0, -EINVAL);
103 assert_return(uid, -EINVAL);
104
105 r = getpeercred(fd, &ucred);
106 if (r < 0)
107 return r;
108
109 return cg_pid_get_owner_uid(ucred.pid, uid);
110 }
111
112 _public_ int sd_peer_get_unit(int fd, char **unit) {
113 struct ucred ucred;
114 int r;
115
116 assert_return(fd >= 0, -EINVAL);
117 assert_return(unit, -EINVAL);
118
119 r = getpeercred(fd, &ucred);
120 if (r < 0)
121 return r;
122
123 return cg_pid_get_unit(ucred.pid, unit);
124 }
125
126 _public_ int sd_peer_get_user_unit(int fd, char **unit) {
127 struct ucred ucred;
128 int r;
129
130 assert_return(fd >= 0, -EINVAL);
131 assert_return(unit, -EINVAL);
132
133 r = getpeercred(fd, &ucred);
134 if (r < 0)
135 return r;
136
137 return cg_pid_get_user_unit(ucred.pid, unit);
138 }
139
140 _public_ int sd_peer_get_machine_name(int fd, char **machine) {
141 struct ucred ucred;
142 int r;
143
144 assert_return(fd >= 0, -EINVAL);
145 assert_return(machine, -EINVAL);
146
147 r = getpeercred(fd, &ucred);
148 if (r < 0)
149 return r;
150
151 return cg_pid_get_machine_name(ucred.pid, machine);
152 }
153
154 _public_ int sd_peer_get_slice(int fd, char **slice) {
155 struct ucred ucred;
156 int r;
157
158 assert_return(fd >= 0, -EINVAL);
159 assert_return(slice, -EINVAL);
160
161 r = getpeercred(fd, &ucred);
162 if (r < 0)
163 return r;
164
165 return cg_pid_get_slice(ucred.pid, slice);
166 }
167
168 _public_ int sd_uid_get_state(uid_t uid, char**state) {
169 _cleanup_free_ char *p = NULL;
170 char *s = NULL;
171 int r;
172
173 assert_return(state, -EINVAL);
174
175 if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
176 return -ENOMEM;
177
178 r = parse_env_file(p, NEWLINE, "STATE", &s, NULL);
179 if (r == -ENOENT) {
180 free(s);
181 s = strdup("offline");
182 if (!s)
183 return -ENOMEM;
184
185 } else if (r < 0) {
186 free(s);
187 return r;
188 } else if (!s)
189 return -EIO;
190
191 *state = s;
192 return 0;
193 }
194
195 _public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) {
196 char *w, *state;
197 _cleanup_free_ char *t = NULL, *s = NULL, *p = NULL;
198 size_t l;
199 int r;
200 const char *variable;
201
202 assert_return(seat, -EINVAL);
203
204 variable = require_active ? "ACTIVE_UID" : "UIDS";
205
206 p = strappend("/run/systemd/seats/", seat);
207 if (!p)
208 return -ENOMEM;
209
210 r = parse_env_file(p, NEWLINE, variable, &s, NULL);
211
212 if (r < 0)
213 return r;
214
215 if (!s)
216 return -EIO;
217
218 if (asprintf(&t, "%lu", (unsigned long) uid) < 0)
219 return -ENOMEM;
220
221 FOREACH_WORD(w, l, s, state) {
222 if (strneq(t, w, l))
223 return 1;
224 }
225
226 return 0;
227 }
228
229 static int uid_get_array(uid_t uid, const char *variable, char ***array) {
230 _cleanup_free_ char *p = NULL, *s = NULL;
231 char **a;
232 int r;
233
234 if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
235 return -ENOMEM;
236
237 r = parse_env_file(p, NEWLINE,
238 variable, &s,
239 NULL);
240 if (r < 0) {
241 if (r == -ENOENT) {
242 if (array)
243 *array = NULL;
244 return 0;
245 }
246
247 return r;
248 }
249
250 if (!s) {
251 if (array)
252 *array = NULL;
253 return 0;
254 }
255
256 a = strv_split(s, " ");
257
258 if (!a)
259 return -ENOMEM;
260
261 strv_uniq(a);
262 r = strv_length(a);
263
264 if (array)
265 *array = a;
266 else
267 strv_free(a);
268
269 return r;
270 }
271
272 _public_ int sd_uid_get_sessions(uid_t uid, int require_active, char ***sessions) {
273 return uid_get_array(
274 uid,
275 require_active == 0 ? "ONLINE_SESSIONS" :
276 require_active > 0 ? "ACTIVE_SESSIONS" :
277 "SESSIONS",
278 sessions);
279 }
280
281 _public_ int sd_uid_get_seats(uid_t uid, int require_active, char ***seats) {
282 return uid_get_array(
283 uid,
284 require_active == 0 ? "ONLINE_SEATS" :
285 require_active > 0 ? "ACTIVE_SEATS" :
286 "SEATS",
287 seats);
288 }
289
290 static int file_of_session(const char *session, char **_p) {
291 char *p;
292 int r;
293
294 assert(_p);
295
296 if (session) {
297 if (!session_id_valid(session))
298 return -EINVAL;
299
300 p = strappend("/run/systemd/sessions/", session);
301 } else {
302 _cleanup_free_ char *buf = NULL;
303
304 r = sd_pid_get_session(0, &buf);
305 if (r < 0)
306 return r;
307
308 p = strappend("/run/systemd/sessions/", buf);
309 }
310
311 if (!p)
312 return -ENOMEM;
313
314 *_p = p;
315 return 0;
316 }
317
318 _public_ int sd_session_is_active(const char *session) {
319 int r;
320 _cleanup_free_ char *p = NULL, *s = NULL;
321
322 r = file_of_session(session, &p);
323 if (r < 0)
324 return r;
325
326 r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL);
327 if (r < 0)
328 return r;
329
330 if (!s)
331 return -EIO;
332
333 return parse_boolean(s);
334 }
335
336 _public_ int sd_session_is_remote(const char *session) {
337 int r;
338 _cleanup_free_ char *p = NULL, *s = NULL;
339
340 r = file_of_session(session, &p);
341 if (r < 0)
342 return r;
343
344 r = parse_env_file(p, NEWLINE, "REMOTE", &s, NULL);
345 if (r < 0)
346 return r;
347
348 if (!s)
349 return -EIO;
350
351 return parse_boolean(s);
352 }
353
354 _public_ int sd_session_get_state(const char *session, char **state) {
355 _cleanup_free_ char *p = NULL, *s = NULL;
356 int r;
357
358 assert_return(state, -EINVAL);
359
360 r = file_of_session(session, &p);
361 if (r < 0)
362 return r;
363
364 r = parse_env_file(p, NEWLINE, "STATE", &s, NULL);
365
366 if (r < 0)
367 return r;
368 else if (!s)
369 return -EIO;
370
371 *state = s;
372 s = NULL;
373
374 return 0;
375 }
376
377 _public_ int sd_session_get_uid(const char *session, uid_t *uid) {
378 int r;
379 _cleanup_free_ char *p = NULL, *s = NULL;
380
381 assert_return(uid, -EINVAL);
382
383 r = file_of_session(session, &p);
384 if (r < 0)
385 return r;
386
387 r = parse_env_file(p, NEWLINE, "UID", &s, NULL);
388 if (r < 0)
389 return r;
390
391 if (!s)
392 return -EIO;
393
394 return parse_uid(s, uid);
395 }
396
397 static int session_get_string(const char *session, const char *field, char **value) {
398 _cleanup_free_ char *p = NULL, *s = NULL;
399 int r;
400
401 assert_return(value, -EINVAL);
402
403 r = file_of_session(session, &p);
404 if (r < 0)
405 return r;
406
407 r = parse_env_file(p, NEWLINE, field, &s, NULL);
408
409 if (r < 0)
410 return r;
411
412 if (isempty(s))
413 return -ENOENT;
414
415 *value = s;
416 s = NULL;
417 return 0;
418 }
419
420 _public_ int sd_session_get_seat(const char *session, char **seat) {
421 return session_get_string(session, "SEAT", seat);
422 }
423
424 _public_ int sd_session_get_tty(const char *session, char **tty) {
425 return session_get_string(session, "TTY", tty);
426 }
427
428 _public_ int sd_session_get_vt(const char *session, unsigned *vtnr) {
429 _cleanup_free_ char *vtnr_string = NULL;
430 unsigned u;
431 int r;
432
433 r = session_get_string(session, "VTNR", &vtnr_string);
434 if (r < 0)
435 return r;
436
437 r = safe_atou(vtnr_string, &u);
438 if (r < 0)
439 return r;
440
441 *vtnr = u;
442 return 0;
443 }
444
445 _public_ int sd_session_get_service(const char *session, char **service) {
446 return session_get_string(session, "SERVICE", service);
447 }
448
449 _public_ int sd_session_get_type(const char *session, char **type) {
450 return session_get_string(session, "TYPE", type);
451 }
452
453 _public_ int sd_session_get_class(const char *session, char **class) {
454 return session_get_string(session, "CLASS", class);
455 }
456
457 _public_ int sd_session_get_display(const char *session, char **display) {
458 return session_get_string(session, "DISPLAY", display);
459 }
460
461 _public_ int sd_session_get_remote_user(const char *session, char **remote_user) {
462 return session_get_string(session, "REMOTE_USER", remote_user);
463 }
464
465 _public_ int sd_session_get_remote_host(const char *session, char **remote_host) {
466 return session_get_string(session, "REMOTE_HOST", remote_host);
467 }
468
469 static int file_of_seat(const char *seat, char **_p) {
470 char *p;
471 int r;
472
473 assert(_p);
474
475 if (seat)
476 p = strappend("/run/systemd/seats/", seat);
477 else {
478 _cleanup_free_ char *buf = NULL;
479
480 r = sd_session_get_seat(NULL, &buf);
481 if (r < 0)
482 return r;
483
484 p = strappend("/run/systemd/seats/", buf);
485 }
486
487 if (!p)
488 return -ENOMEM;
489
490 *_p = p;
491 p = NULL;
492 return 0;
493 }
494
495 _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
496 _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
497 int r;
498
499 assert_return(session || uid, -EINVAL);
500
501 r = file_of_seat(seat, &p);
502 if (r < 0)
503 return r;
504
505 r = parse_env_file(p, NEWLINE,
506 "ACTIVE", &s,
507 "ACTIVE_UID", &t,
508 NULL);
509 if (r < 0)
510 return r;
511
512 if (session && !s)
513 return -ENOENT;
514
515 if (uid && !t)
516 return -ENOENT;
517
518 if (uid && t) {
519 r = parse_uid(t, uid);
520 if (r < 0)
521 return r;
522 }
523
524 if (session && s) {
525 *session = s;
526 s = NULL;
527 }
528
529 return 0;
530 }
531
532 _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uids, unsigned *n_uids) {
533 _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
534 _cleanup_strv_free_ char **a = NULL;
535 _cleanup_free_ uid_t *b = NULL;
536 unsigned n = 0;
537 int r;
538
539 r = file_of_seat(seat, &p);
540 if (r < 0)
541 return r;
542
543 r = parse_env_file(p, NEWLINE,
544 "SESSIONS", &s,
545 "ACTIVE_SESSIONS", &t,
546 NULL);
547
548 if (r < 0)
549 return r;
550
551 if (s) {
552 a = strv_split(s, " ");
553 if (!a)
554 return -ENOMEM;
555 }
556
557 if (uids && t) {
558 char *w, *state;
559 size_t l;
560
561 FOREACH_WORD(w, l, t, state)
562 n++;
563
564 if (n > 0) {
565 unsigned i = 0;
566
567 b = new(uid_t, n);
568 if (!b)
569 return -ENOMEM;
570
571 FOREACH_WORD(w, l, t, state) {
572 _cleanup_free_ char *k = NULL;
573
574 k = strndup(w, l);
575 if (!k)
576 return -ENOMEM;
577
578 r = parse_uid(k, b + i);
579
580 if (r < 0)
581 continue;
582
583 i++;
584 }
585 }
586 }
587
588 r = strv_length(a);
589
590 if (sessions) {
591 *sessions = a;
592 a = NULL;
593 }
594
595 if (uids) {
596 *uids = b;
597 b = NULL;
598 }
599
600 if (n_uids)
601 *n_uids = n;
602
603 return r;
604 }
605
606 static int seat_get_can(const char *seat, const char *variable) {
607 _cleanup_free_ char *p = NULL, *s = NULL;
608 int r;
609
610 assert_return(variable, -EINVAL);
611
612 r = file_of_seat(seat, &p);
613 if (r < 0)
614 return r;
615
616 r = parse_env_file(p, NEWLINE,
617 variable, &s,
618 NULL);
619 if (r < 0)
620 return r;
621 if (!s)
622 return 0;
623
624 return parse_boolean(s);
625 }
626
627 _public_ int sd_seat_can_multi_session(const char *seat) {
628 return seat_get_can(seat, "CAN_MULTI_SESSION");
629 }
630
631 _public_ int sd_seat_can_tty(const char *seat) {
632 return seat_get_can(seat, "CAN_TTY");
633 }
634
635 _public_ int sd_seat_can_graphical(const char *seat) {
636 return seat_get_can(seat, "CAN_GRAPHICAL");
637 }
638
639 _public_ int sd_get_seats(char ***seats) {
640 return get_files_in_directory("/run/systemd/seats/", seats);
641 }
642
643 _public_ int sd_get_sessions(char ***sessions) {
644 return get_files_in_directory("/run/systemd/sessions/", sessions);
645 }
646
647 _public_ int sd_get_uids(uid_t **users) {
648 _cleanup_closedir_ DIR *d;
649 int r = 0;
650 unsigned n = 0;
651 _cleanup_free_ uid_t *l = NULL;
652
653 d = opendir("/run/systemd/users/");
654 if (!d)
655 return -errno;
656
657 for (;;) {
658 struct dirent *de;
659 int k;
660 uid_t uid;
661
662 errno = 0;
663 de = readdir(d);
664 if (!de && errno != 0)
665 return -errno;
666
667 if (!de)
668 break;
669
670 dirent_ensure_type(d, de);
671
672 if (!dirent_is_file(de))
673 continue;
674
675 k = parse_uid(de->d_name, &uid);
676 if (k < 0)
677 continue;
678
679 if (users) {
680 if ((unsigned) r >= n) {
681 uid_t *t;
682
683 n = MAX(16, 2*r);
684 t = realloc(l, sizeof(uid_t) * n);
685 if (!t)
686 return -ENOMEM;
687
688 l = t;
689 }
690
691 assert((unsigned) r < n);
692 l[r++] = uid;
693 } else
694 r++;
695 }
696
697 if (users) {
698 *users = l;
699 l = NULL;
700 }
701
702 return r;
703 }
704
705 _public_ int sd_get_machine_names(char ***machines) {
706 char **l = NULL, **a, **b;
707 int r;
708
709 assert_return(machines, -EINVAL);
710
711 r = get_files_in_directory("/run/systemd/machines/", &l);
712 if (r < 0)
713 return r;
714
715 if (l) {
716 r = 0;
717
718 /* Filter out the unit: symlinks */
719 for (a = l, b = l; *a; a++) {
720 if (startswith(*a, "unit:"))
721 free(*a);
722 else {
723 *b = *a;
724 b++;
725 r++;
726 }
727 }
728
729 *b = NULL;
730 }
731
732 *machines = l;
733 return r;
734 }
735
736 _public_ int sd_machine_get_class(const char *machine, char **class) {
737 _cleanup_free_ char *c = NULL;
738 const char *p;
739 int r;
740
741 assert_return(filename_is_safe(machine), -EINVAL);
742 assert_return(class, -EINVAL);
743
744 p = strappenda("/run/systemd/machines/", machine);
745 r = parse_env_file(p, NEWLINE, "CLASS", &c, NULL);
746 if (r < 0)
747 return r;
748 if (!c)
749 return -EIO;
750
751 *class = c;
752 c = NULL;
753
754 return 0;
755 }
756
757 static inline int MONITOR_TO_FD(sd_login_monitor *m) {
758 return (int) (unsigned long) m - 1;
759 }
760
761 static inline sd_login_monitor* FD_TO_MONITOR(int fd) {
762 return (sd_login_monitor*) (unsigned long) (fd + 1);
763 }
764
765 _public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) {
766 int fd, k;
767 bool good = false;
768
769 assert_return(m, -EINVAL);
770
771 fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
772 if (fd < 0)
773 return -errno;
774
775 if (!category || streq(category, "seat")) {
776 k = inotify_add_watch(fd, "/run/systemd/seats/", IN_MOVED_TO|IN_DELETE);
777 if (k < 0) {
778 safe_close(fd);
779 return -errno;
780 }
781
782 good = true;
783 }
784
785 if (!category || streq(category, "session")) {
786 k = inotify_add_watch(fd, "/run/systemd/sessions/", IN_MOVED_TO|IN_DELETE);
787 if (k < 0) {
788 safe_close(fd);
789 return -errno;
790 }
791
792 good = true;
793 }
794
795 if (!category || streq(category, "uid")) {
796 k = inotify_add_watch(fd, "/run/systemd/users/", IN_MOVED_TO|IN_DELETE);
797 if (k < 0) {
798 safe_close(fd);
799 return -errno;
800 }
801
802 good = true;
803 }
804
805 if (!category || streq(category, "machine")) {
806 k = inotify_add_watch(fd, "/run/systemd/machines/", IN_MOVED_TO|IN_DELETE);
807 if (k < 0) {
808 safe_close(fd);
809 return -errno;
810 }
811
812 good = true;
813 }
814
815 if (!good) {
816 close_nointr(fd);
817 return -EINVAL;
818 }
819
820 *m = FD_TO_MONITOR(fd);
821 return 0;
822 }
823
824 _public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
825 int fd;
826
827 assert_return(m, NULL);
828
829 fd = MONITOR_TO_FD(m);
830 close_nointr(fd);
831
832 return NULL;
833 }
834
835 _public_ int sd_login_monitor_flush(sd_login_monitor *m) {
836
837 assert_return(m, -EINVAL);
838
839 return flush_fd(MONITOR_TO_FD(m));
840 }
841
842 _public_ int sd_login_monitor_get_fd(sd_login_monitor *m) {
843
844 assert_return(m, -EINVAL);
845
846 return MONITOR_TO_FD(m);
847 }
848
849 _public_ int sd_login_monitor_get_events(sd_login_monitor *m) {
850
851 assert_return(m, -EINVAL);
852
853 /* For now we will only return POLLIN here, since we don't
854 * need anything else ever for inotify. However, let's have
855 * this API to keep our options open should we later on need
856 * it. */
857 return POLLIN;
858 }
859
860 _public_ int sd_login_monitor_get_timeout(sd_login_monitor *m, uint64_t *timeout_usec) {
861
862 assert_return(m, -EINVAL);
863 assert_return(timeout_usec, -EINVAL);
864
865 /* For now we will only return (uint64_t) -1, since we don't
866 * need any timeout. However, let's have this API to keep our
867 * options open should we later on need it. */
868 *timeout_usec = (uint64_t) -1;
869 return 0;
870 }