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