diff options
Diffstat (limited to 'src/libsystemd-terminal/sysview.c')
-rw-r--r-- | src/libsystemd-terminal/sysview.c | 1554 |
1 files changed, 0 insertions, 1554 deletions
diff --git a/src/libsystemd-terminal/sysview.c b/src/libsystemd-terminal/sysview.c deleted file mode 100644 index 2e9b15859a..0000000000 --- a/src/libsystemd-terminal/sysview.c +++ /dev/null @@ -1,1554 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <inttypes.h> -#include <libudev.h> -#include <stdbool.h> -#include <stdlib.h> -#include "sd-bus.h" -#include "sd-event.h" -#include "sd-login.h" -#include "macro.h" -#include "udev-util.h" -#include "util.h" -#include "bus-util.h" -#include "sysview.h" -#include "sysview-internal.h" - -static int context_raise_session_control(sysview_context *c, sysview_session *session, int error); - -/* - * Devices - */ - -sysview_device *sysview_find_device(sysview_context *c, const char *name) { - assert_return(c, NULL); - assert_return(name, NULL); - - return hashmap_get(c->device_map, name); -} - -int sysview_device_new(sysview_device **out, sysview_seat *seat, const char *name) { - _cleanup_(sysview_device_freep) sysview_device *device = NULL; - int r; - - assert_return(seat, -EINVAL); - assert_return(name, -EINVAL); - - device = new0(sysview_device, 1); - if (!device) - return -ENOMEM; - - device->seat = seat; - device->type = (unsigned)-1; - - device->name = strdup(name); - if (!device->name) - return -ENOMEM; - - r = hashmap_put(seat->context->device_map, device->name, device); - if (r < 0) - return r; - - r = hashmap_put(seat->device_map, device->name, device); - if (r < 0) - return r; - - if (out) - *out = device; - device = NULL; - return 0; -} - -sysview_device *sysview_device_free(sysview_device *device) { - if (!device) - return NULL; - - if (device->name) { - hashmap_remove_value(device->seat->device_map, device->name, device); - hashmap_remove_value(device->seat->context->device_map, device->name, device); - } - - switch (device->type) { - case SYSVIEW_DEVICE_EVDEV: - device->evdev.ud = udev_device_unref(device->evdev.ud); - break; - case SYSVIEW_DEVICE_DRM: - device->drm.ud = udev_device_unref(device->drm.ud); - break; - } - - free(device->name); - free(device); - - return NULL; -} - -const char *sysview_device_get_name(sysview_device *device) { - assert_return(device, NULL); - - return device->name; -} - -unsigned int sysview_device_get_type(sysview_device *device) { - assert_return(device, (unsigned)-1); - - return device->type; -} - -struct udev_device *sysview_device_get_ud(sysview_device *device) { - assert_return(device, NULL); - - switch (device->type) { - case SYSVIEW_DEVICE_EVDEV: - return device->evdev.ud; - case SYSVIEW_DEVICE_DRM: - return device->drm.ud; - default: - assert_return(0, NULL); - } -} - -static int device_new_ud(sysview_device **out, sysview_seat *seat, unsigned int type, struct udev_device *ud) { - _cleanup_(sysview_device_freep) sysview_device *device = NULL; - int r; - - assert_return(seat, -EINVAL); - assert_return(ud, -EINVAL); - - r = sysview_device_new(&device, seat, udev_device_get_syspath(ud)); - if (r < 0) - return r; - - device->type = type; - - switch (type) { - case SYSVIEW_DEVICE_EVDEV: - device->evdev.ud = udev_device_ref(ud); - break; - case SYSVIEW_DEVICE_DRM: - device->drm.ud = udev_device_ref(ud); - break; - default: - assert_not_reached("sysview: invalid udev-device type"); - } - - if (out) - *out = device; - device = NULL; - return 0; -} - -/* - * Sessions - */ - -sysview_session *sysview_find_session(sysview_context *c, const char *name) { - assert_return(c, NULL); - assert_return(name, NULL); - - return hashmap_get(c->session_map, name); -} - -int sysview_session_new(sysview_session **out, sysview_seat *seat, const char *name) { - _cleanup_(sysview_session_freep) sysview_session *session = NULL; - int r; - - assert_return(seat, -EINVAL); - - session = new0(sysview_session, 1); - if (!session) - return -ENOMEM; - - session->seat = seat; - - if (name) { - /* - * If a name is given, we require it to be a logind session - * name. The session will be put in managed mode and we use - * logind to request controller access. - */ - - session->name = strdup(name); - if (!session->name) - return -ENOMEM; - - r = sd_bus_path_encode("/org/freedesktop/login1/session", - session->name, &session->path); - if (r < 0) - return r; - - session->custom = false; - } else { - /* - * No session name was given. We assume this is an unmanaged - * session controlled by the application. We don't use logind - * at all and leave session management to the application. The - * name of the session-object is set to a unique random string - * that does not clash with the logind namespace. - */ - - r = asprintf(&session->name, "@custom%" PRIu64, - ++seat->context->custom_sid); - if (r < 0) - return -ENOMEM; - - session->custom = true; - } - - r = hashmap_put(seat->context->session_map, session->name, session); - if (r < 0) - return r; - - r = hashmap_put(seat->session_map, session->name, session); - if (r < 0) - return r; - - if (out) - *out = session; - session = NULL; - return 0; -} - -sysview_session *sysview_session_free(sysview_session *session) { - if (!session) - return NULL; - - assert(!session->public); - assert(!session->wants_control); - - if (session->name) { - hashmap_remove_value(session->seat->session_map, session->name, session); - hashmap_remove_value(session->seat->context->session_map, session->name, session); - } - - free(session->path); - free(session->name); - free(session); - - return NULL; -} - -void sysview_session_set_userdata(sysview_session *session, void *userdata) { - assert(session); - - session->userdata = userdata; -} - -void *sysview_session_get_userdata(sysview_session *session) { - assert_return(session, NULL); - - return session->userdata; -} - -const char *sysview_session_get_name(sysview_session *session) { - assert_return(session, NULL); - - return session->name; -} - -sysview_seat *sysview_session_get_seat(sysview_session *session) { - assert_return(session, NULL); - - return session->seat; -} - -static int session_take_control_fn(sd_bus_message *reply, - void *userdata, - sd_bus_error *ret_error) { - sysview_session *session = userdata; - int r, error; - - session->slot_take_control = sd_bus_slot_unref(session->slot_take_control); - - if (sd_bus_message_is_method_error(reply, NULL)) { - const sd_bus_error *e = sd_bus_message_get_error(reply); - - log_debug("sysview: %s: TakeControl failed: %s: %s", - session->name, e->name, e->message); - error = -sd_bus_error_get_errno(e); - } else { - session->has_control = true; - error = 0; - } - - r = context_raise_session_control(session->seat->context, session, error); - if (r < 0) - log_debug_errno(r, "sysview: callback failed while signalling session control '%d' on session '%s': %m", - error, session->name); - - return 0; -} - -int sysview_session_take_control(sysview_session *session) { - _cleanup_bus_message_unref_ sd_bus_message *m = NULL; - int r; - - assert_return(session, -EINVAL); - assert_return(!session->custom, -EINVAL); - - if (session->wants_control) - return 0; - - r = sd_bus_message_new_method_call(session->seat->context->sysbus, - &m, - "org.freedesktop.login1", - session->path, - "org.freedesktop.login1.Session", - "TakeControl"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "b", 0); - if (r < 0) - return r; - - r = sd_bus_call_async(session->seat->context->sysbus, - &session->slot_take_control, - m, - session_take_control_fn, - session, - 0); - if (r < 0) - return r; - - session->wants_control = true; - return 0; -} - -void sysview_session_release_control(sysview_session *session) { - _cleanup_bus_message_unref_ sd_bus_message *m = NULL; - int r; - - assert(session); - assert(!session->custom); - - if (!session->wants_control) - return; - - session->wants_control = false; - - if (!session->has_control && !session->slot_take_control) - return; - - session->has_control = false; - session->slot_take_control = sd_bus_slot_unref(session->slot_take_control); - - r = sd_bus_message_new_method_call(session->seat->context->sysbus, - &m, - "org.freedesktop.login1", - session->path, - "org.freedesktop.login1.Session", - "ReleaseControl"); - if (r >= 0) - r = sd_bus_send(session->seat->context->sysbus, m, NULL); - - if (r < 0 && r != -ENOTCONN) - log_debug_errno(r, "sysview: %s: cannot send ReleaseControl: %m", - session->name); -} - -/* - * Seats - */ - -sysview_seat *sysview_find_seat(sysview_context *c, const char *name) { - assert_return(c, NULL); - assert_return(name, NULL); - - return hashmap_get(c->seat_map, name); -} - -int sysview_seat_new(sysview_seat **out, sysview_context *c, const char *name) { - _cleanup_(sysview_seat_freep) sysview_seat *seat = NULL; - int r; - - assert_return(c, -EINVAL); - assert_return(name, -EINVAL); - - seat = new0(sysview_seat, 1); - if (!seat) - return -ENOMEM; - - seat->context = c; - - seat->name = strdup(name); - if (!seat->name) - return -ENOMEM; - - r = sd_bus_path_encode("/org/freedesktop/login1/seat", seat->name, &seat->path); - if (r < 0) - return r; - - seat->session_map = hashmap_new(&string_hash_ops); - if (!seat->session_map) - return -ENOMEM; - - seat->device_map = hashmap_new(&string_hash_ops); - if (!seat->device_map) - return -ENOMEM; - - r = hashmap_put(c->seat_map, seat->name, seat); - if (r < 0) - return r; - - if (out) - *out = seat; - seat = NULL; - return 0; -} - -sysview_seat *sysview_seat_free(sysview_seat *seat) { - if (!seat) - return NULL; - - assert(!seat->public); - assert(hashmap_size(seat->device_map) == 0); - assert(hashmap_size(seat->session_map) == 0); - - if (seat->name) - hashmap_remove_value(seat->context->seat_map, seat->name, seat); - - hashmap_free(seat->device_map); - hashmap_free(seat->session_map); - free(seat->path); - free(seat->name); - free(seat); - - return NULL; -} - -const char *sysview_seat_get_name(sysview_seat *seat) { - assert_return(seat, NULL); - - return seat->name; -} - -int sysview_seat_switch_to(sysview_seat *seat, uint32_t nr) { - _cleanup_bus_message_unref_ sd_bus_message *m = NULL; - int r; - - assert_return(seat, -EINVAL); - assert_return(seat->context->sysbus, -EINVAL); - - r = sd_bus_message_new_method_call(seat->context->sysbus, - &m, - "org.freedesktop.login1", - seat->path, - "org.freedesktop.login1.Seat", - "SwitchTo"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "u", nr); - if (r < 0) - return r; - - return sd_bus_send(seat->context->sysbus, m, NULL); -} - -/* - * Contexts - */ - -static int context_raise(sysview_context *c, sysview_event *event, int def) { - return c->running ? c->event_fn(c, c->userdata, event) : def; -} - -static int context_raise_settle(sysview_context *c) { - sysview_event event = { - .type = SYSVIEW_EVENT_SETTLE, - }; - - return context_raise(c, &event, 0); -} - -static int context_raise_seat_add(sysview_context *c, sysview_seat *seat) { - sysview_event event = { - .type = SYSVIEW_EVENT_SEAT_ADD, - .seat_add = { - .seat = seat, - } - }; - - return context_raise(c, &event, 0); -} - -static int context_raise_seat_remove(sysview_context *c, sysview_seat *seat) { - sysview_event event = { - .type = SYSVIEW_EVENT_SEAT_REMOVE, - .seat_remove = { - .seat = seat, - } - }; - - return context_raise(c, &event, 0); -} - -static int context_raise_session_filter(sysview_context *c, - const char *id, - const char *seatid, - const char *username, - unsigned int uid) { - sysview_event event = { - .type = SYSVIEW_EVENT_SESSION_FILTER, - .session_filter = { - .id = id, - .seatid = seatid, - .username = username, - .uid = uid, - } - }; - - return context_raise(c, &event, 1); -} - -static int context_raise_session_add(sysview_context *c, sysview_session *session) { - sysview_event event = { - .type = SYSVIEW_EVENT_SESSION_ADD, - .session_add = { - .session = session, - } - }; - - return context_raise(c, &event, 0); -} - -static int context_raise_session_remove(sysview_context *c, sysview_session *session) { - sysview_event event = { - .type = SYSVIEW_EVENT_SESSION_REMOVE, - .session_remove = { - .session = session, - } - }; - - return context_raise(c, &event, 0); -} - -static int context_raise_session_control(sysview_context *c, sysview_session *session, int error) { - sysview_event event = { - .type = SYSVIEW_EVENT_SESSION_CONTROL, - .session_control = { - .session = session, - .error = error, - } - }; - - return context_raise(c, &event, 0); -} - -static int context_raise_session_attach(sysview_context *c, sysview_session *session, sysview_device *device) { - sysview_event event = { - .type = SYSVIEW_EVENT_SESSION_ATTACH, - .session_attach = { - .session = session, - .device = device, - } - }; - - return context_raise(c, &event, 0); -} - -static int context_raise_session_detach(sysview_context *c, sysview_session *session, sysview_device *device) { - sysview_event event = { - .type = SYSVIEW_EVENT_SESSION_DETACH, - .session_detach = { - .session = session, - .device = device, - } - }; - - return context_raise(c, &event, 0); -} - -static int context_raise_session_refresh(sysview_context *c, sysview_session *session, sysview_device *device, struct udev_device *ud) { - sysview_event event = { - .type = SYSVIEW_EVENT_SESSION_REFRESH, - .session_refresh = { - .session = session, - .device = device, - .ud = ud, - } - }; - - return context_raise(c, &event, 0); -} - -static void context_settle(sysview_context *c) { - int r; - - if (c->n_probe <= 0 || --c->n_probe > 0) - return; - - log_debug("sysview: settle"); - - c->settled = true; - - r = context_raise_settle(c); - if (r < 0) - log_debug_errno(r, "sysview: callback failed on settle: %m"); -} - -static void context_add_device(sysview_context *c, sysview_device *device) { - sysview_session *session; - Iterator i; - int r; - - assert(c); - assert(device); - - log_debug("sysview: add device '%s' on seat '%s'", - device->name, device->seat->name); - - HASHMAP_FOREACH(session, device->seat->session_map, i) { - if (!session->public) - continue; - - r = context_raise_session_attach(c, session, device); - if (r < 0) - log_debug_errno(r, "sysview: callback failed while attaching device '%s' to session '%s': %m", - device->name, session->name); - } -} - -static void context_remove_device(sysview_context *c, sysview_device *device) { - sysview_session *session; - Iterator i; - int r; - - assert(c); - assert(device); - - log_debug("sysview: remove device '%s'", device->name); - - HASHMAP_FOREACH(session, device->seat->session_map, i) { - if (!session->public) - continue; - - r = context_raise_session_detach(c, session, device); - if (r < 0) - log_debug_errno(r, "sysview: callback failed while detaching device '%s' from session '%s': %m", - device->name, session->name); - } - - sysview_device_free(device); -} - -static void context_change_device(sysview_context *c, sysview_device *device, struct udev_device *ud) { - sysview_session *session; - Iterator i; - int r; - - assert(c); - assert(device); - - log_debug("sysview: change device '%s'", device->name); - - HASHMAP_FOREACH(session, device->seat->session_map, i) { - if (!session->public) - continue; - - r = context_raise_session_refresh(c, session, device, ud); - if (r < 0) - log_debug_errno(r, "sysview: callback failed while changing device '%s' on session '%s': %m", - device->name, session->name); - } -} - -static void context_add_session(sysview_context *c, sysview_seat *seat, const char *id) { - sysview_session *session; - sysview_device *device; - Iterator i; - int r; - - assert(c); - assert(seat); - assert(id); - - session = sysview_find_session(c, id); - if (session) - return; - - log_debug("sysview: add session '%s' on seat '%s'", id, seat->name); - - r = sysview_session_new(&session, seat, id); - if (r < 0) - goto error; - - if (!seat->scanned) { - r = sysview_context_rescan(c); - if (r < 0) - goto error; - } - - if (seat->public) { - session->public = true; - r = context_raise_session_add(c, session); - if (r < 0) { - log_debug_errno(r, "sysview: callback failed while adding session '%s': %m", - session->name); - session->public = false; - goto error; - } - - HASHMAP_FOREACH(device, seat->device_map, i) { - r = context_raise_session_attach(c, session, device); - if (r < 0) - log_debug_errno(r, "sysview: callback failed while attaching device '%s' to new session '%s': %m", - device->name, session->name); - } - } - - return; - -error: - if (r < 0) - log_debug_errno(r, "sysview: error while adding session '%s': %m", - id); -} - -static void context_remove_session(sysview_context *c, sysview_session *session) { - sysview_device *device; - Iterator i; - int r; - - assert(c); - assert(session); - - log_debug("sysview: remove session '%s'", session->name); - - if (session->public) { - HASHMAP_FOREACH(device, session->seat->device_map, i) { - r = context_raise_session_detach(c, session, device); - if (r < 0) - log_debug_errno(r, "sysview: callback failed while detaching device '%s' from old session '%s': %m", - device->name, session->name); - } - - session->public = false; - r = context_raise_session_remove(c, session); - if (r < 0) - log_debug_errno(r, "sysview: callback failed while removing session '%s': %m", - session->name); - } - - if (!session->custom) - sysview_session_release_control(session); - - sysview_session_free(session); -} - -static void context_add_seat(sysview_context *c, const char *id) { - sysview_seat *seat; - int r; - - assert(c); - assert(id); - - seat = sysview_find_seat(c, id); - if (seat) - return; - - log_debug("sysview: add seat '%s'", id); - - r = sysview_seat_new(&seat, c, id); - if (r < 0) - goto error; - - seat->public = true; - r = context_raise_seat_add(c, seat); - if (r < 0) { - log_debug_errno(r, "sysview: callback failed while adding seat '%s': %m", - seat->name); - seat->public = false; - } - - return; - -error: - if (r < 0) - log_debug_errno(r, "sysview: error while adding seat '%s': %m", - id); -} - -static void context_remove_seat(sysview_context *c, sysview_seat *seat) { - sysview_session *session; - sysview_device *device; - int r; - - assert(c); - assert(seat); - - log_debug("sysview: remove seat '%s'", seat->name); - - while ((device = hashmap_first(seat->device_map))) - context_remove_device(c, device); - - while ((session = hashmap_first(seat->session_map))) - context_remove_session(c, session); - - if (seat->public) { - seat->public = false; - r = context_raise_seat_remove(c, seat); - if (r < 0) - log_debug_errno(r, "sysview: callback failed while removing seat '%s': %m", - seat->name); - } - - sysview_seat_free(seat); -} - -int sysview_context_new(sysview_context **out, - unsigned int flags, - sd_event *event, - sd_bus *sysbus, - struct udev *ud) { - _cleanup_(sysview_context_freep) sysview_context *c = NULL; - int r; - - assert_return(out, -EINVAL); - assert_return(event, -EINVAL); - - log_debug("sysview: new"); - - c = new0(sysview_context, 1); - if (!c) - return -ENOMEM; - - c->event = sd_event_ref(event); - if (flags & SYSVIEW_CONTEXT_SCAN_LOGIND) - c->scan_logind = true; - if (flags & SYSVIEW_CONTEXT_SCAN_EVDEV) - c->scan_evdev = true; - if (flags & SYSVIEW_CONTEXT_SCAN_DRM) - c->scan_drm = true; - - if (sysbus) { - c->sysbus = sd_bus_ref(sysbus); - } else if (c->scan_logind) { - r = sd_bus_open_system(&c->sysbus); - if (r < 0) - return r; - } - - if (ud) { - c->ud = udev_ref(ud); - } else if (c->scan_evdev || c->scan_drm) { - errno = 0; - c->ud = udev_new(); - if (!c->ud) - return errno > 0 ? -errno : -EFAULT; - } - - c->seat_map = hashmap_new(&string_hash_ops); - if (!c->seat_map) - return -ENOMEM; - - c->session_map = hashmap_new(&string_hash_ops); - if (!c->session_map) - return -ENOMEM; - - c->device_map = hashmap_new(&string_hash_ops); - if (!c->device_map) - return -ENOMEM; - - *out = c; - c = NULL; - return 0; -} - -sysview_context *sysview_context_free(sysview_context *c) { - if (!c) - return NULL; - - log_debug("sysview: free"); - - sysview_context_stop(c); - - assert(hashmap_size(c->device_map) == 0); - assert(hashmap_size(c->session_map) == 0); - assert(hashmap_size(c->seat_map) == 0); - - hashmap_free(c->device_map); - hashmap_free(c->session_map); - hashmap_free(c->seat_map); - c->ud = udev_unref(c->ud); - c->sysbus = sd_bus_unref(c->sysbus); - c->event = sd_event_unref(c->event); - free(c); - - return NULL; -} - -static int context_ud_prepare_monitor(sysview_context *c, struct udev_monitor *m) { - int r; - - if (c->scan_evdev) { - r = udev_monitor_filter_add_match_subsystem_devtype(m, "input", NULL); - if (r < 0) - return r; - } - - if (c->scan_drm) { - r = udev_monitor_filter_add_match_subsystem_devtype(m, "drm", NULL); - if (r < 0) - return r; - } - - return 0; -} - -static int context_ud_prepare_scan(sysview_context *c, struct udev_enumerate *e) { - int r; - - if (c->scan_evdev) { - r = udev_enumerate_add_match_subsystem(e, "input"); - if (r < 0) - return r; - } - - if (c->scan_drm) { - r = udev_enumerate_add_match_subsystem(e, "drm"); - if (r < 0) - return r; - } - - r = udev_enumerate_add_match_is_initialized(e); - if (r < 0) - return r; - - return 0; -} - -static int context_ud_hotplug(sysview_context *c, struct udev_device *d) { - const char *syspath, *sysname, *subsystem, *action, *seatname; - sysview_device *device; - int r; - - syspath = udev_device_get_syspath(d); - sysname = udev_device_get_sysname(d); - subsystem = udev_device_get_subsystem(d); - action = udev_device_get_action(d); - - /* not interested in custom devices without syspath/etc */ - if (!syspath || !sysname || !subsystem) - return 0; - - device = sysview_find_device(c, syspath); - - if (streq_ptr(action, "remove")) { - if (!device) - return 0; - - context_remove_device(c, device); - } else if (streq_ptr(action, "change")) { - if (!device) - return 0; - - context_change_device(c, device, d); - } else if (!action || streq_ptr(action, "add")) { - struct udev_device *p; - unsigned int type, t; - sysview_seat *seat; - - if (device) - return 0; - - if (streq(subsystem, "input") && startswith(sysname, "event") && safe_atou(sysname + 5, &t) >= 0) - type = SYSVIEW_DEVICE_EVDEV; - else if (streq(subsystem, "drm") && startswith(sysname, "card")) - type = SYSVIEW_DEVICE_DRM; - else - type = (unsigned)-1; - - if (type >= SYSVIEW_DEVICE_CNT) - return 0; - - p = d; - seatname = NULL; - do { - seatname = udev_device_get_property_value(p, "ID_SEAT"); - if (seatname) - break; - } while ((p = udev_device_get_parent(p))); - - seat = sysview_find_seat(c, seatname ? : "seat0"); - if (!seat) - return 0; - - r = device_new_ud(&device, seat, type, d); - if (r < 0) - return log_debug_errno(r, "sysview: cannot create device for udev-device '%s': %m", - syspath); - - context_add_device(c, device); - } - - return 0; -} - -static int context_ud_monitor_fn(sd_event_source *s, - int fd, - uint32_t revents, - void *userdata) { - sysview_context *c = userdata; - struct udev_device *d; - int r; - - if (revents & EPOLLIN) { - while ((d = udev_monitor_receive_device(c->ud_monitor))) { - r = context_ud_hotplug(c, d); - udev_device_unref(d); - if (r != 0) - return r; - } - - /* as long as EPOLLIN is signalled, read pending data */ - return 0; - } - - if (revents & (EPOLLHUP | EPOLLERR)) { - log_debug("sysview: HUP on udev-monitor"); - c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src); - } - - return 0; -} - -static int context_ud_start(sysview_context *c) { - int r, fd; - - if (!c->ud) - return 0; - - errno = 0; - c->ud_monitor = udev_monitor_new_from_netlink(c->ud, "udev"); - if (!c->ud_monitor) - return errno > 0 ? -errno : -EFAULT; - - r = context_ud_prepare_monitor(c, c->ud_monitor); - if (r < 0) - return r; - - r = udev_monitor_enable_receiving(c->ud_monitor); - if (r < 0) - return r; - - fd = udev_monitor_get_fd(c->ud_monitor); - r = sd_event_add_io(c->event, - &c->ud_monitor_src, - fd, - EPOLLHUP | EPOLLERR | EPOLLIN, - context_ud_monitor_fn, - c); - if (r < 0) - return r; - - return 0; -} - -static void context_ud_stop(sysview_context *c) { - c->ud_monitor_src = sd_event_source_unref(c->ud_monitor_src); - c->ud_monitor = udev_monitor_unref(c->ud_monitor); -} - -static int context_ud_scan(sysview_context *c) { - _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL; - struct udev_list_entry *entry; - struct udev_device *d; - int r; - - if (!c->ud_monitor) - return 0; - - errno = 0; - e = udev_enumerate_new(c->ud); - if (!e) - return errno > 0 ? -errno : -EFAULT; - - r = context_ud_prepare_scan(c, e); - if (r < 0) - return r; - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return r; - - udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { - const char *name; - - name = udev_list_entry_get_name(entry); - - errno = 0; - d = udev_device_new_from_syspath(c->ud, name); - if (!d) { - r = errno > 0 ? -errno : -EFAULT; - log_debug_errno(r, "sysview: cannot create udev-device for %s: %m", - name); - continue; - } - - r = context_ud_hotplug(c, d); - udev_device_unref(d); - if (r != 0) - return r; - } - - return 0; -} - -static int context_ld_seat_new(sysview_context *c, sd_bus_message *signal) { - const char *id, *path; - int r; - - r = sd_bus_message_read(signal, "so", &id, &path); - if (r < 0) - return log_debug_errno(r, "sysview: cannot parse SeatNew from logind: %m"); - - context_add_seat(c, id); - return 0; -} - -static int context_ld_seat_removed(sysview_context *c, sd_bus_message *signal) { - const char *id, *path; - sysview_seat *seat; - int r; - - r = sd_bus_message_read(signal, "so", &id, &path); - if (r < 0) - return log_debug_errno(r, "sysview: cannot parse SeatRemoved from logind: %m"); - - seat = sysview_find_seat(c, id); - if (!seat) - return 0; - - context_remove_seat(c, seat); - return 0; -} - -static int context_ld_session_new(sysview_context *c, sd_bus_message *signal) { - _cleanup_free_ char *seatid = NULL, *username = NULL; - const char *id, *path; - sysview_seat *seat; - uid_t uid; - int r; - - r = sd_bus_message_read(signal, "so", &id, &path); - if (r < 0) - return log_debug_errno(r, "sysview: cannot parse SessionNew from logind: %m"); - - /* - * As the dbus message didn't contain enough information, we - * read missing bits via sd-login. Note that this might race session - * destruction, so we handle ENOENT properly. - */ - - /* ENOENT is also returned for sessions without seats */ - r = sd_session_get_seat(id, &seatid); - if (r == -ENOENT) - return 0; - else if (r < 0) - goto error; - - seat = sysview_find_seat(c, seatid); - if (!seat) - return 0; - - r = sd_session_get_uid(id, &uid); - if (r == -ENOENT) - return 0; - else if (r < 0) - goto error; - - username = lookup_uid(uid); - if (!username) { - r = -ENOMEM; - goto error; - } - - r = context_raise_session_filter(c, id, seatid, username, uid); - if (r < 0) - log_debug_errno(r, "sysview: callback failed while filtering session '%s': %m", - id); - else if (r > 0) - context_add_session(c, seat, id); - - return 0; - -error: - return log_debug_errno(r, "sysview: failed retrieving information for new session '%s': %m", - id); -} - -static int context_ld_session_removed(sysview_context *c, sd_bus_message *signal) { - sysview_session *session; - const char *id, *path; - int r; - - r = sd_bus_message_read(signal, "so", &id, &path); - if (r < 0) - return log_debug_errno(r, "sysview: cannot parse SessionRemoved from logind: %m"); - - session = sysview_find_session(c, id); - if (!session) - return 0; - - context_remove_session(c, session); - return 0; -} - -static int context_ld_manager_signal_fn(sd_bus_message *signal, - void *userdata, - sd_bus_error *ret_error) { - sysview_context *c = userdata; - - if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatNew")) - return context_ld_seat_new(c, signal); - else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SeatRemoved")) - return context_ld_seat_removed(c, signal); - else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionNew")) - return context_ld_session_new(c, signal); - else if (sd_bus_message_is_signal(signal, "org.freedesktop.login1.Manager", "SessionRemoved")) - return context_ld_session_removed(c, signal); - else - return 0; -} - -static int context_ld_start(sysview_context *c) { - int r; - - if (!c->scan_logind) - return 0; - - r = sd_bus_add_match(c->sysbus, - &c->ld_slot_manager_signal, - "type='signal'," - "sender='org.freedesktop.login1'," - "interface='org.freedesktop.login1.Manager'," - "path='/org/freedesktop/login1'", - context_ld_manager_signal_fn, - c); - if (r < 0) - return r; - - return 0; -} - -static void context_ld_stop(sysview_context *c) { - c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions); - c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats); - c->ld_slot_manager_signal = sd_bus_slot_unref(c->ld_slot_manager_signal); -} - -static int context_ld_list_seats_fn(sd_bus_message *reply, - void *userdata, - sd_bus_error *ret_error) { - sysview_context *c = userdata; - int r; - - c->ld_slot_list_seats = sd_bus_slot_unref(c->ld_slot_list_seats); - - if (sd_bus_message_is_method_error(reply, NULL)) { - const sd_bus_error *error = sd_bus_message_get_error(reply); - - log_debug("sysview: ListSeats on logind failed: %s: %s", - error->name, error->message); - r = -sd_bus_error_get_errno(error); - goto settle; - } - - r = sd_bus_message_enter_container(reply, 'a', "(so)"); - if (r < 0) - goto error; - - while ((r = sd_bus_message_enter_container(reply, 'r', "so")) > 0) { - const char *id, *path; - - r = sd_bus_message_read(reply, "so", &id, &path); - if (r < 0) - goto error; - - context_add_seat(c, id); - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto error; - } - - if (r < 0) - goto error; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto error; - - r = 0; - goto settle; - -error: - log_debug_errno(r, "sysview: erroneous ListSeats response from logind: %m"); -settle: - context_settle(c); - return r; -} - -static int context_ld_list_sessions_fn(sd_bus_message *reply, - void *userdata, - sd_bus_error *ret_error) { - sysview_context *c = userdata; - int r; - - c->ld_slot_list_sessions = sd_bus_slot_unref(c->ld_slot_list_sessions); - - if (sd_bus_message_is_method_error(reply, NULL)) { - const sd_bus_error *error = sd_bus_message_get_error(reply); - - log_debug("sysview: ListSessions on logind failed: %s: %s", - error->name, error->message); - r = -sd_bus_error_get_errno(error); - goto settle; - } - - r = sd_bus_message_enter_container(reply, 'a', "(susso)"); - if (r < 0) - goto error; - - while ((r = sd_bus_message_enter_container(reply, 'r', "susso")) > 0) { - const char *id, *username, *seatid, *path; - sysview_seat *seat; - unsigned int uid; - - r = sd_bus_message_read(reply, - "susso", - &id, - &uid, - &username, - &seatid, - &path); - if (r < 0) - goto error; - - seat = sysview_find_seat(c, seatid); - if (seat) { - r = context_raise_session_filter(c, id, seatid, username, uid); - if (r < 0) - log_debug_errno(r, "sysview: callback failed while filtering session '%s': %m", - id); - else if (r > 0) - context_add_session(c, seat, id); - } - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto error; - } - - if (r < 0) - goto error; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto error; - - r = 0; - goto settle; - -error: - log_debug_errno(r, "sysview: erroneous ListSessions response from logind: %m"); -settle: - context_settle(c); - return r; -} - -static int context_ld_scan(sysview_context *c) { - _cleanup_bus_message_unref_ sd_bus_message *m = NULL; - int r; - - if (!c->ld_slot_manager_signal) - return 0; - - /* request seat list */ - - r = sd_bus_message_new_method_call(c->sysbus, - &m, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "ListSeats"); - if (r < 0) - return r; - - r = sd_bus_call_async(c->sysbus, - &c->ld_slot_list_seats, - m, - context_ld_list_seats_fn, - c, - 0); - if (r < 0) - return r; - - if (!c->settled) - ++c->n_probe; - - /* request session list */ - - m = sd_bus_message_unref(m); - r = sd_bus_message_new_method_call(c->sysbus, - &m, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "ListSessions"); - if (r < 0) - return r; - - r = sd_bus_call_async(c->sysbus, - &c->ld_slot_list_sessions, - m, - context_ld_list_sessions_fn, - c, - 0); - if (r < 0) - return r; - - if (!c->settled) - ++c->n_probe; - - return 0; -} - -bool sysview_context_is_running(sysview_context *c) { - return c && c->running; -} - -int sysview_context_start(sysview_context *c, sysview_event_fn event_fn, void *userdata) { - int r; - - assert_return(c, -EINVAL); - assert_return(event_fn, -EINVAL); - - if (c->running) - return -EALREADY; - - log_debug("sysview: start"); - - c->running = true; - c->event_fn = event_fn; - c->userdata = userdata; - - r = context_ld_start(c); - if (r < 0) - goto error; - - r = context_ud_start(c); - if (r < 0) - goto error; - - r = sysview_context_rescan(c); - if (r < 0) - goto error; - - return 0; - -error: - sysview_context_stop(c); - return r; -} - -void sysview_context_stop(sysview_context *c) { - sysview_session *session; - sysview_device *device; - sysview_seat *seat; - - assert(c); - - if (!c->running) - return; - - log_debug("sysview: stop"); - - while ((device = hashmap_first(c->device_map))) - context_remove_device(c, device); - - while ((session = hashmap_first(c->session_map))) - context_remove_session(c, session); - - while ((seat = hashmap_first(c->seat_map))) - context_remove_seat(c, seat); - - c->running = false; - c->scanned = false; - c->settled = false; - c->n_probe = 0; - c->event_fn = NULL; - c->userdata = NULL; - c->scan_src = sd_event_source_unref(c->scan_src); - context_ud_stop(c); - context_ld_stop(c); -} - -static int context_scan_fn(sd_event_source *s, void *userdata) { - sysview_context *c = userdata; - sysview_seat *seat; - Iterator i; - int r; - - c->rescan = false; - - if (!c->scanned) { - r = context_ld_scan(c); - if (r < 0) - return log_debug_errno(r, "sysview: logind scan failed: %m"); - } - - /* skip device scans if no sessions are available */ - if (hashmap_size(c->session_map) > 0) { - r = context_ud_scan(c); - if (r < 0) - return log_debug_errno(r, "sysview: udev scan failed: %m"); - - HASHMAP_FOREACH(seat, c->seat_map, i) - seat->scanned = true; - } - - c->scanned = true; - context_settle(c); - - return 0; -} - -int sysview_context_rescan(sysview_context *c) { - assert(c); - - if (!c->running) - return 0; - - if (!c->rescan) { - c->rescan = true; - if (!c->settled) - ++c->n_probe; - } - - if (c->scan_src) - return sd_event_source_set_enabled(c->scan_src, SD_EVENT_ONESHOT); - else - return sd_event_add_defer(c->event, &c->scan_src, context_scan_fn, c); -} |