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