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 r = sd_bus_path_encode("/org/freedesktop/login1/seat", seat->name, &seat->path);
398 seat->session_map = hashmap_new(&string_hash_ops);
399 if (!seat->session_map)
402 seat->device_map = hashmap_new(&string_hash_ops);
403 if (!seat->device_map)
406 r = hashmap_put(c->seat_map, seat->name, seat);
416 sysview_seat *sysview_seat_free(sysview_seat *seat) {
420 assert(!seat->public);
421 assert(hashmap_size(seat->device_map) == 0);
422 assert(hashmap_size(seat->session_map) == 0);
425 hashmap_remove_value(seat->context->seat_map, seat->name, seat);
427 hashmap_free(seat->device_map);
428 hashmap_free(seat->session_map);
436 const char *sysview_seat_get_name(sysview_seat *seat) {
437 assert_return(seat, NULL);
442 int sysview_seat_switch_to(sysview_seat *seat, uint32_t nr) {
443 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
446 assert_return(seat, -EINVAL);
447 assert_return(seat->context->sysbus, -EINVAL);
449 r = sd_bus_message_new_method_call(seat->context->sysbus,
451 "org.freedesktop.login1",
453 "org.freedesktop.login1.Seat",
458 r = sd_bus_message_append(m, "u", nr);
462 return sd_bus_send(seat->context->sysbus, m, NULL);
469 static int context_raise(sysview_context *c, sysview_event *event, int def) {
470 return c->running ? c->event_fn(c, c->userdata, event) : def;
473 static int context_raise_seat_add(sysview_context *c, sysview_seat *seat) {
474 sysview_event event = {
475 .type = SYSVIEW_EVENT_SEAT_ADD,
481 return context_raise(c, &event, 0);
484 static int context_raise_seat_remove(sysview_context *c, sysview_seat *seat) {
485 sysview_event event = {
486 .type = SYSVIEW_EVENT_SEAT_REMOVE,
492 return context_raise(c, &event, 0);
495 static int context_raise_session_filter(sysview_context *c,
498 const char *username,
500 sysview_event event = {
501 .type = SYSVIEW_EVENT_SESSION_FILTER,
505 .username = username,
510 return context_raise(c, &event, 1);
513 static int context_raise_session_add(sysview_context *c, sysview_session *session) {
514 sysview_event event = {
515 .type = SYSVIEW_EVENT_SESSION_ADD,
521 return context_raise(c, &event, 0);
524 static int context_raise_session_remove(sysview_context *c, sysview_session *session) {
525 sysview_event event = {
526 .type = SYSVIEW_EVENT_SESSION_REMOVE,
532 return context_raise(c, &event, 0);
535 static int context_raise_session_control(sysview_context *c, sysview_session *session, int error) {
536 sysview_event event = {
537 .type = SYSVIEW_EVENT_SESSION_CONTROL,
544 return context_raise(c, &event, 0);
547 static int context_raise_session_attach(sysview_context *c, sysview_session *session, sysview_device *device) {
548 sysview_event event = {
549 .type = SYSVIEW_EVENT_SESSION_ATTACH,
556 return context_raise(c, &event, 0);
559 static int context_raise_session_detach(sysview_context *c, sysview_session *session, sysview_device *device) {
560 sysview_event event = {
561 .type = SYSVIEW_EVENT_SESSION_DETACH,
568 return context_raise(c, &event, 0);
571 static int context_raise_session_refresh(sysview_context *c, sysview_session *session, sysview_device *device, struct udev_device *ud) {
572 sysview_event event = {
573 .type = SYSVIEW_EVENT_SESSION_REFRESH,
581 return context_raise(c, &event, 0);
584 static void context_add_device(sysview_context *c, sysview_device *device) {
585 sysview_session *session;
592 log_debug("sysview: add device '%s' on seat '%s'",
593 device->name, device->seat->name);
595 HASHMAP_FOREACH(session, device->seat->session_map, i) {
596 if (!session->public)
599 r = context_raise_session_attach(c, session, device);
601 log_debug("sysview: callback failed while attaching device '%s' to session '%s': %s",
602 device->name, session->name, strerror(-r));
606 static void context_remove_device(sysview_context *c, sysview_device *device) {
607 sysview_session *session;
614 log_debug("sysview: remove device '%s'", device->name);
616 HASHMAP_FOREACH(session, device->seat->session_map, i) {
617 if (!session->public)
620 r = context_raise_session_detach(c, session, device);
622 log_debug("sysview: callback failed while detaching device '%s' from session '%s': %s",
623 device->name, session->name, strerror(-r));
626 sysview_device_free(device);
629 static void context_change_device(sysview_context *c, sysview_device *device, struct udev_device *ud) {
630 sysview_session *session;
637 log_debug("sysview: change device '%s'", device->name);
639 HASHMAP_FOREACH(session, device->seat->session_map, i) {
640 if (!session->public)
643 r = context_raise_session_refresh(c, session, device, ud);
645 log_debug("sysview: callback failed while changing device '%s' on session '%s': %s",
646 device->name, session->name, strerror(-r));
650 static void context_add_session(sysview_context *c, sysview_seat *seat, const char *id) {
651 sysview_session *session;
652 sysview_device *device;
660 session = sysview_find_session(c, id);
664 log_debug("sysview: add session '%s' on seat '%s'", id, seat->name);
666 r = sysview_session_new(&session, seat, id);
670 if (!seat->scanned) {
671 r = sysview_context_rescan(c);
677 session->public = true;
678 r = context_raise_session_add(c, session);
680 log_debug("sysview: callback failed while adding session '%s': %s",
681 session->name, strerror(-r));
682 session->public = false;
686 HASHMAP_FOREACH(device, seat->device_map, i) {
687 r = context_raise_session_attach(c, session, device);
689 log_debug("sysview: callback failed while attaching device '%s' to new session '%s': %s",
690 device->name, session->name, strerror(-r));
698 log_debug("sysview: error while adding session '%s': %s",
702 static void context_remove_session(sysview_context *c, sysview_session *session) {
703 sysview_device *device;
710 log_debug("sysview: remove session '%s'", session->name);
712 if (session->public) {
713 HASHMAP_FOREACH(device, session->seat->device_map, i) {
714 r = context_raise_session_detach(c, session, device);
716 log_debug("sysview: callback failed while detaching device '%s' from old session '%s': %s",
717 device->name, session->name, strerror(-r));
720 session->public = false;
721 r = context_raise_session_remove(c, session);
723 log_debug("sysview: callback failed while removing session '%s': %s",
724 session->name, strerror(-r));
727 if (!session->custom)
728 sysview_session_release_control(session);
730 sysview_session_free(session);
733 static void context_add_seat(sysview_context *c, const char *id) {
740 seat = sysview_find_seat(c, id);
744 log_debug("sysview: add seat '%s'", id);
746 r = sysview_seat_new(&seat, c, id);
751 r = context_raise_seat_add(c, seat);
753 log_debug("sysview: callback failed while adding seat '%s': %s",
754 seat->name, strerror(-r));
755 seat->public = false;
762 log_debug("sysview: error while adding seat '%s': %s",
766 static void context_remove_seat(sysview_context *c, sysview_seat *seat) {
767 sysview_session *session;
768 sysview_device *device;
774 log_debug("sysview: remove seat '%s'", seat->name);
776 while ((device = hashmap_first(seat->device_map)))
777 context_remove_device(c, device);
779 while ((session = hashmap_first(seat->session_map)))
780 context_remove_session(c, session);
783 seat->public = false;
784 r = context_raise_seat_remove(c, seat);
786 log_debug("sysview: callback failed while removing seat '%s': %s",
787 seat->name, strerror(-r));
790 sysview_seat_free(seat);
793 int sysview_context_new(sysview_context **out,
798 _cleanup_(sysview_context_freep) sysview_context *c = NULL;
801 assert_return(out, -EINVAL);
802 assert_return(event, -EINVAL);
804 log_debug("sysview: new");
806 c = new0(sysview_context, 1);
810 c->event = sd_event_ref(event);
811 if (flags & SYSVIEW_CONTEXT_SCAN_LOGIND)
812 c->scan_logind = true;
813 if (flags & SYSVIEW_CONTEXT_SCAN_EVDEV)
814 c->scan_evdev = true;
815 if (flags & SYSVIEW_CONTEXT_SCAN_DRM)
819 c->sysbus = sd_bus_ref(sysbus);
820 } else if (c->scan_logind) {
821 r = sd_bus_open_system(&c->sysbus);
827 c->ud = udev_ref(ud);
828 } else if (c->scan_evdev || c->scan_drm) {
832 return errno > 0 ? -errno : -EFAULT;
835 c->seat_map = hashmap_new(&string_hash_ops);
839 c->session_map = hashmap_new(&string_hash_ops);
843 c->device_map = hashmap_new(&string_hash_ops);
852 sysview_context *sysview_context_free(sysview_context *c) {
856 log_debug("sysview: free");
858 sysview_context_stop(c);
860 assert(hashmap_size(c->device_map) == 0);
861 assert(hashmap_size(c->session_map) == 0);
862 assert(hashmap_size(c->seat_map) == 0);
864 hashmap_free(c->device_map);
865 hashmap_free(c->session_map);
866 hashmap_free(c->seat_map);
867 c->ud = udev_unref(c->ud);
868 c->sysbus = sd_bus_unref(c->sysbus);
869 c->event = sd_event_unref(c->event);
875 static int context_ud_prepare_monitor(sysview_context *c, struct udev_monitor *m) {
879 r = udev_monitor_filter_add_match_subsystem_devtype(m, "input", NULL);
885 r = udev_monitor_filter_add_match_subsystem_devtype(m, "drm", NULL);
893 static int context_ud_prepare_scan(sysview_context *c, struct udev_enumerate *e) {
897 r = udev_enumerate_add_match_subsystem(e, "input");
903 r = udev_enumerate_add_match_subsystem(e, "drm");
908 r = udev_enumerate_add_match_is_initialized(e);
915 static int context_ud_hotplug(sysview_context *c, struct udev_device *d) {
916 const char *syspath, *sysname, *subsystem, *action, *seatname;
917 sysview_device *device;
920 syspath = udev_device_get_syspath(d);
921 sysname = udev_device_get_sysname(d);
922 subsystem = udev_device_get_subsystem(d);
923 action = udev_device_get_action(d);
925 /* not interested in custom devices without syspath/etc */
926 if (!syspath || !sysname || !subsystem)
929 device = sysview_find_device(c, syspath);
931 if (streq_ptr(action, "remove")) {
935 context_remove_device(c, device);
936 } else if (streq_ptr(action, "change")) {
940 context_change_device(c, device, d);
941 } else if (!action || streq_ptr(action, "add")) {
942 struct udev_device *p;
943 unsigned int type, t;
949 if (streq(subsystem, "input") && startswith(sysname, "event") && safe_atou(sysname + 5, &t) >= 0)
950 type = SYSVIEW_DEVICE_EVDEV;
951 else if (streq(subsystem, "drm") && startswith(sysname, "card"))
952 type = SYSVIEW_DEVICE_DRM;
956 if (type >= SYSVIEW_DEVICE_CNT)
962 seatname = udev_device_get_property_value(p, "ID_SEAT");
965 } while ((p = udev_device_get_parent(p)));
967 seat = sysview_find_seat(c, seatname ? : "seat0");
971 r = device_new_ud(&device, seat, type, d);
973 log_debug("sysview: cannot create device for udev-device '%s': %s",
974 syspath, strerror(-r));
978 context_add_device(c, device);
984 static int context_ud_monitor_fn(sd_event_source *s,
988 sysview_context *c = userdata;
989 struct udev_device *d;
992 if (revents & EPOLLIN) {
993 while ((d = udev_monitor_receive_device(c->ud_monitor))) {
994 r = context_ud_hotplug(c, d);
995 udev_device_unref(d);
1000 /* as long as EPOLLIN is signalled, read pending data */
1004 if (revents & (EPOLLHUP | EPOLLERR)) {
1005 log_debug("sysview: HUP on udev-monitor");
1006 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
1012 static int context_ud_start(sysview_context *c) {
1019 c->ud_monitor = udev_monitor_new_from_netlink(c->ud, "udev");
1021 return errno > 0 ? -errno : -EFAULT;
1023 r = context_ud_prepare_monitor(c, c->ud_monitor);
1027 r = udev_monitor_enable_receiving(c->ud_monitor);
1031 fd = udev_monitor_get_fd(c->ud_monitor);
1032 r = sd_event_add_io(c->event,
1035 EPOLLHUP | EPOLLERR | EPOLLIN,
1036 context_ud_monitor_fn,
1044 static void context_ud_stop(sysview_context *c) {
1045 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
1046 c->ud_monitor = udev_monitor_unref(c->ud_monitor);
1049 static int context_ud_scan(sysview_context *c) {
1050 _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
1051 struct udev_list_entry *entry;
1052 struct udev_device *d;
1059 e = udev_enumerate_new(c->ud);
1061 return errno > 0 ? -errno : -EFAULT;
1063 r = context_ud_prepare_scan(c, e);
1067 r = udev_enumerate_scan_devices(e);
1071 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
1074 name = udev_list_entry_get_name(entry);
1077 d = udev_device_new_from_syspath(c->ud, name);
1079 r = errno > 0 ? -errno : -EFAULT;
1080 log_debug("sysview: cannot create udev-device for %s: %s",
1081 name, strerror(-r));
1085 r = context_ud_hotplug(c, d);
1086 udev_device_unref(d);
1094 static int context_ld_seat_new(sysview_context *c, sd_bus_message *signal) {
1095 const char *id, *path;
1098 r = sd_bus_message_read(signal, "so", &id, &path);
1100 log_debug("sysview: cannot parse SeatNew from logind: %s",
1105 context_add_seat(c, id);
1109 static int context_ld_seat_removed(sysview_context *c, sd_bus_message *signal) {
1110 const char *id, *path;
1114 r = sd_bus_message_read(signal, "so", &id, &path);
1116 log_debug("sysview: cannot parse SeatRemoved from logind: %s",
1121 seat = sysview_find_seat(c, id);
1125 context_remove_seat(c, seat);
1129 static int context_ld_session_new(sysview_context *c, sd_bus_message *signal) {
1130 _cleanup_free_ char *seatid = NULL, *username = NULL;
1131 const char *id, *path;
1136 r = sd_bus_message_read(signal, "so", &id, &path);
1138 log_debug("sysview: cannot parse SessionNew from logind: %s",
1144 * As the dbus message didn't contain enough information, we
1145 * read missing bits via sd-login. Note that this might race session
1146 * destruction, so we handle ENOENT properly.
1149 /* ENOENT is also returned for sessions without seats */
1150 r = sd_session_get_seat(id, &seatid);
1156 seat = sysview_find_seat(c, seatid);
1160 r = sd_session_get_uid(id, &uid);
1166 username = lookup_uid(uid);
1172 r = context_raise_session_filter(c, id, seatid, username, uid);
1174 log_debug("sysview: callback failed while filtering session '%s': %s",
1177 context_add_session(c, seat, id);
1182 log_debug("sysview: failed retrieving information for new session '%s': %s",
1187 static int context_ld_session_removed(sysview_context *c, sd_bus_message *signal) {
1188 sysview_session *session;
1189 const char *id, *path;
1192 r = sd_bus_message_read(signal, "so", &id, &path);
1194 log_debug("sysview: cannot parse SessionRemoved from logind: %s",
1199 session = sysview_find_session(c, id);
1203 context_remove_session(c, session);
1207 static int context_ld_manager_signal_fn(sd_bus *bus,
1208 sd_bus_message *signal,
1210 sd_bus_error *ret_error) {
1211 sysview_context *c = userdata;
1213 if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatNew"))
1214 return context_ld_seat_new(c, signal);
1215 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatRemoved"))
1216 return context_ld_seat_removed(c, signal);
1217 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionNew"))
1218 return context_ld_session_new(c, signal);
1219 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionRemoved"))
1220 return context_ld_session_removed(c, signal);
1225 static int context_ld_start(sysview_context *c) {
1228 if (!c->scan_logind)
1231 r = sd_bus_add_match(c->sysbus,
1232 &c->ld_slot_manager_signal,
1234 "sender='org.freedesktop.login1',"
1235 "interface='org.freedesktop.login1.Manager',"
1236 "path='/org/freedesktop/login1'",
1237 context_ld_manager_signal_fn,
1245 static void context_ld_stop(sysview_context *c) {
1246 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1247 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1248 c->ld_slot_manager_signal = sd_bus_slot_unref(c->ld_slot_manager_signal);
1251 static int context_ld_list_seats_fn(sd_bus *bus,
1252 sd_bus_message *reply,
1254 sd_bus_error *ret_error) {
1255 sysview_context *c = userdata;
1258 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1260 if (sd_bus_message_is_method_error(reply, NULL)) {
1261 const sd_bus_error *error = sd_bus_message_get_error(reply);
1263 log_debug("sysview: ListSeats on logind failed: %s: %s",
1264 error->name, error->message);
1265 return -sd_bus_error_get_errno(error);
1268 r = sd_bus_message_enter_container(reply, 'a', "(so)");
1272 while ((r = sd_bus_message_enter_container(reply, 'r', "so")) > 0) {
1273 const char *id, *path;
1275 r = sd_bus_message_read(reply, "so", &id, &path);
1279 context_add_seat(c, id);
1281 r = sd_bus_message_exit_container(reply);
1289 r = sd_bus_message_exit_container(reply);
1296 log_debug("sysview: erroneous ListSeats response from logind: %s",
1301 static int context_ld_list_sessions_fn(sd_bus *bus,
1302 sd_bus_message *reply,
1304 sd_bus_error *ret_error) {
1305 sysview_context *c = userdata;
1308 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1310 if (sd_bus_message_is_method_error(reply, NULL)) {
1311 const sd_bus_error *error = sd_bus_message_get_error(reply);
1313 log_debug("sysview: ListSessions on logind failed: %s: %s",
1314 error->name, error->message);
1315 return -sd_bus_error_get_errno(error);
1318 r = sd_bus_message_enter_container(reply, 'a', "(susso)");
1322 while ((r = sd_bus_message_enter_container(reply, 'r', "susso")) > 0) {
1323 const char *id, *username, *seatid, *path;
1327 r = sd_bus_message_read(reply,
1337 seat = sysview_find_seat(c, seatid);
1339 r = context_raise_session_filter(c, id, seatid, username, uid);
1341 log_debug("sysview: callback failed while filtering session '%s': %s",
1344 context_add_session(c, seat, id);
1347 r = sd_bus_message_exit_container(reply);
1355 r = sd_bus_message_exit_container(reply);
1362 log_debug("sysview: erroneous ListSessions response from logind: %s",
1367 static int context_ld_scan(sysview_context *c) {
1368 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1371 if (!c->ld_slot_manager_signal)
1374 /* request seat list */
1376 r = sd_bus_message_new_method_call(c->sysbus,
1378 "org.freedesktop.login1",
1379 "/org/freedesktop/login1",
1380 "org.freedesktop.login1.Manager",
1385 r = sd_bus_call_async(c->sysbus,
1386 &c->ld_slot_list_seats,
1388 context_ld_list_seats_fn,
1394 /* request session list */
1396 m = sd_bus_message_unref(m);
1397 r = sd_bus_message_new_method_call(c->sysbus,
1399 "org.freedesktop.login1",
1400 "/org/freedesktop/login1",
1401 "org.freedesktop.login1.Manager",
1406 r = sd_bus_call_async(c->sysbus,
1407 &c->ld_slot_list_sessions,
1409 context_ld_list_sessions_fn,
1418 bool sysview_context_is_running(sysview_context *c) {
1419 return c && c->running;
1422 int sysview_context_start(sysview_context *c, sysview_event_fn event_fn, void *userdata) {
1425 assert_return(c, -EINVAL);
1426 assert_return(event_fn, -EINVAL);
1431 log_debug("sysview: start");
1434 c->event_fn = event_fn;
1435 c->userdata = userdata;
1437 r = context_ld_start(c);
1441 r = context_ud_start(c);
1445 r = sysview_context_rescan(c);
1452 sysview_context_stop(c);
1456 void sysview_context_stop(sysview_context *c) {
1457 sysview_session *session;
1458 sysview_device *device;
1466 log_debug("sysview: stop");
1468 while ((device = hashmap_first(c->device_map)))
1469 context_remove_device(c, device);
1471 while ((session = hashmap_first(c->session_map)))
1472 context_remove_session(c, session);
1474 while ((seat = hashmap_first(c->seat_map)))
1475 context_remove_seat(c, seat);
1481 c->scan_src = sd_event_source_unref(c->scan_src);
1486 static int context_scan_fn(sd_event_source *s, void *userdata) {
1487 sysview_context *c = userdata;
1493 r = context_ld_scan(c);
1495 log_debug("sysview: logind scan failed: %s", strerror(-r));
1500 /* skip device scans if no sessions are available */
1501 if (hashmap_size(c->session_map) > 0) {
1502 r = context_ud_scan(c);
1504 log_debug("sysview: udev scan failed: %s", strerror(-r));
1508 HASHMAP_FOREACH(seat, c->seat_map, i)
1509 seat->scanned = true;
1517 int sysview_context_rescan(sysview_context *c) {
1524 return sd_event_source_set_enabled(c->scan_src, SD_EVENT_ONESHOT);
1526 return sd_event_add_defer(c->event, &c->scan_src, context_scan_fn, c);