chiark / gitweb /
udev: only tag the main card device of a sound card with 'seat'
[elogind.git] / src / logind.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 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>
29 #include <sys/ioctl.h>
30 #include <linux/vt.h>
31
32 #include "logind.h"
33 #include "dbus-common.h"
34 #include "dbus-loop.h"
35
36 Manager *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;
45         m->udev_seat_fd = -1;
46         m->udev_vcsa_fd = -1;
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);
54         m->cgroups = hashmap_new(string_hash_func, string_compare_func);
55         m->pipe_fds = hashmap_new(trivial_hash_func, trivial_compare_func);
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
76 void 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);
100         hashmap_free(m->cgroups);
101         hashmap_free(m->pipe_fds);
102
103         if (m->console_active_fd >= 0)
104                 close_nointr_nofail(m->console_active_fd);
105
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);
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
131 int 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
155 int 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
179 int 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
203 int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) {
204         User *u;
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
221         if (_user)
222                 *_user = u;
223
224         return 0;
225 }
226
227 int 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
241 int 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
254 int manager_process_seat_device(Manager *m, struct udev_device *d) {
255         Device *device;
256         int r;
257
258         assert(m);
259
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
265         if (streq_ptr(udev_device_get_action(d), "remove")) {
266
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));
269                 if (!device)
270                         return 0;
271
272                 seat_add_to_gc_queue(device->seat);
273                 device_free(device);
274
275         } else {
276                 const char *sn;
277                 Seat *seat;
278
279                 sn = udev_device_get_property_value(d, "ID_SEAT");
280                 if (!sn)
281                         sn = "seat0";
282
283                 if (!seat_name_is_valid(sn)) {
284                         log_warning("Device with invalid seat name %s found, ignoring.", sn);
285                         return 0;
286                 }
287
288                 r = manager_add_device(m, udev_device_get_sysname(d), &device);
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);
301                 seat_start(seat);
302         }
303
304         return 0;
305 }
306
307 int manager_enumerate_devices(Manager *m) {
308         struct udev_list_entry *item = NULL, *first = NULL;
309         struct udev_enumerate *e;
310         int r;
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);
318         if (!e) {
319                 r = -ENOMEM;
320                 goto finish;
321         }
322
323         r = udev_enumerate_add_match_subsystem(e, "graphics");
324         if (r < 0)
325                 goto finish;
326
327         r = udev_enumerate_add_match_tag(e, "seat");
328         if (r < 0)
329                 goto finish;
330
331         r = udev_enumerate_scan_devices(e);
332         if (r < 0)
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));
341                 if (!d) {
342                         r = -ENOMEM;
343                         goto finish;
344                 }
345
346                 k = manager_process_seat_device(m, d);
347                 udev_device_unref(d);
348
349                 if (k < 0)
350                         r = k;
351         }
352
353 finish:
354         if (e)
355                 udev_enumerate_unref(e);
356
357         return r;
358 }
359
360 int 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
403 static 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;
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                         }
436
437                 free(name);
438         }
439
440         closedir(d);
441
442         return r;
443 }
444
445
446 static 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))
464                         continue;
465
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;
470                 }
471         }
472
473         closedir(d);
474
475         return r;
476 }
477
478 int manager_enumerate_users(Manager *m) {
479         DIR *d;
480         struct dirent *de;
481         int r, k;
482
483         assert(m);
484
485         /* First, enumerate user cgroups */
486         r = manager_enumerate_users_from_cgroup(m);
487
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 */
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;
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
532 static 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
542                 if (!u->cgroup_path)
543                         continue;
544
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);
559                         if (k < 0) {
560                                 free(name);
561                                 break;
562                         }
563
564                         session_add_to_gc_queue(session);
565
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);
574                 }
575
576                 closedir(d);
577
578                 if (k < 0)
579                         r = k;
580         }
581
582         return r;
583 }
584
585 int manager_enumerate_sessions(Manager *m) {
586         DIR *d;
587         struct dirent *de;
588         int r = 0;
589
590         assert(m);
591
592         /* First enumerate session cgroups */
593         r = manager_enumerate_sessions_from_cgroup(m);
594
595         /* Second, read in session data stored on disk */
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
628 int manager_dispatch_seat_udev(Manager *m) {
629         struct udev_device *d;
630         int r;
631
632         assert(m);
633
634         d = udev_monitor_receive_device(m->udev_seat_monitor);
635         if (!d)
636                 return -ENOMEM;
637
638         r = manager_process_seat_device(m, d);
639         udev_device_unref(d);
640
641         return r;
642 }
643
644
645 int 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
664         udev_device_unref(d);
665
666         return r;
667 }
668
669 int manager_dispatch_console(Manager *m) {
670         assert(m);
671
672         if (m->vtconsole)
673                 seat_read_active_vt(m->vtconsole);
674
675         return 0;
676 }
677
678 static int vt_is_busy(int vtnr) {
679         struct vt_stat vt_stat;
680         int r = 0, fd;
681
682         assert(vtnr >= 1);
683
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);
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
704 int manager_spawn_autovt(Manager *m, int vtnr) {
705         int r;
706         DBusMessage *message = NULL, *reply = NULL;
707         char *name = NULL;
708         const char *mode = "fail";
709         DBusError error;
710
711         assert(m);
712         assert(vtnr >= 1);
713
714         dbus_error_init(&error);
715
716         if ((unsigned) vtnr > m->n_autovts)
717                 return 0;
718
719         r = vt_is_busy(vtnr);
720         if (r < 0)
721                 return r;
722         else if (r > 0)
723                 return -EBUSY;
724
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         }
731
732         if (asprintf(&name, "autovt@tty%i.service", vtnr) < 0) {
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
755 finish:
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;
767 }
768
769 void 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;
784
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);
797 }
798
799 static void manager_pipe_notify_eof(Manager *m, int fd) {
800         Session *s;
801
802         assert_se(m);
803         assert_se(fd >= 0);
804
805         assert_se(s = hashmap_get(m->pipe_fds, INT_TO_PTR(fd + 1)));
806         assert(s->pipe_fd == fd);
807         session_unset_pipe_fd(s);
808
809         session_stop(s);
810 }
811
812 static int manager_connect_bus(Manager *m) {
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) {
825                 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
826                 r = -ECONNREFUSED;
827                 goto fail;
828         }
829
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) ||
834             !dbus_connection_add_filter(m->bus, bus_message_filter, m, NULL)) {
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)) {
848                 log_error("Failed to register match: %s", bus_error_message(&error));
849                 r = -EIO;
850                 goto fail;
851         }
852
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.");
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
881 fail:
882         dbus_error_free(&error);
883
884         return r;
885 }
886
887 static 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);
900         ev.events = 0;
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
909 static int manager_connect_udev(Manager *m) {
910         struct epoll_event ev;
911         int r;
912
913         assert(m);
914         assert(!m->udev_seat_monitor);
915         assert(!m->udev_vcsa_monitor);
916
917         m->udev_seat_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
918         if (!m->udev_seat_monitor)
919                 return -ENOMEM;
920
921         r = udev_monitor_filter_add_match_tag(m->udev_seat_monitor, "seat");
922         if (r < 0)
923                 return r;
924
925         r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_seat_monitor, "graphics", NULL);
926         if (r < 0)
927                 return r;
928
929         r = udev_monitor_enable_receiving(m->udev_seat_monitor);
930         if (r < 0)
931                 return r;
932
933         m->udev_seat_fd = udev_monitor_get_fd(m->udev_seat_monitor);
934
935         zero(ev);
936         ev.events = EPOLLIN;
937         ev.data.u32 = FD_SEAT_UDEV;
938
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)
964                 return -errno;
965
966         return 0;
967 }
968
969 void 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
1007 int 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
1044 int manager_startup(Manager *m) {
1045         int r;
1046         Seat *seat;
1047         Session *session;
1048         User *user;
1049         Iterator i;
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
1073         /* Instantiate magic seat 0 */
1074         r = manager_add_seat(m, "seat0", &m->vtconsole);
1075         if (r < 0)
1076                 return r;
1077
1078         /* Deserialize state */
1079         manager_enumerate_devices(m);
1080         manager_enumerate_seats(m);
1081         manager_enumerate_users(m);
1082         manager_enumerate_sessions(m);
1083
1084         /* Get rid of objects that are no longer used */
1085         manager_gc(m);
1086
1087         /* And start everything */
1088         HASHMAP_FOREACH(seat, m->seats, i)
1089                 seat_start(seat);
1090
1091         HASHMAP_FOREACH(user, m->users, i)
1092                 user_start(user);
1093
1094         HASHMAP_FOREACH(session, m->sessions, i)
1095                 session_start(session);
1096
1097         return 0;
1098 }
1099
1100 int manager_run(Manager *m) {
1101         assert(m);
1102
1103         for (;;) {
1104                 struct epoll_event event;
1105                 int n;
1106
1107                 manager_gc(m);
1108
1109                 if (dbus_connection_dispatch(m->bus) != DBUS_DISPATCH_COMPLETE)
1110                         continue;
1111
1112                 manager_gc(m);
1113
1114                 n = epoll_wait(m->epoll_fd, &event, 1, -1);
1115                 if (n < 0) {
1116                         if (errno == EINTR || errno == EAGAIN)
1117                                 continue;
1118
1119                         log_error("epoll() failed: %m");
1120                         return -errno;
1121                 }
1122
1123                 switch (event.data.u32) {
1124
1125                 case FD_SEAT_UDEV:
1126                         manager_dispatch_seat_udev(m);
1127                         break;
1128
1129                 case FD_VCSA_UDEV:
1130                         manager_dispatch_vcsa_udev(m);
1131                         break;
1132
1133                 case FD_CONSOLE:
1134                         manager_dispatch_console(m);
1135                         break;
1136
1137                 case FD_BUS:
1138                         bus_loop_dispatch(m->bus_fd);
1139                         break;
1140
1141                 default:
1142                         if (event.data.u32 >= FD_PIPE_BASE)
1143                                 manager_pipe_notify_eof(m, event.data.u32 - FD_PIPE_BASE);
1144                 }
1145         }
1146
1147         return 0;
1148 }
1149
1150 int main(int argc, char *argv[]) {
1151         Manager *m = NULL;
1152         int r;
1153
1154         log_set_target(LOG_TARGET_AUTO);
1155         log_parse_environment();
1156         log_open();
1157
1158         if (argc != 1) {
1159                 log_error("This program takes no arguments.");
1160                 r = -EINVAL;
1161                 goto finish;
1162         }
1163
1164         umask(0022);
1165
1166         m = manager_new();
1167         if (!m) {
1168                 log_error("Out of memory");
1169                 r = -ENOMEM;
1170                 goto finish;
1171         }
1172
1173         r = manager_startup(m);
1174         if (r < 0) {
1175                 log_error("Failed to fully start up daemon: %s", strerror(-r));
1176                 goto finish;
1177         }
1178
1179         r = manager_run(m);
1180
1181 finish:
1182         if (m)
1183                 manager_free(m);
1184
1185         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1186 }