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 sysview_seat *sysview_session_get_seat(sysview_session *session) {
271 assert_return(session, NULL);
273 return session->seat;
276 static int session_take_control_fn(sd_bus *bus,
277 sd_bus_message *reply,
279 sd_bus_error *ret_error) {
280 sysview_session *session = userdata;
283 session->slot_take_control = sd_bus_slot_unref(session->slot_take_control);
285 if (sd_bus_message_is_method_error(reply, NULL)) {
286 const sd_bus_error *e = sd_bus_message_get_error(reply);
288 log_debug("sysview: %s: TakeControl failed: %s: %s",
289 session->name, e->name, e->message);
290 error = -sd_bus_error_get_errno(e);
292 session->has_control = true;
296 r = context_raise_session_control(session->seat->context, session, error);
298 log_debug_errno(r, "sysview: callback failed while signalling session control '%d' on session '%s': %m",
299 error, session->name);
304 int sysview_session_take_control(sysview_session *session) {
305 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
308 assert_return(session, -EINVAL);
309 assert_return(!session->custom, -EINVAL);
311 if (session->wants_control)
314 r = sd_bus_message_new_method_call(session->seat->context->sysbus,
316 "org.freedesktop.login1",
318 "org.freedesktop.login1.Session",
323 r = sd_bus_message_append(m, "b", 0);
327 r = sd_bus_call_async(session->seat->context->sysbus,
328 &session->slot_take_control,
330 session_take_control_fn,
336 session->wants_control = true;
340 void sysview_session_release_control(sysview_session *session) {
341 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
345 assert(!session->custom);
347 if (!session->wants_control)
350 session->wants_control = false;
352 if (!session->has_control && !session->slot_take_control)
355 session->has_control = false;
356 session->slot_take_control = sd_bus_slot_unref(session->slot_take_control);
358 r = sd_bus_message_new_method_call(session->seat->context->sysbus,
360 "org.freedesktop.login1",
362 "org.freedesktop.login1.Session",
365 r = sd_bus_send(session->seat->context->sysbus, m, NULL);
367 if (r < 0 && r != -ENOTCONN)
368 log_debug_errno(r, "sysview: %s: cannot send ReleaseControl: %m",
376 sysview_seat *sysview_find_seat(sysview_context *c, const char *name) {
377 assert_return(c, NULL);
378 assert_return(name, NULL);
380 return hashmap_get(c->seat_map, name);
383 int sysview_seat_new(sysview_seat **out, sysview_context *c, const char *name) {
384 _cleanup_(sysview_seat_freep) sysview_seat *seat = NULL;
387 assert_return(c, -EINVAL);
388 assert_return(name, -EINVAL);
390 seat = new0(sysview_seat, 1);
396 seat->name = strdup(name);
400 r = sd_bus_path_encode("/org/freedesktop/login1/seat", seat->name, &seat->path);
404 seat->session_map = hashmap_new(&string_hash_ops);
405 if (!seat->session_map)
408 seat->device_map = hashmap_new(&string_hash_ops);
409 if (!seat->device_map)
412 r = hashmap_put(c->seat_map, seat->name, seat);
422 sysview_seat *sysview_seat_free(sysview_seat *seat) {
426 assert(!seat->public);
427 assert(hashmap_size(seat->device_map) == 0);
428 assert(hashmap_size(seat->session_map) == 0);
431 hashmap_remove_value(seat->context->seat_map, seat->name, seat);
433 hashmap_free(seat->device_map);
434 hashmap_free(seat->session_map);
442 const char *sysview_seat_get_name(sysview_seat *seat) {
443 assert_return(seat, NULL);
448 int sysview_seat_switch_to(sysview_seat *seat, uint32_t nr) {
449 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
452 assert_return(seat, -EINVAL);
453 assert_return(seat->context->sysbus, -EINVAL);
455 r = sd_bus_message_new_method_call(seat->context->sysbus,
457 "org.freedesktop.login1",
459 "org.freedesktop.login1.Seat",
464 r = sd_bus_message_append(m, "u", nr);
468 return sd_bus_send(seat->context->sysbus, m, NULL);
475 static int context_raise(sysview_context *c, sysview_event *event, int def) {
476 return c->running ? c->event_fn(c, c->userdata, event) : def;
479 static int context_raise_seat_add(sysview_context *c, sysview_seat *seat) {
480 sysview_event event = {
481 .type = SYSVIEW_EVENT_SEAT_ADD,
487 return context_raise(c, &event, 0);
490 static int context_raise_seat_remove(sysview_context *c, sysview_seat *seat) {
491 sysview_event event = {
492 .type = SYSVIEW_EVENT_SEAT_REMOVE,
498 return context_raise(c, &event, 0);
501 static int context_raise_session_filter(sysview_context *c,
504 const char *username,
506 sysview_event event = {
507 .type = SYSVIEW_EVENT_SESSION_FILTER,
511 .username = username,
516 return context_raise(c, &event, 1);
519 static int context_raise_session_add(sysview_context *c, sysview_session *session) {
520 sysview_event event = {
521 .type = SYSVIEW_EVENT_SESSION_ADD,
527 return context_raise(c, &event, 0);
530 static int context_raise_session_remove(sysview_context *c, sysview_session *session) {
531 sysview_event event = {
532 .type = SYSVIEW_EVENT_SESSION_REMOVE,
538 return context_raise(c, &event, 0);
541 static int context_raise_session_control(sysview_context *c, sysview_session *session, int error) {
542 sysview_event event = {
543 .type = SYSVIEW_EVENT_SESSION_CONTROL,
550 return context_raise(c, &event, 0);
553 static int context_raise_session_attach(sysview_context *c, sysview_session *session, sysview_device *device) {
554 sysview_event event = {
555 .type = SYSVIEW_EVENT_SESSION_ATTACH,
562 return context_raise(c, &event, 0);
565 static int context_raise_session_detach(sysview_context *c, sysview_session *session, sysview_device *device) {
566 sysview_event event = {
567 .type = SYSVIEW_EVENT_SESSION_DETACH,
574 return context_raise(c, &event, 0);
577 static int context_raise_session_refresh(sysview_context *c, sysview_session *session, sysview_device *device, struct udev_device *ud) {
578 sysview_event event = {
579 .type = SYSVIEW_EVENT_SESSION_REFRESH,
587 return context_raise(c, &event, 0);
590 static void context_add_device(sysview_context *c, sysview_device *device) {
591 sysview_session *session;
598 log_debug("sysview: add device '%s' on seat '%s'",
599 device->name, device->seat->name);
601 HASHMAP_FOREACH(session, device->seat->session_map, i) {
602 if (!session->public)
605 r = context_raise_session_attach(c, session, device);
607 log_debug_errno(r, "sysview: callback failed while attaching device '%s' to session '%s': %m",
608 device->name, session->name);
612 static void context_remove_device(sysview_context *c, sysview_device *device) {
613 sysview_session *session;
620 log_debug("sysview: remove device '%s'", device->name);
622 HASHMAP_FOREACH(session, device->seat->session_map, i) {
623 if (!session->public)
626 r = context_raise_session_detach(c, session, device);
628 log_debug_errno(r, "sysview: callback failed while detaching device '%s' from session '%s': %m",
629 device->name, session->name);
632 sysview_device_free(device);
635 static void context_change_device(sysview_context *c, sysview_device *device, struct udev_device *ud) {
636 sysview_session *session;
643 log_debug("sysview: change device '%s'", device->name);
645 HASHMAP_FOREACH(session, device->seat->session_map, i) {
646 if (!session->public)
649 r = context_raise_session_refresh(c, session, device, ud);
651 log_debug_errno(r, "sysview: callback failed while changing device '%s' on session '%s': %m",
652 device->name, session->name);
656 static void context_add_session(sysview_context *c, sysview_seat *seat, const char *id) {
657 sysview_session *session;
658 sysview_device *device;
666 session = sysview_find_session(c, id);
670 log_debug("sysview: add session '%s' on seat '%s'", id, seat->name);
672 r = sysview_session_new(&session, seat, id);
676 if (!seat->scanned) {
677 r = sysview_context_rescan(c);
683 session->public = true;
684 r = context_raise_session_add(c, session);
686 log_debug_errno(r, "sysview: callback failed while adding session '%s': %m",
688 session->public = false;
692 HASHMAP_FOREACH(device, seat->device_map, i) {
693 r = context_raise_session_attach(c, session, device);
695 log_debug_errno(r, "sysview: callback failed while attaching device '%s' to new session '%s': %m",
696 device->name, session->name);
704 log_debug_errno(r, "sysview: error while adding session '%s': %m",
708 static void context_remove_session(sysview_context *c, sysview_session *session) {
709 sysview_device *device;
716 log_debug("sysview: remove session '%s'", session->name);
718 if (session->public) {
719 HASHMAP_FOREACH(device, session->seat->device_map, i) {
720 r = context_raise_session_detach(c, session, device);
722 log_debug_errno(r, "sysview: callback failed while detaching device '%s' from old session '%s': %m",
723 device->name, session->name);
726 session->public = false;
727 r = context_raise_session_remove(c, session);
729 log_debug_errno(r, "sysview: callback failed while removing session '%s': %m",
733 if (!session->custom)
734 sysview_session_release_control(session);
736 sysview_session_free(session);
739 static void context_add_seat(sysview_context *c, const char *id) {
746 seat = sysview_find_seat(c, id);
750 log_debug("sysview: add seat '%s'", id);
752 r = sysview_seat_new(&seat, c, id);
757 r = context_raise_seat_add(c, seat);
759 log_debug_errno(r, "sysview: callback failed while adding seat '%s': %m",
761 seat->public = false;
768 log_debug_errno(r, "sysview: error while adding seat '%s': %m",
772 static void context_remove_seat(sysview_context *c, sysview_seat *seat) {
773 sysview_session *session;
774 sysview_device *device;
780 log_debug("sysview: remove seat '%s'", seat->name);
782 while ((device = hashmap_first(seat->device_map)))
783 context_remove_device(c, device);
785 while ((session = hashmap_first(seat->session_map)))
786 context_remove_session(c, session);
789 seat->public = false;
790 r = context_raise_seat_remove(c, seat);
792 log_debug_errno(r, "sysview: callback failed while removing seat '%s': %m",
796 sysview_seat_free(seat);
799 int sysview_context_new(sysview_context **out,
804 _cleanup_(sysview_context_freep) sysview_context *c = NULL;
807 assert_return(out, -EINVAL);
808 assert_return(event, -EINVAL);
810 log_debug("sysview: new");
812 c = new0(sysview_context, 1);
816 c->event = sd_event_ref(event);
817 if (flags & SYSVIEW_CONTEXT_SCAN_LOGIND)
818 c->scan_logind = true;
819 if (flags & SYSVIEW_CONTEXT_SCAN_EVDEV)
820 c->scan_evdev = true;
821 if (flags & SYSVIEW_CONTEXT_SCAN_DRM)
825 c->sysbus = sd_bus_ref(sysbus);
826 } else if (c->scan_logind) {
827 r = sd_bus_open_system(&c->sysbus);
833 c->ud = udev_ref(ud);
834 } else if (c->scan_evdev || c->scan_drm) {
838 return errno > 0 ? -errno : -EFAULT;
841 c->seat_map = hashmap_new(&string_hash_ops);
845 c->session_map = hashmap_new(&string_hash_ops);
849 c->device_map = hashmap_new(&string_hash_ops);
858 sysview_context *sysview_context_free(sysview_context *c) {
862 log_debug("sysview: free");
864 sysview_context_stop(c);
866 assert(hashmap_size(c->device_map) == 0);
867 assert(hashmap_size(c->session_map) == 0);
868 assert(hashmap_size(c->seat_map) == 0);
870 hashmap_free(c->device_map);
871 hashmap_free(c->session_map);
872 hashmap_free(c->seat_map);
873 c->ud = udev_unref(c->ud);
874 c->sysbus = sd_bus_unref(c->sysbus);
875 c->event = sd_event_unref(c->event);
881 static int context_ud_prepare_monitor(sysview_context *c, struct udev_monitor *m) {
885 r = udev_monitor_filter_add_match_subsystem_devtype(m, "input", NULL);
891 r = udev_monitor_filter_add_match_subsystem_devtype(m, "drm", NULL);
899 static int context_ud_prepare_scan(sysview_context *c, struct udev_enumerate *e) {
903 r = udev_enumerate_add_match_subsystem(e, "input");
909 r = udev_enumerate_add_match_subsystem(e, "drm");
914 r = udev_enumerate_add_match_is_initialized(e);
921 static int context_ud_hotplug(sysview_context *c, struct udev_device *d) {
922 const char *syspath, *sysname, *subsystem, *action, *seatname;
923 sysview_device *device;
926 syspath = udev_device_get_syspath(d);
927 sysname = udev_device_get_sysname(d);
928 subsystem = udev_device_get_subsystem(d);
929 action = udev_device_get_action(d);
931 /* not interested in custom devices without syspath/etc */
932 if (!syspath || !sysname || !subsystem)
935 device = sysview_find_device(c, syspath);
937 if (streq_ptr(action, "remove")) {
941 context_remove_device(c, device);
942 } else if (streq_ptr(action, "change")) {
946 context_change_device(c, device, d);
947 } else if (!action || streq_ptr(action, "add")) {
948 struct udev_device *p;
949 unsigned int type, t;
955 if (streq(subsystem, "input") && startswith(sysname, "event") && safe_atou(sysname + 5, &t) >= 0)
956 type = SYSVIEW_DEVICE_EVDEV;
957 else if (streq(subsystem, "drm") && startswith(sysname, "card"))
958 type = SYSVIEW_DEVICE_DRM;
962 if (type >= SYSVIEW_DEVICE_CNT)
968 seatname = udev_device_get_property_value(p, "ID_SEAT");
971 } while ((p = udev_device_get_parent(p)));
973 seat = sysview_find_seat(c, seatname ? : "seat0");
977 r = device_new_ud(&device, seat, type, d);
979 log_debug_errno(r, "sysview: cannot create device for udev-device '%s': %m",
984 context_add_device(c, device);
990 static int context_ud_monitor_fn(sd_event_source *s,
994 sysview_context *c = userdata;
995 struct udev_device *d;
998 if (revents & EPOLLIN) {
999 while ((d = udev_monitor_receive_device(c->ud_monitor))) {
1000 r = context_ud_hotplug(c, d);
1001 udev_device_unref(d);
1006 /* as long as EPOLLIN is signalled, read pending data */
1010 if (revents & (EPOLLHUP | EPOLLERR)) {
1011 log_debug("sysview: HUP on udev-monitor");
1012 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
1018 static int context_ud_start(sysview_context *c) {
1025 c->ud_monitor = udev_monitor_new_from_netlink(c->ud, "udev");
1027 return errno > 0 ? -errno : -EFAULT;
1029 r = context_ud_prepare_monitor(c, c->ud_monitor);
1033 r = udev_monitor_enable_receiving(c->ud_monitor);
1037 fd = udev_monitor_get_fd(c->ud_monitor);
1038 r = sd_event_add_io(c->event,
1041 EPOLLHUP | EPOLLERR | EPOLLIN,
1042 context_ud_monitor_fn,
1050 static void context_ud_stop(sysview_context *c) {
1051 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
1052 c->ud_monitor = udev_monitor_unref(c->ud_monitor);
1055 static int context_ud_scan(sysview_context *c) {
1056 _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
1057 struct udev_list_entry *entry;
1058 struct udev_device *d;
1065 e = udev_enumerate_new(c->ud);
1067 return errno > 0 ? -errno : -EFAULT;
1069 r = context_ud_prepare_scan(c, e);
1073 r = udev_enumerate_scan_devices(e);
1077 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
1080 name = udev_list_entry_get_name(entry);
1083 d = udev_device_new_from_syspath(c->ud, name);
1085 r = errno > 0 ? -errno : -EFAULT;
1086 log_debug_errno(r, "sysview: cannot create udev-device for %s: %m",
1091 r = context_ud_hotplug(c, d);
1092 udev_device_unref(d);
1100 static int context_ld_seat_new(sysview_context *c, sd_bus_message *signal) {
1101 const char *id, *path;
1104 r = sd_bus_message_read(signal, "so", &id, &path);
1106 log_debug_errno(r, "sysview: cannot parse SeatNew from logind: %m");
1110 context_add_seat(c, id);
1114 static int context_ld_seat_removed(sysview_context *c, sd_bus_message *signal) {
1115 const char *id, *path;
1119 r = sd_bus_message_read(signal, "so", &id, &path);
1121 log_debug_errno(r, "sysview: cannot parse SeatRemoved from logind: %m");
1125 seat = sysview_find_seat(c, id);
1129 context_remove_seat(c, seat);
1133 static int context_ld_session_new(sysview_context *c, sd_bus_message *signal) {
1134 _cleanup_free_ char *seatid = NULL, *username = NULL;
1135 const char *id, *path;
1140 r = sd_bus_message_read(signal, "so", &id, &path);
1142 log_debug_errno(r, "sysview: cannot parse SessionNew from logind: %m");
1147 * As the dbus message didn't contain enough information, we
1148 * read missing bits via sd-login. Note that this might race session
1149 * destruction, so we handle ENOENT properly.
1152 /* ENOENT is also returned for sessions without seats */
1153 r = sd_session_get_seat(id, &seatid);
1159 seat = sysview_find_seat(c, seatid);
1163 r = sd_session_get_uid(id, &uid);
1169 username = lookup_uid(uid);
1175 r = context_raise_session_filter(c, id, seatid, username, uid);
1177 log_debug_errno(r, "sysview: callback failed while filtering session '%s': %m",
1180 context_add_session(c, seat, id);
1185 log_debug_errno(r, "sysview: failed retrieving information for new session '%s': %m",
1190 static int context_ld_session_removed(sysview_context *c, sd_bus_message *signal) {
1191 sysview_session *session;
1192 const char *id, *path;
1195 r = sd_bus_message_read(signal, "so", &id, &path);
1197 log_debug_errno(r, "sysview: cannot parse SessionRemoved from logind: %m");
1201 session = sysview_find_session(c, id);
1205 context_remove_session(c, session);
1209 static int context_ld_manager_signal_fn(sd_bus *bus,
1210 sd_bus_message *signal,
1212 sd_bus_error *ret_error) {
1213 sysview_context *c = userdata;
1215 if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatNew"))
1216 return context_ld_seat_new(c, signal);
1217 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatRemoved"))
1218 return context_ld_seat_removed(c, signal);
1219 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionNew"))
1220 return context_ld_session_new(c, signal);
1221 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionRemoved"))
1222 return context_ld_session_removed(c, signal);
1227 static int context_ld_start(sysview_context *c) {
1230 if (!c->scan_logind)
1233 r = sd_bus_add_match(c->sysbus,
1234 &c->ld_slot_manager_signal,
1236 "sender='org.freedesktop.login1',"
1237 "interface='org.freedesktop.login1.Manager',"
1238 "path='/org/freedesktop/login1'",
1239 context_ld_manager_signal_fn,
1247 static void context_ld_stop(sysview_context *c) {
1248 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1249 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1250 c->ld_slot_manager_signal = sd_bus_slot_unref(c->ld_slot_manager_signal);
1253 static int context_ld_list_seats_fn(sd_bus *bus,
1254 sd_bus_message *reply,
1256 sd_bus_error *ret_error) {
1257 sysview_context *c = userdata;
1260 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1262 if (sd_bus_message_is_method_error(reply, NULL)) {
1263 const sd_bus_error *error = sd_bus_message_get_error(reply);
1265 log_debug("sysview: ListSeats on logind failed: %s: %s",
1266 error->name, error->message);
1267 return -sd_bus_error_get_errno(error);
1270 r = sd_bus_message_enter_container(reply, 'a', "(so)");
1274 while ((r = sd_bus_message_enter_container(reply, 'r', "so")) > 0) {
1275 const char *id, *path;
1277 r = sd_bus_message_read(reply, "so", &id, &path);
1281 context_add_seat(c, id);
1283 r = sd_bus_message_exit_container(reply);
1291 r = sd_bus_message_exit_container(reply);
1298 log_debug_errno(r, "sysview: erroneous ListSeats response from logind: %m");
1302 static int context_ld_list_sessions_fn(sd_bus *bus,
1303 sd_bus_message *reply,
1305 sd_bus_error *ret_error) {
1306 sysview_context *c = userdata;
1309 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1311 if (sd_bus_message_is_method_error(reply, NULL)) {
1312 const sd_bus_error *error = sd_bus_message_get_error(reply);
1314 log_debug("sysview: ListSessions on logind failed: %s: %s",
1315 error->name, error->message);
1316 return -sd_bus_error_get_errno(error);
1319 r = sd_bus_message_enter_container(reply, 'a', "(susso)");
1323 while ((r = sd_bus_message_enter_container(reply, 'r', "susso")) > 0) {
1324 const char *id, *username, *seatid, *path;
1328 r = sd_bus_message_read(reply,
1338 seat = sysview_find_seat(c, seatid);
1340 r = context_raise_session_filter(c, id, seatid, username, uid);
1342 log_debug_errno(r, "sysview: callback failed while filtering session '%s': %m",
1345 context_add_session(c, seat, id);
1348 r = sd_bus_message_exit_container(reply);
1356 r = sd_bus_message_exit_container(reply);
1363 log_debug_errno(r, "sysview: erroneous ListSessions response from logind: %m");
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_errno(r, "sysview: logind scan failed: %m");
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_errno(r, "sysview: udev scan failed: %m");
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);