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 r = context_raise_session_control(session->seat->context, session, error);
292 log_debug("sysview: callback failed while signalling session control '%d' on session '%s': %s",
293 error, session->name, strerror(-r));
298 int sysview_session_take_control(sysview_session *session) {
299 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
302 assert_return(session, -EINVAL);
303 assert_return(!session->custom, -EINVAL);
305 if (session->wants_control)
308 r = sd_bus_message_new_method_call(session->seat->context->sysbus,
310 "org.freedesktop.login1",
312 "org.freedesktop.login1.Session",
317 r = sd_bus_message_append(m, "b", 0);
321 r = sd_bus_call_async(session->seat->context->sysbus,
322 &session->slot_take_control,
324 session_take_control_fn,
330 session->wants_control = true;
334 void sysview_session_release_control(sysview_session *session) {
335 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
339 assert(!session->custom);
341 if (!session->wants_control)
344 session->wants_control = false;
346 if (!session->has_control && !session->slot_take_control)
349 session->has_control = false;
350 session->slot_take_control = sd_bus_slot_unref(session->slot_take_control);
352 r = sd_bus_message_new_method_call(session->seat->context->sysbus,
354 "org.freedesktop.login1",
356 "org.freedesktop.login1.Session",
359 r = sd_bus_send(session->seat->context->sysbus, m, NULL);
361 if (r < 0 && r != -ENOTCONN)
362 log_debug("sysview: %s: cannot send ReleaseControl: %s",
363 session->name, strerror(-r));
370 sysview_seat *sysview_find_seat(sysview_context *c, const char *name) {
371 assert_return(c, NULL);
372 assert_return(name, NULL);
374 return hashmap_get(c->seat_map, name);
377 int sysview_seat_new(sysview_seat **out, sysview_context *c, const char *name) {
378 _cleanup_(sysview_seat_freep) sysview_seat *seat = NULL;
381 assert_return(c, -EINVAL);
382 assert_return(name, -EINVAL);
384 seat = new0(sysview_seat, 1);
390 seat->name = strdup(name);
394 seat->session_map = hashmap_new(&string_hash_ops);
395 if (!seat->session_map)
398 seat->device_map = hashmap_new(&string_hash_ops);
399 if (!seat->device_map)
402 r = hashmap_put(c->seat_map, seat->name, seat);
412 sysview_seat *sysview_seat_free(sysview_seat *seat) {
416 assert(!seat->public);
417 assert(hashmap_size(seat->device_map) == 0);
418 assert(hashmap_size(seat->session_map) == 0);
421 hashmap_remove_value(seat->context->seat_map, seat->name, seat);
423 hashmap_free(seat->device_map);
424 hashmap_free(seat->session_map);
431 const char *sysview_seat_get_name(sysview_seat *seat) {
432 assert_return(seat, NULL);
441 static int context_raise(sysview_context *c, sysview_event *event, int def) {
442 return c->running ? c->event_fn(c, c->userdata, event) : def;
445 static int context_raise_seat_add(sysview_context *c, sysview_seat *seat) {
446 sysview_event event = {
447 .type = SYSVIEW_EVENT_SEAT_ADD,
453 return context_raise(c, &event, 0);
456 static int context_raise_seat_remove(sysview_context *c, sysview_seat *seat) {
457 sysview_event event = {
458 .type = SYSVIEW_EVENT_SEAT_REMOVE,
464 return context_raise(c, &event, 0);
467 static int context_raise_session_filter(sysview_context *c,
470 const char *username,
472 sysview_event event = {
473 .type = SYSVIEW_EVENT_SESSION_FILTER,
477 .username = username,
482 return context_raise(c, &event, 1);
485 static int context_raise_session_add(sysview_context *c, sysview_session *session) {
486 sysview_event event = {
487 .type = SYSVIEW_EVENT_SESSION_ADD,
493 return context_raise(c, &event, 0);
496 static int context_raise_session_remove(sysview_context *c, sysview_session *session) {
497 sysview_event event = {
498 .type = SYSVIEW_EVENT_SESSION_REMOVE,
504 return context_raise(c, &event, 0);
507 static int context_raise_session_control(sysview_context *c, sysview_session *session, int error) {
508 sysview_event event = {
509 .type = SYSVIEW_EVENT_SESSION_CONTROL,
516 return context_raise(c, &event, 0);
519 static int context_raise_session_attach(sysview_context *c, sysview_session *session, sysview_device *device) {
520 sysview_event event = {
521 .type = SYSVIEW_EVENT_SESSION_ATTACH,
528 return context_raise(c, &event, 0);
531 static int context_raise_session_detach(sysview_context *c, sysview_session *session, sysview_device *device) {
532 sysview_event event = {
533 .type = SYSVIEW_EVENT_SESSION_DETACH,
540 return context_raise(c, &event, 0);
543 static int context_raise_session_refresh(sysview_context *c, sysview_session *session, sysview_device *device, struct udev_device *ud) {
544 sysview_event event = {
545 .type = SYSVIEW_EVENT_SESSION_REFRESH,
553 return context_raise(c, &event, 0);
556 static void context_add_device(sysview_context *c, sysview_device *device) {
557 sysview_session *session;
564 log_debug("sysview: add device '%s' on seat '%s'",
565 device->name, device->seat->name);
567 HASHMAP_FOREACH(session, device->seat->session_map, i) {
568 if (!session->public)
571 r = context_raise_session_attach(c, session, device);
573 log_debug("sysview: callback failed while attaching device '%s' to session '%s': %s",
574 device->name, session->name, strerror(-r));
578 static void context_remove_device(sysview_context *c, sysview_device *device) {
579 sysview_session *session;
586 log_debug("sysview: remove device '%s'", device->name);
588 HASHMAP_FOREACH(session, device->seat->session_map, i) {
589 if (!session->public)
592 r = context_raise_session_detach(c, session, device);
594 log_debug("sysview: callback failed while detaching device '%s' from session '%s': %s",
595 device->name, session->name, strerror(-r));
598 sysview_device_free(device);
601 static void context_change_device(sysview_context *c, sysview_device *device, struct udev_device *ud) {
602 sysview_session *session;
609 log_debug("sysview: change device '%s'", device->name);
611 HASHMAP_FOREACH(session, device->seat->session_map, i) {
612 if (!session->public)
615 r = context_raise_session_refresh(c, session, device, ud);
617 log_debug("sysview: callback failed while changing device '%s' on session '%s': %s",
618 device->name, session->name, strerror(-r));
622 static void context_add_session(sysview_context *c, sysview_seat *seat, const char *id) {
623 sysview_session *session;
624 sysview_device *device;
632 session = sysview_find_session(c, id);
636 log_debug("sysview: add session '%s' on seat '%s'", id, seat->name);
638 r = sysview_session_new(&session, seat, id);
642 if (!seat->scanned) {
643 r = sysview_context_rescan(c);
649 session->public = true;
650 r = context_raise_session_add(c, session);
652 log_debug("sysview: callback failed while adding session '%s': %s",
653 session->name, strerror(-r));
654 session->public = false;
658 HASHMAP_FOREACH(device, seat->device_map, i) {
659 r = context_raise_session_attach(c, session, device);
661 log_debug("sysview: callback failed while attaching device '%s' to new session '%s': %s",
662 device->name, session->name, strerror(-r));
670 log_debug("sysview: error while adding session '%s': %s",
674 static void context_remove_session(sysview_context *c, sysview_session *session) {
675 sysview_device *device;
682 log_debug("sysview: remove session '%s'", session->name);
684 if (session->public) {
685 HASHMAP_FOREACH(device, session->seat->device_map, i) {
686 r = context_raise_session_detach(c, session, device);
688 log_debug("sysview: callback failed while detaching device '%s' from old session '%s': %s",
689 device->name, session->name, strerror(-r));
692 session->public = false;
693 r = context_raise_session_remove(c, session);
695 log_debug("sysview: callback failed while removing session '%s': %s",
696 session->name, strerror(-r));
699 if (!session->custom)
700 sysview_session_release_control(session);
702 sysview_session_free(session);
705 static void context_add_seat(sysview_context *c, const char *id) {
712 seat = sysview_find_seat(c, id);
716 log_debug("sysview: add seat '%s'", id);
718 r = sysview_seat_new(&seat, c, id);
723 r = context_raise_seat_add(c, seat);
725 log_debug("sysview: callback failed while adding seat '%s': %s",
726 seat->name, strerror(-r));
727 seat->public = false;
734 log_debug("sysview: error while adding seat '%s': %s",
738 static void context_remove_seat(sysview_context *c, sysview_seat *seat) {
739 sysview_session *session;
740 sysview_device *device;
746 log_debug("sysview: remove seat '%s'", seat->name);
748 while ((device = hashmap_first(seat->device_map)))
749 context_remove_device(c, device);
751 while ((session = hashmap_first(seat->session_map)))
752 context_remove_session(c, session);
755 seat->public = false;
756 r = context_raise_seat_remove(c, seat);
758 log_debug("sysview: callback failed while removing seat '%s': %s",
759 seat->name, strerror(-r));
762 sysview_seat_free(seat);
765 int sysview_context_new(sysview_context **out,
770 _cleanup_(sysview_context_freep) sysview_context *c = NULL;
773 assert_return(out, -EINVAL);
774 assert_return(event, -EINVAL);
776 log_debug("sysview: new");
778 c = new0(sysview_context, 1);
782 c->event = sd_event_ref(event);
783 if (flags & SYSVIEW_CONTEXT_SCAN_LOGIND)
784 c->scan_logind = true;
785 if (flags & SYSVIEW_CONTEXT_SCAN_EVDEV)
786 c->scan_evdev = true;
787 if (flags & SYSVIEW_CONTEXT_SCAN_DRM)
791 c->sysbus = sd_bus_ref(sysbus);
792 } else if (c->scan_logind) {
793 r = sd_bus_open_system(&c->sysbus);
799 c->ud = udev_ref(ud);
800 } else if (c->scan_evdev || c->scan_drm) {
804 return errno > 0 ? -errno : -EFAULT;
807 c->seat_map = hashmap_new(&string_hash_ops);
811 c->session_map = hashmap_new(&string_hash_ops);
815 c->device_map = hashmap_new(&string_hash_ops);
824 sysview_context *sysview_context_free(sysview_context *c) {
828 log_debug("sysview: free");
830 sysview_context_stop(c);
832 assert(hashmap_size(c->device_map) == 0);
833 assert(hashmap_size(c->session_map) == 0);
834 assert(hashmap_size(c->seat_map) == 0);
836 hashmap_free(c->device_map);
837 hashmap_free(c->session_map);
838 hashmap_free(c->seat_map);
839 c->ud = udev_unref(c->ud);
840 c->sysbus = sd_bus_unref(c->sysbus);
841 c->event = sd_event_unref(c->event);
847 static int context_ud_prepare_monitor(sysview_context *c, struct udev_monitor *m) {
851 r = udev_monitor_filter_add_match_subsystem_devtype(m, "input", NULL);
857 r = udev_monitor_filter_add_match_subsystem_devtype(m, "drm", NULL);
865 static int context_ud_prepare_scan(sysview_context *c, struct udev_enumerate *e) {
869 r = udev_enumerate_add_match_subsystem(e, "input");
875 r = udev_enumerate_add_match_subsystem(e, "drm");
880 r = udev_enumerate_add_match_is_initialized(e);
887 static int context_ud_hotplug(sysview_context *c, struct udev_device *d) {
888 const char *syspath, *sysname, *subsystem, *action, *seatname;
889 sysview_device *device;
892 syspath = udev_device_get_syspath(d);
893 sysname = udev_device_get_sysname(d);
894 subsystem = udev_device_get_subsystem(d);
895 action = udev_device_get_action(d);
897 /* not interested in custom devices without syspath/etc */
898 if (!syspath || !sysname || !subsystem)
901 device = sysview_find_device(c, syspath);
903 if (streq_ptr(action, "remove")) {
907 context_remove_device(c, device);
908 } else if (streq_ptr(action, "change")) {
912 context_change_device(c, device, d);
913 } else if (!action || streq_ptr(action, "add")) {
914 struct udev_device *p;
915 unsigned int type, t;
921 if (streq(subsystem, "input") && startswith(sysname, "event") && safe_atou(sysname + 5, &t) >= 0)
922 type = SYSVIEW_DEVICE_EVDEV;
923 else if (streq(subsystem, "drm") && startswith(sysname, "card"))
924 type = SYSVIEW_DEVICE_DRM;
928 if (type >= SYSVIEW_DEVICE_CNT)
934 seatname = udev_device_get_property_value(p, "ID_SEAT");
937 } while ((p = udev_device_get_parent(p)));
939 seat = sysview_find_seat(c, seatname ? : "seat0");
943 r = device_new_ud(&device, seat, type, d);
945 log_debug("sysview: cannot create device for udev-device '%s': %s",
946 syspath, strerror(-r));
950 context_add_device(c, device);
956 static int context_ud_monitor_fn(sd_event_source *s,
960 sysview_context *c = userdata;
961 struct udev_device *d;
964 if (revents & EPOLLIN) {
965 while ((d = udev_monitor_receive_device(c->ud_monitor))) {
966 r = context_ud_hotplug(c, d);
967 udev_device_unref(d);
972 /* as long as EPOLLIN is signalled, read pending data */
976 if (revents & (EPOLLHUP | EPOLLERR)) {
977 log_debug("sysview: HUP on udev-monitor");
978 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
984 static int context_ud_start(sysview_context *c) {
991 c->ud_monitor = udev_monitor_new_from_netlink(c->ud, "udev");
993 return errno > 0 ? -errno : -EFAULT;
995 r = context_ud_prepare_monitor(c, c->ud_monitor);
999 r = udev_monitor_enable_receiving(c->ud_monitor);
1003 fd = udev_monitor_get_fd(c->ud_monitor);
1004 r = sd_event_add_io(c->event,
1007 EPOLLHUP | EPOLLERR | EPOLLIN,
1008 context_ud_monitor_fn,
1016 static void context_ud_stop(sysview_context *c) {
1017 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
1018 c->ud_monitor = udev_monitor_unref(c->ud_monitor);
1021 static int context_ud_scan(sysview_context *c) {
1022 _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
1023 struct udev_list_entry *entry;
1024 struct udev_device *d;
1031 e = udev_enumerate_new(c->ud);
1033 return errno > 0 ? -errno : -EFAULT;
1035 r = context_ud_prepare_scan(c, e);
1039 r = udev_enumerate_scan_devices(e);
1043 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
1046 name = udev_list_entry_get_name(entry);
1049 d = udev_device_new_from_syspath(c->ud, name);
1051 r = errno > 0 ? -errno : -EFAULT;
1052 log_debug("sysview: cannot create udev-device for %s: %s",
1053 name, strerror(-r));
1057 r = context_ud_hotplug(c, d);
1058 udev_device_unref(d);
1066 static int context_ld_seat_new(sysview_context *c, sd_bus_message *signal) {
1067 const char *id, *path;
1070 r = sd_bus_message_read(signal, "so", &id, &path);
1072 log_debug("sysview: cannot parse SeatNew from logind: %s",
1077 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 context_remove_seat(c, seat);
1101 static int context_ld_session_new(sysview_context *c, sd_bus_message *signal) {
1102 _cleanup_free_ char *seatid = NULL, *username = NULL;
1103 const char *id, *path;
1108 r = sd_bus_message_read(signal, "so", &id, &path);
1110 log_debug("sysview: cannot parse SessionNew from logind: %s",
1116 * As the dbus message didn't contain enough information, we
1117 * read missing bits via sd-login. Note that this might race session
1118 * destruction, so we handle ENOENT properly.
1121 /* ENOENT is also returned for sessions without seats */
1122 r = sd_session_get_seat(id, &seatid);
1128 seat = sysview_find_seat(c, seatid);
1132 r = sd_session_get_uid(id, &uid);
1138 username = lookup_uid(uid);
1144 r = context_raise_session_filter(c, id, seatid, username, uid);
1146 log_debug("sysview: callback failed while filtering session '%s': %s",
1149 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 context_remove_session(c, session);
1179 static int context_ld_manager_signal_fn(sd_bus *bus,
1180 sd_bus_message *signal,
1182 sd_bus_error *ret_error) {
1183 sysview_context *c = userdata;
1185 if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatNew"))
1186 return context_ld_seat_new(c, signal);
1187 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatRemoved"))
1188 return context_ld_seat_removed(c, signal);
1189 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionNew"))
1190 return context_ld_session_new(c, signal);
1191 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionRemoved"))
1192 return context_ld_session_removed(c, signal);
1197 static int context_ld_start(sysview_context *c) {
1200 if (!c->scan_logind)
1203 r = sd_bus_add_match(c->sysbus,
1204 &c->ld_slot_manager_signal,
1206 "sender='org.freedesktop.login1',"
1207 "interface='org.freedesktop.login1.Manager',"
1208 "path='/org/freedesktop/login1'",
1209 context_ld_manager_signal_fn,
1217 static void context_ld_stop(sysview_context *c) {
1218 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1219 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1220 c->ld_slot_manager_signal = sd_bus_slot_unref(c->ld_slot_manager_signal);
1223 static int context_ld_list_seats_fn(sd_bus *bus,
1224 sd_bus_message *reply,
1226 sd_bus_error *ret_error) {
1227 sysview_context *c = userdata;
1230 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1232 if (sd_bus_message_is_method_error(reply, NULL)) {
1233 const sd_bus_error *error = sd_bus_message_get_error(reply);
1235 log_debug("sysview: ListSeats on logind failed: %s: %s",
1236 error->name, error->message);
1237 return -sd_bus_error_get_errno(error);
1240 r = sd_bus_message_enter_container(reply, 'a', "(so)");
1244 while ((r = sd_bus_message_enter_container(reply, 'r', "so")) > 0) {
1245 const char *id, *path;
1247 r = sd_bus_message_read(reply, "so", &id, &path);
1251 context_add_seat(c, id);
1253 r = sd_bus_message_exit_container(reply);
1261 r = sd_bus_message_exit_container(reply);
1268 log_debug("sysview: erroneous ListSeats response from logind: %s",
1273 static int context_ld_list_sessions_fn(sd_bus *bus,
1274 sd_bus_message *reply,
1276 sd_bus_error *ret_error) {
1277 sysview_context *c = userdata;
1280 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1282 if (sd_bus_message_is_method_error(reply, NULL)) {
1283 const sd_bus_error *error = sd_bus_message_get_error(reply);
1285 log_debug("sysview: ListSessions on logind failed: %s: %s",
1286 error->name, error->message);
1287 return -sd_bus_error_get_errno(error);
1290 r = sd_bus_message_enter_container(reply, 'a', "(susso)");
1294 while ((r = sd_bus_message_enter_container(reply, 'r', "susso")) > 0) {
1295 const char *id, *username, *seatid, *path;
1299 r = sd_bus_message_read(reply,
1309 seat = sysview_find_seat(c, seatid);
1311 r = context_raise_session_filter(c, id, seatid, username, uid);
1313 log_debug("sysview: callback failed while filtering session '%s': %s",
1316 context_add_session(c, seat, id);
1319 r = sd_bus_message_exit_container(reply);
1327 r = sd_bus_message_exit_container(reply);
1334 log_debug("sysview: erroneous ListSessions response from logind: %s",
1339 static int context_ld_scan(sysview_context *c) {
1340 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1343 if (!c->ld_slot_manager_signal)
1346 /* request seat list */
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_seats,
1360 context_ld_list_seats_fn,
1366 /* request session list */
1368 m = sd_bus_message_unref(m);
1369 r = sd_bus_message_new_method_call(c->sysbus,
1371 "org.freedesktop.login1",
1372 "/org/freedesktop/login1",
1373 "org.freedesktop.login1.Manager",
1378 r = sd_bus_call_async(c->sysbus,
1379 &c->ld_slot_list_sessions,
1381 context_ld_list_sessions_fn,
1390 bool sysview_context_is_running(sysview_context *c) {
1391 return c && c->running;
1394 int sysview_context_start(sysview_context *c, sysview_event_fn event_fn, void *userdata) {
1397 assert_return(c, -EINVAL);
1398 assert_return(event_fn, -EINVAL);
1403 log_debug("sysview: start");
1406 c->event_fn = event_fn;
1407 c->userdata = userdata;
1409 r = context_ld_start(c);
1413 r = context_ud_start(c);
1417 r = sysview_context_rescan(c);
1424 sysview_context_stop(c);
1428 void sysview_context_stop(sysview_context *c) {
1429 sysview_session *session;
1430 sysview_device *device;
1438 log_debug("sysview: stop");
1440 while ((device = hashmap_first(c->device_map)))
1441 context_remove_device(c, device);
1443 while ((session = hashmap_first(c->session_map)))
1444 context_remove_session(c, session);
1446 while ((seat = hashmap_first(c->seat_map)))
1447 context_remove_seat(c, seat);
1453 c->scan_src = sd_event_source_unref(c->scan_src);
1458 static int context_scan_fn(sd_event_source *s, void *userdata) {
1459 sysview_context *c = userdata;
1465 r = context_ld_scan(c);
1467 log_debug("sysview: logind scan failed: %s", strerror(-r));
1472 /* skip device scans if no sessions are available */
1473 if (hashmap_size(c->session_map) > 0) {
1474 r = context_ud_scan(c);
1476 log_debug("sysview: udev scan failed: %s", strerror(-r));
1480 HASHMAP_FOREACH(seat, c->seat_map, i)
1481 seat->scanned = true;
1489 int sysview_context_rescan(sysview_context *c) {
1496 return sd_event_source_set_enabled(c->scan_src, SD_EVENT_ONESHOT);
1498 return sd_event_add_defer(c->event, &c->scan_src, context_scan_fn, c);