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_session_refresh(sysview_context *c, sysview_session *session, sysview_device *device, struct udev_device *ud) {
521 sysview_event event = {
522 .type = SYSVIEW_EVENT_SESSION_REFRESH,
530 return context_raise(c, &event, 0);
533 static int context_add_device(sysview_context *c, sysview_device *device) {
534 sysview_session *session;
541 log_debug("sysview: add device '%s' on seat '%s'",
542 device->name, device->seat->name);
544 HASHMAP_FOREACH(session, device->seat->session_map, i) {
545 if (!session->public)
548 r = context_raise_session_attach(c, session, device);
554 log_debug("sysview: error while adding device '%s': %s",
555 device->name, strerror(-r));
559 static int context_remove_device(sysview_context *c, sysview_device *device) {
560 sysview_session *session;
567 log_debug("sysview: remove device '%s'", device->name);
569 HASHMAP_FOREACH(session, device->seat->session_map, i) {
570 if (!session->public)
573 r = context_raise_session_detach(c, session, device);
579 log_debug("sysview: error while removing device '%s': %s",
580 device->name, strerror(-r));
581 sysview_device_free(device);
585 static int context_change_device(sysview_context *c, sysview_device *device, struct udev_device *ud) {
586 sysview_session *session;
593 log_debug("sysview: change device '%s'", device->name);
595 HASHMAP_FOREACH(session, device->seat->session_map, i) {
596 if (!session->public)
599 r = context_raise_session_refresh(c, session, device, ud);
605 log_debug("sysview: error while changing device '%s': %s",
606 device->name, strerror(-r));
610 static int context_add_session(sysview_context *c, sysview_seat *seat, const char *id) {
611 sysview_session *session;
612 sysview_device *device;
620 session = sysview_find_session(c, id);
624 log_debug("sysview: add session '%s' on seat '%s'", id, seat->name);
626 r = sysview_session_new(&session, seat, id);
630 if (!seat->scanned) {
631 r = sysview_context_rescan(c);
637 session->public = true;
638 r = context_raise_session_add(c, session);
640 session->public = false;
644 HASHMAP_FOREACH(device, seat->device_map, i) {
645 r = context_raise_session_attach(c, session, device);
659 log_debug("sysview: error while adding session '%s': %s",
664 static int context_remove_session(sysview_context *c, sysview_session *session) {
665 sysview_device *device;
672 log_debug("sysview: remove session '%s'", session->name);
674 if (session->public) {
675 HASHMAP_FOREACH(device, session->seat->device_map, i) {
676 r = context_raise_session_detach(c, session, device);
681 session->public = false;
682 r = context_raise_session_remove(c, session);
687 if (!session->custom)
688 sysview_session_release_control(session);
691 log_debug("sysview: error while removing session '%s': %s",
692 session->name, strerror(-error));
693 sysview_session_free(session);
697 static int context_add_seat(sysview_context *c, const char *id) {
704 seat = sysview_find_seat(c, id);
708 log_debug("sysview: add seat '%s'", id);
710 r = sysview_seat_new(&seat, c, id);
715 r = context_raise_seat_add(c, seat);
717 seat->public = false;
725 log_debug("sysview: error while adding seat '%s': %s",
730 static int context_remove_seat(sysview_context *c, sysview_seat *seat) {
731 sysview_session *session;
732 sysview_device *device;
738 log_debug("sysview: remove seat '%s'", seat->name);
740 while ((device = hashmap_first(seat->device_map))) {
741 r = context_remove_device(c, device);
746 while ((session = hashmap_first(seat->session_map))) {
747 r = context_remove_session(c, session);
753 seat->public = false;
754 r = context_raise_seat_remove(c, seat);
760 log_debug("sysview: error while removing seat '%s': %s",
761 seat->name, strerror(-error));
762 sysview_seat_free(seat);
766 int sysview_context_new(sysview_context **out,
771 _cleanup_(sysview_context_freep) sysview_context *c = NULL;
774 assert_return(out, -EINVAL);
775 assert_return(event, -EINVAL);
777 log_debug("sysview: new");
779 c = new0(sysview_context, 1);
783 c->event = sd_event_ref(event);
784 if (flags & SYSVIEW_CONTEXT_SCAN_LOGIND)
785 c->scan_logind = true;
786 if (flags & SYSVIEW_CONTEXT_SCAN_EVDEV)
787 c->scan_evdev = true;
788 if (flags & SYSVIEW_CONTEXT_SCAN_DRM)
792 c->sysbus = sd_bus_ref(sysbus);
793 } else if (c->scan_logind) {
794 r = sd_bus_open_system(&c->sysbus);
800 c->ud = udev_ref(ud);
801 } else if (c->scan_evdev || c->scan_drm) {
805 return errno > 0 ? -errno : -EFAULT;
808 c->seat_map = hashmap_new(&string_hash_ops);
812 c->session_map = hashmap_new(&string_hash_ops);
816 c->device_map = hashmap_new(&string_hash_ops);
825 sysview_context *sysview_context_free(sysview_context *c) {
829 log_debug("sysview: free");
831 sysview_context_stop(c);
833 assert(hashmap_size(c->device_map) == 0);
834 assert(hashmap_size(c->session_map) == 0);
835 assert(hashmap_size(c->seat_map) == 0);
837 hashmap_free(c->device_map);
838 hashmap_free(c->session_map);
839 hashmap_free(c->seat_map);
840 c->ud = udev_unref(c->ud);
841 c->sysbus = sd_bus_unref(c->sysbus);
842 c->event = sd_event_unref(c->event);
848 static int context_ud_prepare_monitor(sysview_context *c, struct udev_monitor *m) {
852 r = udev_monitor_filter_add_match_subsystem_devtype(m, "input", NULL);
858 r = udev_monitor_filter_add_match_subsystem_devtype(m, "drm", NULL);
866 static int context_ud_prepare_scan(sysview_context *c, struct udev_enumerate *e) {
870 r = udev_enumerate_add_match_subsystem(e, "input");
876 r = udev_enumerate_add_match_subsystem(e, "drm");
881 r = udev_enumerate_add_match_is_initialized(e);
888 static int context_ud_hotplug(sysview_context *c, struct udev_device *d) {
889 const char *syspath, *sysname, *subsystem, *action, *seatname;
890 sysview_device *device;
893 syspath = udev_device_get_syspath(d);
894 sysname = udev_device_get_sysname(d);
895 subsystem = udev_device_get_subsystem(d);
896 action = udev_device_get_action(d);
898 /* not interested in custom devices without syspath/etc */
899 if (!syspath || !sysname || !subsystem)
902 device = sysview_find_device(c, syspath);
904 if (streq_ptr(action, "remove")) {
908 return context_remove_device(c, device);
909 } else if (streq_ptr(action, "change")) {
913 return context_change_device(c, device, d);
914 } else if (!action || streq_ptr(action, "add")) {
915 struct udev_device *p;
916 unsigned int type, t;
922 if (streq(subsystem, "input") && startswith(sysname, "event") && safe_atou(sysname + 5, &t) >= 0)
923 type = SYSVIEW_DEVICE_EVDEV;
924 else if (streq(subsystem, "drm") && startswith(sysname, "card"))
925 type = SYSVIEW_DEVICE_DRM;
929 if (type >= SYSVIEW_DEVICE_CNT)
935 seatname = udev_device_get_property_value(p, "ID_SEAT");
938 } while ((p = udev_device_get_parent(p)));
940 seat = sysview_find_seat(c, seatname ? : "seat0");
944 r = device_new_ud(&device, seat, type, d);
946 log_debug("sysview: cannot create device for udev-device '%s': %s",
947 syspath, strerror(-r));
951 return context_add_device(c, device);
957 static int context_ud_monitor_fn(sd_event_source *s,
961 sysview_context *c = userdata;
962 struct udev_device *d;
965 if (revents & EPOLLIN) {
966 while ((d = udev_monitor_receive_device(c->ud_monitor))) {
967 r = context_ud_hotplug(c, d);
968 udev_device_unref(d);
973 /* as long as EPOLLIN is signalled, read pending data */
977 if (revents & (EPOLLHUP | EPOLLERR)) {
978 log_debug("sysview: HUP on udev-monitor");
979 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
985 static int context_ud_start(sysview_context *c) {
992 c->ud_monitor = udev_monitor_new_from_netlink(c->ud, "udev");
994 return errno > 0 ? -errno : -EFAULT;
996 r = context_ud_prepare_monitor(c, c->ud_monitor);
1000 r = udev_monitor_enable_receiving(c->ud_monitor);
1004 fd = udev_monitor_get_fd(c->ud_monitor);
1005 r = sd_event_add_io(c->event,
1008 EPOLLHUP | EPOLLERR | EPOLLIN,
1009 context_ud_monitor_fn,
1017 static void context_ud_stop(sysview_context *c) {
1018 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
1019 c->ud_monitor = udev_monitor_unref(c->ud_monitor);
1022 static int context_ud_scan(sysview_context *c) {
1023 _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
1024 struct udev_list_entry *entry;
1025 struct udev_device *d;
1032 e = udev_enumerate_new(c->ud);
1034 return errno > 0 ? -errno : -EFAULT;
1036 r = context_ud_prepare_scan(c, e);
1040 r = udev_enumerate_scan_devices(e);
1044 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
1047 name = udev_list_entry_get_name(entry);
1050 d = udev_device_new_from_syspath(c->ud, name);
1052 r = errno > 0 ? -errno : -EFAULT;
1053 log_debug("sysview: cannot create udev-device for %s: %s",
1054 name, strerror(-r));
1058 r = context_ud_hotplug(c, d);
1059 udev_device_unref(d);
1067 static int context_ld_seat_new(sysview_context *c, sd_bus_message *signal) {
1068 const char *id, *path;
1071 r = sd_bus_message_read(signal, "so", &id, &path);
1073 log_debug("sysview: cannot parse SeatNew from logind: %s",
1078 return context_add_seat(c, id);
1081 static int context_ld_seat_removed(sysview_context *c, sd_bus_message *signal) {
1082 const char *id, *path;
1086 r = sd_bus_message_read(signal, "so", &id, &path);
1088 log_debug("sysview: cannot parse SeatRemoved from logind: %s",
1093 seat = sysview_find_seat(c, id);
1097 return context_remove_seat(c, seat);
1100 static int context_ld_session_new(sysview_context *c, sd_bus_message *signal) {
1101 _cleanup_free_ char *seatid = NULL, *username = NULL;
1102 const char *id, *path;
1107 r = sd_bus_message_read(signal, "so", &id, &path);
1109 log_debug("sysview: cannot parse SessionNew from logind: %s",
1115 * As the dbus message didn't contain enough information, we
1116 * read missing bits via sd-login. Note that this might race session
1117 * destruction, so we handle ENOENT properly.
1120 /* ENOENT is also returned for sessions without seats */
1121 r = sd_session_get_seat(id, &seatid);
1127 seat = sysview_find_seat(c, seatid);
1131 r = sd_session_get_uid(id, &uid);
1137 username = lookup_uid(uid);
1143 r = context_raise_session_filter(c, id, seatid, username, uid);
1146 log_debug("sysview: cannot filter new session '%s' on seat '%s': %s",
1147 id, seatid, strerror(-r));
1151 return context_add_session(c, seat, id);
1154 log_debug("sysview: failed retrieving information for new session '%s': %s",
1159 static int context_ld_session_removed(sysview_context *c, sd_bus_message *signal) {
1160 sysview_session *session;
1161 const char *id, *path;
1164 r = sd_bus_message_read(signal, "so", &id, &path);
1166 log_debug("sysview: cannot parse SessionRemoved from logind: %s",
1171 session = sysview_find_session(c, id);
1175 return context_remove_session(c, session);
1178 static int context_ld_manager_signal_fn(sd_bus *bus,
1179 sd_bus_message *signal,
1181 sd_bus_error *ret_error) {
1182 sysview_context *c = userdata;
1184 if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatNew"))
1185 return context_ld_seat_new(c, signal);
1186 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatRemoved"))
1187 return context_ld_seat_removed(c, signal);
1188 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionNew"))
1189 return context_ld_session_new(c, signal);
1190 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionRemoved"))
1191 return context_ld_session_removed(c, signal);
1196 static int context_ld_start(sysview_context *c) {
1199 if (!c->scan_logind)
1202 r = sd_bus_add_match(c->sysbus,
1203 &c->ld_slot_manager_signal,
1205 "sender='org.freedesktop.login1',"
1206 "interface='org.freedesktop.login1.Manager',"
1207 "path='/org/freedesktop/login1'",
1208 context_ld_manager_signal_fn,
1216 static void context_ld_stop(sysview_context *c) {
1217 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1218 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1219 c->ld_slot_manager_signal = sd_bus_slot_unref(c->ld_slot_manager_signal);
1222 static int context_ld_list_seats_fn(sd_bus *bus,
1223 sd_bus_message *reply,
1225 sd_bus_error *ret_error) {
1226 sysview_context *c = userdata;
1229 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1231 if (sd_bus_message_is_method_error(reply, NULL)) {
1232 const sd_bus_error *error = sd_bus_message_get_error(reply);
1234 log_debug("sysview: ListSeats on logind failed: %s: %s",
1235 error->name, error->message);
1236 return -sd_bus_error_get_errno(error);
1239 r = sd_bus_message_enter_container(reply, 'a', "(so)");
1243 while ((r = sd_bus_message_enter_container(reply, 'r', "so")) > 0) {
1244 const char *id, *path;
1246 r = sd_bus_message_read(reply, "so", &id, &path);
1250 r = context_add_seat(c, id);
1254 r = sd_bus_message_exit_container(reply);
1262 r = sd_bus_message_exit_container(reply);
1269 log_debug("sysview: erroneous ListSeats response from logind: %s",
1274 static int context_ld_list_sessions_fn(sd_bus *bus,
1275 sd_bus_message *reply,
1277 sd_bus_error *ret_error) {
1278 sysview_context *c = userdata;
1281 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1283 if (sd_bus_message_is_method_error(reply, NULL)) {
1284 const sd_bus_error *error = sd_bus_message_get_error(reply);
1286 log_debug("sysview: ListSessions on logind failed: %s: %s",
1287 error->name, error->message);
1288 return -sd_bus_error_get_errno(error);
1291 r = sd_bus_message_enter_container(reply, 'a', "(susso)");
1295 while ((r = sd_bus_message_enter_container(reply, 'r', "susso")) > 0) {
1296 const char *id, *username, *seatid, *path;
1300 r = sd_bus_message_read(reply,
1310 seat = sysview_find_seat(c, seatid);
1312 r = context_raise_session_filter(c, id, seatid, username, uid);
1314 log_debug("sysview: cannot filter listed session '%s' on seat '%s': %s",
1315 id, seatid, strerror(-r));
1318 r = context_add_session(c, seat, id);
1324 r = sd_bus_message_exit_container(reply);
1332 r = sd_bus_message_exit_container(reply);
1339 log_debug("sysview: erroneous ListSessions response from logind: %s",
1344 static int context_ld_scan(sysview_context *c) {
1345 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1348 if (!c->ld_slot_manager_signal)
1351 /* request seat list */
1353 r = sd_bus_message_new_method_call(c->sysbus,
1355 "org.freedesktop.login1",
1356 "/org/freedesktop/login1",
1357 "org.freedesktop.login1.Manager",
1362 r = sd_bus_call_async(c->sysbus,
1363 &c->ld_slot_list_seats,
1365 context_ld_list_seats_fn,
1371 /* request session list */
1373 m = sd_bus_message_unref(m);
1374 r = sd_bus_message_new_method_call(c->sysbus,
1376 "org.freedesktop.login1",
1377 "/org/freedesktop/login1",
1378 "org.freedesktop.login1.Manager",
1383 r = sd_bus_call_async(c->sysbus,
1384 &c->ld_slot_list_sessions,
1386 context_ld_list_sessions_fn,
1395 bool sysview_context_is_running(sysview_context *c) {
1396 return c && c->running;
1399 int sysview_context_start(sysview_context *c, sysview_event_fn event_fn, void *userdata) {
1402 assert_return(c, -EINVAL);
1403 assert_return(event_fn, -EINVAL);
1408 log_debug("sysview: start");
1411 c->event_fn = event_fn;
1412 c->userdata = userdata;
1414 r = context_ld_start(c);
1418 r = context_ud_start(c);
1422 r = sysview_context_rescan(c);
1429 sysview_context_stop(c);
1433 void sysview_context_stop(sysview_context *c) {
1434 sysview_session *session;
1435 sysview_device *device;
1443 log_debug("sysview: stop");
1449 c->scan_src = sd_event_source_unref(c->scan_src);
1454 * Event-callbacks are already cleared, hence we can safely ignore
1455 * return codes of the context_remove_*() helpers. They cannot be
1456 * originated from user-callbacks, so we already handled them.
1459 while ((device = hashmap_first(c->device_map)))
1460 context_remove_device(c, device);
1462 while ((session = hashmap_first(c->session_map)))
1463 context_remove_session(c, session);
1465 while ((seat = hashmap_first(c->seat_map)))
1466 context_remove_seat(c, seat);
1469 static int context_scan_fn(sd_event_source *s, void *userdata) {
1470 sysview_context *c = userdata;
1476 r = context_ld_scan(c);
1478 log_debug("sysview: logind scan failed: %s", strerror(-r));
1483 /* skip device scans if no sessions are available */
1484 if (hashmap_size(c->session_map) > 0) {
1485 r = context_ud_scan(c);
1487 log_debug("sysview: udev scan failed: %s", strerror(-r));
1491 HASHMAP_FOREACH(seat, c->seat_map, i)
1492 seat->scanned = true;
1500 int sysview_context_rescan(sysview_context *c) {
1507 return sd_event_source_set_enabled(c->scan_src, SD_EVENT_ONESHOT);
1509 return sd_event_add_defer(c->event, &c->scan_src, context_scan_fn, c);