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