]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/login/logind.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.git] / src / login / logind.c
... / ...
CommitLineData
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 <errno.h>
23#include <pwd.h>
24#include <libudev.h>
25#include <fcntl.h>
26#include <string.h>
27#include <unistd.h>
28#include <sys/epoll.h>
29#include <sys/ioctl.h>
30#include <linux/vt.h>
31
32#include <systemd/sd-daemon.h>
33
34#include "logind.h"
35#include "dbus-common.h"
36#include "dbus-loop.h"
37#include "strv.h"
38#include "conf-parser.h"
39
40Manager *manager_new(void) {
41 Manager *m;
42
43 m = new0(Manager, 1);
44 if (!m)
45 return NULL;
46
47 m->console_active_fd = -1;
48 m->bus_fd = -1;
49 m->udev_seat_fd = -1;
50 m->udev_vcsa_fd = -1;
51 m->epoll_fd = -1;
52 m->n_autovts = 6;
53
54 m->devices = hashmap_new(string_hash_func, string_compare_func);
55 m->seats = hashmap_new(string_hash_func, string_compare_func);
56 m->sessions = hashmap_new(string_hash_func, string_compare_func);
57 m->users = hashmap_new(trivial_hash_func, trivial_compare_func);
58 m->cgroups = hashmap_new(string_hash_func, string_compare_func);
59 m->fifo_fds = hashmap_new(trivial_hash_func, trivial_compare_func);
60
61 if (!m->devices || !m->seats || !m->sessions || !m->users || !m->cgroups || !m->fifo_fds) {
62 manager_free(m);
63 return NULL;
64 }
65
66 m->reset_controllers = strv_new("cpu", NULL);
67 m->kill_exclude_users = strv_new("root", NULL);
68 if (!m->reset_controllers || !m->kill_exclude_users) {
69 manager_free(m);
70 return NULL;
71 }
72
73 m->udev = udev_new();
74 if (!m->udev) {
75 manager_free(m);
76 return NULL;
77 }
78
79 if (cg_get_user_path(&m->cgroup_path) < 0) {
80 manager_free(m);
81 return NULL;
82 }
83
84 return m;
85}
86
87void manager_free(Manager *m) {
88 Session *session;
89 User *u;
90 Device *d;
91 Seat *s;
92
93 assert(m);
94
95 while ((session = hashmap_first(m->sessions)))
96 session_free(session);
97
98 while ((u = hashmap_first(m->users)))
99 user_free(u);
100
101 while ((d = hashmap_first(m->devices)))
102 device_free(d);
103
104 while ((s = hashmap_first(m->seats)))
105 seat_free(s);
106
107 hashmap_free(m->sessions);
108 hashmap_free(m->users);
109 hashmap_free(m->devices);
110 hashmap_free(m->seats);
111 hashmap_free(m->cgroups);
112 hashmap_free(m->fifo_fds);
113
114 if (m->console_active_fd >= 0)
115 close_nointr_nofail(m->console_active_fd);
116
117 if (m->udev_seat_monitor)
118 udev_monitor_unref(m->udev_seat_monitor);
119
120 if (m->udev_vcsa_monitor)
121 udev_monitor_unref(m->udev_vcsa_monitor);
122
123 if (m->udev)
124 udev_unref(m->udev);
125
126 if (m->bus) {
127 dbus_connection_flush(m->bus);
128 dbus_connection_close(m->bus);
129 dbus_connection_unref(m->bus);
130 }
131
132 if (m->bus_fd >= 0)
133 close_nointr_nofail(m->bus_fd);
134
135 if (m->epoll_fd >= 0)
136 close_nointr_nofail(m->epoll_fd);
137
138 strv_free(m->controllers);
139 strv_free(m->reset_controllers);
140 strv_free(m->kill_only_users);
141 strv_free(m->kill_exclude_users);
142
143 free(m->cgroup_path);
144 free(m);
145}
146
147int manager_add_device(Manager *m, const char *sysfs, Device **_device) {
148 Device *d;
149
150 assert(m);
151 assert(sysfs);
152
153 d = hashmap_get(m->devices, sysfs);
154 if (d) {
155 if (_device)
156 *_device = d;
157
158 return 0;
159 }
160
161 d = device_new(m, sysfs);
162 if (!d)
163 return -ENOMEM;
164
165 if (_device)
166 *_device = d;
167
168 return 0;
169}
170
171int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
172 Seat *s;
173
174 assert(m);
175 assert(id);
176
177 s = hashmap_get(m->seats, id);
178 if (s) {
179 if (_seat)
180 *_seat = s;
181
182 return 0;
183 }
184
185 s = seat_new(m, id);
186 if (!s)
187 return -ENOMEM;
188
189 if (_seat)
190 *_seat = s;
191
192 return 0;
193}
194
195int manager_add_session(Manager *m, User *u, const char *id, Session **_session) {
196 Session *s;
197
198 assert(m);
199 assert(id);
200
201 s = hashmap_get(m->sessions, id);
202 if (s) {
203 if (_session)
204 *_session = s;
205
206 return 0;
207 }
208
209 s = session_new(m, u, id);
210 if (!s)
211 return -ENOMEM;
212
213 if (_session)
214 *_session = s;
215
216 return 0;
217}
218
219int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) {
220 User *u;
221
222 assert(m);
223 assert(name);
224
225 u = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
226 if (u) {
227 if (_user)
228 *_user = u;
229
230 return 0;
231 }
232
233 u = user_new(m, uid, gid, name);
234 if (!u)
235 return -ENOMEM;
236
237 if (_user)
238 *_user = u;
239
240 return 0;
241}
242
243int manager_add_user_by_name(Manager *m, const char *name, User **_user) {
244 uid_t uid;
245 gid_t gid;
246 int r;
247
248 assert(m);
249 assert(name);
250
251 r = get_user_creds(&name, &uid, &gid, NULL);
252 if (r < 0)
253 return r;
254
255 return manager_add_user(m, uid, gid, name, _user);
256}
257
258int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
259 struct passwd *p;
260
261 assert(m);
262
263 errno = 0;
264 p = getpwuid(uid);
265 if (!p)
266 return errno ? -errno : -ENOENT;
267
268 return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user);
269}
270
271int manager_process_seat_device(Manager *m, struct udev_device *d) {
272 Device *device;
273 int r;
274
275 assert(m);
276
277 if (streq_ptr(udev_device_get_action(d), "remove")) {
278
279 device = hashmap_get(m->devices, udev_device_get_syspath(d));
280 if (!device)
281 return 0;
282
283 seat_add_to_gc_queue(device->seat);
284 device_free(device);
285
286 } else {
287 const char *sn;
288 Seat *seat;
289
290 sn = udev_device_get_property_value(d, "ID_SEAT");
291 if (isempty(sn))
292 sn = "seat0";
293
294 if (!seat_name_is_valid(sn)) {
295 log_warning("Device with invalid seat name %s found, ignoring.", sn);
296 return 0;
297 }
298
299 r = manager_add_device(m, udev_device_get_syspath(d), &device);
300 if (r < 0)
301 return r;
302
303 r = manager_add_seat(m, sn, &seat);
304 if (r < 0) {
305 if (!device->seat)
306 device_free(device);
307
308 return r;
309 }
310
311 device_attach(device, seat);
312 seat_start(seat);
313 }
314
315 return 0;
316}
317
318int manager_enumerate_devices(Manager *m) {
319 struct udev_list_entry *item = NULL, *first = NULL;
320 struct udev_enumerate *e;
321 int r;
322
323 assert(m);
324
325 /* Loads devices from udev and creates seats for them as
326 * necessary */
327
328 e = udev_enumerate_new(m->udev);
329 if (!e) {
330 r = -ENOMEM;
331 goto finish;
332 }
333
334 r = udev_enumerate_add_match_subsystem(e, "graphics");
335 if (r < 0)
336 goto finish;
337
338 r = udev_enumerate_add_match_tag(e, "seat");
339 if (r < 0)
340 goto finish;
341
342 r = udev_enumerate_scan_devices(e);
343 if (r < 0)
344 goto finish;
345
346 first = udev_enumerate_get_list_entry(e);
347 udev_list_entry_foreach(item, first) {
348 struct udev_device *d;
349 int k;
350
351 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
352 if (!d) {
353 r = -ENOMEM;
354 goto finish;
355 }
356
357 k = manager_process_seat_device(m, d);
358 udev_device_unref(d);
359
360 if (k < 0)
361 r = k;
362 }
363
364finish:
365 if (e)
366 udev_enumerate_unref(e);
367
368 return r;
369}
370
371int manager_enumerate_seats(Manager *m) {
372 DIR *d;
373 struct dirent *de;
374 int r = 0;
375
376 assert(m);
377
378 /* This loads data about seats stored on disk, but does not
379 * actually create any seats. Removes data of seats that no
380 * longer exist. */
381
382 d = opendir("/run/systemd/seats");
383 if (!d) {
384 if (errno == ENOENT)
385 return 0;
386
387 log_error("Failed to open /run/systemd/seats: %m");
388 return -errno;
389 }
390
391 while ((de = readdir(d))) {
392 Seat *s;
393 int k;
394
395 if (!dirent_is_file(de))
396 continue;
397
398 s = hashmap_get(m->seats, de->d_name);
399 if (!s) {
400 unlinkat(dirfd(d), de->d_name, 0);
401 continue;
402 }
403
404 k = seat_load(s);
405 if (k < 0)
406 r = k;
407 }
408
409 closedir(d);
410
411 return r;
412}
413
414static int manager_enumerate_users_from_cgroup(Manager *m) {
415 int r = 0;
416 char *name;
417 DIR *d;
418 int k;
419
420 r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, &d);
421 if (r < 0) {
422 if (r == -ENOENT)
423 return 0;
424
425 log_error("Failed to open %s: %s", m->cgroup_path, strerror(-r));
426 return r;
427 }
428
429 while ((k = cg_read_subgroup(d, &name)) > 0) {
430 User *user;
431
432 k = manager_add_user_by_name(m, name, &user);
433 if (k < 0) {
434 free(name);
435 r = k;
436 continue;
437 }
438
439 user_add_to_gc_queue(user);
440
441 if (!user->cgroup_path)
442 if (asprintf(&user->cgroup_path, "%s/%s", m->cgroup_path, name) < 0) {
443 r = -ENOMEM;
444 free(name);
445 break;
446 }
447
448 free(name);
449 }
450
451 if (r >= 0 && k < 0)
452 r = k;
453
454 closedir(d);
455
456 return r;
457}
458
459static int manager_enumerate_linger_users(Manager *m) {
460 DIR *d;
461 struct dirent *de;
462 int r = 0;
463
464 d = opendir("/var/lib/systemd/linger");
465 if (!d) {
466 if (errno == ENOENT)
467 return 0;
468
469 log_error("Failed to open /var/lib/systemd/linger/: %m");
470 return -errno;
471 }
472
473 while ((de = readdir(d))) {
474 int k;
475
476 if (!dirent_is_file(de))
477 continue;
478
479 k = manager_add_user_by_name(m, de->d_name, NULL);
480 if (k < 0) {
481 log_notice("Couldn't add lingering user %s: %s", de->d_name, strerror(-k));
482 r = k;
483 }
484 }
485
486 closedir(d);
487
488 return r;
489}
490
491int manager_enumerate_users(Manager *m) {
492 DIR *d;
493 struct dirent *de;
494 int r, k;
495
496 assert(m);
497
498 /* First, enumerate user cgroups */
499 r = manager_enumerate_users_from_cgroup(m);
500
501 /* Second, add lingering users on top */
502 k = manager_enumerate_linger_users(m);
503 if (k < 0)
504 r = k;
505
506 /* Third, read in user data stored on disk */
507 d = opendir("/run/systemd/users");
508 if (!d) {
509 if (errno == ENOENT)
510 return 0;
511
512 log_error("Failed to open /run/systemd/users: %m");
513 return -errno;
514 }
515
516 while ((de = readdir(d))) {
517 uid_t uid;
518 User *u;
519
520 if (!dirent_is_file(de))
521 continue;
522
523 k = parse_uid(de->d_name, &uid);
524 if (k < 0) {
525 log_error("Failed to parse file name %s: %s", de->d_name, strerror(-k));
526 continue;
527 }
528
529 u = hashmap_get(m->users, ULONG_TO_PTR(uid));
530 if (!u) {
531 unlinkat(dirfd(d), de->d_name, 0);
532 continue;
533 }
534
535 k = user_load(u);
536 if (k < 0)
537 r = k;
538 }
539
540 closedir(d);
541
542 return r;
543}
544
545static int manager_enumerate_sessions_from_cgroup(Manager *m) {
546 User *u;
547 Iterator i;
548 int r = 0;
549
550 HASHMAP_FOREACH(u, m->users, i) {
551 DIR *d;
552 char *name;
553 int k;
554
555 if (!u->cgroup_path)
556 continue;
557
558 k = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &d);
559 if (k < 0) {
560 if (k == -ENOENT)
561 continue;
562
563 log_error("Failed to open %s: %s", u->cgroup_path, strerror(-k));
564 r = k;
565 continue;
566 }
567
568 while ((k = cg_read_subgroup(d, &name)) > 0) {
569 Session *session;
570
571 if (streq(name, "shared"))
572 continue;
573
574 k = manager_add_session(m, u, name, &session);
575 if (k < 0) {
576 free(name);
577 break;
578 }
579
580 session_add_to_gc_queue(session);
581
582 if (!session->cgroup_path)
583 if (asprintf(&session->cgroup_path, "%s/%s", u->cgroup_path, name) < 0) {
584 k = -ENOMEM;
585 free(name);
586 break;
587 }
588
589 free(name);
590 }
591
592 closedir(d);
593
594 if (k < 0)
595 r = k;
596 }
597
598 return r;
599}
600
601int manager_enumerate_sessions(Manager *m) {
602 DIR *d;
603 struct dirent *de;
604 int r = 0;
605
606 assert(m);
607
608 /* First enumerate session cgroups */
609 r = manager_enumerate_sessions_from_cgroup(m);
610
611 /* Second, read in session data stored on disk */
612 d = opendir("/run/systemd/sessions");
613 if (!d) {
614 if (errno == ENOENT)
615 return 0;
616
617 log_error("Failed to open /run/systemd/sessions: %m");
618 return -errno;
619 }
620
621 while ((de = readdir(d))) {
622 struct Session *s;
623 int k;
624
625 if (!dirent_is_file(de))
626 continue;
627
628 s = hashmap_get(m->sessions, de->d_name);
629 if (!s) {
630 unlinkat(dirfd(d), de->d_name, 0);
631 continue;
632 }
633
634 k = session_load(s);
635 if (k < 0)
636 r = k;
637 }
638
639 closedir(d);
640
641 return r;
642}
643
644int manager_dispatch_seat_udev(Manager *m) {
645 struct udev_device *d;
646 int r;
647
648 assert(m);
649
650 d = udev_monitor_receive_device(m->udev_seat_monitor);
651 if (!d)
652 return -ENOMEM;
653
654 r = manager_process_seat_device(m, d);
655 udev_device_unref(d);
656
657 return r;
658}
659
660int manager_dispatch_vcsa_udev(Manager *m) {
661 struct udev_device *d;
662 int r = 0;
663 const char *name;
664
665 assert(m);
666
667 d = udev_monitor_receive_device(m->udev_vcsa_monitor);
668 if (!d)
669 return -ENOMEM;
670
671 name = udev_device_get_sysname(d);
672
673 /* Whenever a VCSA device is removed try to reallocate our
674 * VTs, to make sure our auto VTs never go away. */
675
676 if (name && startswith(name, "vcsa") && streq_ptr(udev_device_get_action(d), "remove"))
677 r = seat_preallocate_vts(m->vtconsole);
678
679 udev_device_unref(d);
680
681 return r;
682}
683
684int manager_dispatch_console(Manager *m) {
685 assert(m);
686
687 if (m->vtconsole)
688 seat_read_active_vt(m->vtconsole);
689
690 return 0;
691}
692
693static int vt_is_busy(int vtnr) {
694 struct vt_stat vt_stat;
695 int r = 0, fd;
696
697 assert(vtnr >= 1);
698
699 /* We explicitly open /dev/tty1 here instead of /dev/tty0. If
700 * we'd open the latter we'd open the foreground tty which
701 * hence would be unconditionally busy. By opening /dev/tty1
702 * we avoid this. Since tty1 is special and needs to be an
703 * explicitly loaded getty or DM this is safe. */
704
705 fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC);
706 if (fd < 0)
707 return -errno;
708
709 if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
710 r = -errno;
711 else
712 r = !!(vt_stat.v_state & (1 << vtnr));
713
714 close_nointr_nofail(fd);
715
716 return r;
717}
718
719int manager_spawn_autovt(Manager *m, int vtnr) {
720 int r;
721 DBusMessage *message = NULL, *reply = NULL;
722 char *name = NULL;
723 const char *mode = "fail";
724 DBusError error;
725
726 assert(m);
727 assert(vtnr >= 1);
728
729 dbus_error_init(&error);
730
731 if ((unsigned) vtnr > m->n_autovts)
732 return 0;
733
734 r = vt_is_busy(vtnr);
735 if (r < 0)
736 return r;
737 else if (r > 0)
738 return -EBUSY;
739
740 message = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnit");
741 if (!message) {
742 log_error("Could not allocate message.");
743 r = -ENOMEM;
744 goto finish;
745 }
746
747 if (asprintf(&name, "autovt@tty%i.service", vtnr) < 0) {
748 log_error("Could not allocate service name.");
749 r = -ENOMEM;
750 goto finish;
751 }
752
753 if (!dbus_message_append_args(message,
754 DBUS_TYPE_STRING, &name,
755 DBUS_TYPE_STRING, &mode,
756 DBUS_TYPE_INVALID)) {
757 log_error("Could not attach target and flag information to message.");
758 r = -ENOMEM;
759 goto finish;
760 }
761
762 reply = dbus_connection_send_with_reply_and_block(m->bus, message, -1, &error);
763 if (!reply) {
764 log_error("Failed to start unit: %s", bus_error_message(&error));
765 goto finish;
766 }
767
768 r = 0;
769
770finish:
771 free(name);
772
773 if (message)
774 dbus_message_unref(message);
775
776 if (reply)
777 dbus_message_unref(reply);
778
779 dbus_error_free(&error);
780
781 return r;
782}
783
784int manager_get_session_by_cgroup(Manager *m, const char *cgroup, Session **session) {
785 Session *s;
786 char *p;
787
788 assert(m);
789 assert(cgroup);
790 assert(session);
791
792 s = hashmap_get(m->cgroups, cgroup);
793 if (s) {
794 *session = s;
795 return 1;
796 }
797
798 p = strdup(cgroup);
799 if (!p) {
800 log_error("Out of memory.");
801 return -ENOMEM;
802 }
803
804 for (;;) {
805 char *e;
806
807 e = strrchr(p, '/');
808 if (!e || e == p) {
809 free(p);
810 *session = NULL;
811 return 0;
812 }
813
814 *e = 0;
815
816 s = hashmap_get(m->cgroups, p);
817 if (s) {
818 free(p);
819 *session = s;
820 return 1;
821 }
822 }
823}
824
825int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
826 char *p;
827 int r;
828
829 assert(m);
830 assert(pid >= 1);
831 assert(session);
832
833 r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &p);
834 if (r < 0)
835 return r;
836
837 r = manager_get_session_by_cgroup(m, p, session);
838 free(p);
839
840 return r;
841}
842
843void manager_cgroup_notify_empty(Manager *m, const char *cgroup) {
844 Session *s;
845 int r;
846
847 r = manager_get_session_by_cgroup(m, cgroup, &s);
848 if (r <= 0)
849 return;
850
851 session_add_to_gc_queue(s);
852}
853
854static void manager_pipe_notify_eof(Manager *m, int fd) {
855 Session *s;
856
857 assert_se(m);
858 assert_se(fd >= 0);
859
860 assert_se(s = hashmap_get(m->fifo_fds, INT_TO_PTR(fd + 1)));
861 assert(s->fifo_fd == fd);
862 session_remove_fifo(s);
863
864 session_stop(s);
865}
866
867static int manager_connect_bus(Manager *m) {
868 DBusError error;
869 int r;
870 struct epoll_event ev;
871
872 assert(m);
873 assert(!m->bus);
874 assert(m->bus_fd < 0);
875
876 dbus_error_init(&error);
877
878 m->bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
879 if (!m->bus) {
880 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
881 r = -ECONNREFUSED;
882 goto fail;
883 }
884
885 if (!dbus_connection_register_object_path(m->bus, "/org/freedesktop/login1", &bus_manager_vtable, m) ||
886 !dbus_connection_register_fallback(m->bus, "/org/freedesktop/login1/seat", &bus_seat_vtable, m) ||
887 !dbus_connection_register_fallback(m->bus, "/org/freedesktop/login1/session", &bus_session_vtable, m) ||
888 !dbus_connection_register_fallback(m->bus, "/org/freedesktop/login1/user", &bus_user_vtable, m) ||
889 !dbus_connection_add_filter(m->bus, bus_message_filter, m, NULL)) {
890 log_error("Not enough memory");
891 r = -ENOMEM;
892 goto fail;
893 }
894
895 dbus_bus_add_match(m->bus,
896 "type='signal',"
897 "interface='org.freedesktop.systemd1.Agent',"
898 "member='Released',"
899 "path='/org/freedesktop/systemd1/agent'",
900 &error);
901
902 if (dbus_error_is_set(&error)) {
903 log_error("Failed to register match: %s", bus_error_message(&error));
904 r = -EIO;
905 goto fail;
906 }
907
908 r = dbus_bus_request_name(m->bus, "org.freedesktop.login1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
909 if (dbus_error_is_set(&error)) {
910 log_error("Failed to register name on bus: %s", bus_error_message(&error));
911 r = -EIO;
912 goto fail;
913 }
914
915 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
916 log_error("Failed to acquire name.");
917 r = -EEXIST;
918 goto fail;
919 }
920
921 m->bus_fd = bus_loop_open(m->bus);
922 if (m->bus_fd < 0) {
923 r = m->bus_fd;
924 goto fail;
925 }
926
927 zero(ev);
928 ev.events = EPOLLIN;
929 ev.data.u32 = FD_BUS;
930
931 if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->bus_fd, &ev) < 0)
932 goto fail;
933
934 return 0;
935
936fail:
937 dbus_error_free(&error);
938
939 return r;
940}
941
942static int manager_connect_console(Manager *m) {
943 struct epoll_event ev;
944
945 assert(m);
946 assert(m->console_active_fd < 0);
947
948 m->console_active_fd = open("/sys/class/tty/tty0/active", O_RDONLY|O_NOCTTY|O_CLOEXEC);
949 if (m->console_active_fd < 0) {
950
951 /* On certain architectures (S390 and Xen), /dev/tty0
952 does not exist, so don't fail if we can't open it.*/
953 if (errno == ENOENT)
954 return 0;
955
956 log_error("Failed to open /sys/class/tty/tty0/active: %m");
957 return -errno;
958 }
959
960 zero(ev);
961 ev.events = 0;
962 ev.data.u32 = FD_CONSOLE;
963
964 if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->console_active_fd, &ev) < 0)
965 return -errno;
966
967 return 0;
968}
969
970static int manager_connect_udev(Manager *m) {
971 struct epoll_event ev;
972 int r;
973
974 assert(m);
975 assert(!m->udev_seat_monitor);
976 assert(!m->udev_vcsa_monitor);
977
978 m->udev_seat_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
979 if (!m->udev_seat_monitor)
980 return -ENOMEM;
981
982 r = udev_monitor_filter_add_match_tag(m->udev_seat_monitor, "seat");
983 if (r < 0)
984 return r;
985
986 r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_seat_monitor, "graphics", NULL);
987 if (r < 0)
988 return r;
989
990 r = udev_monitor_enable_receiving(m->udev_seat_monitor);
991 if (r < 0)
992 return r;
993
994 m->udev_seat_fd = udev_monitor_get_fd(m->udev_seat_monitor);
995
996 zero(ev);
997 ev.events = EPOLLIN;
998 ev.data.u32 = FD_SEAT_UDEV;
999
1000 /* Don't bother watching VCSA devices, if nobody cares */
1001 if (m->n_autovts <= 0 || m->console_active_fd < 0)
1002 return 0;
1003
1004 if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_seat_fd, &ev) < 0)
1005 return -errno;
1006
1007 m->udev_vcsa_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
1008 if (!m->udev_vcsa_monitor)
1009 return -ENOMEM;
1010
1011 r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_vcsa_monitor, "vc", NULL);
1012 if (r < 0)
1013 return r;
1014
1015 r = udev_monitor_enable_receiving(m->udev_vcsa_monitor);
1016 if (r < 0)
1017 return r;
1018
1019 m->udev_vcsa_fd = udev_monitor_get_fd(m->udev_vcsa_monitor);
1020
1021 zero(ev);
1022 ev.events = EPOLLIN;
1023 ev.data.u32 = FD_VCSA_UDEV;
1024
1025 if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_vcsa_fd, &ev) < 0)
1026 return -errno;
1027
1028 return 0;
1029}
1030
1031void manager_gc(Manager *m, bool drop_not_started) {
1032 Seat *seat;
1033 Session *session;
1034 User *user;
1035
1036 assert(m);
1037
1038 while ((seat = m->seat_gc_queue)) {
1039 LIST_REMOVE(Seat, gc_queue, m->seat_gc_queue, seat);
1040 seat->in_gc_queue = false;
1041
1042 if (seat_check_gc(seat, drop_not_started) == 0) {
1043 seat_stop(seat);
1044 seat_free(seat);
1045 }
1046 }
1047
1048 while ((session = m->session_gc_queue)) {
1049 LIST_REMOVE(Session, gc_queue, m->session_gc_queue, session);
1050 session->in_gc_queue = false;
1051
1052 if (session_check_gc(session, drop_not_started) == 0) {
1053 session_stop(session);
1054 session_free(session);
1055 }
1056 }
1057
1058 while ((user = m->user_gc_queue)) {
1059 LIST_REMOVE(User, gc_queue, m->user_gc_queue, user);
1060 user->in_gc_queue = false;
1061
1062 if (user_check_gc(user, drop_not_started) == 0) {
1063 user_stop(user);
1064 user_free(user);
1065 }
1066 }
1067}
1068
1069int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
1070 Session *s;
1071 bool idle_hint = true;
1072 dual_timestamp ts = { 0, 0 };
1073 Iterator i;
1074
1075 assert(m);
1076
1077 HASHMAP_FOREACH(s, m->sessions, i) {
1078 dual_timestamp k;
1079 int ih;
1080
1081 ih = session_get_idle_hint(s, &k);
1082 if (ih < 0)
1083 return ih;
1084
1085 if (!ih) {
1086 if (!idle_hint) {
1087 if (k.monotonic < ts.monotonic)
1088 ts = k;
1089 } else {
1090 idle_hint = false;
1091 ts = k;
1092 }
1093 } else if (idle_hint) {
1094
1095 if (k.monotonic > ts.monotonic)
1096 ts = k;
1097 }
1098 }
1099
1100 if (t)
1101 *t = ts;
1102
1103 return idle_hint;
1104}
1105
1106int manager_startup(Manager *m) {
1107 int r;
1108 Seat *seat;
1109 Session *session;
1110 User *user;
1111 Iterator i;
1112
1113 assert(m);
1114 assert(m->epoll_fd <= 0);
1115
1116 m->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1117 if (m->epoll_fd < 0)
1118 return -errno;
1119
1120 /* Connect to console */
1121 r = manager_connect_console(m);
1122 if (r < 0)
1123 return r;
1124
1125 /* Connect to udev */
1126 r = manager_connect_udev(m);
1127 if (r < 0)
1128 return r;
1129
1130 /* Connect to the bus */
1131 r = manager_connect_bus(m);
1132 if (r < 0)
1133 return r;
1134
1135 /* Instantiate magic seat 0 */
1136 r = manager_add_seat(m, "seat0", &m->vtconsole);
1137 if (r < 0)
1138 return r;
1139
1140 /* Deserialize state */
1141 manager_enumerate_devices(m);
1142 manager_enumerate_seats(m);
1143 manager_enumerate_users(m);
1144 manager_enumerate_sessions(m);
1145
1146 /* Remove stale objects before we start them */
1147 manager_gc(m, false);
1148
1149 /* And start everything */
1150 HASHMAP_FOREACH(seat, m->seats, i)
1151 seat_start(seat);
1152
1153 HASHMAP_FOREACH(user, m->users, i)
1154 user_start(user);
1155
1156 HASHMAP_FOREACH(session, m->sessions, i)
1157 session_start(session);
1158
1159 return 0;
1160}
1161
1162int manager_run(Manager *m) {
1163 assert(m);
1164
1165 for (;;) {
1166 struct epoll_event event;
1167 int n;
1168
1169 manager_gc(m, true);
1170
1171 if (dbus_connection_dispatch(m->bus) != DBUS_DISPATCH_COMPLETE)
1172 continue;
1173
1174 manager_gc(m, true);
1175
1176 n = epoll_wait(m->epoll_fd, &event, 1, -1);
1177 if (n < 0) {
1178 if (errno == EINTR || errno == EAGAIN)
1179 continue;
1180
1181 log_error("epoll() failed: %m");
1182 return -errno;
1183 }
1184
1185 switch (event.data.u32) {
1186
1187 case FD_SEAT_UDEV:
1188 manager_dispatch_seat_udev(m);
1189 break;
1190
1191 case FD_VCSA_UDEV:
1192 manager_dispatch_vcsa_udev(m);
1193 break;
1194
1195 case FD_CONSOLE:
1196 manager_dispatch_console(m);
1197 break;
1198
1199 case FD_BUS:
1200 bus_loop_dispatch(m->bus_fd);
1201 break;
1202
1203 default:
1204 if (event.data.u32 >= FD_FIFO_BASE)
1205 manager_pipe_notify_eof(m, event.data.u32 - FD_FIFO_BASE);
1206 }
1207 }
1208
1209 return 0;
1210}
1211
1212static int manager_parse_config_file(Manager *m) {
1213 FILE *f;
1214 const char *fn;
1215 int r;
1216
1217 assert(m);
1218
1219 fn = "/etc/systemd/logind.conf";
1220 f = fopen(fn, "re");
1221 if (!f) {
1222 if (errno == ENOENT)
1223 return 0;
1224
1225 log_warning("Failed to open configuration file %s: %m", fn);
1226 return -errno;
1227 }
1228
1229 r = config_parse(fn, f, "Login\0", config_item_perf_lookup, (void*) logind_gperf_lookup, false, m);
1230 if (r < 0)
1231 log_warning("Failed to parse configuration file: %s", strerror(-r));
1232
1233 fclose(f);
1234
1235 return r;
1236}
1237
1238int main(int argc, char *argv[]) {
1239 Manager *m = NULL;
1240 int r;
1241
1242 log_set_target(LOG_TARGET_AUTO);
1243 log_set_facility(LOG_AUTH);
1244 log_parse_environment();
1245 log_open();
1246
1247 umask(0022);
1248
1249 if (argc != 1) {
1250 log_error("This program takes no arguments.");
1251 r = -EINVAL;
1252 goto finish;
1253 }
1254
1255 m = manager_new();
1256 if (!m) {
1257 log_error("Out of memory");
1258 r = -ENOMEM;
1259 goto finish;
1260 }
1261
1262 manager_parse_config_file(m);
1263
1264 r = manager_startup(m);
1265 if (r < 0) {
1266 log_error("Failed to fully start up daemon: %s", strerror(-r));
1267 goto finish;
1268 }
1269
1270 log_debug("systemd-logind running as pid %lu", (unsigned long) getpid());
1271
1272 sd_notify(false,
1273 "READY=1\n"
1274 "STATUS=Processing requests...");
1275
1276 r = manager_run(m);
1277
1278 log_debug("systemd-logind stopped as pid %lu", (unsigned long) getpid());
1279
1280finish:
1281 sd_notify(false,
1282 "STATUS=Shutting down...");
1283
1284 if (m)
1285 manager_free(m);
1286
1287 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1288}