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>
32 #include "sysview-internal.h"
33 #include "udev-util.h"
36 static int context_raise_session_control(sysview_context *c, sysview_session *session, int error);
42 sysview_device *sysview_find_device(sysview_context *c, const char *name) {
43 assert_return(c, NULL);
44 assert_return(name, NULL);
46 return hashmap_get(c->device_map, name);
49 int sysview_device_new(sysview_device **out, sysview_seat *seat, const char *name) {
50 _cleanup_(sysview_device_freep) sysview_device *device = NULL;
53 assert_return(seat, -EINVAL);
54 assert_return(name, -EINVAL);
56 device = new0(sysview_device, 1);
61 device->type = (unsigned)-1;
63 device->name = strdup(name);
67 r = hashmap_put(seat->context->device_map, device->name, device);
71 r = hashmap_put(seat->device_map, device->name, device);
81 sysview_device *sysview_device_free(sysview_device *device) {
86 hashmap_remove_value(device->seat->device_map, device->name, device);
87 hashmap_remove_value(device->seat->context->device_map, device->name, device);
90 switch (device->type) {
91 case SYSVIEW_DEVICE_EVDEV:
92 device->evdev.ud = udev_device_unref(device->evdev.ud);
94 case SYSVIEW_DEVICE_DRM:
95 device->drm.ud = udev_device_unref(device->drm.ud);
105 const char *sysview_device_get_name(sysview_device *device) {
106 assert_return(device, NULL);
111 unsigned int sysview_device_get_type(sysview_device *device) {
112 assert_return(device, (unsigned)-1);
117 struct udev_device *sysview_device_get_ud(sysview_device *device) {
118 assert_return(device, NULL);
120 switch (device->type) {
121 case SYSVIEW_DEVICE_EVDEV:
122 return device->evdev.ud;
123 case SYSVIEW_DEVICE_DRM:
124 return device->drm.ud;
126 assert_return(0, NULL);
130 static int device_new_ud(sysview_device **out, sysview_seat *seat, unsigned int type, struct udev_device *ud) {
131 _cleanup_(sysview_device_freep) sysview_device *device = NULL;
134 assert_return(seat, -EINVAL);
135 assert_return(ud, -EINVAL);
137 r = sysview_device_new(&device, seat, udev_device_get_syspath(ud));
144 case SYSVIEW_DEVICE_EVDEV:
145 device->evdev.ud = udev_device_ref(ud);
147 case SYSVIEW_DEVICE_DRM:
148 device->drm.ud = udev_device_ref(ud);
151 assert_not_reached("sysview: invalid udev-device type");
164 sysview_session *sysview_find_session(sysview_context *c, const char *name) {
165 assert_return(c, NULL);
166 assert_return(name, NULL);
168 return hashmap_get(c->session_map, name);
171 int sysview_session_new(sysview_session **out, sysview_seat *seat, const char *name) {
172 _cleanup_(sysview_session_freep) sysview_session *session = NULL;
175 assert_return(seat, -EINVAL);
177 session = new0(sysview_session, 1);
181 session->seat = seat;
185 * If a name is given, we require it to be a logind session
186 * name. The session will be put in managed mode and we use
187 * logind to request controller access.
190 session->name = strdup(name);
194 r = sd_bus_path_encode("/org/freedesktop/login1/session",
195 session->name, &session->path);
199 session->custom = false;;
202 * No session name was given. We assume this is an unmanaged
203 * session controlled by the application. We don't use logind
204 * at all and leave session management to the application. The
205 * name of the session-object is set to a unique random string
206 * that does not clash with the logind namespace.
209 r = asprintf(&session->name, "@custom%" PRIu64,
210 ++seat->context->custom_sid);
214 session->custom = true;
217 r = hashmap_put(seat->context->session_map, session->name, session);
221 r = hashmap_put(seat->session_map, session->name, session);
231 sysview_session *sysview_session_free(sysview_session *session) {
235 assert(!session->public);
236 assert(!session->wants_control);
239 hashmap_remove_value(session->seat->session_map, session->name, session);
240 hashmap_remove_value(session->seat->context->session_map, session->name, session);
250 void sysview_session_set_userdata(sysview_session *session, void *userdata) {
253 session->userdata = userdata;
256 void *sysview_session_get_userdata(sysview_session *session) {
257 assert_return(session, NULL);
259 return session->userdata;
262 const char *sysview_session_get_name(sysview_session *session) {
263 assert_return(session, NULL);
265 return session->name;
268 sysview_seat *sysview_session_get_seat(sysview_session *session) {
269 assert_return(session, NULL);
271 return session->seat;
274 static int session_take_control_fn(sd_bus *bus,
275 sd_bus_message *reply,
277 sd_bus_error *ret_error) {
278 sysview_session *session = userdata;
281 session->slot_take_control = sd_bus_slot_unref(session->slot_take_control);
283 if (sd_bus_message_is_method_error(reply, NULL)) {
284 const sd_bus_error *e = sd_bus_message_get_error(reply);
286 log_debug("sysview: %s: TakeControl failed: %s: %s",
287 session->name, e->name, e->message);
288 error = -sd_bus_error_get_errno(e);
290 session->has_control = true;
294 r = context_raise_session_control(session->seat->context, session, error);
296 log_debug_errno(r, "sysview: callback failed while signalling session control '%d' on session '%s': %m",
297 error, session->name);
302 int sysview_session_take_control(sysview_session *session) {
303 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
306 assert_return(session, -EINVAL);
307 assert_return(!session->custom, -EINVAL);
309 if (session->wants_control)
312 r = sd_bus_message_new_method_call(session->seat->context->sysbus,
314 "org.freedesktop.login1",
316 "org.freedesktop.login1.Session",
321 r = sd_bus_message_append(m, "b", 0);
325 r = sd_bus_call_async(session->seat->context->sysbus,
326 &session->slot_take_control,
328 session_take_control_fn,
334 session->wants_control = true;
338 void sysview_session_release_control(sysview_session *session) {
339 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
343 assert(!session->custom);
345 if (!session->wants_control)
348 session->wants_control = false;
350 if (!session->has_control && !session->slot_take_control)
353 session->has_control = false;
354 session->slot_take_control = sd_bus_slot_unref(session->slot_take_control);
356 r = sd_bus_message_new_method_call(session->seat->context->sysbus,
358 "org.freedesktop.login1",
360 "org.freedesktop.login1.Session",
363 r = sd_bus_send(session->seat->context->sysbus, m, NULL);
365 if (r < 0 && r != -ENOTCONN)
366 log_debug_errno(r, "sysview: %s: cannot send ReleaseControl: %m",
374 sysview_seat *sysview_find_seat(sysview_context *c, const char *name) {
375 assert_return(c, NULL);
376 assert_return(name, NULL);
378 return hashmap_get(c->seat_map, name);
381 int sysview_seat_new(sysview_seat **out, sysview_context *c, const char *name) {
382 _cleanup_(sysview_seat_freep) sysview_seat *seat = NULL;
385 assert_return(c, -EINVAL);
386 assert_return(name, -EINVAL);
388 seat = new0(sysview_seat, 1);
394 seat->name = strdup(name);
398 r = sd_bus_path_encode("/org/freedesktop/login1/seat", seat->name, &seat->path);
402 seat->session_map = hashmap_new(&string_hash_ops);
403 if (!seat->session_map)
406 seat->device_map = hashmap_new(&string_hash_ops);
407 if (!seat->device_map)
410 r = hashmap_put(c->seat_map, seat->name, seat);
420 sysview_seat *sysview_seat_free(sysview_seat *seat) {
424 assert(!seat->public);
425 assert(hashmap_size(seat->device_map) == 0);
426 assert(hashmap_size(seat->session_map) == 0);
429 hashmap_remove_value(seat->context->seat_map, seat->name, seat);
431 hashmap_free(seat->device_map);
432 hashmap_free(seat->session_map);
440 const char *sysview_seat_get_name(sysview_seat *seat) {
441 assert_return(seat, NULL);
446 int sysview_seat_switch_to(sysview_seat *seat, uint32_t nr) {
447 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
450 assert_return(seat, -EINVAL);
451 assert_return(seat->context->sysbus, -EINVAL);
453 r = sd_bus_message_new_method_call(seat->context->sysbus,
455 "org.freedesktop.login1",
457 "org.freedesktop.login1.Seat",
462 r = sd_bus_message_append(m, "u", nr);
466 return sd_bus_send(seat->context->sysbus, m, NULL);
473 static int context_raise(sysview_context *c, sysview_event *event, int def) {
474 return c->running ? c->event_fn(c, c->userdata, event) : def;
477 static int context_raise_settle(sysview_context *c) {
478 sysview_event event = {
479 .type = SYSVIEW_EVENT_SETTLE,
482 return context_raise(c, &event, 0);
485 static int context_raise_seat_add(sysview_context *c, sysview_seat *seat) {
486 sysview_event event = {
487 .type = SYSVIEW_EVENT_SEAT_ADD,
493 return context_raise(c, &event, 0);
496 static int context_raise_seat_remove(sysview_context *c, sysview_seat *seat) {
497 sysview_event event = {
498 .type = SYSVIEW_EVENT_SEAT_REMOVE,
504 return context_raise(c, &event, 0);
507 static int context_raise_session_filter(sysview_context *c,
510 const char *username,
512 sysview_event event = {
513 .type = SYSVIEW_EVENT_SESSION_FILTER,
517 .username = username,
522 return context_raise(c, &event, 1);
525 static int context_raise_session_add(sysview_context *c, sysview_session *session) {
526 sysview_event event = {
527 .type = SYSVIEW_EVENT_SESSION_ADD,
533 return context_raise(c, &event, 0);
536 static int context_raise_session_remove(sysview_context *c, sysview_session *session) {
537 sysview_event event = {
538 .type = SYSVIEW_EVENT_SESSION_REMOVE,
544 return context_raise(c, &event, 0);
547 static int context_raise_session_control(sysview_context *c, sysview_session *session, int error) {
548 sysview_event event = {
549 .type = SYSVIEW_EVENT_SESSION_CONTROL,
556 return context_raise(c, &event, 0);
559 static int context_raise_session_attach(sysview_context *c, sysview_session *session, sysview_device *device) {
560 sysview_event event = {
561 .type = SYSVIEW_EVENT_SESSION_ATTACH,
568 return context_raise(c, &event, 0);
571 static int context_raise_session_detach(sysview_context *c, sysview_session *session, sysview_device *device) {
572 sysview_event event = {
573 .type = SYSVIEW_EVENT_SESSION_DETACH,
580 return context_raise(c, &event, 0);
583 static int context_raise_session_refresh(sysview_context *c, sysview_session *session, sysview_device *device, struct udev_device *ud) {
584 sysview_event event = {
585 .type = SYSVIEW_EVENT_SESSION_REFRESH,
593 return context_raise(c, &event, 0);
596 static void context_settle(sysview_context *c) {
599 if (c->n_probe <= 0 || --c->n_probe > 0)
602 log_debug("sysview: settle");
606 r = context_raise_settle(c);
608 log_debug_errno(r, "sysview: callback failed on settle: %m");
611 static void context_add_device(sysview_context *c, sysview_device *device) {
612 sysview_session *session;
619 log_debug("sysview: add device '%s' on seat '%s'",
620 device->name, device->seat->name);
622 HASHMAP_FOREACH(session, device->seat->session_map, i) {
623 if (!session->public)
626 r = context_raise_session_attach(c, session, device);
628 log_debug_errno(r, "sysview: callback failed while attaching device '%s' to session '%s': %m",
629 device->name, session->name);
633 static void context_remove_device(sysview_context *c, sysview_device *device) {
634 sysview_session *session;
641 log_debug("sysview: remove device '%s'", device->name);
643 HASHMAP_FOREACH(session, device->seat->session_map, i) {
644 if (!session->public)
647 r = context_raise_session_detach(c, session, device);
649 log_debug_errno(r, "sysview: callback failed while detaching device '%s' from session '%s': %m",
650 device->name, session->name);
653 sysview_device_free(device);
656 static void context_change_device(sysview_context *c, sysview_device *device, struct udev_device *ud) {
657 sysview_session *session;
664 log_debug("sysview: change device '%s'", device->name);
666 HASHMAP_FOREACH(session, device->seat->session_map, i) {
667 if (!session->public)
670 r = context_raise_session_refresh(c, session, device, ud);
672 log_debug_errno(r, "sysview: callback failed while changing device '%s' on session '%s': %m",
673 device->name, session->name);
677 static void context_add_session(sysview_context *c, sysview_seat *seat, const char *id) {
678 sysview_session *session;
679 sysview_device *device;
687 session = sysview_find_session(c, id);
691 log_debug("sysview: add session '%s' on seat '%s'", id, seat->name);
693 r = sysview_session_new(&session, seat, id);
697 if (!seat->scanned) {
698 r = sysview_context_rescan(c);
704 session->public = true;
705 r = context_raise_session_add(c, session);
707 log_debug_errno(r, "sysview: callback failed while adding session '%s': %m",
709 session->public = false;
713 HASHMAP_FOREACH(device, seat->device_map, i) {
714 r = context_raise_session_attach(c, session, device);
716 log_debug_errno(r, "sysview: callback failed while attaching device '%s' to new session '%s': %m",
717 device->name, session->name);
725 log_debug_errno(r, "sysview: error while adding session '%s': %m",
729 static void context_remove_session(sysview_context *c, sysview_session *session) {
730 sysview_device *device;
737 log_debug("sysview: remove session '%s'", session->name);
739 if (session->public) {
740 HASHMAP_FOREACH(device, session->seat->device_map, i) {
741 r = context_raise_session_detach(c, session, device);
743 log_debug_errno(r, "sysview: callback failed while detaching device '%s' from old session '%s': %m",
744 device->name, session->name);
747 session->public = false;
748 r = context_raise_session_remove(c, session);
750 log_debug_errno(r, "sysview: callback failed while removing session '%s': %m",
754 if (!session->custom)
755 sysview_session_release_control(session);
757 sysview_session_free(session);
760 static void context_add_seat(sysview_context *c, const char *id) {
767 seat = sysview_find_seat(c, id);
771 log_debug("sysview: add seat '%s'", id);
773 r = sysview_seat_new(&seat, c, id);
778 r = context_raise_seat_add(c, seat);
780 log_debug_errno(r, "sysview: callback failed while adding seat '%s': %m",
782 seat->public = false;
789 log_debug_errno(r, "sysview: error while adding seat '%s': %m",
793 static void context_remove_seat(sysview_context *c, sysview_seat *seat) {
794 sysview_session *session;
795 sysview_device *device;
801 log_debug("sysview: remove seat '%s'", seat->name);
803 while ((device = hashmap_first(seat->device_map)))
804 context_remove_device(c, device);
806 while ((session = hashmap_first(seat->session_map)))
807 context_remove_session(c, session);
810 seat->public = false;
811 r = context_raise_seat_remove(c, seat);
813 log_debug_errno(r, "sysview: callback failed while removing seat '%s': %m",
817 sysview_seat_free(seat);
820 int sysview_context_new(sysview_context **out,
825 _cleanup_(sysview_context_freep) sysview_context *c = NULL;
828 assert_return(out, -EINVAL);
829 assert_return(event, -EINVAL);
831 log_debug("sysview: new");
833 c = new0(sysview_context, 1);
837 c->event = sd_event_ref(event);
838 if (flags & SYSVIEW_CONTEXT_SCAN_LOGIND)
839 c->scan_logind = true;
840 if (flags & SYSVIEW_CONTEXT_SCAN_EVDEV)
841 c->scan_evdev = true;
842 if (flags & SYSVIEW_CONTEXT_SCAN_DRM)
846 c->sysbus = sd_bus_ref(sysbus);
847 } else if (c->scan_logind) {
848 r = sd_bus_open_system(&c->sysbus);
854 c->ud = udev_ref(ud);
855 } else if (c->scan_evdev || c->scan_drm) {
859 return errno > 0 ? -errno : -EFAULT;
862 c->seat_map = hashmap_new(&string_hash_ops);
866 c->session_map = hashmap_new(&string_hash_ops);
870 c->device_map = hashmap_new(&string_hash_ops);
879 sysview_context *sysview_context_free(sysview_context *c) {
883 log_debug("sysview: free");
885 sysview_context_stop(c);
887 assert(hashmap_size(c->device_map) == 0);
888 assert(hashmap_size(c->session_map) == 0);
889 assert(hashmap_size(c->seat_map) == 0);
891 hashmap_free(c->device_map);
892 hashmap_free(c->session_map);
893 hashmap_free(c->seat_map);
894 c->ud = udev_unref(c->ud);
895 c->sysbus = sd_bus_unref(c->sysbus);
896 c->event = sd_event_unref(c->event);
902 static int context_ud_prepare_monitor(sysview_context *c, struct udev_monitor *m) {
906 r = udev_monitor_filter_add_match_subsystem_devtype(m, "input", NULL);
912 r = udev_monitor_filter_add_match_subsystem_devtype(m, "drm", NULL);
920 static int context_ud_prepare_scan(sysview_context *c, struct udev_enumerate *e) {
924 r = udev_enumerate_add_match_subsystem(e, "input");
930 r = udev_enumerate_add_match_subsystem(e, "drm");
935 r = udev_enumerate_add_match_is_initialized(e);
942 static int context_ud_hotplug(sysview_context *c, struct udev_device *d) {
943 const char *syspath, *sysname, *subsystem, *action, *seatname;
944 sysview_device *device;
947 syspath = udev_device_get_syspath(d);
948 sysname = udev_device_get_sysname(d);
949 subsystem = udev_device_get_subsystem(d);
950 action = udev_device_get_action(d);
952 /* not interested in custom devices without syspath/etc */
953 if (!syspath || !sysname || !subsystem)
956 device = sysview_find_device(c, syspath);
958 if (streq_ptr(action, "remove")) {
962 context_remove_device(c, device);
963 } else if (streq_ptr(action, "change")) {
967 context_change_device(c, device, d);
968 } else if (!action || streq_ptr(action, "add")) {
969 struct udev_device *p;
970 unsigned int type, t;
976 if (streq(subsystem, "input") && startswith(sysname, "event") && safe_atou(sysname + 5, &t) >= 0)
977 type = SYSVIEW_DEVICE_EVDEV;
978 else if (streq(subsystem, "drm") && startswith(sysname, "card"))
979 type = SYSVIEW_DEVICE_DRM;
983 if (type >= SYSVIEW_DEVICE_CNT)
989 seatname = udev_device_get_property_value(p, "ID_SEAT");
992 } while ((p = udev_device_get_parent(p)));
994 seat = sysview_find_seat(c, seatname ? : "seat0");
998 r = device_new_ud(&device, seat, type, d);
1000 return log_debug_errno(r, "sysview: cannot create device for udev-device '%s': %m",
1003 context_add_device(c, device);
1009 static int context_ud_monitor_fn(sd_event_source *s,
1013 sysview_context *c = userdata;
1014 struct udev_device *d;
1017 if (revents & EPOLLIN) {
1018 while ((d = udev_monitor_receive_device(c->ud_monitor))) {
1019 r = context_ud_hotplug(c, d);
1020 udev_device_unref(d);
1025 /* as long as EPOLLIN is signalled, read pending data */
1029 if (revents & (EPOLLHUP | EPOLLERR)) {
1030 log_debug("sysview: HUP on udev-monitor");
1031 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
1037 static int context_ud_start(sysview_context *c) {
1044 c->ud_monitor = udev_monitor_new_from_netlink(c->ud, "udev");
1046 return errno > 0 ? -errno : -EFAULT;
1048 r = context_ud_prepare_monitor(c, c->ud_monitor);
1052 r = udev_monitor_enable_receiving(c->ud_monitor);
1056 fd = udev_monitor_get_fd(c->ud_monitor);
1057 r = sd_event_add_io(c->event,
1060 EPOLLHUP | EPOLLERR | EPOLLIN,
1061 context_ud_monitor_fn,
1069 static void context_ud_stop(sysview_context *c) {
1070 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
1071 c->ud_monitor = udev_monitor_unref(c->ud_monitor);
1074 static int context_ud_scan(sysview_context *c) {
1075 _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
1076 struct udev_list_entry *entry;
1077 struct udev_device *d;
1084 e = udev_enumerate_new(c->ud);
1086 return errno > 0 ? -errno : -EFAULT;
1088 r = context_ud_prepare_scan(c, e);
1092 r = udev_enumerate_scan_devices(e);
1096 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
1099 name = udev_list_entry_get_name(entry);
1102 d = udev_device_new_from_syspath(c->ud, name);
1104 r = errno > 0 ? -errno : -EFAULT;
1105 log_debug_errno(r, "sysview: cannot create udev-device for %s: %m",
1110 r = context_ud_hotplug(c, d);
1111 udev_device_unref(d);
1119 static int context_ld_seat_new(sysview_context *c, sd_bus_message *signal) {
1120 const char *id, *path;
1123 r = sd_bus_message_read(signal, "so", &id, &path);
1125 return log_debug_errno(r, "sysview: cannot parse SeatNew from logind: %m");
1127 context_add_seat(c, id);
1131 static int context_ld_seat_removed(sysview_context *c, sd_bus_message *signal) {
1132 const char *id, *path;
1136 r = sd_bus_message_read(signal, "so", &id, &path);
1138 return log_debug_errno(r, "sysview: cannot parse SeatRemoved from logind: %m");
1140 seat = sysview_find_seat(c, id);
1144 context_remove_seat(c, seat);
1148 static int context_ld_session_new(sysview_context *c, sd_bus_message *signal) {
1149 _cleanup_free_ char *seatid = NULL, *username = NULL;
1150 const char *id, *path;
1155 r = sd_bus_message_read(signal, "so", &id, &path);
1157 return log_debug_errno(r, "sysview: cannot parse SessionNew from logind: %m");
1160 * As the dbus message didn't contain enough information, we
1161 * read missing bits via sd-login. Note that this might race session
1162 * destruction, so we handle ENOENT properly.
1165 /* ENOENT is also returned for sessions without seats */
1166 r = sd_session_get_seat(id, &seatid);
1172 seat = sysview_find_seat(c, seatid);
1176 r = sd_session_get_uid(id, &uid);
1182 username = lookup_uid(uid);
1188 r = context_raise_session_filter(c, id, seatid, username, uid);
1190 log_debug_errno(r, "sysview: callback failed while filtering session '%s': %m",
1193 context_add_session(c, seat, id);
1198 return log_debug_errno(r, "sysview: failed retrieving information for new session '%s': %m",
1202 static int context_ld_session_removed(sysview_context *c, sd_bus_message *signal) {
1203 sysview_session *session;
1204 const char *id, *path;
1207 r = sd_bus_message_read(signal, "so", &id, &path);
1209 return log_debug_errno(r, "sysview: cannot parse SessionRemoved from logind: %m");
1211 session = sysview_find_session(c, id);
1215 context_remove_session(c, session);
1219 static int context_ld_manager_signal_fn(sd_bus *bus,
1220 sd_bus_message *signal,
1222 sd_bus_error *ret_error) {
1223 sysview_context *c = userdata;
1225 if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatNew"))
1226 return context_ld_seat_new(c, signal);
1227 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatRemoved"))
1228 return context_ld_seat_removed(c, signal);
1229 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionNew"))
1230 return context_ld_session_new(c, signal);
1231 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionRemoved"))
1232 return context_ld_session_removed(c, signal);
1237 static int context_ld_start(sysview_context *c) {
1240 if (!c->scan_logind)
1243 r = sd_bus_add_match(c->sysbus,
1244 &c->ld_slot_manager_signal,
1246 "sender='org.freedesktop.login1',"
1247 "interface='org.freedesktop.login1.Manager',"
1248 "path='/org/freedesktop/login1'",
1249 context_ld_manager_signal_fn,
1257 static void context_ld_stop(sysview_context *c) {
1258 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1259 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1260 c->ld_slot_manager_signal = sd_bus_slot_unref(c->ld_slot_manager_signal);
1263 static int context_ld_list_seats_fn(sd_bus *bus,
1264 sd_bus_message *reply,
1266 sd_bus_error *ret_error) {
1267 sysview_context *c = userdata;
1270 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1272 if (sd_bus_message_is_method_error(reply, NULL)) {
1273 const sd_bus_error *error = sd_bus_message_get_error(reply);
1275 log_debug("sysview: ListSeats on logind failed: %s: %s",
1276 error->name, error->message);
1277 r = -sd_bus_error_get_errno(error);
1281 r = sd_bus_message_enter_container(reply, 'a', "(so)");
1285 while ((r = sd_bus_message_enter_container(reply, 'r', "so")) > 0) {
1286 const char *id, *path;
1288 r = sd_bus_message_read(reply, "so", &id, &path);
1292 context_add_seat(c, id);
1294 r = sd_bus_message_exit_container(reply);
1302 r = sd_bus_message_exit_container(reply);
1310 log_debug_errno(r, "sysview: erroneous ListSeats response from logind: %m");
1316 static int context_ld_list_sessions_fn(sd_bus *bus,
1317 sd_bus_message *reply,
1319 sd_bus_error *ret_error) {
1320 sysview_context *c = userdata;
1323 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1325 if (sd_bus_message_is_method_error(reply, NULL)) {
1326 const sd_bus_error *error = sd_bus_message_get_error(reply);
1328 log_debug("sysview: ListSessions on logind failed: %s: %s",
1329 error->name, error->message);
1330 r = -sd_bus_error_get_errno(error);
1334 r = sd_bus_message_enter_container(reply, 'a', "(susso)");
1338 while ((r = sd_bus_message_enter_container(reply, 'r', "susso")) > 0) {
1339 const char *id, *username, *seatid, *path;
1343 r = sd_bus_message_read(reply,
1353 seat = sysview_find_seat(c, seatid);
1355 r = context_raise_session_filter(c, id, seatid, username, uid);
1357 log_debug_errno(r, "sysview: callback failed while filtering session '%s': %m",
1360 context_add_session(c, seat, id);
1363 r = sd_bus_message_exit_container(reply);
1371 r = sd_bus_message_exit_container(reply);
1379 log_debug_errno(r, "sysview: erroneous ListSessions response from logind: %m");
1385 static int context_ld_scan(sysview_context *c) {
1386 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1389 if (!c->ld_slot_manager_signal)
1392 /* request seat list */
1394 r = sd_bus_message_new_method_call(c->sysbus,
1396 "org.freedesktop.login1",
1397 "/org/freedesktop/login1",
1398 "org.freedesktop.login1.Manager",
1403 r = sd_bus_call_async(c->sysbus,
1404 &c->ld_slot_list_seats,
1406 context_ld_list_seats_fn,
1415 /* request session list */
1417 m = sd_bus_message_unref(m);
1418 r = sd_bus_message_new_method_call(c->sysbus,
1420 "org.freedesktop.login1",
1421 "/org/freedesktop/login1",
1422 "org.freedesktop.login1.Manager",
1427 r = sd_bus_call_async(c->sysbus,
1428 &c->ld_slot_list_sessions,
1430 context_ld_list_sessions_fn,
1442 bool sysview_context_is_running(sysview_context *c) {
1443 return c && c->running;
1446 int sysview_context_start(sysview_context *c, sysview_event_fn event_fn, void *userdata) {
1449 assert_return(c, -EINVAL);
1450 assert_return(event_fn, -EINVAL);
1455 log_debug("sysview: start");
1458 c->event_fn = event_fn;
1459 c->userdata = userdata;
1461 r = context_ld_start(c);
1465 r = context_ud_start(c);
1469 r = sysview_context_rescan(c);
1476 sysview_context_stop(c);
1480 void sysview_context_stop(sysview_context *c) {
1481 sysview_session *session;
1482 sysview_device *device;
1490 log_debug("sysview: stop");
1492 while ((device = hashmap_first(c->device_map)))
1493 context_remove_device(c, device);
1495 while ((session = hashmap_first(c->session_map)))
1496 context_remove_session(c, session);
1498 while ((seat = hashmap_first(c->seat_map)))
1499 context_remove_seat(c, seat);
1507 c->scan_src = sd_event_source_unref(c->scan_src);
1512 static int context_scan_fn(sd_event_source *s, void *userdata) {
1513 sysview_context *c = userdata;
1521 r = context_ld_scan(c);
1523 return log_debug_errno(r, "sysview: logind scan failed: %m");
1526 /* skip device scans if no sessions are available */
1527 if (hashmap_size(c->session_map) > 0) {
1528 r = context_ud_scan(c);
1530 return log_debug_errno(r, "sysview: udev scan failed: %m");
1532 HASHMAP_FOREACH(seat, c->seat_map, i)
1533 seat->scanned = true;
1542 int sysview_context_rescan(sysview_context *c) {
1555 return sd_event_source_set_enabled(c->scan_src, SD_EVENT_ONESHOT);
1557 return sd_event_add_defer(c->event, &c->scan_src, context_scan_fn, c);