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 return log_debug_errno(r, "sysview: cannot create device for udev-device '%s': %m",
982 context_add_device(c, device);
988 static int context_ud_monitor_fn(sd_event_source *s,
992 sysview_context *c = userdata;
993 struct udev_device *d;
996 if (revents & EPOLLIN) {
997 while ((d = udev_monitor_receive_device(c->ud_monitor))) {
998 r = context_ud_hotplug(c, d);
999 udev_device_unref(d);
1004 /* as long as EPOLLIN is signalled, read pending data */
1008 if (revents & (EPOLLHUP | EPOLLERR)) {
1009 log_debug("sysview: HUP on udev-monitor");
1010 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
1016 static int context_ud_start(sysview_context *c) {
1023 c->ud_monitor = udev_monitor_new_from_netlink(c->ud, "udev");
1025 return errno > 0 ? -errno : -EFAULT;
1027 r = context_ud_prepare_monitor(c, c->ud_monitor);
1031 r = udev_monitor_enable_receiving(c->ud_monitor);
1035 fd = udev_monitor_get_fd(c->ud_monitor);
1036 r = sd_event_add_io(c->event,
1039 EPOLLHUP | EPOLLERR | EPOLLIN,
1040 context_ud_monitor_fn,
1048 static void context_ud_stop(sysview_context *c) {
1049 c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src);
1050 c->ud_monitor = udev_monitor_unref(c->ud_monitor);
1053 static int context_ud_scan(sysview_context *c) {
1054 _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
1055 struct udev_list_entry *entry;
1056 struct udev_device *d;
1063 e = udev_enumerate_new(c->ud);
1065 return errno > 0 ? -errno : -EFAULT;
1067 r = context_ud_prepare_scan(c, e);
1071 r = udev_enumerate_scan_devices(e);
1075 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
1078 name = udev_list_entry_get_name(entry);
1081 d = udev_device_new_from_syspath(c->ud, name);
1083 r = errno > 0 ? -errno : -EFAULT;
1084 log_debug_errno(r, "sysview: cannot create udev-device for %s: %m",
1089 r = context_ud_hotplug(c, d);
1090 udev_device_unref(d);
1098 static int context_ld_seat_new(sysview_context *c, sd_bus_message *signal) {
1099 const char *id, *path;
1102 r = sd_bus_message_read(signal, "so", &id, &path);
1104 return log_debug_errno(r, "sysview: cannot parse SeatNew from logind: %m");
1106 context_add_seat(c, id);
1110 static int context_ld_seat_removed(sysview_context *c, sd_bus_message *signal) {
1111 const char *id, *path;
1115 r = sd_bus_message_read(signal, "so", &id, &path);
1117 return log_debug_errno(r, "sysview: cannot parse SeatRemoved from logind: %m");
1119 seat = sysview_find_seat(c, id);
1123 context_remove_seat(c, seat);
1127 static int context_ld_session_new(sysview_context *c, sd_bus_message *signal) {
1128 _cleanup_free_ char *seatid = NULL, *username = NULL;
1129 const char *id, *path;
1134 r = sd_bus_message_read(signal, "so", &id, &path);
1136 return log_debug_errno(r, "sysview: cannot parse SessionNew from logind: %m");
1139 * As the dbus message didn't contain enough information, we
1140 * read missing bits via sd-login. Note that this might race session
1141 * destruction, so we handle ENOENT properly.
1144 /* ENOENT is also returned for sessions without seats */
1145 r = sd_session_get_seat(id, &seatid);
1151 seat = sysview_find_seat(c, seatid);
1155 r = sd_session_get_uid(id, &uid);
1161 username = lookup_uid(uid);
1167 r = context_raise_session_filter(c, id, seatid, username, uid);
1169 log_debug_errno(r, "sysview: callback failed while filtering session '%s': %m",
1172 context_add_session(c, seat, id);
1177 return log_debug_errno(r, "sysview: failed retrieving information for new session '%s': %m",
1181 static int context_ld_session_removed(sysview_context *c, sd_bus_message *signal) {
1182 sysview_session *session;
1183 const char *id, *path;
1186 r = sd_bus_message_read(signal, "so", &id, &path);
1188 return log_debug_errno(r, "sysview: cannot parse SessionRemoved from logind: %m");
1190 session = sysview_find_session(c, id);
1194 context_remove_session(c, session);
1198 static int context_ld_manager_signal_fn(sd_bus *bus,
1199 sd_bus_message *signal,
1201 sd_bus_error *ret_error) {
1202 sysview_context *c = userdata;
1204 if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatNew"))
1205 return context_ld_seat_new(c, signal);
1206 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatRemoved"))
1207 return context_ld_seat_removed(c, signal);
1208 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionNew"))
1209 return context_ld_session_new(c, signal);
1210 else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionRemoved"))
1211 return context_ld_session_removed(c, signal);
1216 static int context_ld_start(sysview_context *c) {
1219 if (!c->scan_logind)
1222 r = sd_bus_add_match(c->sysbus,
1223 &c->ld_slot_manager_signal,
1225 "sender='org.freedesktop.login1',"
1226 "interface='org.freedesktop.login1.Manager',"
1227 "path='/org/freedesktop/login1'",
1228 context_ld_manager_signal_fn,
1236 static void context_ld_stop(sysview_context *c) {
1237 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1238 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1239 c->ld_slot_manager_signal = sd_bus_slot_unref(c->ld_slot_manager_signal);
1242 static int context_ld_list_seats_fn(sd_bus *bus,
1243 sd_bus_message *reply,
1245 sd_bus_error *ret_error) {
1246 sysview_context *c = userdata;
1249 c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats);
1251 if (sd_bus_message_is_method_error(reply, NULL)) {
1252 const sd_bus_error *error = sd_bus_message_get_error(reply);
1254 log_debug("sysview: ListSeats on logind failed: %s: %s",
1255 error->name, error->message);
1256 return -sd_bus_error_get_errno(error);
1259 r = sd_bus_message_enter_container(reply, 'a', "(so)");
1263 while ((r = sd_bus_message_enter_container(reply, 'r', "so")) > 0) {
1264 const char *id, *path;
1266 r = sd_bus_message_read(reply, "so", &id, &path);
1270 context_add_seat(c, id);
1272 r = sd_bus_message_exit_container(reply);
1280 r = sd_bus_message_exit_container(reply);
1287 return log_debug_errno(r, "sysview: erroneous ListSeats response from logind: %m");
1290 static int context_ld_list_sessions_fn(sd_bus *bus,
1291 sd_bus_message *reply,
1293 sd_bus_error *ret_error) {
1294 sysview_context *c = userdata;
1297 c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions);
1299 if (sd_bus_message_is_method_error(reply, NULL)) {
1300 const sd_bus_error *error = sd_bus_message_get_error(reply);
1302 log_debug("sysview: ListSessions on logind failed: %s: %s",
1303 error->name, error->message);
1304 return -sd_bus_error_get_errno(error);
1307 r = sd_bus_message_enter_container(reply, 'a', "(susso)");
1311 while ((r = sd_bus_message_enter_container(reply, 'r', "susso")) > 0) {
1312 const char *id, *username, *seatid, *path;
1316 r = sd_bus_message_read(reply,
1326 seat = sysview_find_seat(c, seatid);
1328 r = context_raise_session_filter(c, id, seatid, username, uid);
1330 log_debug_errno(r, "sysview: callback failed while filtering session '%s': %m",
1333 context_add_session(c, seat, id);
1336 r = sd_bus_message_exit_container(reply);
1344 r = sd_bus_message_exit_container(reply);
1351 return log_debug_errno(r, "sysview: erroneous ListSessions response from logind: %m");
1354 static int context_ld_scan(sysview_context *c) {
1355 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1358 if (!c->ld_slot_manager_signal)
1361 /* request seat list */
1363 r = sd_bus_message_new_method_call(c->sysbus,
1365 "org.freedesktop.login1",
1366 "/org/freedesktop/login1",
1367 "org.freedesktop.login1.Manager",
1372 r = sd_bus_call_async(c->sysbus,
1373 &c->ld_slot_list_seats,
1375 context_ld_list_seats_fn,
1381 /* request session list */
1383 m = sd_bus_message_unref(m);
1384 r = sd_bus_message_new_method_call(c->sysbus,
1386 "org.freedesktop.login1",
1387 "/org/freedesktop/login1",
1388 "org.freedesktop.login1.Manager",
1393 r = sd_bus_call_async(c->sysbus,
1394 &c->ld_slot_list_sessions,
1396 context_ld_list_sessions_fn,
1405 bool sysview_context_is_running(sysview_context *c) {
1406 return c && c->running;
1409 int sysview_context_start(sysview_context *c, sysview_event_fn event_fn, void *userdata) {
1412 assert_return(c, -EINVAL);
1413 assert_return(event_fn, -EINVAL);
1418 log_debug("sysview: start");
1421 c->event_fn = event_fn;
1422 c->userdata = userdata;
1424 r = context_ld_start(c);
1428 r = context_ud_start(c);
1432 r = sysview_context_rescan(c);
1439 sysview_context_stop(c);
1443 void sysview_context_stop(sysview_context *c) {
1444 sysview_session *session;
1445 sysview_device *device;
1453 log_debug("sysview: stop");
1455 while ((device = hashmap_first(c->device_map)))
1456 context_remove_device(c, device);
1458 while ((session = hashmap_first(c->session_map)))
1459 context_remove_session(c, session);
1461 while ((seat = hashmap_first(c->seat_map)))
1462 context_remove_seat(c, seat);
1468 c->scan_src = sd_event_source_unref(c->scan_src);
1473 static int context_scan_fn(sd_event_source *s, void *userdata) {
1474 sysview_context *c = userdata;
1480 r = context_ld_scan(c);
1482 return log_debug_errno(r, "sysview: logind scan failed: %m");
1485 /* skip device scans if no sessions are available */
1486 if (hashmap_size(c->session_map) > 0) {
1487 r = context_ud_scan(c);
1489 return log_debug_errno(r, "sysview: udev scan failed: %m");
1491 HASHMAP_FOREACH(seat, c->seat_map, i)
1492 seat->scanned = true;
1500 int sysview_context_rescan(sysview_context *c) {
1507 return sd_event_source_set_enabled(c->scan_src, SD_EVENT_ONESHOT);
1509 return sd_event_add_defer(c->event, &c->scan_src, context_scan_fn, c);