1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
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.
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.
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/>.
26 #include <systemd/sd-bus.h>
27 #include <systemd/sd-event.h>
28 #include <systemd/sd-login.h>
30 #include "event-util.h"
34 #include "sysview-internal.h"
35 #include "udev-util.h"
38 static int context_raise_session_control(sysview_context *c, sysview_session *session, int error);
44 sysview_device *sysview_find_device(sysview_context *c, const char *name) {
45 assert_return(c, NULL);
46 assert_return(name, NULL);
48 return hashmap_get(c->device_map, name);
51 int sysview_device_new(sysview_device **out, sysview_seat *seat, const char *name) {
52 _cleanup_(sysview_device_freep) sysview_device *device = NULL;
55 assert_return(seat, -EINVAL);
56 assert_return(name, -EINVAL);
58 device = new0(sysview_device, 1);
63 device->type = (unsigned)-1;
65 device->name = strdup(name);
69 r = hashmap_put(seat->context->device_map, device->name, device);
73 r = hashmap_put(seat->device_map, device->name, device);
83 sysview_device *sysview_device_free(sysview_device *device) {
88 hashmap_remove_value(device->seat->device_map, device->name, device);
89 hashmap_remove_value(device->seat->context->device_map, device->name, device);
92 switch (device->type) {
93 case SYSVIEW_DEVICE_EVDEV:
94 device->evdev.ud = udev_device_unref(device->evdev.ud);
96 case SYSVIEW_DEVICE_DRM:
97 device->drm.ud = udev_device_unref(device->drm.ud);
107 unsigned int sysview_device_get_type(sysview_device *device) {
108 assert_return(device, (unsigned)-1);
113 struct udev_device *sysview_device_get_ud(sysview_device *device) {
114 assert_return(device, NULL);
116 switch (device->type) {
117 case SYSVIEW_DEVICE_EVDEV:
118 return device->evdev.ud;
119 case SYSVIEW_DEVICE_DRM:
120 return device->drm.ud;
122 assert_return(0, NULL);
126 static int device_new_ud(sysview_device **out, sysview_seat *seat, unsigned int type, struct udev_device *ud) {
127 _cleanup_(sysview_device_freep) sysview_device *device = NULL;
130 assert_return(seat, -EINVAL);
131 assert_return(ud, -EINVAL);
133 r = sysview_device_new(&device, seat, udev_device_get_syspath(ud));
140 case SYSVIEW_DEVICE_EVDEV:
141 device->evdev.ud = udev_device_ref(ud);
143 case SYSVIEW_DEVICE_DRM:
144 device->drm.ud = udev_device_ref(ud);
147 assert_not_reached("sysview: invalid udev-device type");
160 sysview_session *sysview_find_session(sysview_context *c, const char *name) {
161 assert_return(c, NULL);
162 assert_return(name, NULL);
164 return hashmap_get(c->session_map, name);
167 int sysview_session_new(sysview_session **out, sysview_seat *seat, const char *name) {
168 _cleanup_(sysview_session_freep) sysview_session *session = NULL;
171 assert_return(seat, -EINVAL);
173 session = new0(sysview_session, 1);
177 session->seat = seat;
181 * If a name is given, we require it to be a logind session
182 * name. The session will be put in managed mode and we use
183 * logind to request controller access.
186 session->name = strdup(name);
190 r = sd_bus_path_encode("/org/freedesktop/login1/session",
191 session->name, &session->path);
195 session->custom = false;;
198 * No session name was given. We assume this is an unmanaged
199 * session controlled by the application. We don't use logind
200 * at all and leave session management to the application. The
201 * name of the session-object is set to a unique random string
202 * that does not clash with the logind namespace.
205 r = asprintf(&session->name, "@custom%" PRIu64,
206 ++seat->context->custom_sid);
210 session->custom = true;
213 r = hashmap_put(seat->context->session_map, session->name, session);
217 r = hashmap_put(seat->session_map, session->name, session);
227 sysview_session *sysview_session_free(sysview_session *session) {
231 assert(!session->public);
232 assert(!session->wants_control);
235 hashmap_remove_value(session->seat->session_map, session->name, session);
236 hashmap_remove_value(session->seat->context->session_map, session->name, session);
246 const char *sysview_session_get_name(sysview_session *session) {
247 assert_return(session, NULL);
249 return session->name;
252 static int session_take_control_fn(sd_bus *bus,
253 sd_bus_message *reply,
255 sd_bus_error *ret_error) {
256 sysview_session *session = userdata;
259 session->slot_take_control = sd_bus_slot_unref(session->slot_take_control);
261 if (sd_bus_message_is_method_error(reply, NULL)) {
262 const sd_bus_error *e = sd_bus_message_get_error(reply);
264 log_debug("sysview: %s: TakeControl failed: %s: %s",
265 session->name, e->name, e->message);
266 error = -sd_bus_error_get_errno(e);
268 session->has_control = true;
272 return context_raise_session_control(session->seat->context, session, error);
275 int sysview_session_take_control(sysview_session *session) {
276 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
279 assert_return(session, -EINVAL);
280 assert_return(!session->custom, -EINVAL);
282 if (session->wants_control)
285 r = sd_bus_message_new_method_call(session->seat->context->sysbus,
287 "org.freedesktop.login1",
289 "org.freedesktop.login1.Session",
294 r = sd_bus_message_append(m, "b", 0);
298 r = sd_bus_call_async(session->seat->context->sysbus,
299 &session->slot_take_control,
301 session_take_control_fn,
307 session->wants_control = true;
311 void sysview_session_release_control(sysview_session *session) {
312 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
316 assert(!session->custom);
318 if (!session->wants_control)
321 session->wants_control = false;
323 if (!session->has_control && !session->slot_take_control)
326 session->has_control = false;
327 session->slot_take_control = sd_bus_slot_unref(session->slot_take_control);
329 r = sd_bus_message_new_method_call(session->seat->context->sysbus,
331 "org.freedesktop.login1",
333 "org.freedesktop.login1.Session",
336 r = sd_bus_send(session->seat->context->sysbus, m, NULL);
338 if (r < 0 && r != -ENOTCONN)
339 log_debug("sysview: %s: cannot send ReleaseControl: %s",
340 session->name, strerror(-r));
347 sysview_seat *sysview_find_seat(sysview_context *c, const char *name) {
348 assert_return(c, NULL);
349 assert_return(name, NULL);
351 return hashmap_get(c->seat_map, name);
354 int sysview_seat_new(sysview_seat **out, sysview_context *c, const char *name) {
355 _cleanup_(sysview_seat_freep) sysview_seat *seat = NULL;
358 assert_return(c, -EINVAL);
359 assert_return(name, -EINVAL);
361 seat = new0(sysview_seat, 1);
367 seat->name = strdup(name);
371 seat->session_map = hashmap_new(&string_hash_ops);
372 if (!seat->session_map)
375 seat->device_map = hashmap_new(&string_hash_ops);
376 if (!seat->device_map)
379 r = hashmap_put(c->seat_map, seat->name, seat);
389 sysview_seat *sysview_seat_free(sysview_seat *seat) {
393 assert(!seat->public);
394 assert(hashmap_size(seat->device_map) == 0);
395 assert(hashmap_size(seat->session_map) == 0);
398 hashmap_remove_value(seat->context->seat_map, seat->name, seat);
400 hashmap_free(seat->device_map);
401 hashmap_free(seat->session_map);
408 const char *sysview_seat_get_name(sysview_seat *seat) {
409 assert_return(seat, NULL);
418 static int context_raise(sysview_context *c, sysview_event *event, int def) {
419 return c->running ? c->event_fn(c, c->userdata, event) : def;
422 static int context_raise_seat_add(sysview_context *c, sysview_seat *seat) {
423 sysview_event event = {
424 .type = SYSVIEW_EVENT_SEAT_ADD,
430 return context_raise(c, &event, 0);
433 static int context_raise_seat_remove(sysview_context *c, sysview_seat *seat) {
434 sysview_event event = {
435 .type = SYSVIEW_EVENT_SEAT_REMOVE,
441 return context_raise(c, &event, 0);
444 static int context_raise_session_filter(sysview_context *c,
447 const char *username,
449 sysview_event event = {
450 .type = SYSVIEW_EVENT_SESSION_FILTER,
454 .username = username,
459 return context_raise(c, &event, 1);
462 static int context_raise_session_add(sysview_context *c, sysview_session *session) {
463 sysview_event event = {
464 .type = SYSVIEW_EVENT_SESSION_ADD,
470 return context_raise(c, &event, 0);
473 static int context_raise_session_remove(sysview_context *c, sysview_session *session) {
474 sysview_event event = {
475 .type = SYSVIEW_EVENT_SESSION_REMOVE,
481 return context_raise(c, &event, 0);
484 static int context_raise_session_control(sysview_context *c, sysview_session *session, int error) {
485 sysview_event event = {
486 .type = SYSVIEW_EVENT_SESSION_CONTROL,
493 return context_raise(c, &event, 0);
496 static int context_raise_session_attach(sysview_context *c, sysview_session *session, sysview_device *device) {
497 sysview_event event = {
498 .type = SYSVIEW_EVENT_SESSION_ATTACH,
505 return context_raise(c, &event, 0);
508 static int context_raise_session_detach(sysview_context *c, sysview_session *session, sysview_device *device) {
509 sysview_event event = {
510 .type = SYSVIEW_EVENT_SESSION_DETACH,
517 return context_raise(c, &event, 0);
520 static int context_raise_device_change(sysview_context *c, sysview_device *device, struct udev_device *ud) {
521 sysview_event event = {
522 .type = SYSVIEW_EVENT_DEVICE_CHANGE,
529 return context_raise(c, &event, 0);
532 static int context_add_device(sysview_context *c, sysview_device *device) {
533 sysview_session *session;
540 log_debug("sysview: add device '%s' on seat '%s'",
541 device->name, device->seat->name);
543 HASHMAP_FOREACH(session, device->seat->session_map, i) {
544 if (!session->public)
547 r = context_raise_session_attach(c, session, device);
553 log_debug("sysview: error while adding device '%s': %s",
554 device->name, strerror(-r));
558 static int context_remove_device(sysview_context *c, sysview_device *device) {
559 sysview_session *session;
566 log_debug("sysview: remove device '%s'", device->name);
568 HASHMAP_FOREACH(session, device->seat->session_map, i) {
569 if (!session->public)
572 r = context_raise_session_detach(c, session, device);
578 log_debug("sysview: error while removing device '%s': %s",
579 device->name, strerror(-r));
580 sysview_device_free(device);
584 static int context_add_session(sysview_context *c, sysview_seat *seat, const char *id) {
585 sysview_session *session;
586 sysview_device *device;
594 session = sysview_find_session(c, id);
598 log_debug("sysview: add session '%s' on seat '%s'", id, seat->name);
600 r = sysview_session_new(&session, seat, id);
604 if (!seat->scanned) {
605 r = sysview_context_rescan(c);
611 session->public = true;
612 r = context_raise_session_add(c, session);
614 session->public = false;
618 HASHMAP_FOREACH(device, seat->device_map, i) {
619 r = context_raise_session_attach(c, session, device);
633 log_debug("sysview: error while adding session '%s': %s",
638 static int context_remove_session(sysview_context *c, sysview_session *session) {
639 sysview_device *device;
646 log_debug("sysview: remove session '%s'", session->name);
648 if (session->public) {
649 HASHMAP_FOREACH(device, session->seat->device_map, i) {
650 r = context_raise_session_detach(c, session, device);
655 session->public = false;
656 r = context_raise_session_remove(c, session);
661 if (!session->custom)
662 sysview_session_release_control(session);
665 log_debug("sysview: error while removing session '%s': %s",
666 session->name, strerror(-error));
667 sysview_session_free(session);
671 static int context_add_seat(sysview_context *c, const char *id) {
678 seat = sysview_find_seat(c, id);
682 log_debug("sysview: add seat '%s'", id);
684 r = sysview_seat_new(&seat, c, id);
689 r = context_raise_seat_add(c, seat);
691 seat->public = false;
699 log_debug("sysview: error while adding seat '%s': %s",
704 static int context_remove_seat(sysview_context *c, sysview_seat *seat) {
705 sysview_session *session;
706 sysview_device *device;
712 log_debug("sysview: remove seat '%s'", seat->name);
714 while ((device = hashmap_first(seat->device_map))) {
715 r = context_remove_device(c, device);
720 while ((session = hashmap_first(seat->session_map))) {
721 r = context_remove_session(c, session);
727 seat->public = false;
728 r = context_raise_seat_remove(c, seat);
734 log_debug("sysview: error while removing seat '%s': %s",
735 seat->name, strerror(-error));
736 sysview_seat_free(seat);
740 int sysview_context_new(sysview_context **out,
745 _cleanup_(sysview_context_freep) sysview_context *c = NULL;
748 assert_return(out, -EINVAL);
749 assert_return(event, -EINVAL);
751 log_debug("sysview: new");
753 c = new0(sysview_context, 1);
757 c->event = sd_event_ref(event);
758 if (flags & SYSVIEW_CONTEXT_SCAN_LOGIND)
759 c->scan_logind = true;
760 if (flags & SYSVIEW_CONTEXT_SCAN_EVDEV)
761 c->scan_evdev = true;
762 if (flags & SYSVIEW_CONTEXT_SCAN_DRM)
766 c->sysbus = sd_bus_ref(sysbus);
767 } else if (c->scan_logind) {
768 r = sd_bus_open_system(&c->sysbus);
774 c->ud = udev_ref(ud);
775 } else if (c->scan_evdev || c->scan_drm) {
779 return errno > 0 ? -errno : -EFAULT;
782 c->seat_map = hashmap_new(&string_hash_ops);
786 c->session_map = hashmap_new(&string_hash_ops);
790 c->device_map = hashmap_new(&string_hash_ops);
799 sysview_context *sysview_context_free(sysview_context *c) {
803 log_debug("sysview: free");
805 sysview_context_stop(c);
807 assert(hashmap_size(c->device_map) == 0);
808 assert(hashmap_size(c->session_map) == 0);
809 assert(hashmap_size(c->seat_map) == 0);
811 hashmap_free(c->device_map);
812 hashmap_free(c->session_map);
813 hashmap_free(c->seat_map);
814 c->ud = udev_unref(c->ud);
815 c->sysbus = sd_bus_unref(c->sysbus);
816 c->event = sd_event_unref(c->event);
822 static int context_ud_prepare_monitor(sysview_context *c, struct udev_monitor *m) {
826 r = udev_monitor_filter_add_match_subsystem_devtype(m, "input", NULL);
832 r = udev_monitor_filter_add_match_subsystem_devtype(m, "drm", NULL);
840 static int context_ud_prepare_scan(sysview_context *c, struct udev_enumerate *e) {
844 r = udev_enumerate_add_match_subsystem(e, "input");
850 r = udev_enumerate_add_match_subsystem(e, "drm");
855 r = udev_enumerate_add_match_is_initialized(e);
862 static int context_ud_hotplug(sysview_context *c, struct udev_device *d) {
863 const char *syspath, *sysname, *subsystem, *action, *seatname;
864 sysview_device *device;
867 syspath = udev_device_get_syspath(d);
868 sysname = udev_device_get_sysname(d);
869 subsystem = udev_device_get_subsystem(d);
870 action = udev_device_get_action(d);
872 /* not interested in custom devices without syspath/etc */
873 if (!syspath || !sysname || !subsystem)
876 device = sysview_find_device(c, syspath);
878 if (streq_ptr(action, "remove")) {
882 return context_remove_device(c, device);
883 } else if (streq_ptr(action, "change")) {
887 return context_raise_device_change(c, device, d);
888 } else if (!action || streq_ptr(action, "add")) {
889 struct udev_device *p;
890 unsigned int type, t;
896 if (streq(subsystem, "input") && startswith(sysname, "event") && safe_atou(sysname + 5, &t) >= 0)
897 type = SYSVIEW_DEVICE_EVDEV;
898 else if (streq(subsystem, "drm") && startswith(sysname, "card"))
899 type = SYSVIEW_DEVICE_DRM;
903 if (type >= SYSVIEW_DEVICE_CNT)
909 seatname = udev_device_get_property_value(p, "ID_SEAT");
912 } while ((p = udev_device_get_parent(p)));
914 seat = sysview_find_seat(c, seatname ? : "seat0");
918 r = device_new_ud(&device, seat, type, d);
920 log_debug("sysview: cannot create device for udev-device '%s': %s",
921 syspath, strerror(-r));
925 return context_add_device(c, device);
931 static int context_ud_monitor_fn(sd_event_source *s,
935 sysview_context *c = userdata;
936 struct udev_device *d;
939 if (revents & EPOLLIN) {
940 while ((d = udev_monitor_receive_device(c->ud_monitor))) {
941 r = context_ud_hotplug(c, d);
942 udev_device_unref(d);
947 /* as long as EPOLLIN is signalled, read pending data */
951 if (revents & (EPOLLHUP | EPOLLERR)) {
952 log_debug("sysview: HUP on udev-monitor");
953 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
959 static int context_ud_start(sysview_context *c) {
966 c->ud_monitor = udev_monitor_new_from_netlink(c->ud, "udev");
968 return errno > 0 ? -errno : -EFAULT;
970 r = context_ud_prepare_monitor(c, c->ud_monitor);
974 r = udev_monitor_enable_receiving(c->ud_monitor);
978 fd = udev_monitor_get_fd(c->ud_monitor);
979 r = sd_event_add_io(c->event,
982 EPOLLHUP | EPOLLERR | EPOLLIN,
983 context_ud_monitor_fn,
991 static void context_ud_stop(sysview_context *c) {
992 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
993 c->ud_monitor = udev_monitor_unref(c->ud_monitor);
996 static int context_ud_scan(sysview_context *c) {
997 _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
998 struct udev_list_entry *entry;
999 struct udev_device *d;
1006 e = udev_enumerate_new(c->ud);
1008 return errno > 0 ? -errno : -EFAULT;
1010 r = context_ud_prepare_scan(c, e);
1014 r = udev_enumerate_scan_devices(e);
1018 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
1021 name = udev_list_entry_get_name(entry);
1024 d = udev_device_new_from_syspath(c->ud, name);
1026 r = errno > 0 ? -errno : -EFAULT;
1027 log_debug("sysview: cannot create udev-device for %s: %s",
1028 name, strerror(-r));
1032 r = context_ud_hotplug(c, d);
1033 udev_device_unref(d);
1041 static int context_ld_seat_new(sysview_context *c, sd_bus_message *signal) {
1042 const char *id, *path;
1045 r = sd_bus_message_read(signal, "so", &id, &path);
1047 log_debug("sysview: cannot parse SeatNew from logind: %s",
1052 return context_add_seat(c, id);
1055 static int context_ld_seat_removed(sysview_context *c, sd_bus_message *signal) {
1056 const char *id, *path;
1060 r = sd_bus_message_read(signal, "so", &id, &path);
1062 log_debug("sysview: cannot parse SeatRemoved from logind: %s",
1067 seat = sysview_find_seat(c, id);
1071 return context_remove_seat(c, seat);
1074 static int context_ld_session_new(sysview_context *c, sd_bus_message *signal) {
1075 _cleanup_free_ char *seatid = NULL, *username = NULL;
1076 const char *id, *path;
1081 r = sd_bus_message_read(signal, "so", &id, &path);
1083 log_debug("sysview: cannot parse SessionNew from logind: %s",
1089 * As the dbus message didn't contain enough information, we
1090 * read missing bits via sd-login. Note that this might race session
1091 * destruction, so we handle ENOENT properly.
1094 /* ENOENT is also returned for sessions without seats */
1095 r = sd_session_get_seat(id, &seatid);
1101 seat = sysview_find_seat(c, seatid);
1105 r = sd_session_get_uid(id, &uid);
1111 username = lookup_uid(uid);
1117 r = context_raise_session_filter(c, id, seatid, username, uid);
1120 log_debug("sysview: cannot filter new session '%s' on seat '%s': %s",
1121 id, seatid, strerror(-r));
1125 return context_add_session(c, seat, id);
1128 log_debug("sysview: failed retrieving information for new session '%s': %s",
1133 static int context_ld_session_removed(sysview_context *c, sd_bus_message *signal) {
1134 sysview_session *session;
1135 const char *id, *path;
1138 r = sd_bus_message_read(signal, "so", &id, &path);
1140 log_debug("sysview: cannot parse SessionRemoved from logind: %s",
1145 session = sysview_find_session(c, id);
1149 return context_remove_session(c, session);
1152 static int context_ld_manager_signal_fn(sd_bus *bus,
1153 sd_bus_message *signal,
1155 sd_bus_error *ret_error) {
1156 sysview_context *c = userdata;
1158 if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatNew"))
1159 return context_ld_seat_new(c, signal);
1160 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatRemoved"))
1161 return context_ld_seat_removed(c, signal);
1162 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionNew"))
1163 return context_ld_session_new(c, signal);
1164 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionRemoved"))
1165 return context_ld_session_removed(c, signal);
1170 static int context_ld_start(sysview_context *c) {
1173 if (!c->scan_logind)
1176 r = sd_bus_add_match(c->sysbus,
1177 &c->ld_slot_manager_signal,
1179 "sender='org.freedesktop.login1',"
1180 "interface='org.freedesktop.login1.Manager',"
1181 "path='/org/freedesktop/login1'",
1182 context_ld_manager_signal_fn,
1190 static void context_ld_stop(sysview_context *c) {
1191 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1192 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1193 c->ld_slot_manager_signal = sd_bus_slot_unref(c->ld_slot_manager_signal);
1196 static int context_ld_list_seats_fn(sd_bus *bus,
1197 sd_bus_message *reply,
1199 sd_bus_error *ret_error) {
1200 sysview_context *c = userdata;
1203 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1205 if (sd_bus_message_is_method_error(reply, NULL)) {
1206 const sd_bus_error *error = sd_bus_message_get_error(reply);
1208 log_debug("sysview: ListSeats on logind failed: %s: %s",
1209 error->name, error->message);
1210 return -sd_bus_error_get_errno(error);
1213 r = sd_bus_message_enter_container(reply, 'a', "(so)");
1217 while ((r = sd_bus_message_enter_container(reply, 'r', "so")) > 0) {
1218 const char *id, *path;
1220 r = sd_bus_message_read(reply, "so", &id, &path);
1224 r = context_add_seat(c, id);
1228 r = sd_bus_message_exit_container(reply);
1236 r = sd_bus_message_exit_container(reply);
1243 log_debug("sysview: erroneous ListSeats response from logind: %s",
1248 static int context_ld_list_sessions_fn(sd_bus *bus,
1249 sd_bus_message *reply,
1251 sd_bus_error *ret_error) {
1252 sysview_context *c = userdata;
1255 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1257 if (sd_bus_message_is_method_error(reply, NULL)) {
1258 const sd_bus_error *error = sd_bus_message_get_error(reply);
1260 log_debug("sysview: ListSessions on logind failed: %s: %s",
1261 error->name, error->message);
1262 return -sd_bus_error_get_errno(error);
1265 r = sd_bus_message_enter_container(reply, 'a', "(susso)");
1269 while ((r = sd_bus_message_enter_container(reply, 'r', "susso")) > 0) {
1270 const char *id, *username, *seatid, *path;
1274 r = sd_bus_message_read(reply,
1284 seat = sysview_find_seat(c, seatid);
1286 r = context_raise_session_filter(c, id, seatid, username, uid);
1288 log_debug("sysview: cannot filter listed session '%s' on seat '%s': %s",
1289 id, seatid, strerror(-r));
1292 r = context_add_session(c, seat, id);
1298 r = sd_bus_message_exit_container(reply);
1306 r = sd_bus_message_exit_container(reply);
1313 log_debug("sysview: erroneous ListSessions response from logind: %s",
1318 static int context_ld_scan(sysview_context *c) {
1319 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1322 if (!c->ld_slot_manager_signal)
1325 /* request seat list */
1327 r = sd_bus_message_new_method_call(c->sysbus,
1329 "org.freedesktop.login1",
1330 "/org/freedesktop/login1",
1331 "org.freedesktop.login1.Manager",
1336 r = sd_bus_call_async(c->sysbus,
1337 &c->ld_slot_list_seats,
1339 context_ld_list_seats_fn,
1345 /* request session list */
1347 m = sd_bus_message_unref(m);
1348 r = sd_bus_message_new_method_call(c->sysbus,
1350 "org.freedesktop.login1",
1351 "/org/freedesktop/login1",
1352 "org.freedesktop.login1.Manager",
1357 r = sd_bus_call_async(c->sysbus,
1358 &c->ld_slot_list_sessions,
1360 context_ld_list_sessions_fn,
1369 bool sysview_context_is_running(sysview_context *c) {
1370 return c && c->running;
1373 int sysview_context_start(sysview_context *c, sysview_event_fn event_fn, void *userdata) {
1376 assert_return(c, -EINVAL);
1377 assert_return(event_fn, -EINVAL);
1382 log_debug("sysview: start");
1385 c->event_fn = event_fn;
1386 c->userdata = userdata;
1388 r = context_ld_start(c);
1392 r = context_ud_start(c);
1396 r = sysview_context_rescan(c);
1403 sysview_context_stop(c);
1407 void sysview_context_stop(sysview_context *c) {
1408 sysview_session *session;
1409 sysview_device *device;
1417 log_debug("sysview: stop");
1423 c->scan_src = sd_event_source_unref(c->scan_src);
1428 * Event-callbacks are already cleared, hence we can safely ignore
1429 * return codes of the context_remove_*() helpers. They cannot be
1430 * originated from user-callbacks, so we already handled them.
1433 while ((device = hashmap_first(c->device_map)))
1434 context_remove_device(c, device);
1436 while ((session = hashmap_first(c->session_map)))
1437 context_remove_session(c, session);
1439 while ((seat = hashmap_first(c->seat_map)))
1440 context_remove_seat(c, seat);
1443 static int context_scan_fn(sd_event_source *s, void *userdata) {
1444 sysview_context *c = userdata;
1450 r = context_ld_scan(c);
1452 log_debug("sysview: logind scan failed: %s", strerror(-r));
1457 /* skip device scans if no sessions are available */
1458 if (hashmap_size(c->session_map) > 0) {
1459 r = context_ud_scan(c);
1461 log_debug("sysview: udev scan failed: %s", strerror(-r));
1465 HASHMAP_FOREACH(seat, c->seat_map, i)
1466 seat->scanned = true;
1474 int sysview_context_rescan(sysview_context *c) {
1481 return sd_event_source_set_enabled(c->scan_src, SD_EVENT_ONESHOT);
1483 return sd_event_add_defer(c->event, &c->scan_src, context_scan_fn, c);