From 3f49d45a45c6c585098590174c3245d2d9bdde0a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 26 May 2011 02:21:16 +0200 Subject: [PATCH] logind: implement D-Bus properties --- Makefile.am | 4 + src/70-uaccess.rules | 72 +++++++++++ src/71-seat.rules | 20 +++ src/logind-dbus.c | 235 ++++++++++++++++++++++++++++++++++ src/logind-device.c | 2 +- src/logind-seat-dbus.c | 218 ++++++++++++++++++++++++++++++++ src/logind-seat.c | 44 ++++++- src/logind-seat.h | 6 + src/logind-session-dbus.c | 256 ++++++++++++++++++++++++++++++++++++++ src/logind-session.c | 8 +- src/logind-session.h | 11 +- src/logind-user-dbus.c | 240 +++++++++++++++++++++++++++++++++++ src/logind-user.h | 4 + src/logind.c | 51 ++++---- src/logind.h | 6 +- 15 files changed, 1141 insertions(+), 36 deletions(-) create mode 100644 src/70-uaccess.rules create mode 100644 src/71-seat.rules create mode 100644 src/logind-dbus.c create mode 100644 src/logind-seat-dbus.c create mode 100644 src/logind-session-dbus.c create mode 100644 src/logind-user-dbus.c diff --git a/Makefile.am b/Makefile.am index f455f211..0bc51692 100644 --- a/Makefile.am +++ b/Makefile.am @@ -810,10 +810,14 @@ systemd_hostnamed_LDADD = \ systemd_logind_SOURCES = \ src/logind.c \ + src/logind-dbus.c \ src/logind-device.c \ src/logind-seat.c \ + src/logind-seat-dbus.c \ src/logind-session.c \ + src/logind-session-dbus.c \ src/logind-user.c \ + src/logind-user-dbus.c \ src/logind-acl.c \ src/dbus-common.c \ src/dbus-loop.c \ diff --git a/src/70-uaccess.rules b/src/70-uaccess.rules new file mode 100644 index 00000000..69324922 --- /dev/null +++ b/src/70-uaccess.rules @@ -0,0 +1,72 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +ACTION=="remove", GOTO="uaccess_end" +ENV{MAJOR}=="", GOTO="uaccess_end" + +# PTP/MTP protocol devices, cameras, portable media players +SUBSYSTEM=="usb", ENV{ID_USB_INTERFACES}=="", ENV{DEVTYPE}=="usb_device", IMPORT{program}="usb_id --export %p" +SUBSYSTEM=="usb", ENV{ID_USB_INTERFACES}=="*:060101:*", TAG+="uaccess" + +# Digicams with proprietary protocol +ENV{ID_GPHOTO2}=="*?", TAG+="uaccess" + +# SCSI and USB scanners +ENV{libsane_matched}=="yes", TAG+="uaccess" + +# HPLIP devices (necessary for ink level check and HP tool maintenance) +ENV{ID_HPLIP}=="1", TAG+="uaccess" + +# optical drives +SUBSYSTEM=="block", ENV{ID_CDROM}=="1", TAG+="uaccess" +SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", TAG+="uaccess" + +# Sound devices +SUBSYSTEM=="sound", TAG+="uaccess" + +# ffado is an userspace driver for firewire sound cards +SUBSYSTEM=="firewire", ENV{ID_FFADO}=="1", TAG+="uaccess" + +# Webcams, frame grabber, TV cards +SUBSYSTEM=="video4linux", TAG+="uaccess" +SUBSYSTEM=="dvb", TAG+="uaccess" + +# IIDC devices: industrial cameras and some webcams +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*", TAG+="uaccess" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*", TAG+="uaccess" +# AV/C devices: camcorders, set-top boxes, TV sets, audio devices, and more +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", TAG+="uaccess" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", TAG+="uaccess" + +# DRI video devices +SUBSYSTEM=="drm", KERNEL=="card*", TAG+="uaccess" + +# KVM +SUBSYSTEM=="misc", KERNEL=="kvm", TAG+="uaccess" + +# smart-card readers +ENV{ID_SMARTCARD_READER}=="*?", TAG+="uaccess" + +# PDA devices +ENV{ID_PDA}=="*?", TAG+="uaccess" + +# Programmable remote control +ENV{ID_REMOTE_CONTROL}=="1", TAG+="uaccess" + +# joysticks +SUBSYSTEM=="input", ENV{ID_INPUT_JOYSTICK}=="?*", TAG+="uaccess" + +# color measurement devices +ENV{COLOR_MEASUREMENT_DEVICE}=="*?", TAG+="uaccess" + +# DDC/CI device, usually high-end monitors such as the DreamColor +ENV{DDC_DEVICE}=="*?", TAG+="uaccess" + +# media player raw devices (for user-mode drivers, Android SDK, etc.) +SUBSYSTEM=="usb", ENV{ID_MEDIA_PLAYER}=="?*", TAG+="uaccess" + +LABEL="uaccess_end" diff --git a/src/71-seat.rules b/src/71-seat.rules new file mode 100644 index 00000000..39698379 --- /dev/null +++ b/src/71-seat.rules @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +ACTION=="remove", GOTO="seat_end" + +TAG=="uaccess", TAG+="seat" +SUBSYSTEM=="input", TAG+="seat" +SUBSYSTEM=="graphics", KERNEL=="fb[0-9]*", TAG+="seat" +SUBSYSTEM=="usb", ATTR{bDeviceClass}=="09", TAG+="seat" +SUBSYSTEM=="usb", ATTR{idVendor}=="2230", ATTR{idProduct}=="0001", ENV{ID_AUTOSEAT}="1" + +IMPORT{parent}="ID_SEAT" +ENV{ID_AUTOSEAT}=="1", ENV{ID_SEAT}=="", ENV{ID_SEAT}="seat-foo" +ENV{ID_SEAT}!="", TAG+="seat-foo" + +LABEL="seat_end" diff --git a/src/logind-dbus.c b/src/logind-dbus.c new file mode 100644 index 00000000..90db9418 --- /dev/null +++ b/src/logind-dbus.c @@ -0,0 +1,235 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include "logind.h" +#include "dbus-common.h" + +#define BUS_MANAGER_INTERFACE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define INTROSPECTION_BEGIN \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + BUS_MANAGER_INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_PEER_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE + +#define INTROSPECTION_END \ + "\n" + +#define INTERFACES_LIST \ + BUS_GENERIC_INTERFACES_LIST \ + "org.freedesktop.login1.Manager\0" + +static DBusHandlerResult manager_message_handler( + DBusConnection *connection, + DBusMessage *message, + void *userdata) { + + Manager *m = userdata; + + const BusProperty properties[] = { + { "org.freedesktop.login1.Manager", "ControlGroupHierarchy", bus_property_append_string, "s", m->cgroup_path }, + { "org.freedesktop.login1.Manager", "Controllers", bus_property_append_strv, "as", m->controllers }, + { "org.freedesktop.login1.Manager", "ResetControllers", bus_property_append_strv, "as", m->reset_controllers }, + { "org.freedesktop.login1.Manager", "NAutoVTs", bus_property_append_unsigned, "u", &m->n_autovts }, + { "org.freedesktop.login1.Manager", "KillOnlyUsers", bus_property_append_strv, "as", m->kill_only_users }, + { "org.freedesktop.login1.Manager", "KillExcludeUsers", bus_property_append_strv, "as", m->kill_exclude_users }, + { "org.freedesktop.login1.Manager", "KillUserProcesses", bus_property_append_bool, "b", &m->kill_user_processes }, + { NULL, NULL, NULL, NULL, NULL } + }; + + DBusError error; + DBusMessage *reply = NULL; + + assert(connection); + assert(message); + assert(m); + + dbus_error_init(&error); + + if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) { + char *introspection = NULL; + FILE *f; + Iterator i; + Session *session; + Seat *seat; + User *user; + size_t size; + char *p; + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + /* We roll our own introspection code here, instead of + * relying on bus_default_message_handler() because we + * need to generate our introspection string + * dynamically. */ + + if (!(f = open_memstream(&introspection, &size))) + goto oom; + + fputs(INTROSPECTION_BEGIN, f); + + HASHMAP_FOREACH(seat, m->seats, i) { + p = bus_path_escape(seat->id); + + if (p) { + fprintf(f, "", p); + free(p); + } + } + + HASHMAP_FOREACH(user, m->users, i) + fprintf(f, "", (unsigned long long) user->uid); + + HASHMAP_FOREACH(session, m->sessions, i) { + p = bus_path_escape(session->id); + + if (p) { + fprintf(f, "", p); + free(p); + } + } + + fputs(INTROSPECTION_END, f); + + if (ferror(f)) { + fclose(f); + free(introspection); + goto oom; + } + + fclose(f); + + if (!introspection) + goto oom; + + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) { + free(introspection); + goto oom; + } + + free(introspection); + } else + return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, properties); + + if (reply) { + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + } + + return DBUS_HANDLER_RESULT_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +const DBusObjectPathVTable bus_manager_vtable = { + .message_function = manager_message_handler +}; diff --git a/src/logind-device.c b/src/logind-device.c index 4e076c20..31afa4ff 100644 --- a/src/logind-device.c +++ b/src/logind-device.c @@ -80,6 +80,6 @@ void device_attach(Device *d, Seat *s) { if (d->seat) device_detach(d); - LIST_PREPEND(Device, devices, d->seat->devices, d); d->seat = s; + LIST_PREPEND(Device, devices, s->devices, d); } diff --git a/src/logind-seat-dbus.c b/src/logind-seat-dbus.c new file mode 100644 index 00000000..63b1bd5e --- /dev/null +++ b/src/logind-seat-dbus.c @@ -0,0 +1,218 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include "logind.h" +#include "logind-seat.h" +#include "dbus-common.h" +#include "util.h" + +#define BUS_SEAT_INTERFACE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + BUS_SEAT_INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_PEER_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + "\n" + +#define INTERFACES_LIST \ + BUS_GENERIC_INTERFACES_LIST \ + "org.freedesktop.login1.Seat\0" + +static int bus_seat_append_active(DBusMessageIter *i, const char *property, void *data) { + DBusMessageIter sub; + Seat *s = data; + const char *id, *path; + char *p = NULL; + + assert(i); + assert(property); + assert(s); + + if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub)) + return -ENOMEM; + + if (s->active) { + id = s->active->id; + path = p = session_bus_path(s->active); + + if (!p) + return -ENOMEM; + } else { + id = ""; + path = "/"; + } + + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) || + !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) { + free(p); + return -ENOMEM; + } + + free(p); + + if (!dbus_message_iter_close_container(i, &sub)) + return -ENOMEM; + + return 0; +} + +static int bus_seat_append_sessions(DBusMessageIter *i, const char *property, void *data) { + DBusMessageIter sub, sub2; + Seat *s = data; + Session *session; + + assert(i); + assert(property); + assert(s); + + if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "so", &sub)) + return -ENOMEM; + + LIST_FOREACH(sessions_by_seat, session, s->sessions) { + char *p; + + if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2)) + return -ENOMEM; + + p = session_bus_path(session); + if (!p) + return -ENOMEM; + + if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) { + free(p); + return -ENOMEM; + } + + free(p); + + if (!dbus_message_iter_close_container(&sub, &sub2)) + return -ENOMEM; + } + + if (!dbus_message_iter_close_container(i, &sub)) + return -ENOMEM; + + return 0; +} + +static int get_seat_for_path(Manager *m, const char *path, Seat **_s) { + Seat *s; + char *id; + + assert(m); + assert(path); + assert(_s); + + if (!startswith(path, "/org/freedesktop/login1/seat/")) + return -EINVAL; + + id = bus_path_unescape(path + 29); + if (!id) + return -ENOMEM; + + s = hashmap_get(m->seats, id); + free(id); + + if (!s) + return -ENOENT; + + *_s = s; + return 0; +} + +static DBusHandlerResult seat_message_dispatch( + Seat *s, + DBusConnection *connection, + DBusMessage *message) { + + const BusProperty properties[] = { + { "org.freedesktop.login1.Seat", "Id", bus_property_append_string, "s", s->id }, + { "org.freedesktop.login1.Seat", "Active", bus_seat_append_active, "(so)", s }, + { "org.freedesktop.login1.Seat", "Sessions", bus_seat_append_sessions, "a(so)", s }, + { NULL, NULL, NULL, NULL, NULL } + }; + + assert(s); + assert(connection); + assert(message); + + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); +} + +static DBusHandlerResult seat_message_handler( + DBusConnection *connection, + DBusMessage *message, + void *userdata) { + + Manager *m = userdata; + Seat *s; + int r; + + r = get_seat_for_path(m, dbus_message_get_path(message), &s); + if (r < 0) { + + if (r == -ENOMEM) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + if (r == -ENOENT) { + DBusError e; + + dbus_error_init(&e); + dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown seat"); + return bus_send_error_reply(connection, message, &e, r); + } + + return bus_send_error_reply(connection, message, NULL, r); + } + + return seat_message_dispatch(s, connection, message); +} + +const DBusObjectPathVTable bus_seat_vtable = { + .message_function = seat_message_handler +}; + +char *seat_bus_path(Seat *s) { + char *t, *r; + + assert(s); + + t = bus_path_escape(s->id); + if (!t) + return NULL; + + r = strappend("/org/freedesktop/login1/seat/", t); + free(t); + + return r; +} diff --git a/src/logind-seat.c b/src/logind-seat.c index ae89ec9d..2ba3060b 100644 --- a/src/logind-seat.c +++ b/src/logind-seat.c @@ -182,7 +182,8 @@ static int vt_allocate(int vtnr) { } static int seat_preallocate_vts(Seat *s) { - int i, r = 0; + int r = 0; + unsigned i; assert(s); assert(s->manager); @@ -295,6 +296,11 @@ int seat_read_active_vt(Seat *s) { int seat_start(Seat *s) { assert(s); + if (s->started) + return 0; + + log_info("New seat %s.", s->id); + /* Initialize VT magic stuff */ seat_preallocate_vts(s); @@ -304,6 +310,8 @@ int seat_start(Seat *s) { /* Save seat data */ seat_save(s); + s->started = true; + return 0; } @@ -313,6 +321,11 @@ int seat_stop(Seat *s) { assert(s); + if (!s->started) + return 0; + + log_info("Removed seat %s.", s->id); + LIST_FOREACH(sessions_by_seat, session, s->sessions) { k = session_stop(session); if (k < 0) @@ -322,6 +335,8 @@ int seat_stop(Seat *s) { unlink(s->state_file); seat_add_to_gc_queue(s); + s->started = false; + return r; } @@ -343,3 +358,30 @@ void seat_add_to_gc_queue(Seat *s) { LIST_PREPEND(Seat, gc_queue, s->manager->seat_gc_queue, s); s->in_gc_queue = true; } + +static bool seat_name_valid_char(char c) { + return + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == '-' || + c == '_'; +} + +bool seat_name_is_valid(const char *name) { + const char *p; + + assert(name); + + if (!startswith(name, "seat")) + return false; + + if (!name[4]) + return false; + + for (p = name; *p; p++) + if (!seat_name_valid_char(*p)) + return false; + + return true; +} diff --git a/src/logind-seat.h b/src/logind-seat.h index b1a8d619..b045bde9 100644 --- a/src/logind-seat.h +++ b/src/logind-seat.h @@ -42,6 +42,7 @@ struct Seat { LIST_HEAD(Session, sessions); bool in_gc_queue:1; + bool started:1; LIST_FIELDS(Seat, gc_queue); }; @@ -62,4 +63,9 @@ int seat_stop(Seat *s); int seat_check_gc(Seat *s); void seat_add_to_gc_queue(Seat *s); +bool seat_name_is_valid(const char *name); +char *seat_bus_path(Seat *s); + +extern const DBusObjectPathVTable bus_seat_vtable; + #endif diff --git a/src/logind-session-dbus.c b/src/logind-session-dbus.c new file mode 100644 index 00000000..41af6585 --- /dev/null +++ b/src/logind-session-dbus.c @@ -0,0 +1,256 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include "logind.h" +#include "logind-session.h" +#include "dbus-common.h" +#include "util.h" + +#define BUS_SESSION_INTERFACE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + BUS_SESSION_INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_PEER_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + "\n" + +#define INTERFACES_LIST \ + BUS_GENERIC_INTERFACES_LIST \ + "org.freedesktop.login1.Session\0" + +static int bus_session_append_seat(DBusMessageIter *i, const char *property, void *data) { + DBusMessageIter sub; + Session *s = data; + const char *id, *path; + char *p = NULL; + + assert(i); + assert(property); + assert(s); + + if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub)) + return -ENOMEM; + + if (s->seat) { + id = s->seat->id; + path = p = seat_bus_path(s->seat); + + if (!p) + return -ENOMEM; + } else { + id = ""; + path = "/"; + } + + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) || + !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) { + free(p); + return -ENOMEM; + } + + free(p); + + if (!dbus_message_iter_close_container(i, &sub)) + return -ENOMEM; + + return 0; +} + +static int bus_session_append_user(DBusMessageIter *i, const char *property, void *data) { + DBusMessageIter sub; + Session *s = data; + char *p = NULL; + + assert(i); + assert(property); + assert(s); + + if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub)) + return -ENOMEM; + + p = user_bus_path(s->user); + if (!p) + return -ENOMEM; + + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &s->user->uid) || + !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) { + free(p); + return -ENOMEM; + } + + free(p); + + if (!dbus_message_iter_close_container(i, &sub)) + return -ENOMEM; + + return 0; +} + +static int bus_session_append_active(DBusMessageIter *i, const char *property, void *data) { + Session *s = data; + bool b; + + assert(i); + assert(property); + assert(s); + + b = session_is_active(s); + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) + return -ENOMEM; + + return 0; +} + +static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType); + +static int get_session_for_path(Manager *m, const char *path, Session **_s) { + Session *s; + char *id; + + assert(m); + assert(path); + assert(_s); + + if (!startswith(path, "/org/freedesktop/login1/session/")) + return -EINVAL; + + id = bus_path_unescape(path + 32); + if (!id) + return -ENOMEM; + + s = hashmap_get(m->sessions, id); + free(id); + + if (!s) + return -ENOENT; + + *_s = s; + return 0; +} + +static DBusHandlerResult session_message_dispatch( + Session *s, + DBusConnection *connection, + DBusMessage *message) { + + const BusProperty properties[] = { + { "org.freedesktop.login1.Session", "Id", bus_property_append_string, "s", s->id }, + { "org.freedesktop.login1.Session", "User", bus_session_append_user, "(uo)", s }, + { "org.freedesktop.login1.Session", "Name", bus_property_append_string, "s", s->user->name }, + { "org.freedesktop.login1.Session", "ControlGroupPath", bus_property_append_string, "s", s->cgroup_path }, + { "org.freedesktop.login1.Session", "VTNr", bus_property_append_uint32, "u", &s->vtnr }, + { "org.freedesktop.login1.Session", "Seat", bus_session_append_seat, "(so)", s }, + { "org.freedesktop.login1.Session", "TTY", bus_property_append_string, "s", s->tty }, + { "org.freedesktop.login1.Session", "Display", bus_property_append_string, "s", s->display }, + { "org.freedesktop.login1.Session", "Remote", bus_property_append_bool, "b", &s->remote }, + { "org.freedesktop.login1.Session", "RemoteUser", bus_property_append_string, "s", s->remote_user }, + { "org.freedesktop.login1.Session", "RemoteHost", bus_property_append_string, "s", s->remote_host }, + { "org.freedesktop.login1.Session", "Leader", bus_property_append_pid, "u", &s->leader }, + { "org.freedesktop.login1.Session", "Audit", bus_property_append_uint32, "u", &s->audit_id }, + { "org.freedesktop.login1.Session", "Type", bus_session_append_type, "s", &s->type }, + { "org.freedesktop.login1.Session", "Active", bus_session_append_active, "b", s }, + { "org.freedesktop.login1.Session", "Controllers", bus_property_append_strv, "as", s->controllers }, + { "org.freedesktop.login1.Session", "ResetControllers", bus_property_append_strv, "as", s->reset_controllers }, + { "org.freedesktop.login1.Session", "KillProcesses", bus_property_append_bool, "b", &s->kill_processes }, + { NULL, NULL, NULL, NULL, NULL } + }; + + assert(s); + assert(connection); + assert(message); + + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); +} + +static DBusHandlerResult session_message_handler( + DBusConnection *connection, + DBusMessage *message, + void *userdata) { + + Manager *m = userdata; + Session *s; + int r; + + r = get_session_for_path(m, dbus_message_get_path(message), &s); + if (r < 0) { + + if (r == -ENOMEM) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + if (r == -ENOENT) { + DBusError e; + + dbus_error_init(&e); + dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session"); + return bus_send_error_reply(connection, message, &e, r); + } + + return bus_send_error_reply(connection, message, NULL, r); + } + + return session_message_dispatch(s, connection, message); +} + +const DBusObjectPathVTable bus_session_vtable = { + .message_function = session_message_handler +}; + +char *session_bus_path(Session *s) { + char *t, *r; + + assert(s); + + t = bus_path_escape(s->id); + if (!t) + return NULL; + + r = strappend("/org/freedesktop/login1/session/", t); + free(t); + + return r; +} diff --git a/src/logind-session.c b/src/logind-session.c index 8f1280db..6b3b2778 100644 --- a/src/logind-session.c +++ b/src/logind-session.c @@ -83,6 +83,7 @@ void session_free(Session *s) { free(s->tty); free(s->display); free(s->remote_host); + free(s->remote_user); hashmap_remove(s->manager->sessions, s->id); @@ -147,6 +148,11 @@ int session_save(Session *s) { "REMOTE_HOST=%s\n", s->remote_host); + if (s->remote_user) + fprintf(f, + "REMOTE_USER=%s\n", + s->remote_user); + if (s->seat && s->seat->manager->vtconsole == s->seat) fprintf(f, "VTNR=%i\n", @@ -495,7 +501,7 @@ void session_add_to_gc_queue(Session *s) { } static const char* const session_type_table[_SESSION_TYPE_MAX] = { - [SESSION_TERMINAL] = "terminal", + [SESSION_TTY] = "tty", [SESSION_X11] = "x11" }; diff --git a/src/logind-session.h b/src/logind-session.h index 4c6e768f..60ac1c54 100644 --- a/src/logind-session.h +++ b/src/logind-session.h @@ -31,7 +31,7 @@ typedef struct Session Session; #include "logind-user.h" typedef enum SessionType { - SESSION_TERMINAL, + SESSION_TTY, SESSION_X11, _SESSION_TYPE_MAX, _SESSION_TYPE_INVALID = -1 @@ -53,20 +53,21 @@ struct Session { char *display; bool remote; + char *remote_user; char *remote_host; int vtnr; Seat *seat; pid_t leader; - uint64_t audit_id; + uint32_t audit_id; int pipe_fd; char *cgroup_path; char **controllers, **reset_controllers; - bool kill_processes:1; + bool kill_processes; bool in_gc_queue:1; LIST_FIELDS(Session, sessions_by_user); @@ -86,6 +87,10 @@ int session_stop(Session *s); int session_save(Session *s); int session_load(Session *s); +char *session_bus_path(Session *s); + +extern const DBusObjectPathVTable bus_session_vtable; + const char* session_type_to_string(SessionType t); SessionType session_type_from_string(const char *s); diff --git a/src/logind-user-dbus.c b/src/logind-user-dbus.c new file mode 100644 index 00000000..7c8bb27c --- /dev/null +++ b/src/logind-user-dbus.c @@ -0,0 +1,240 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include "logind.h" +#include "logind-user.h" +#include "dbus-common.h" + +#define BUS_USER_INTERFACE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + BUS_USER_INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_PEER_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + "\n" + +#define INTERFACES_LIST \ + BUS_GENERIC_INTERFACES_LIST \ + "org.freedesktop.login1.User\0" + +static int bus_user_append_display(DBusMessageIter *i, const char *property, void *data) { + DBusMessageIter sub; + User *u = data; + const char *id, *path; + char *p = NULL; + + assert(i); + assert(property); + assert(u); + + if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub)) + return -ENOMEM; + + if (u->display) { + id = u->display->id; + path = p = session_bus_path(u->display); + + if (!p) + return -ENOMEM; + } else { + id = ""; + path = "/"; + } + + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) || + !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) { + free(p); + return -ENOMEM; + } + + free(p); + + if (!dbus_message_iter_close_container(i, &sub)) + return -ENOMEM; + + return 0; +} + +static int bus_user_append_state(DBusMessageIter *i, const char *property, void *data) { + User *u = data; + const char *state; + + assert(i); + assert(property); + assert(u); + + state = user_state_to_string(user_get_state(u)); + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state)) + return -ENOMEM; + + return 0; +} + +static int bus_user_append_sessions(DBusMessageIter *i, const char *property, void *data) { + DBusMessageIter sub, sub2; + User *u = data; + Session *session; + + assert(i); + assert(property); + assert(u); + + if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "so", &sub)) + return -ENOMEM; + + LIST_FOREACH(sessions_by_user, session, u->sessions) { + char *p; + + if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2)) + return -ENOMEM; + + p = session_bus_path(session); + if (!p) + return -ENOMEM; + + if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) { + free(p); + return -ENOMEM; + } + + free(p); + + if (!dbus_message_iter_close_container(&sub, &sub2)) + return -ENOMEM; + } + + if (!dbus_message_iter_close_container(i, &sub)) + return -ENOMEM; + + return 0; +} + +static int get_user_for_path(Manager *m, const char *path, User **_u) { + User *u; + unsigned long lu; + int r; + + assert(m); + assert(path); + assert(_u); + + if (!startswith(path, "/org/freedesktop/login1/user/")) + return -EINVAL; + + r = safe_atolu(path + 29, &lu); + if (r < 0) + return r; + + u = hashmap_get(m->users, ULONG_TO_PTR(lu)); + if (!u) + return -ENOENT; + + *_u = u; + return 0; +} + +static DBusHandlerResult user_message_dispatch( + User *u, + DBusConnection *connection, + DBusMessage *message) { + + const BusProperty properties[] = { + { "org.freedesktop.login1.User", "UID", bus_property_append_uid, "u", &u->uid }, + { "org.freedesktop.login1.User", "GID", bus_property_append_gid, "u", &u->gid }, + { "org.freedesktop.login1.User", "Name", bus_property_append_string, "s", u->name }, + { "org.freedesktop.login1.User", "RuntimePath", bus_property_append_string, "s", u->runtime_path }, + { "org.freedesktop.login1.User", "ControlGroupPath", bus_property_append_string, "s", u->cgroup_path }, + { "org.freedesktop.login1.User", "Service", bus_property_append_string, "s", u->service }, + { "org.freedesktop.login1.User", "Display", bus_user_append_display, "(so)", u }, + { "org.freedesktop.login1.User", "State", bus_user_append_state, "s", u }, + { "org.freedesktop.login1.User", "Sessions", bus_user_append_sessions, "a(so)", u }, + { NULL, NULL, NULL, NULL, NULL } + }; + + assert(u); + assert(connection); + assert(message); + + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); +} + +static DBusHandlerResult user_message_handler( + DBusConnection *connection, + DBusMessage *message, + void *userdata) { + + Manager *m = userdata; + User *u; + int r; + + r = get_user_for_path(m, dbus_message_get_path(message), &u); + if (r < 0) { + + if (r == -ENOMEM) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + if (r == -ENOENT) { + DBusError e; + + dbus_error_init(&e); + dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown user"); + return bus_send_error_reply(connection, message, &e, r); + } + + return bus_send_error_reply(connection, message, NULL, r); + } + + return user_message_dispatch(u, connection, message); +} + +const DBusObjectPathVTable bus_user_vtable = { + .message_function = user_message_handler +}; + +char *user_bus_path(User *u) { + char *s; + + assert(u); + + if (asprintf(&s, "/org/freedesktop/login1/user/%llu", (unsigned long long) u->uid) < 0) + return NULL; + + return s; +} diff --git a/src/logind-user.h b/src/logind-user.h index fc480594..7f58aa2b 100644 --- a/src/logind-user.h +++ b/src/logind-user.h @@ -70,6 +70,10 @@ UserState user_get_state(User *u); int user_save(User *u); int user_load(User *u); +char *user_bus_path(User *s); + +extern const DBusObjectPathVTable bus_user_vtable; + const char* user_state_to_string(UserState s); UserState user_state_from_string(const char *s); diff --git a/src/logind.c b/src/logind.c index 5d5181a1..a6280289 100644 --- a/src/logind.c +++ b/src/logind.c @@ -255,9 +255,15 @@ int manager_process_device(Manager *m, struct udev_device *d) { assert(m); + /* FIXME: drop this check as soon as libudev's enum support + * honours tags and subsystem matches at the same time */ + if (!streq_ptr(udev_device_get_subsystem(d), "graphics")) + return 0; + if (streq_ptr(udev_device_get_action(d), "remove")) { - device = hashmap_get(m->devices, udev_device_get_syspath(d)); + /* FIXME: use syspath instead of sysname here, as soon as fb driver is fixed */ + device = hashmap_get(m->devices, udev_device_get_sysname(d)); if (!device) return 0; @@ -268,14 +274,16 @@ int manager_process_device(Manager *m, struct udev_device *d) { const char *sn; Seat *seat; - sn = udev_device_get_property_value(d, "SEAT"); + sn = udev_device_get_property_value(d, "ID_SEAT"); if (!sn) sn = "seat0"; - if (!startswith(sn, "seat")) - return -EINVAL; + if (!seat_name_is_valid(sn)) { + log_warning("Device with invalid seat name %s found, ignoring.", sn); + return 0; + } - r = manager_add_device(m, udev_device_get_syspath(d), &device); + r = manager_add_device(m, udev_device_get_sysname(d), &device); if (r < 0) return r; @@ -288,6 +296,7 @@ int manager_process_device(Manager *m, struct udev_device *d) { } device_attach(device, seat); + seat_start(seat); } return 0; @@ -373,9 +382,6 @@ int manager_enumerate_seats(Manager *m) { if (!dirent_is_file(de)) continue; - if (!startswith(de->d_name, "seat")) - continue; - s = hashmap_get(m->seats, de->d_name); if (!s) { unlinkat(dirfd(d), de->d_name, 0); @@ -676,27 +682,15 @@ int manager_spawn_autovt(Manager *m, int vtnr) { return 0; } -static DBusHandlerResult login_message_handler( - DBusConnection *connection, - DBusMessage *message, - void *userdata) { - - return DBUS_HANDLER_RESULT_HANDLED; -} - static DBusHandlerResult login_message_filter( DBusConnection *connection, DBusMessage *message, void *userdata) { - return DBUS_HANDLER_RESULT_HANDLED; + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static int manager_connect_bus(Manager *m) { - const DBusObjectPathVTable login_vtable = { - .message_function = login_message_handler - }; - DBusError error; int r; struct epoll_event ev; @@ -714,13 +708,11 @@ static int manager_connect_bus(Manager *m) { goto fail; } - if (!dbus_connection_register_object_path(m->bus, "/org/freedesktop/login1", &login_vtable, NULL)) { - log_error("Not enough memory"); - r = -ENOMEM; - goto fail; - } - - if (!dbus_connection_add_filter(m->bus, login_message_filter, m, NULL)) { + if (!dbus_connection_register_object_path(m->bus, "/org/freedesktop/login1", &bus_manager_vtable, m) || + !dbus_connection_register_fallback(m->bus, "/org/freedesktop/login1/seat", &bus_seat_vtable, m) || + !dbus_connection_register_fallback(m->bus, "/org/freedesktop/login1/session", &bus_session_vtable, m) || + !dbus_connection_register_fallback(m->bus, "/org/freedesktop/login1/user", &bus_user_vtable, m) || + !dbus_connection_add_filter(m->bus, login_message_filter, m, NULL)) { log_error("Not enough memory"); r = -ENOMEM; goto fail; @@ -931,6 +923,9 @@ int manager_run(Manager *m) { n = epoll_wait(m->epoll_fd, &event, 1, -1); if (n < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + log_error("epoll() failed: %m"); return -errno; } diff --git a/src/logind.h b/src/logind.h index a8b387b0..e4b7a3c4 100644 --- a/src/logind.h +++ b/src/logind.h @@ -72,14 +72,14 @@ struct Manager { int bus_fd; int epoll_fd; - int n_autovts; + unsigned n_autovts; Seat *vtconsole; char *cgroup_path; char **controllers, **reset_controllers; - char **kill_only_users, **kill_exlude_users; + char **kill_only_users, **kill_exclude_users; bool kill_user_processes; }; @@ -111,4 +111,6 @@ void manager_gc(Manager *m); bool x11_display_is_local(const char *display); +extern const DBusObjectPathVTable bus_manager_vtable; + #endif -- 2.39.5