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_seat_add(sysview_context *c, sysview_seat *seat) {
478 sysview_event event = {
479 .type = SYSVIEW_EVENT_SEAT_ADD,
485 return context_raise(c, &event, 0);
488 static int context_raise_seat_remove(sysview_context *c, sysview_seat *seat) {
489 sysview_event event = {
490 .type = SYSVIEW_EVENT_SEAT_REMOVE,
496 return context_raise(c, &event, 0);
499 static int context_raise_session_filter(sysview_context *c,
502 const char *username,
504 sysview_event event = {
505 .type = SYSVIEW_EVENT_SESSION_FILTER,
509 .username = username,
514 return context_raise(c, &event, 1);
517 static int context_raise_session_add(sysview_context *c, sysview_session *session) {
518 sysview_event event = {
519 .type = SYSVIEW_EVENT_SESSION_ADD,
525 return context_raise(c, &event, 0);
528 static int context_raise_session_remove(sysview_context *c, sysview_session *session) {
529 sysview_event event = {
530 .type = SYSVIEW_EVENT_SESSION_REMOVE,
536 return context_raise(c, &event, 0);
539 static int context_raise_session_control(sysview_context *c, sysview_session *session, int error) {
540 sysview_event event = {
541 .type = SYSVIEW_EVENT_SESSION_CONTROL,
548 return context_raise(c, &event, 0);
551 static int context_raise_session_attach(sysview_context *c, sysview_session *session, sysview_device *device) {
552 sysview_event event = {
553 .type = SYSVIEW_EVENT_SESSION_ATTACH,
560 return context_raise(c, &event, 0);
563 static int context_raise_session_detach(sysview_context *c, sysview_session *session, sysview_device *device) {
564 sysview_event event = {
565 .type = SYSVIEW_EVENT_SESSION_DETACH,
572 return context_raise(c, &event, 0);
575 static int context_raise_session_refresh(sysview_context *c, sysview_session *session, sysview_device *device, struct udev_device *ud) {
576 sysview_event event = {
577 .type = SYSVIEW_EVENT_SESSION_REFRESH,
585 return context_raise(c, &event, 0);
588 static void context_add_device(sysview_context *c, sysview_device *device) {
589 sysview_session *session;
596 log_debug("sysview: add device '%s' on seat '%s'",
597 device->name, device->seat->name);
599 HASHMAP_FOREACH(session, device->seat->session_map, i) {
600 if (!session->public)
603 r = context_raise_session_attach(c, session, device);
605 log_debug_errno(r, "sysview: callback failed while attaching device '%s' to session '%s': %m",
606 device->name, session->name);
610 static void context_remove_device(sysview_context *c, sysview_device *device) {
611 sysview_session *session;
618 log_debug("sysview: remove device '%s'", device->name);
620 HASHMAP_FOREACH(session, device->seat->session_map, i) {
621 if (!session->public)
624 r = context_raise_session_detach(c, session, device);
626 log_debug_errno(r, "sysview: callback failed while detaching device '%s' from session '%s': %m",
627 device->name, session->name);
630 sysview_device_free(device);
633 static void context_change_device(sysview_context *c, sysview_device *device, struct udev_device *ud) {
634 sysview_session *session;
641 log_debug("sysview: change device '%s'", device->name);
643 HASHMAP_FOREACH(session, device->seat->session_map, i) {
644 if (!session->public)
647 r = context_raise_session_refresh(c, session, device, ud);
649 log_debug_errno(r, "sysview: callback failed while changing device '%s' on session '%s': %m",
650 device->name, session->name);
654 static void context_add_session(sysview_context *c, sysview_seat *seat, const char *id) {
655 sysview_session *session;
656 sysview_device *device;
664 session = sysview_find_session(c, id);
668 log_debug("sysview: add session '%s' on seat '%s'", id, seat->name);
670 r = sysview_session_new(&session, seat, id);
674 if (!seat->scanned) {
675 r = sysview_context_rescan(c);
681 session->public = true;
682 r = context_raise_session_add(c, session);
684 log_debug_errno(r, "sysview: callback failed while adding session '%s': %m",
686 session->public = false;
690 HASHMAP_FOREACH(device, seat->device_map, i) {
691 r = context_raise_session_attach(c, session, device);
693 log_debug_errno(r, "sysview: callback failed while attaching device '%s' to new session '%s': %m",
694 device->name, session->name);
702 log_debug_errno(r, "sysview: error while adding session '%s': %m",
706 static void context_remove_session(sysview_context *c, sysview_session *session) {
707 sysview_device *device;
714 log_debug("sysview: remove session '%s'", session->name);
716 if (session->public) {
717 HASHMAP_FOREACH(device, session->seat->device_map, i) {
718 r = context_raise_session_detach(c, session, device);
720 log_debug_errno(r, "sysview: callback failed while detaching device '%s' from old session '%s': %m",
721 device->name, session->name);
724 session->public = false;
725 r = context_raise_session_remove(c, session);
727 log_debug_errno(r, "sysview: callback failed while removing session '%s': %m",
731 if (!session->custom)
732 sysview_session_release_control(session);
734 sysview_session_free(session);
737 static void context_add_seat(sysview_context *c, const char *id) {
744 seat = sysview_find_seat(c, id);
748 log_debug("sysview: add seat '%s'", id);
750 r = sysview_seat_new(&seat, c, id);
755 r = context_raise_seat_add(c, seat);
757 log_debug_errno(r, "sysview: callback failed while adding seat '%s': %m",
759 seat->public = false;
766 log_debug_errno(r, "sysview: error while adding seat '%s': %m",
770 static void context_remove_seat(sysview_context *c, sysview_seat *seat) {
771 sysview_session *session;
772 sysview_device *device;
778 log_debug("sysview: remove seat '%s'", seat->name);
780 while ((device = hashmap_first(seat->device_map)))
781 context_remove_device(c, device);
783 while ((session = hashmap_first(seat->session_map)))
784 context_remove_session(c, session);
787 seat->public = false;
788 r = context_raise_seat_remove(c, seat);
790 log_debug_errno(r, "sysview: callback failed while removing seat '%s': %m",
794 sysview_seat_free(seat);
797 int sysview_context_new(sysview_context **out,
802 _cleanup_(sysview_context_freep) sysview_context *c = NULL;
805 assert_return(out, -EINVAL);
806 assert_return(event, -EINVAL);
808 log_debug("sysview: new");
810 c = new0(sysview_context, 1);
814 c->event = sd_event_ref(event);
815 if (flags & SYSVIEW_CONTEXT_SCAN_LOGIND)
816 c->scan_logind = true;
817 if (flags & SYSVIEW_CONTEXT_SCAN_EVDEV)
818 c->scan_evdev = true;
819 if (flags & SYSVIEW_CONTEXT_SCAN_DRM)
823 c->sysbus = sd_bus_ref(sysbus);
824 } else if (c->scan_logind) {
825 r = sd_bus_open_system(&c->sysbus);
831 c->ud = udev_ref(ud);
832 } else if (c->scan_evdev || c->scan_drm) {
836 return errno > 0 ? -errno : -EFAULT;
839 c->seat_map = hashmap_new(&string_hash_ops);
843 c->session_map = hashmap_new(&string_hash_ops);
847 c->device_map = hashmap_new(&string_hash_ops);
856 sysview_context *sysview_context_free(sysview_context *c) {
860 log_debug("sysview: free");
862 sysview_context_stop(c);
864 assert(hashmap_size(c->device_map) == 0);
865 assert(hashmap_size(c->session_map) == 0);
866 assert(hashmap_size(c->seat_map) == 0);
868 hashmap_free(c->device_map);
869 hashmap_free(c->session_map);
870 hashmap_free(c->seat_map);
871 c->ud = udev_unref(c->ud);
872 c->sysbus = sd_bus_unref(c->sysbus);
873 c->event = sd_event_unref(c->event);
879 static int context_ud_prepare_monitor(sysview_context *c, struct udev_monitor *m) {
883 r = udev_monitor_filter_add_match_subsystem_devtype(m, "input", NULL);
889 r = udev_monitor_filter_add_match_subsystem_devtype(m, "drm", NULL);
897 static int context_ud_prepare_scan(sysview_context *c, struct udev_enumerate *e) {
901 r = udev_enumerate_add_match_subsystem(e, "input");
907 r = udev_enumerate_add_match_subsystem(e, "drm");
912 r = udev_enumerate_add_match_is_initialized(e);
919 static int context_ud_hotplug(sysview_context *c, struct udev_device *d) {
920 const char *syspath, *sysname, *subsystem, *action, *seatname;
921 sysview_device *device;
924 syspath = udev_device_get_syspath(d);
925 sysname = udev_device_get_sysname(d);
926 subsystem = udev_device_get_subsystem(d);
927 action = udev_device_get_action(d);
929 /* not interested in custom devices without syspath/etc */
930 if (!syspath || !sysname || !subsystem)
933 device = sysview_find_device(c, syspath);
935 if (streq_ptr(action, "remove")) {
939 context_remove_device(c, device);
940 } else if (streq_ptr(action, "change")) {
944 context_change_device(c, device, d);
945 } else if (!action || streq_ptr(action, "add")) {
946 struct udev_device *p;
947 unsigned int type, t;
953 if (streq(subsystem, "input") && startswith(sysname, "event") && safe_atou(sysname + 5, &t) >= 0)
954 type = SYSVIEW_DEVICE_EVDEV;
955 else if (streq(subsystem, "drm") && startswith(sysname, "card"))
956 type = SYSVIEW_DEVICE_DRM;
960 if (type >= SYSVIEW_DEVICE_CNT)
966 seatname = udev_device_get_property_value(p, "ID_SEAT");
969 } while ((p = udev_device_get_parent(p)));
971 seat = sysview_find_seat(c, seatname ? : "seat0");
975 r = device_new_ud(&device, seat, type, d);
977 return log_debug_errno(r, "sysview: cannot create device for udev-device '%s': %m",
980 context_add_device(c, device);
986 static int context_ud_monitor_fn(sd_event_source *s,
990 sysview_context *c = userdata;
991 struct udev_device *d;
994 if (revents & EPOLLIN) {
995 while ((d = udev_monitor_receive_device(c->ud_monitor))) {
996 r = context_ud_hotplug(c, d);
997 udev_device_unref(d);
1002 /* as long as EPOLLIN is signalled, read pending data */
1006 if (revents & (EPOLLHUP | EPOLLERR)) {
1007 log_debug("sysview: HUP on udev-monitor");
1008 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
1014 static int context_ud_start(sysview_context *c) {
1021 c->ud_monitor = udev_monitor_new_from_netlink(c->ud, "udev");
1023 return errno > 0 ? -errno : -EFAULT;
1025 r = context_ud_prepare_monitor(c, c->ud_monitor);
1029 r = udev_monitor_enable_receiving(c->ud_monitor);
1033 fd = udev_monitor_get_fd(c->ud_monitor);
1034 r = sd_event_add_io(c->event,
1037 EPOLLHUP | EPOLLERR | EPOLLIN,
1038 context_ud_monitor_fn,
1046 static void context_ud_stop(sysview_context *c) {
1047 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
1048 c->ud_monitor = udev_monitor_unref(c->ud_monitor);
1051 static int context_ud_scan(sysview_context *c) {
1052 _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
1053 struct udev_list_entry *entry;
1054 struct udev_device *d;
1061 e = udev_enumerate_new(c->ud);
1063 return errno > 0 ? -errno : -EFAULT;
1065 r = context_ud_prepare_scan(c, e);
1069 r = udev_enumerate_scan_devices(e);
1073 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
1076 name = udev_list_entry_get_name(entry);
1079 d = udev_device_new_from_syspath(c->ud, name);
1081 r = errno > 0 ? -errno : -EFAULT;
1082 log_debug_errno(r, "sysview: cannot create udev-device for %s: %m",
1087 r = context_ud_hotplug(c, d);
1088 udev_device_unref(d);
1096 static int context_ld_seat_new(sysview_context *c, sd_bus_message *signal) {
1097 const char *id, *path;
1100 r = sd_bus_message_read(signal, "so", &id, &path);
1102 return log_debug_errno(r, "sysview: cannot parse SeatNew from logind: %m");
1104 context_add_seat(c, id);
1108 static int context_ld_seat_removed(sysview_context *c, sd_bus_message *signal) {
1109 const char *id, *path;
1113 r = sd_bus_message_read(signal, "so", &id, &path);
1115 return log_debug_errno(r, "sysview: cannot parse SeatRemoved from logind: %m");
1117 seat = sysview_find_seat(c, id);
1121 context_remove_seat(c, seat);
1125 static int context_ld_session_new(sysview_context *c, sd_bus_message *signal) {
1126 _cleanup_free_ char *seatid = NULL, *username = NULL;
1127 const char *id, *path;
1132 r = sd_bus_message_read(signal, "so", &id, &path);
1134 return log_debug_errno(r, "sysview: cannot parse SessionNew from logind: %m");
1137 * As the dbus message didn't contain enough information, we
1138 * read missing bits via sd-login. Note that this might race session
1139 * destruction, so we handle ENOENT properly.
1142 /* ENOENT is also returned for sessions without seats */
1143 r = sd_session_get_seat(id, &seatid);
1149 seat = sysview_find_seat(c, seatid);
1153 r = sd_session_get_uid(id, &uid);
1159 username = lookup_uid(uid);
1165 r = context_raise_session_filter(c, id, seatid, username, uid);
1167 log_debug_errno(r, "sysview: callback failed while filtering session '%s': %m",
1170 context_add_session(c, seat, id);
1175 return log_debug_errno(r, "sysview: failed retrieving information for new session '%s': %m",
1179 static int context_ld_session_removed(sysview_context *c, sd_bus_message *signal) {
1180 sysview_session *session;
1181 const char *id, *path;
1184 r = sd_bus_message_read(signal, "so", &id, &path);
1186 return log_debug_errno(r, "sysview: cannot parse SessionRemoved from logind: %m");
1188 session = sysview_find_session(c, id);
1192 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 context_add_seat(c, id);
1270 r = sd_bus_message_exit_container(reply);
1278 r = sd_bus_message_exit_container(reply);
1285 return log_debug_errno(r, "sysview: erroneous ListSeats response from logind: %m");
1288 static int context_ld_list_sessions_fn(sd_bus *bus,
1289 sd_bus_message *reply,
1291 sd_bus_error *ret_error) {
1292 sysview_context *c = userdata;
1295 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1297 if (sd_bus_message_is_method_error(reply, NULL)) {
1298 const sd_bus_error *error = sd_bus_message_get_error(reply);
1300 log_debug("sysview: ListSessions on logind failed: %s: %s",
1301 error->name, error->message);
1302 return -sd_bus_error_get_errno(error);
1305 r = sd_bus_message_enter_container(reply, 'a', "(susso)");
1309 while ((r = sd_bus_message_enter_container(reply, 'r', "susso")) > 0) {
1310 const char *id, *username, *seatid, *path;
1314 r = sd_bus_message_read(reply,
1324 seat = sysview_find_seat(c, seatid);
1326 r = context_raise_session_filter(c, id, seatid, username, uid);
1328 log_debug_errno(r, "sysview: callback failed while filtering session '%s': %m",
1331 context_add_session(c, seat, id);
1334 r = sd_bus_message_exit_container(reply);
1342 r = sd_bus_message_exit_container(reply);
1349 return log_debug_errno(r, "sysview: erroneous ListSessions response from logind: %m");
1352 static int context_ld_scan(sysview_context *c) {
1353 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1356 if (!c->ld_slot_manager_signal)
1359 /* request seat list */
1361 r = sd_bus_message_new_method_call(c->sysbus,
1363 "org.freedesktop.login1",
1364 "/org/freedesktop/login1",
1365 "org.freedesktop.login1.Manager",
1370 r = sd_bus_call_async(c->sysbus,
1371 &c->ld_slot_list_seats,
1373 context_ld_list_seats_fn,
1379 /* request session list */
1381 m = sd_bus_message_unref(m);
1382 r = sd_bus_message_new_method_call(c->sysbus,
1384 "org.freedesktop.login1",
1385 "/org/freedesktop/login1",
1386 "org.freedesktop.login1.Manager",
1391 r = sd_bus_call_async(c->sysbus,
1392 &c->ld_slot_list_sessions,
1394 context_ld_list_sessions_fn,
1403 bool sysview_context_is_running(sysview_context *c) {
1404 return c && c->running;
1407 int sysview_context_start(sysview_context *c, sysview_event_fn event_fn, void *userdata) {
1410 assert_return(c, -EINVAL);
1411 assert_return(event_fn, -EINVAL);
1416 log_debug("sysview: start");
1419 c->event_fn = event_fn;
1420 c->userdata = userdata;
1422 r = context_ld_start(c);
1426 r = context_ud_start(c);
1430 r = sysview_context_rescan(c);
1437 sysview_context_stop(c);
1441 void sysview_context_stop(sysview_context *c) {
1442 sysview_session *session;
1443 sysview_device *device;
1451 log_debug("sysview: stop");
1453 while ((device = hashmap_first(c->device_map)))
1454 context_remove_device(c, device);
1456 while ((session = hashmap_first(c->session_map)))
1457 context_remove_session(c, session);
1459 while ((seat = hashmap_first(c->seat_map)))
1460 context_remove_seat(c, seat);
1466 c->scan_src = sd_event_source_unref(c->scan_src);
1471 static int context_scan_fn(sd_event_source *s, void *userdata) {
1472 sysview_context *c = userdata;
1478 r = context_ld_scan(c);
1480 return log_debug_errno(r, "sysview: logind scan failed: %m");
1483 /* skip device scans if no sessions are available */
1484 if (hashmap_size(c->session_map) > 0) {
1485 r = context_ud_scan(c);
1487 return log_debug_errno(r, "sysview: udev scan failed: %m");
1489 HASHMAP_FOREACH(seat, c->seat_map, i)
1490 seat->scanned = true;
1498 int sysview_context_rescan(sysview_context *c) {
1505 return sd_event_source_set_enabled(c->scan_src, SD_EVENT_ONESHOT);
1507 return sd_event_add_defer(c->event, &c->scan_src, context_scan_fn, c);