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 " <signal name=\"Lock\"/>\n" \
44 " <signal name=\"Unlock\"/>\n" \
45 " <property name=\"Id\" type=\"s\" access=\"read\"/>\n" \
46 " <property name=\"User\" type=\"(uo)\" access=\"read\"/>\n" \
47 " <property name=\"Name\" type=\"s\" access=\"read\"/>\n" \
48 " <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \
49 " <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
50 " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
51 " <property name=\"VTNr\" type=\"u\" access=\"read\"/>\n" \
52 " <property name=\"Seat\" type=\"(so)\" access=\"read\"/>\n" \
53 " <property name=\"TTY\" type=\"s\" access=\"read\"/>\n" \
54 " <property name=\"Display\" type=\"s\" access=\"read\"/>\n" \
55 " <property name=\"Remote\" type=\"b\" access=\"read\"/>\n" \
56 " <property name=\"RemoteHost\" type=\"s\" access=\"read\"/>\n" \
57 " <property name=\"RemoteUser\" type=\"s\" access=\"read\"/>\n" \
58 " <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \
59 " <property name=\"Leader\" type=\"u\" access=\"read\"/>\n" \
60 " <property name=\"Audit\" type=\"u\" access=\"read\"/>\n" \
61 " <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \
62 " <property name=\"Class\" type=\"s\" access=\"read\"/>\n" \
63 " <property name=\"Active\" type=\"b\" access=\"read\"/>\n" \
64 " <property name=\"State\" type=\"s\" access=\"read\"/>\n" \
65 " <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
66 " <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
67 " <property name=\"KillProcesses\" type=\"b\" access=\"read\"/>\n" \
68 " <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
69 " <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
70 " <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
73 #define INTROSPECTION \
74 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
76 BUS_SESSION_INTERFACE \
77 BUS_PROPERTIES_INTERFACE \
79 BUS_INTROSPECTABLE_INTERFACE \
82 #define INTERFACES_LIST \
83 BUS_GENERIC_INTERFACES_LIST \
84 "org.freedesktop.login1.Session\0"
86 static int bus_session_append_seat(DBusMessageIter *i, const char *property, void *data) {
89 const char *id, *path;
96 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
101 path = p = seat_bus_path(s->seat);
110 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
111 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) {
118 if (!dbus_message_iter_close_container(i, &sub))
124 static int bus_session_append_user(DBusMessageIter *i, const char *property, void *data) {
133 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
136 p = user_bus_path(u);
140 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->uid) ||
141 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
148 if (!dbus_message_iter_close_container(i, &sub))
154 static int bus_session_append_active(DBusMessageIter *i, const char *property, void *data) {
162 b = session_is_active(s);
163 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
169 static int bus_session_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
177 b = session_get_idle_hint(s, NULL) > 0;
178 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
184 static int bus_session_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
194 r = session_get_idle_hint(s, &t);
198 u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
200 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
206 static int bus_session_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
216 r = cg_join_spec(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, &t);
220 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
223 return success ? 0 : -ENOMEM;
226 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType);
227 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_class, session_class, SessionClass);
229 static int bus_session_append_state(DBusMessageIter *i, const char *property, void *data) {
237 state = session_state_to_string(session_get_state(s));
239 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
245 static int get_session_for_path(Manager *m, const char *path, Session **_s) {
253 if (!startswith(path, "/org/freedesktop/login1/session/"))
256 id = bus_path_unescape(path + 32);
260 s = hashmap_get(m->sessions, id);
270 static const BusProperty bus_login_session_properties[] = {
271 { "Id", bus_property_append_string, "s", offsetof(Session, id), true },
272 { "Timestamp", bus_property_append_usec, "t", offsetof(Session, timestamp.realtime) },
273 { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(Session, timestamp.monotonic) },
274 { "DefaultControlGroup", bus_session_append_default_cgroup, "s", 0, },
275 { "VTNr", bus_property_append_uint32, "u", offsetof(Session, vtnr) },
276 { "Seat", bus_session_append_seat, "(so)", 0 },
277 { "TTY", bus_property_append_string, "s", offsetof(Session, tty), true },
278 { "Display", bus_property_append_string, "s", offsetof(Session, display), true },
279 { "Remote", bus_property_append_bool, "b", offsetof(Session, remote) },
280 { "RemoteUser", bus_property_append_string, "s", offsetof(Session, remote_user), true },
281 { "RemoteHost", bus_property_append_string, "s", offsetof(Session, remote_host), true },
282 { "Service", bus_property_append_string, "s", offsetof(Session, service), true },
283 { "Leader", bus_property_append_pid, "u", offsetof(Session, leader) },
284 { "Audit", bus_property_append_uint32, "u", offsetof(Session, audit_id) },
285 { "Type", bus_session_append_type, "s", offsetof(Session, type) },
286 { "Class", bus_session_append_class, "s", offsetof(Session, class) },
287 { "Active", bus_session_append_active, "b", 0 },
288 { "State", bus_session_append_state, "s", 0 },
289 { "Controllers", bus_property_append_strv, "as", offsetof(Session, controllers), true },
290 { "ResetControllers", bus_property_append_strv, "as", offsetof(Session, reset_controllers), true },
291 { "KillProcesses", bus_property_append_bool, "b", offsetof(Session, kill_processes) },
292 { "IdleHint", bus_session_append_idle_hint, "b", 0 },
293 { "IdleSinceHint", bus_session_append_idle_hint_since, "t", 0 },
294 { "IdleSinceHintMonotonic", bus_session_append_idle_hint_since, "t", 0 },
298 static const BusProperty bus_login_session_user_properties[] = {
299 { "User", bus_session_append_user, "(uo)", 0 },
300 { "Name", bus_property_append_string, "s", offsetof(User, name), true },
304 static DBusHandlerResult session_message_dispatch(
306 DBusConnection *connection,
307 DBusMessage *message) {
310 DBusMessage *reply = NULL;
317 dbus_error_init(&error);
319 if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Terminate")) {
323 return bus_send_error_reply(connection, message, NULL, r);
325 reply = dbus_message_new_method_return(message);
329 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Activate")) {
331 r = session_activate(s);
333 return bus_send_error_reply(connection, message, NULL, r);
335 reply = dbus_message_new_method_return(message);
339 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Lock") ||
340 dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Unlock")) {
342 if (session_send_lock(s, streq(dbus_message_get_member(message), "Lock")) < 0)
345 reply = dbus_message_new_method_return(message);
349 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "SetIdleHint")) {
353 if (!dbus_message_get_args(
356 DBUS_TYPE_BOOLEAN, &b,
358 return bus_send_error_reply(connection, message, &error, -EINVAL);
360 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
361 if (ul == (unsigned long) -1)
362 return bus_send_error_reply(connection, message, &error, -EIO);
364 if (ul != 0 && ul != s->user->uid)
365 return bus_send_error_reply(connection, message, NULL, -EPERM);
367 session_set_idle_hint(s, b);
369 reply = dbus_message_new_method_return(message);
373 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Kill")) {
378 if (!dbus_message_get_args(
381 DBUS_TYPE_STRING, &swho,
382 DBUS_TYPE_INT32, &signo,
384 return bus_send_error_reply(connection, message, &error, -EINVAL);
389 who = kill_who_from_string(swho);
391 return bus_send_error_reply(connection, message, &error, -EINVAL);
394 if (signo <= 0 || signo >= _NSIG)
395 return bus_send_error_reply(connection, message, &error, -EINVAL);
397 r = session_kill(s, who, signo);
399 return bus_send_error_reply(connection, message, NULL, r);
401 reply = dbus_message_new_method_return(message);
406 const BusBoundProperties bps[] = {
407 { "org.freedesktop.login1.Session", bus_login_session_properties, s },
408 { "org.freedesktop.login1.Session", bus_login_session_user_properties, s->user },
411 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
415 if (!dbus_connection_send(connection, reply, NULL))
418 dbus_message_unref(reply);
421 return DBUS_HANDLER_RESULT_HANDLED;
425 dbus_message_unref(reply);
427 dbus_error_free(&error);
429 return DBUS_HANDLER_RESULT_NEED_MEMORY;
432 static DBusHandlerResult session_message_handler(
433 DBusConnection *connection,
434 DBusMessage *message,
437 Manager *m = userdata;
441 r = get_session_for_path(m, dbus_message_get_path(message), &s);
445 return DBUS_HANDLER_RESULT_NEED_MEMORY;
451 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session");
452 return bus_send_error_reply(connection, message, &e, r);
455 return bus_send_error_reply(connection, message, NULL, r);
458 return session_message_dispatch(s, connection, message);
461 const DBusObjectPathVTable bus_session_vtable = {
462 .message_function = session_message_handler
465 char *session_bus_path(Session *s) {
470 t = bus_path_escape(s->id);
474 r = strappend("/org/freedesktop/login1/session/", t);
480 int session_send_signal(Session *s, bool new_session) {
487 m = dbus_message_new_signal("/org/freedesktop/login1",
488 "org.freedesktop.login1.Manager",
489 new_session ? "SessionNew" : "SessionRemoved");
494 p = session_bus_path(s);
498 if (!dbus_message_append_args(
500 DBUS_TYPE_STRING, &s->id,
501 DBUS_TYPE_OBJECT_PATH, &p,
505 if (!dbus_connection_send(s->manager->bus, m, NULL))
511 dbus_message_unref(m);
517 int session_send_changed(Session *s, const char *properties) {
527 p = session_bus_path(s);
531 m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
535 if (!dbus_connection_send(s->manager->bus, m, NULL))
542 dbus_message_unref(m);
548 int session_send_lock(Session *s, bool lock) {
555 p = session_bus_path(s);
559 m = dbus_message_new_signal(p, "org.freedesktop.login1.Session", lock ? "Lock" : "Unlock");
565 b = dbus_connection_send(s->manager->bus, m, NULL);
566 dbus_message_unref(m);