1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
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 "logind-session.h"
27 #include "dbus-common.h"
30 #define BUS_SESSION_INTERFACE \
31 " <interface name=\"org.freedesktop.login1.Session\">\n" \
32 " <method name=\"Terminate\"/>\n" \
33 " <method name=\"Activate\"/>\n" \
34 " <method name=\"Lock\"/>\n" \
35 " <method name=\"Unlock\"/>\n" \
36 " <method name=\"SetIdleHint\">\n" \
37 " <arg name=\"b\" type=\"b\"/>\n" \
39 " <method name=\"Kill\">\n" \
40 " <arg name=\"who\" type=\"s\"/>\n" \
41 " <arg name=\"signal\" type=\"s\"/>\n" \
43 " <property name=\"Id\" type=\"s\" access=\"read\"/>\n" \
44 " <property name=\"User\" type=\"(uo)\" access=\"read\"/>\n" \
45 " <property name=\"Name\" type=\"s\" access=\"read\"/>\n" \
46 " <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \
47 " <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
48 " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
49 " <property name=\"VTNr\" type=\"u\" access=\"read\"/>\n" \
50 " <property name=\"Seat\" type=\"(so)\" access=\"read\"/>\n" \
51 " <property name=\"TTY\" type=\"s\" access=\"read\"/>\n" \
52 " <property name=\"Display\" type=\"s\" access=\"read\"/>\n" \
53 " <property name=\"Remote\" type=\"b\" access=\"read\"/>\n" \
54 " <property name=\"RemoteHost\" type=\"s\" access=\"read\"/>\n" \
55 " <property name=\"RemoteUser\" type=\"s\" access=\"read\"/>\n" \
56 " <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \
57 " <property name=\"Leader\" type=\"u\" access=\"read\"/>\n" \
58 " <property name=\"Audit\" type=\"u\" access=\"read\"/>\n" \
59 " <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \
60 " <property name=\"Class\" type=\"s\" access=\"read\"/>\n" \
61 " <property name=\"Active\" type=\"b\" access=\"read\"/>\n" \
62 " <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
63 " <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
64 " <property name=\"KillProcesses\" type=\"b\" access=\"read\"/>\n" \
65 " <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
66 " <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
67 " <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
70 #define INTROSPECTION \
71 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
73 BUS_SESSION_INTERFACE \
74 BUS_PROPERTIES_INTERFACE \
76 BUS_INTROSPECTABLE_INTERFACE \
79 #define INTERFACES_LIST \
80 BUS_GENERIC_INTERFACES_LIST \
81 "org.freedesktop.login1.Session\0"
83 static int bus_session_append_seat(DBusMessageIter *i, const char *property, void *data) {
86 const char *id, *path;
93 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
98 path = p = seat_bus_path(s->seat);
107 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
108 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) {
115 if (!dbus_message_iter_close_container(i, &sub))
121 static int bus_session_append_user(DBusMessageIter *i, const char *property, void *data) {
130 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
133 p = user_bus_path(u);
137 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->uid) ||
138 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
145 if (!dbus_message_iter_close_container(i, &sub))
151 static int bus_session_append_active(DBusMessageIter *i, const char *property, void *data) {
159 b = session_is_active(s);
160 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
166 static int bus_session_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
174 b = session_get_idle_hint(s, NULL) > 0;
175 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
181 static int bus_session_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
190 session_get_idle_hint(s, &t);
191 u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
193 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
199 static int bus_session_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
209 r = cg_join_spec(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, &t);
213 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
216 return success ? 0 : -ENOMEM;
219 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType);
220 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_class, session_class, SessionClass);
222 static int get_session_for_path(Manager *m, const char *path, Session **_s) {
230 if (!startswith(path, "/org/freedesktop/login1/session/"))
233 id = bus_path_unescape(path + 32);
237 s = hashmap_get(m->sessions, id);
247 static const BusProperty bus_login_session_properties[] = {
248 { "Id", bus_property_append_string, "s", offsetof(Session, id), true },
249 { "Timestamp", bus_property_append_usec, "t", offsetof(Session, timestamp.realtime) },
250 { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(Session, timestamp.monotonic) },
251 { "DefaultControlGroup", bus_session_append_default_cgroup, "s", 0, },
252 { "VTNr", bus_property_append_uint32, "u", offsetof(Session, vtnr) },
253 { "Seat", bus_session_append_seat, "(so)", 0 },
254 { "TTY", bus_property_append_string, "s", offsetof(Session, tty), true },
255 { "Display", bus_property_append_string, "s", offsetof(Session, display), true },
256 { "Remote", bus_property_append_bool, "b", offsetof(Session, remote) },
257 { "RemoteUser", bus_property_append_string, "s", offsetof(Session, remote_user), true },
258 { "RemoteHost", bus_property_append_string, "s", offsetof(Session, remote_host), true },
259 { "Service", bus_property_append_string, "s", offsetof(Session, service), true },
260 { "Leader", bus_property_append_pid, "u", offsetof(Session, leader) },
261 { "Audit", bus_property_append_uint32, "u", offsetof(Session, audit_id) },
262 { "Type", bus_session_append_type, "s", offsetof(Session, type) },
263 { "Class", bus_session_append_class, "s", offsetof(Session, class) },
264 { "Active", bus_session_append_active, "b", 0 },
265 { "Controllers", bus_property_append_strv, "as", offsetof(Session, controllers), true },
266 { "ResetControllers", bus_property_append_strv, "as", offsetof(Session, reset_controllers), true },
267 { "KillProcesses", bus_property_append_bool, "b", offsetof(Session, kill_processes) },
268 { "IdleHint", bus_session_append_idle_hint, "b", 0 },
269 { "IdleSinceHint", bus_session_append_idle_hint_since, "t", 0 },
270 { "IdleSinceHintMonotonic", bus_session_append_idle_hint_since, "t", 0 },
274 static const BusProperty bus_login_session_user_properties[] = {
275 { "User", bus_session_append_user, "(uo)", 0 },
276 { "Name", bus_property_append_string, "s", offsetof(User, name), true },
280 static DBusHandlerResult session_message_dispatch(
282 DBusConnection *connection,
283 DBusMessage *message) {
286 DBusMessage *reply = NULL;
293 dbus_error_init(&error);
295 if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Terminate")) {
299 return bus_send_error_reply(connection, message, NULL, r);
301 reply = dbus_message_new_method_return(message);
305 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Activate")) {
307 r = session_activate(s);
309 return bus_send_error_reply(connection, message, NULL, r);
311 reply = dbus_message_new_method_return(message);
315 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Lock") ||
316 dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Unlock")) {
318 if (session_send_lock(s, streq(dbus_message_get_member(message), "Lock")) < 0)
321 reply = dbus_message_new_method_return(message);
325 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "SetIdleHint")) {
329 if (!dbus_message_get_args(
332 DBUS_TYPE_BOOLEAN, &b,
334 return bus_send_error_reply(connection, message, &error, -EINVAL);
336 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
337 if (ul == (unsigned long) -1)
338 return bus_send_error_reply(connection, message, &error, -EIO);
340 if (ul != 0 && ul != s->user->uid)
341 return bus_send_error_reply(connection, message, NULL, -EPERM);
343 session_set_idle_hint(s, b);
345 reply = dbus_message_new_method_return(message);
349 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Kill")) {
354 if (!dbus_message_get_args(
357 DBUS_TYPE_STRING, &swho,
358 DBUS_TYPE_INT32, &signo,
360 return bus_send_error_reply(connection, message, &error, -EINVAL);
365 who = kill_who_from_string(swho);
367 return bus_send_error_reply(connection, message, &error, -EINVAL);
370 if (signo <= 0 || signo >= _NSIG)
371 return bus_send_error_reply(connection, message, &error, -EINVAL);
373 r = session_kill(s, who, signo);
375 return bus_send_error_reply(connection, message, NULL, r);
377 reply = dbus_message_new_method_return(message);
382 const BusBoundProperties bps[] = {
383 { "org.freedesktop.login1.Session", bus_login_session_properties, s },
384 { "org.freedesktop.login1.Session", bus_login_session_user_properties, s->user },
387 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
391 if (!dbus_connection_send(connection, reply, NULL))
394 dbus_message_unref(reply);
397 return DBUS_HANDLER_RESULT_HANDLED;
401 dbus_message_unref(reply);
403 dbus_error_free(&error);
405 return DBUS_HANDLER_RESULT_NEED_MEMORY;
408 static DBusHandlerResult session_message_handler(
409 DBusConnection *connection,
410 DBusMessage *message,
413 Manager *m = userdata;
417 r = get_session_for_path(m, dbus_message_get_path(message), &s);
421 return DBUS_HANDLER_RESULT_NEED_MEMORY;
427 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session");
428 return bus_send_error_reply(connection, message, &e, r);
431 return bus_send_error_reply(connection, message, NULL, r);
434 return session_message_dispatch(s, connection, message);
437 const DBusObjectPathVTable bus_session_vtable = {
438 .message_function = session_message_handler
441 char *session_bus_path(Session *s) {
446 t = bus_path_escape(s->id);
450 r = strappend("/org/freedesktop/login1/session/", t);
456 int session_send_signal(Session *s, bool new_session) {
463 m = dbus_message_new_signal("/org/freedesktop/login1",
464 "org.freedesktop.login1.Manager",
465 new_session ? "SessionNew" : "SessionRemoved");
470 p = session_bus_path(s);
474 if (!dbus_message_append_args(
476 DBUS_TYPE_STRING, &s->id,
477 DBUS_TYPE_OBJECT_PATH, &p,
481 if (!dbus_connection_send(s->manager->bus, m, NULL))
487 dbus_message_unref(m);
493 int session_send_changed(Session *s, const char *properties) {
503 p = session_bus_path(s);
507 m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
511 if (!dbus_connection_send(s->manager->bus, m, NULL))
518 dbus_message_unref(m);
524 int session_send_lock(Session *s, bool lock) {
531 p = session_bus_path(s);
535 m = dbus_message_new_signal(p, "org.freedesktop.login1.Session", lock ? "Lock" : "Unlock");
541 b = dbus_connection_send(s->manager->bus, m, NULL);
542 dbus_message_unref(m);