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("sysview: callback failed while signalling session control '%d' on session '%s': %s",
299 error, session->name, strerror(-r));
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("sysview: %s: cannot send ReleaseControl: %s",
369 session->name, strerror(-r));
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("sysview: callback failed while attaching device '%s' to session '%s': %s",
608 device->name, session->name, strerror(-r));
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("sysview: callback failed while detaching device '%s' from session '%s': %s",
629 device->name, session->name, strerror(-r));
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("sysview: callback failed while changing device '%s' on session '%s': %s",
652 device->name, session->name, strerror(-r));
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("sysview: callback failed while adding session '%s': %s",
687 session->name, strerror(-r));
688 session->public = false;
692 HASHMAP_FOREACH(device, seat->device_map, i) {
693 r = context_raise_session_attach(c, session, device);
695 log_debug("sysview: callback failed while attaching device '%s' to new session '%s': %s",
696 device->name, session->name, strerror(-r));
704 log_debug("sysview: error while adding session '%s': %s",
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("sysview: callback failed while detaching device '%s' from old session '%s': %s",
723 device->name, session->name, strerror(-r));
726 session->public = false;
727 r = context_raise_session_remove(c, session);
729 log_debug("sysview: callback failed while removing session '%s': %s",
730 session->name, strerror(-r));
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("sysview: callback failed while adding seat '%s': %s",
760 seat->name, strerror(-r));
761 seat->public = false;
768 log_debug("sysview: error while adding seat '%s': %s",
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("sysview: callback failed while removing seat '%s': %s",
793 seat->name, strerror(-r));
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("sysview: cannot create device for udev-device '%s': %s",
980 syspath, strerror(-r));
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("sysview: cannot create udev-device for %s: %s",
1087 name, strerror(-r));
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("sysview: cannot parse SeatNew from logind: %s",
1111 context_add_seat(c, id);
1115 static int context_ld_seat_removed(sysview_context *c, sd_bus_message *signal) {
1116 const char *id, *path;
1120 r = sd_bus_message_read(signal, "so", &id, &path);
1122 log_debug("sysview: cannot parse SeatRemoved from logind: %s",
1127 seat = sysview_find_seat(c, id);
1131 context_remove_seat(c, seat);
1135 static int context_ld_session_new(sysview_context *c, sd_bus_message *signal) {
1136 _cleanup_free_ char *seatid = NULL, *username = NULL;
1137 const char *id, *path;
1142 r = sd_bus_message_read(signal, "so", &id, &path);
1144 log_debug("sysview: cannot parse SessionNew from logind: %s",
1150 * As the dbus message didn't contain enough information, we
1151 * read missing bits via sd-login. Note that this might race session
1152 * destruction, so we handle ENOENT properly.
1155 /* ENOENT is also returned for sessions without seats */
1156 r = sd_session_get_seat(id, &seatid);
1162 seat = sysview_find_seat(c, seatid);
1166 r = sd_session_get_uid(id, &uid);
1172 username = lookup_uid(uid);
1178 r = context_raise_session_filter(c, id, seatid, username, uid);
1180 log_debug("sysview: callback failed while filtering session '%s': %s",
1183 context_add_session(c, seat, id);
1188 log_debug("sysview: failed retrieving information for new session '%s': %s",
1193 static int context_ld_session_removed(sysview_context *c, sd_bus_message *signal) {
1194 sysview_session *session;
1195 const char *id, *path;
1198 r = sd_bus_message_read(signal, "so", &id, &path);
1200 log_debug("sysview: cannot parse SessionRemoved from logind: %s",
1205 session = sysview_find_session(c, id);
1209 context_remove_session(c, session);
1213 static int context_ld_manager_signal_fn(sd_bus *bus,
1214 sd_bus_message *signal,
1216 sd_bus_error *ret_error) {
1217 sysview_context *c = userdata;
1219 if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatNew"))
1220 return context_ld_seat_new(c, signal);
1221 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatRemoved"))
1222 return context_ld_seat_removed(c, signal);
1223 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionNew"))
1224 return context_ld_session_new(c, signal);
1225 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionRemoved"))
1226 return context_ld_session_removed(c, signal);
1231 static int context_ld_start(sysview_context *c) {
1234 if (!c->scan_logind)
1237 r = sd_bus_add_match(c->sysbus,
1238 &c->ld_slot_manager_signal,
1240 "sender='org.freedesktop.login1',"
1241 "interface='org.freedesktop.login1.Manager',"
1242 "path='/org/freedesktop/login1'",
1243 context_ld_manager_signal_fn,
1251 static void context_ld_stop(sysview_context *c) {
1252 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1253 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1254 c->ld_slot_manager_signal = sd_bus_slot_unref(c->ld_slot_manager_signal);
1257 static int context_ld_list_seats_fn(sd_bus *bus,
1258 sd_bus_message *reply,
1260 sd_bus_error *ret_error) {
1261 sysview_context *c = userdata;
1264 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1266 if (sd_bus_message_is_method_error(reply, NULL)) {
1267 const sd_bus_error *error = sd_bus_message_get_error(reply);
1269 log_debug("sysview: ListSeats on logind failed: %s: %s",
1270 error->name, error->message);
1271 return -sd_bus_error_get_errno(error);
1274 r = sd_bus_message_enter_container(reply, 'a', "(so)");
1278 while ((r = sd_bus_message_enter_container(reply, 'r', "so")) > 0) {
1279 const char *id, *path;
1281 r = sd_bus_message_read(reply, "so", &id, &path);
1285 context_add_seat(c, id);
1287 r = sd_bus_message_exit_container(reply);
1295 r = sd_bus_message_exit_container(reply);
1302 log_debug("sysview: erroneous ListSeats response from logind: %s",
1307 static int context_ld_list_sessions_fn(sd_bus *bus,
1308 sd_bus_message *reply,
1310 sd_bus_error *ret_error) {
1311 sysview_context *c = userdata;
1314 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1316 if (sd_bus_message_is_method_error(reply, NULL)) {
1317 const sd_bus_error *error = sd_bus_message_get_error(reply);
1319 log_debug("sysview: ListSessions on logind failed: %s: %s",
1320 error->name, error->message);
1321 return -sd_bus_error_get_errno(error);
1324 r = sd_bus_message_enter_container(reply, 'a', "(susso)");
1328 while ((r = sd_bus_message_enter_container(reply, 'r', "susso")) > 0) {
1329 const char *id, *username, *seatid, *path;
1333 r = sd_bus_message_read(reply,
1343 seat = sysview_find_seat(c, seatid);
1345 r = context_raise_session_filter(c, id, seatid, username, uid);
1347 log_debug("sysview: callback failed while filtering session '%s': %s",
1350 context_add_session(c, seat, id);
1353 r = sd_bus_message_exit_container(reply);
1361 r = sd_bus_message_exit_container(reply);
1368 log_debug("sysview: erroneous ListSessions response from logind: %s",
1373 static int context_ld_scan(sysview_context *c) {
1374 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1377 if (!c->ld_slot_manager_signal)
1380 /* request seat list */
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_seats,
1394 context_ld_list_seats_fn,
1400 /* request session list */
1402 m = sd_bus_message_unref(m);
1403 r = sd_bus_message_new_method_call(c->sysbus,
1405 "org.freedesktop.login1",
1406 "/org/freedesktop/login1",
1407 "org.freedesktop.login1.Manager",
1412 r = sd_bus_call_async(c->sysbus,
1413 &c->ld_slot_list_sessions,
1415 context_ld_list_sessions_fn,
1424 bool sysview_context_is_running(sysview_context *c) {
1425 return c && c->running;
1428 int sysview_context_start(sysview_context *c, sysview_event_fn event_fn, void *userdata) {
1431 assert_return(c, -EINVAL);
1432 assert_return(event_fn, -EINVAL);
1437 log_debug("sysview: start");
1440 c->event_fn = event_fn;
1441 c->userdata = userdata;
1443 r = context_ld_start(c);
1447 r = context_ud_start(c);
1451 r = sysview_context_rescan(c);
1458 sysview_context_stop(c);
1462 void sysview_context_stop(sysview_context *c) {
1463 sysview_session *session;
1464 sysview_device *device;
1472 log_debug("sysview: stop");
1474 while ((device = hashmap_first(c->device_map)))
1475 context_remove_device(c, device);
1477 while ((session = hashmap_first(c->session_map)))
1478 context_remove_session(c, session);
1480 while ((seat = hashmap_first(c->seat_map)))
1481 context_remove_seat(c, seat);
1487 c->scan_src = sd_event_source_unref(c->scan_src);
1492 static int context_scan_fn(sd_event_source *s, void *userdata) {
1493 sysview_context *c = userdata;
1499 r = context_ld_scan(c);
1501 log_debug("sysview: logind scan failed: %s", strerror(-r));
1506 /* skip device scans if no sessions are available */
1507 if (hashmap_size(c->session_map) > 0) {
1508 r = context_ud_scan(c);
1510 log_debug("sysview: udev scan failed: %s", strerror(-r));
1514 HASHMAP_FOREACH(seat, c->seat_map, i)
1515 seat->scanned = true;
1523 int sysview_context_rescan(sysview_context *c) {
1530 return sd_event_source_set_enabled(c->scan_src, SD_EVENT_ONESHOT);
1532 return sd_event_add_defer(c->event, &c->scan_src, context_scan_fn, c);