From 718d006a63f773c42106494e823250c48942cf08 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Tue, 17 Sep 2013 17:39:54 +0200 Subject: logind: listen actively for session devices Session compositors need access to fbdev, DRM and evdev devices if they control a session. To make logind pass them to sessions, we need to listen for them actively. However, we avoid creating new seats for non master-of-seat devices. Only once a seat is created, we start remembering all other session devices. If the last master-device is removed (even if there are other non-master devices still available), we destroy the seat. This is the current behavior, but we need to explicitly implement it now as there may be non-master devices in the seat->devices list. Unlike master devices, we don't care whether our list of non-master devices is complete. We don't export this list but use it only as cache if sessions request these devices. Hence, if a session requests a device that is not in the list, we will simply look it up. However, once a session requested a device, we must be notified of "remove" udev events. So we must link the devices somehow into the device-list. Regarding the implementation, we now sort the device list by the "master" flag. This guarantees that master devices are at the front and non-master devices at the tail of the list. Thus, we can easily test whether a seat has a master device attached. --- src/login/logind.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 9 deletions(-) (limited to 'src/login/logind.c') diff --git a/src/login/logind.c b/src/login/logind.c index 4ef92b8253..29019c214b 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -151,6 +151,8 @@ void manager_free(Manager *m) { if (m->udev_seat_monitor) udev_monitor_unref(m->udev_seat_monitor); + if (m->udev_device_monitor) + udev_monitor_unref(m->udev_device_monitor); if (m->udev_vcsa_monitor) udev_monitor_unref(m->udev_vcsa_monitor); if (m->udev_button_monitor) @@ -184,7 +186,7 @@ void manager_free(Manager *m) { free(m); } -int manager_add_device(Manager *m, const char *sysfs, Device **_device) { +int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) { Device *d; assert(m); @@ -195,10 +197,13 @@ int manager_add_device(Manager *m, const char *sysfs, Device **_device) { if (_device) *_device = d; + /* we support adding master-flags, but not removing them */ + d->master = d->master || master; + return 0; } - d = device_new(m, sysfs); + d = device_new(m, sysfs, master); if (!d) return -ENOMEM; @@ -373,7 +378,8 @@ int manager_process_seat_device(Manager *m, struct udev_device *d) { } else { const char *sn; - Seat *seat; + Seat *seat = NULL; + bool master; sn = udev_device_get_property_value(d, "ID_SEAT"); if (isempty(sn)) @@ -384,16 +390,23 @@ int manager_process_seat_device(Manager *m, struct udev_device *d) { return 0; } - r = manager_add_device(m, udev_device_get_syspath(d), &device); + /* ignore non-master devices for unknown seats */ + master = udev_device_has_tag(d, "master-of-seat"); + if (!master && !(seat = hashmap_get(m->seats, sn))) + return 0; + + r = manager_add_device(m, udev_device_get_syspath(d), master, &device); if (r < 0) return r; - r = manager_add_seat(m, sn, &seat); - if (r < 0) { - if (!device->seat) - device_free(device); + if (!seat) { + r = manager_add_seat(m, sn, &seat); + if (r < 0) { + if (!device->seat) + device_free(device); - return r; + return r; + } } device_attach(device, seat); @@ -762,6 +775,22 @@ int manager_dispatch_seat_udev(Manager *m) { return r; } +static int manager_dispatch_device_udev(Manager *m) { + struct udev_device *d; + int r; + + assert(m); + + d = udev_monitor_receive_device(m->udev_device_monitor); + if (!d) + return -ENOMEM; + + r = manager_process_seat_device(m, d); + udev_device_unref(d); + + return r; +} + int manager_dispatch_vcsa_udev(Manager *m) { struct udev_device *d; int r = 0; @@ -1149,6 +1178,7 @@ static int manager_connect_udev(Manager *m) { assert(m); assert(!m->udev_seat_monitor); + assert(!m->udev_device_monitor); assert(!m->udev_vcsa_monitor); assert(!m->udev_button_monitor); @@ -1169,6 +1199,33 @@ static int manager_connect_udev(Manager *m) { if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_seat_fd, &ev) < 0) return -errno; + m->udev_device_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); + if (!m->udev_device_monitor) + return -ENOMEM; + + r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_device_monitor, "input", NULL); + if (r < 0) + return r; + + r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_device_monitor, "graphics", NULL); + if (r < 0) + return r; + + r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_device_monitor, "drm", NULL); + if (r < 0) + return r; + + r = udev_monitor_enable_receiving(m->udev_device_monitor); + if (r < 0) + return r; + + m->udev_device_fd = udev_monitor_get_fd(m->udev_device_monitor); + zero(ev); + ev.events = EPOLLIN; + ev.data.u32 = FD_DEVICE_UDEV; + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_device_fd, &ev) < 0) + return -errno; + /* Don't watch keys if nobody cares */ if (m->handle_power_key != HANDLE_IGNORE || m->handle_suspend_key != HANDLE_IGNORE || @@ -1545,6 +1602,10 @@ int manager_run(Manager *m) { manager_dispatch_seat_udev(m); break; + case FD_DEVICE_UDEV: + manager_dispatch_device_udev(m); + break; + case FD_VCSA_UDEV: manager_dispatch_vcsa_udev(m); break; -- cgit v1.2.3-54-g00ecf