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 const char *sysview_device_get_name(sysview_device *device) {
108 assert_return(device, NULL);
113 unsigned int sysview_device_get_type(sysview_device *device) {
114 assert_return(device, (unsigned)-1);
119 struct udev_device *sysview_device_get_ud(sysview_device *device) {
120 assert_return(device, NULL);
122 switch (device->type) {
123 case SYSVIEW_DEVICE_EVDEV:
124 return device->evdev.ud;
125 case SYSVIEW_DEVICE_DRM:
126 return device->drm.ud;
128 assert_return(0, NULL);
132 static int device_new_ud(sysview_device **out, sysview_seat *seat, unsigned int type, struct udev_device *ud) {
133 _cleanup_(sysview_device_freep) sysview_device *device = NULL;
136 assert_return(seat, -EINVAL);
137 assert_return(ud, -EINVAL);
139 r = sysview_device_new(&device, seat, udev_device_get_syspath(ud));
146 case SYSVIEW_DEVICE_EVDEV:
147 device->evdev.ud = udev_device_ref(ud);
149 case SYSVIEW_DEVICE_DRM:
150 device->drm.ud = udev_device_ref(ud);
153 assert_not_reached("sysview: invalid udev-device type");
166 sysview_session *sysview_find_session(sysview_context *c, const char *name) {
167 assert_return(c, NULL);
168 assert_return(name, NULL);
170 return hashmap_get(c->session_map, name);
173 int sysview_session_new(sysview_session **out, sysview_seat *seat, const char *name) {
174 _cleanup_(sysview_session_freep) sysview_session *session = NULL;
177 assert_return(seat, -EINVAL);
179 session = new0(sysview_session, 1);
183 session->seat = seat;
187 * If a name is given, we require it to be a logind session
188 * name. The session will be put in managed mode and we use
189 * logind to request controller access.
192 session->name = strdup(name);
196 r = sd_bus_path_encode("/org/freedesktop/login1/session",
197 session->name, &session->path);
201 session->custom = false;;
204 * No session name was given. We assume this is an unmanaged
205 * session controlled by the application. We don't use logind
206 * at all and leave session management to the application. The
207 * name of the session-object is set to a unique random string
208 * that does not clash with the logind namespace.
211 r = asprintf(&session->name, "@custom%" PRIu64,
212 ++seat->context->custom_sid);
216 session->custom = true;
219 r = hashmap_put(seat->context->session_map, session->name, session);
223 r = hashmap_put(seat->session_map, session->name, session);
233 sysview_session *sysview_session_free(sysview_session *session) {
237 assert(!session->public);
238 assert(!session->wants_control);
241 hashmap_remove_value(session->seat->session_map, session->name, session);
242 hashmap_remove_value(session->seat->context->session_map, session->name, session);
252 void sysview_session_set_userdata(sysview_session *session, void *userdata) {
255 session->userdata = userdata;
258 void *sysview_session_get_userdata(sysview_session *session) {
259 assert_return(session, NULL);
261 return session->userdata;
264 const char *sysview_session_get_name(sysview_session *session) {
265 assert_return(session, NULL);
267 return session->name;
270 static int session_take_control_fn(sd_bus *bus,
271 sd_bus_message *reply,
273 sd_bus_error *ret_error) {
274 sysview_session *session = userdata;
277 session->slot_take_control = sd_bus_slot_unref(session->slot_take_control);
279 if (sd_bus_message_is_method_error(reply, NULL)) {
280 const sd_bus_error *e = sd_bus_message_get_error(reply);
282 log_debug("sysview: %s: TakeControl failed: %s: %s",
283 session->name, e->name, e->message);
284 error = -sd_bus_error_get_errno(e);
286 session->has_control = true;
290 return context_raise_session_control(session->seat->context, session, error);
293 int sysview_session_take_control(sysview_session *session) {
294 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
297 assert_return(session, -EINVAL);
298 assert_return(!session->custom, -EINVAL);
300 if (session->wants_control)
303 r = sd_bus_message_new_method_call(session->seat->context->sysbus,
305 "org.freedesktop.login1",
307 "org.freedesktop.login1.Session",
312 r = sd_bus_message_append(m, "b", 0);
316 r = sd_bus_call_async(session->seat->context->sysbus,
317 &session->slot_take_control,
319 session_take_control_fn,
325 session->wants_control = true;
329 void sysview_session_release_control(sysview_session *session) {
330 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
334 assert(!session->custom);
336 if (!session->wants_control)
339 session->wants_control = false;
341 if (!session->has_control && !session->slot_take_control)
344 session->has_control = false;
345 session->slot_take_control = sd_bus_slot_unref(session->slot_take_control);
347 r = sd_bus_message_new_method_call(session->seat->context->sysbus,
349 "org.freedesktop.login1",
351 "org.freedesktop.login1.Session",
354 r = sd_bus_send(session->seat->context->sysbus, m, NULL);
356 if (r < 0 && r != -ENOTCONN)
357 log_debug("sysview: %s: cannot send ReleaseControl: %s",
358 session->name, strerror(-r));
365 sysview_seat *sysview_find_seat(sysview_context *c, const char *name) {
366 assert_return(c, NULL);
367 assert_return(name, NULL);
369 return hashmap_get(c->seat_map, name);
372 int sysview_seat_new(sysview_seat **out, sysview_context *c, const char *name) {
373 _cleanup_(sysview_seat_freep) sysview_seat *seat = NULL;
376 assert_return(c, -EINVAL);
377 assert_return(name, -EINVAL);
379 seat = new0(sysview_seat, 1);
385 seat->name = strdup(name);
389 seat->session_map = hashmap_new(&string_hash_ops);
390 if (!seat->session_map)
393 seat->device_map = hashmap_new(&string_hash_ops);
394 if (!seat->device_map)
397 r = hashmap_put(c->seat_map, seat->name, seat);
407 sysview_seat *sysview_seat_free(sysview_seat *seat) {
411 assert(!seat->public);
412 assert(hashmap_size(seat->device_map) == 0);
413 assert(hashmap_size(seat->session_map) == 0);
416 hashmap_remove_value(seat->context->seat_map, seat->name, seat);
418 hashmap_free(seat->device_map);
419 hashmap_free(seat->session_map);
426 const char *sysview_seat_get_name(sysview_seat *seat) {
427 assert_return(seat, NULL);
436 static int context_raise(sysview_context *c, sysview_event *event, int def) {
437 return c->running ? c->event_fn(c, c->userdata, event) : def;
440 static int context_raise_seat_add(sysview_context *c, sysview_seat *seat) {
441 sysview_event event = {
442 .type = SYSVIEW_EVENT_SEAT_ADD,
448 return context_raise(c, &event, 0);
451 static int context_raise_seat_remove(sysview_context *c, sysview_seat *seat) {
452 sysview_event event = {
453 .type = SYSVIEW_EVENT_SEAT_REMOVE,
459 return context_raise(c, &event, 0);
462 static int context_raise_session_filter(sysview_context *c,
465 const char *username,
467 sysview_event event = {
468 .type = SYSVIEW_EVENT_SESSION_FILTER,
472 .username = username,
477 return context_raise(c, &event, 1);
480 static int context_raise_session_add(sysview_context *c, sysview_session *session) {
481 sysview_event event = {
482 .type = SYSVIEW_EVENT_SESSION_ADD,
488 return context_raise(c, &event, 0);
491 static int context_raise_session_remove(sysview_context *c, sysview_session *session) {
492 sysview_event event = {
493 .type = SYSVIEW_EVENT_SESSION_REMOVE,
499 return context_raise(c, &event, 0);
502 static int context_raise_session_control(sysview_context *c, sysview_session *session, int error) {
503 sysview_event event = {
504 .type = SYSVIEW_EVENT_SESSION_CONTROL,
511 return context_raise(c, &event, 0);
514 static int context_raise_session_attach(sysview_context *c, sysview_session *session, sysview_device *device) {
515 sysview_event event = {
516 .type = SYSVIEW_EVENT_SESSION_ATTACH,
523 return context_raise(c, &event, 0);
526 static int context_raise_session_detach(sysview_context *c, sysview_session *session, sysview_device *device) {
527 sysview_event event = {
528 .type = SYSVIEW_EVENT_SESSION_DETACH,
535 return context_raise(c, &event, 0);
538 static int context_raise_session_refresh(sysview_context *c, sysview_session *session, sysview_device *device, struct udev_device *ud) {
539 sysview_event event = {
540 .type = SYSVIEW_EVENT_SESSION_REFRESH,
548 return context_raise(c, &event, 0);
551 static int context_add_device(sysview_context *c, sysview_device *device) {
552 sysview_session *session;
559 log_debug("sysview: add device '%s' on seat '%s'",
560 device->name, device->seat->name);
562 HASHMAP_FOREACH(session, device->seat->session_map, i) {
563 if (!session->public)
566 r = context_raise_session_attach(c, session, device);
572 log_debug("sysview: error while adding device '%s': %s",
573 device->name, strerror(-r));
577 static int context_remove_device(sysview_context *c, sysview_device *device) {
578 sysview_session *session;
585 log_debug("sysview: remove device '%s'", device->name);
587 HASHMAP_FOREACH(session, device->seat->session_map, i) {
588 if (!session->public)
591 r = context_raise_session_detach(c, session, device);
597 log_debug("sysview: error while removing device '%s': %s",
598 device->name, strerror(-r));
599 sysview_device_free(device);
603 static int context_change_device(sysview_context *c, sysview_device *device, struct udev_device *ud) {
604 sysview_session *session;
611 log_debug("sysview: change device '%s'", device->name);
613 HASHMAP_FOREACH(session, device->seat->session_map, i) {
614 if (!session->public)
617 r = context_raise_session_refresh(c, session, device, ud);
623 log_debug("sysview: error while changing device '%s': %s",
624 device->name, strerror(-r));
628 static int context_add_session(sysview_context *c, sysview_seat *seat, const char *id) {
629 sysview_session *session;
630 sysview_device *device;
638 session = sysview_find_session(c, id);
642 log_debug("sysview: add session '%s' on seat '%s'", id, seat->name);
644 r = sysview_session_new(&session, seat, id);
648 if (!seat->scanned) {
649 r = sysview_context_rescan(c);
655 session->public = true;
656 r = context_raise_session_add(c, session);
658 session->public = false;
662 HASHMAP_FOREACH(device, seat->device_map, i) {
663 r = context_raise_session_attach(c, session, device);
677 log_debug("sysview: error while adding session '%s': %s",
682 static int context_remove_session(sysview_context *c, sysview_session *session) {
683 sysview_device *device;
690 log_debug("sysview: remove session '%s'", session->name);
692 if (session->public) {
693 HASHMAP_FOREACH(device, session->seat->device_map, i) {
694 r = context_raise_session_detach(c, session, device);
699 session->public = false;
700 r = context_raise_session_remove(c, session);
705 if (!session->custom)
706 sysview_session_release_control(session);
709 log_debug("sysview: error while removing session '%s': %s",
710 session->name, strerror(-error));
711 sysview_session_free(session);
715 static int context_add_seat(sysview_context *c, const char *id) {
722 seat = sysview_find_seat(c, id);
726 log_debug("sysview: add seat '%s'", id);
728 r = sysview_seat_new(&seat, c, id);
733 r = context_raise_seat_add(c, seat);
735 seat->public = false;
743 log_debug("sysview: error while adding seat '%s': %s",
748 static int context_remove_seat(sysview_context *c, sysview_seat *seat) {
749 sysview_session *session;
750 sysview_device *device;
756 log_debug("sysview: remove seat '%s'", seat->name);
758 while ((device = hashmap_first(seat->device_map))) {
759 r = context_remove_device(c, device);
764 while ((session = hashmap_first(seat->session_map))) {
765 r = context_remove_session(c, session);
771 seat->public = false;
772 r = context_raise_seat_remove(c, seat);
778 log_debug("sysview: error while removing seat '%s': %s",
779 seat->name, strerror(-error));
780 sysview_seat_free(seat);
784 int sysview_context_new(sysview_context **out,
789 _cleanup_(sysview_context_freep) sysview_context *c = NULL;
792 assert_return(out, -EINVAL);
793 assert_return(event, -EINVAL);
795 log_debug("sysview: new");
797 c = new0(sysview_context, 1);
801 c->event = sd_event_ref(event);
802 if (flags & SYSVIEW_CONTEXT_SCAN_LOGIND)
803 c->scan_logind = true;
804 if (flags & SYSVIEW_CONTEXT_SCAN_EVDEV)
805 c->scan_evdev = true;
806 if (flags & SYSVIEW_CONTEXT_SCAN_DRM)
810 c->sysbus = sd_bus_ref(sysbus);
811 } else if (c->scan_logind) {
812 r = sd_bus_open_system(&c->sysbus);
818 c->ud = udev_ref(ud);
819 } else if (c->scan_evdev || c->scan_drm) {
823 return errno > 0 ? -errno : -EFAULT;
826 c->seat_map = hashmap_new(&string_hash_ops);
830 c->session_map = hashmap_new(&string_hash_ops);
834 c->device_map = hashmap_new(&string_hash_ops);
843 sysview_context *sysview_context_free(sysview_context *c) {
847 log_debug("sysview: free");
849 sysview_context_stop(c);
851 assert(hashmap_size(c->device_map) == 0);
852 assert(hashmap_size(c->session_map) == 0);
853 assert(hashmap_size(c->seat_map) == 0);
855 hashmap_free(c->device_map);
856 hashmap_free(c->session_map);
857 hashmap_free(c->seat_map);
858 c->ud = udev_unref(c->ud);
859 c->sysbus = sd_bus_unref(c->sysbus);
860 c->event = sd_event_unref(c->event);
866 static int context_ud_prepare_monitor(sysview_context *c, struct udev_monitor *m) {
870 r = udev_monitor_filter_add_match_subsystem_devtype(m, "input", NULL);
876 r = udev_monitor_filter_add_match_subsystem_devtype(m, "drm", NULL);
884 static int context_ud_prepare_scan(sysview_context *c, struct udev_enumerate *e) {
888 r = udev_enumerate_add_match_subsystem(e, "input");
894 r = udev_enumerate_add_match_subsystem(e, "drm");
899 r = udev_enumerate_add_match_is_initialized(e);
906 static int context_ud_hotplug(sysview_context *c, struct udev_device *d) {
907 const char *syspath, *sysname, *subsystem, *action, *seatname;
908 sysview_device *device;
911 syspath = udev_device_get_syspath(d);
912 sysname = udev_device_get_sysname(d);
913 subsystem = udev_device_get_subsystem(d);
914 action = udev_device_get_action(d);
916 /* not interested in custom devices without syspath/etc */
917 if (!syspath || !sysname || !subsystem)
920 device = sysview_find_device(c, syspath);
922 if (streq_ptr(action, "remove")) {
926 return context_remove_device(c, device);
927 } else if (streq_ptr(action, "change")) {
931 return context_change_device(c, device, d);
932 } else if (!action || streq_ptr(action, "add")) {
933 struct udev_device *p;
934 unsigned int type, t;
940 if (streq(subsystem, "input") && startswith(sysname, "event") && safe_atou(sysname + 5, &t) >= 0)
941 type = SYSVIEW_DEVICE_EVDEV;
942 else if (streq(subsystem, "drm") && startswith(sysname, "card"))
943 type = SYSVIEW_DEVICE_DRM;
947 if (type >= SYSVIEW_DEVICE_CNT)
953 seatname = udev_device_get_property_value(p, "ID_SEAT");
956 } while ((p = udev_device_get_parent(p)));
958 seat = sysview_find_seat(c, seatname ? : "seat0");
962 r = device_new_ud(&device, seat, type, d);
964 log_debug("sysview: cannot create device for udev-device '%s': %s",
965 syspath, strerror(-r));
969 return context_add_device(c, device);
975 static int context_ud_monitor_fn(sd_event_source *s,
979 sysview_context *c = userdata;
980 struct udev_device *d;
983 if (revents & EPOLLIN) {
984 while ((d = udev_monitor_receive_device(c->ud_monitor))) {
985 r = context_ud_hotplug(c, d);
986 udev_device_unref(d);
991 /* as long as EPOLLIN is signalled, read pending data */
995 if (revents & (EPOLLHUP | EPOLLERR)) {
996 log_debug("sysview: HUP on udev-monitor");
997 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
1003 static int context_ud_start(sysview_context *c) {
1010 c->ud_monitor = udev_monitor_new_from_netlink(c->ud, "udev");
1012 return errno > 0 ? -errno : -EFAULT;
1014 r = context_ud_prepare_monitor(c, c->ud_monitor);
1018 r = udev_monitor_enable_receiving(c->ud_monitor);
1022 fd = udev_monitor_get_fd(c->ud_monitor);
1023 r = sd_event_add_io(c->event,
1026 EPOLLHUP | EPOLLERR | EPOLLIN,
1027 context_ud_monitor_fn,
1035 static void context_ud_stop(sysview_context *c) {
1036 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
1037 c->ud_monitor = udev_monitor_unref(c->ud_monitor);
1040 static int context_ud_scan(sysview_context *c) {
1041 _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
1042 struct udev_list_entry *entry;
1043 struct udev_device *d;
1050 e = udev_enumerate_new(c->ud);
1052 return errno > 0 ? -errno : -EFAULT;
1054 r = context_ud_prepare_scan(c, e);
1058 r = udev_enumerate_scan_devices(e);
1062 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
1065 name = udev_list_entry_get_name(entry);
1068 d = udev_device_new_from_syspath(c->ud, name);
1070 r = errno > 0 ? -errno : -EFAULT;
1071 log_debug("sysview: cannot create udev-device for %s: %s",
1072 name, strerror(-r));
1076 r = context_ud_hotplug(c, d);
1077 udev_device_unref(d);
1085 static int context_ld_seat_new(sysview_context *c, sd_bus_message *signal) {
1086 const char *id, *path;
1089 r = sd_bus_message_read(signal, "so", &id, &path);
1091 log_debug("sysview: cannot parse SeatNew from logind: %s",
1096 return context_add_seat(c, id);
1099 static int context_ld_seat_removed(sysview_context *c, sd_bus_message *signal) {
1100 const char *id, *path;
1104 r = sd_bus_message_read(signal, "so", &id, &path);
1106 log_debug("sysview: cannot parse SeatRemoved from logind: %s",
1111 seat = sysview_find_seat(c, id);
1115 return context_remove_seat(c, seat);
1118 static int context_ld_session_new(sysview_context *c, sd_bus_message *signal) {
1119 _cleanup_free_ char *seatid = NULL, *username = NULL;
1120 const char *id, *path;
1125 r = sd_bus_message_read(signal, "so", &id, &path);
1127 log_debug("sysview: cannot parse SessionNew from logind: %s",
1133 * As the dbus message didn't contain enough information, we
1134 * read missing bits via sd-login. Note that this might race session
1135 * destruction, so we handle ENOENT properly.
1138 /* ENOENT is also returned for sessions without seats */
1139 r = sd_session_get_seat(id, &seatid);
1145 seat = sysview_find_seat(c, seatid);
1149 r = sd_session_get_uid(id, &uid);
1155 username = lookup_uid(uid);
1161 r = context_raise_session_filter(c, id, seatid, username, uid);
1164 log_debug("sysview: cannot filter new session '%s' on seat '%s': %s",
1165 id, seatid, strerror(-r));
1169 return context_add_session(c, seat, id);
1172 log_debug("sysview: failed retrieving information for new session '%s': %s",
1177 static int context_ld_session_removed(sysview_context *c, sd_bus_message *signal) {
1178 sysview_session *session;
1179 const char *id, *path;
1182 r = sd_bus_message_read(signal, "so", &id, &path);
1184 log_debug("sysview: cannot parse SessionRemoved from logind: %s",
1189 session = sysview_find_session(c, id);
1193 return context_remove_session(c, session);
1196 static int context_ld_manager_signal_fn(sd_bus *bus,
1197 sd_bus_message *signal,
1199 sd_bus_error *ret_error) {
1200 sysview_context *c = userdata;
1202 if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatNew"))
1203 return context_ld_seat_new(c, signal);
1204 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatRemoved"))
1205 return context_ld_seat_removed(c, signal);
1206 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionNew"))
1207 return context_ld_session_new(c, signal);
1208 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionRemoved"))
1209 return context_ld_session_removed(c, signal);
1214 static int context_ld_start(sysview_context *c) {
1217 if (!c->scan_logind)
1220 r = sd_bus_add_match(c->sysbus,
1221 &c->ld_slot_manager_signal,
1223 "sender='org.freedesktop.login1',"
1224 "interface='org.freedesktop.login1.Manager',"
1225 "path='/org/freedesktop/login1'",
1226 context_ld_manager_signal_fn,
1234 static void context_ld_stop(sysview_context *c) {
1235 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1236 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1237 c->ld_slot_manager_signal = sd_bus_slot_unref(c->ld_slot_manager_signal);
1240 static int context_ld_list_seats_fn(sd_bus *bus,
1241 sd_bus_message *reply,
1243 sd_bus_error *ret_error) {
1244 sysview_context *c = userdata;
1247 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1249 if (sd_bus_message_is_method_error(reply, NULL)) {
1250 const sd_bus_error *error = sd_bus_message_get_error(reply);
1252 log_debug("sysview: ListSeats on logind failed: %s: %s",
1253 error->name, error->message);
1254 return -sd_bus_error_get_errno(error);
1257 r = sd_bus_message_enter_container(reply, 'a', "(so)");
1261 while ((r = sd_bus_message_enter_container(reply, 'r', "so")) > 0) {
1262 const char *id, *path;
1264 r = sd_bus_message_read(reply, "so", &id, &path);
1268 r = context_add_seat(c, id);
1272 r = sd_bus_message_exit_container(reply);
1280 r = sd_bus_message_exit_container(reply);
1287 log_debug("sysview: erroneous ListSeats response from logind: %s",
1292 static int context_ld_list_sessions_fn(sd_bus *bus,
1293 sd_bus_message *reply,
1295 sd_bus_error *ret_error) {
1296 sysview_context *c = userdata;
1299 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1301 if (sd_bus_message_is_method_error(reply, NULL)) {
1302 const sd_bus_error *error = sd_bus_message_get_error(reply);
1304 log_debug("sysview: ListSessions on logind failed: %s: %s",
1305 error->name, error->message);
1306 return -sd_bus_error_get_errno(error);
1309 r = sd_bus_message_enter_container(reply, 'a', "(susso)");
1313 while ((r = sd_bus_message_enter_container(reply, 'r', "susso")) > 0) {
1314 const char *id, *username, *seatid, *path;
1318 r = sd_bus_message_read(reply,
1328 seat = sysview_find_seat(c, seatid);
1330 r = context_raise_session_filter(c, id, seatid, username, uid);
1332 log_debug("sysview: cannot filter listed session '%s' on seat '%s': %s",
1333 id, seatid, strerror(-r));
1336 r = context_add_session(c, seat, id);
1342 r = sd_bus_message_exit_container(reply);
1350 r = sd_bus_message_exit_container(reply);
1357 log_debug("sysview: erroneous ListSessions response from logind: %s",
1362 static int context_ld_scan(sysview_context *c) {
1363 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1366 if (!c->ld_slot_manager_signal)
1369 /* request seat list */
1371 r = sd_bus_message_new_method_call(c->sysbus,
1373 "org.freedesktop.login1",
1374 "/org/freedesktop/login1",
1375 "org.freedesktop.login1.Manager",
1380 r = sd_bus_call_async(c->sysbus,
1381 &c->ld_slot_list_seats,
1383 context_ld_list_seats_fn,
1389 /* request session list */
1391 m = sd_bus_message_unref(m);
1392 r = sd_bus_message_new_method_call(c->sysbus,
1394 "org.freedesktop.login1",
1395 "/org/freedesktop/login1",
1396 "org.freedesktop.login1.Manager",
1401 r = sd_bus_call_async(c->sysbus,
1402 &c->ld_slot_list_sessions,
1404 context_ld_list_sessions_fn,
1413 bool sysview_context_is_running(sysview_context *c) {
1414 return c && c->running;
1417 int sysview_context_start(sysview_context *c, sysview_event_fn event_fn, void *userdata) {
1420 assert_return(c, -EINVAL);
1421 assert_return(event_fn, -EINVAL);
1426 log_debug("sysview: start");
1429 c->event_fn = event_fn;
1430 c->userdata = userdata;
1432 r = context_ld_start(c);
1436 r = context_ud_start(c);
1440 r = sysview_context_rescan(c);
1447 sysview_context_stop(c);
1451 void sysview_context_stop(sysview_context *c) {
1452 sysview_session *session;
1453 sysview_device *device;
1461 log_debug("sysview: stop");
1467 c->scan_src = sd_event_source_unref(c->scan_src);
1472 * Event-callbacks are already cleared, hence we can safely ignore
1473 * return codes of the context_remove_*() helpers. They cannot be
1474 * originated from user-callbacks, so we already handled them.
1477 while ((device = hashmap_first(c->device_map)))
1478 context_remove_device(c, device);
1480 while ((session = hashmap_first(c->session_map)))
1481 context_remove_session(c, session);
1483 while ((seat = hashmap_first(c->seat_map)))
1484 context_remove_seat(c, seat);
1487 static int context_scan_fn(sd_event_source *s, void *userdata) {
1488 sysview_context *c = userdata;
1494 r = context_ld_scan(c);
1496 log_debug("sysview: logind scan failed: %s", strerror(-r));
1501 /* skip device scans if no sessions are available */
1502 if (hashmap_size(c->session_map) > 0) {
1503 r = context_ud_scan(c);
1505 log_debug("sysview: udev scan failed: %s", strerror(-r));
1509 HASHMAP_FOREACH(seat, c->seat_map, i)
1510 seat->scanned = true;
1518 int sysview_context_rescan(sysview_context *c) {
1525 return sd_event_source_set_enabled(c->scan_src, SD_EVENT_ONESHOT);
1527 return sd_event_add_defer(c->event, &c->scan_src, context_scan_fn, c);