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 " <method name=\"TakeControl\"/>\n" \
44 " <arg name=\"force\" type=\"b\"/>\n" \
46 " <method name=\"ReleaseControl\"/>\n" \
47 " <signal name=\"Lock\"/>\n" \
48 " <signal name=\"Unlock\"/>\n" \
49 " <property name=\"Id\" type=\"s\" access=\"read\"/>\n" \
50 " <property name=\"User\" type=\"(uo)\" access=\"read\"/>\n" \
51 " <property name=\"Name\" type=\"s\" access=\"read\"/>\n" \
52 " <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \
53 " <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
54 " <property name=\"VTNr\" type=\"u\" access=\"read\"/>\n" \
55 " <property name=\"Seat\" type=\"(so)\" access=\"read\"/>\n" \
56 " <property name=\"TTY\" type=\"s\" access=\"read\"/>\n" \
57 " <property name=\"Display\" type=\"s\" access=\"read\"/>\n" \
58 " <property name=\"Remote\" type=\"b\" access=\"read\"/>\n" \
59 " <property name=\"RemoteHost\" type=\"s\" access=\"read\"/>\n" \
60 " <property name=\"RemoteUser\" type=\"s\" access=\"read\"/>\n" \
61 " <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \
62 " <property name=\"Scope\" type=\"s\" access=\"read\"/>\n" \
63 " <property name=\"Leader\" type=\"u\" access=\"read\"/>\n" \
64 " <property name=\"Audit\" type=\"u\" access=\"read\"/>\n" \
65 " <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \
66 " <property name=\"Class\" type=\"s\" access=\"read\"/>\n" \
67 " <property name=\"Active\" type=\"b\" access=\"read\"/>\n" \
68 " <property name=\"State\" type=\"s\" access=\"read\"/>\n" \
69 " <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
70 " <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
71 " <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
74 #define INTROSPECTION \
75 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
77 BUS_SESSION_INTERFACE \
78 BUS_PROPERTIES_INTERFACE \
80 BUS_INTROSPECTABLE_INTERFACE \
83 #define INTERFACES_LIST \
84 BUS_GENERIC_INTERFACES_LIST \
85 "org.freedesktop.login1.Session\0"
87 static int bus_session_append_seat(DBusMessageIter *i, const char *property, void *data) {
90 const char *id, *path;
97 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
102 path = p = seat_bus_path(s->seat);
111 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
112 !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) {
124 _cleanup_free_ char *p = NULL;
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))
141 if (!dbus_message_iter_close_container(i, &sub))
147 static int bus_session_append_active(DBusMessageIter *i, const char *property, void *data) {
155 b = session_is_active(s);
156 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
162 static int bus_session_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
170 b = session_get_idle_hint(s, NULL) > 0;
171 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
177 static int bus_session_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
187 r = 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 DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType);
200 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_class, session_class, SessionClass);
202 static int bus_session_append_state(DBusMessageIter *i, const char *property, void *data) {
210 state = session_state_to_string(session_get_state(s));
212 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
218 static int get_session_for_path(Manager *m, const char *path, Session **_s) {
219 _cleanup_free_ char *id = NULL;
226 if (!startswith(path, "/org/freedesktop/login1/session/"))
229 id = bus_path_unescape(path + 32);
233 s = hashmap_get(m->sessions, id);
241 static const BusProperty bus_login_session_properties[] = {
242 { "Id", bus_property_append_string, "s", offsetof(Session, id), true },
243 { "Timestamp", bus_property_append_usec, "t", offsetof(Session, timestamp.realtime) },
244 { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(Session, timestamp.monotonic) },
245 { "VTNr", bus_property_append_uint32, "u", offsetof(Session, vtnr) },
246 { "Seat", bus_session_append_seat, "(so)", 0 },
247 { "TTY", bus_property_append_string, "s", offsetof(Session, tty), true },
248 { "Display", bus_property_append_string, "s", offsetof(Session, display), true },
249 { "Remote", bus_property_append_bool, "b", offsetof(Session, remote) },
250 { "RemoteUser", bus_property_append_string, "s", offsetof(Session, remote_user), true },
251 { "RemoteHost", bus_property_append_string, "s", offsetof(Session, remote_host), true },
252 { "Service", bus_property_append_string, "s", offsetof(Session, service), true },
253 { "Scope", bus_property_append_string, "s", offsetof(Session, scope), true },
254 { "Leader", bus_property_append_pid, "u", offsetof(Session, leader) },
255 { "Audit", bus_property_append_uint32, "u", offsetof(Session, audit_id) },
256 { "Type", bus_session_append_type, "s", offsetof(Session, type) },
257 { "Class", bus_session_append_class, "s", offsetof(Session, class) },
258 { "Active", bus_session_append_active, "b", 0 },
259 { "State", bus_session_append_state, "s", 0 },
260 { "IdleHint", bus_session_append_idle_hint, "b", 0 },
261 { "IdleSinceHint", bus_session_append_idle_hint_since, "t", 0 },
262 { "IdleSinceHintMonotonic", bus_session_append_idle_hint_since, "t", 0 },
266 static const BusProperty bus_login_session_user_properties[] = {
267 { "User", bus_session_append_user, "(uo)", 0 },
268 { "Name", bus_property_append_string, "s", offsetof(User, name), true },
272 static DBusHandlerResult session_message_dispatch(
274 DBusConnection *connection,
275 DBusMessage *message) {
278 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
285 dbus_error_init(&error);
287 if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Terminate")) {
291 return bus_send_error_reply(connection, message, NULL, r);
293 reply = dbus_message_new_method_return(message);
297 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Activate")) {
299 r = session_activate(s);
301 return bus_send_error_reply(connection, message, NULL, r);
303 reply = dbus_message_new_method_return(message);
307 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Lock") ||
308 dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Unlock")) {
310 if (session_send_lock(s, streq(dbus_message_get_member(message), "Lock")) < 0)
313 reply = dbus_message_new_method_return(message);
317 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "SetIdleHint")) {
321 if (!dbus_message_get_args(
324 DBUS_TYPE_BOOLEAN, &b,
326 return bus_send_error_reply(connection, message, &error, -EINVAL);
328 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
329 if (ul == (unsigned long) -1)
330 return bus_send_error_reply(connection, message, &error, -EIO);
332 if (ul != 0 && ul != s->user->uid)
333 return bus_send_error_reply(connection, message, NULL, -EPERM);
335 session_set_idle_hint(s, b);
337 reply = dbus_message_new_method_return(message);
341 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Kill")) {
346 if (!dbus_message_get_args(
349 DBUS_TYPE_STRING, &swho,
350 DBUS_TYPE_INT32, &signo,
352 return bus_send_error_reply(connection, message, &error, -EINVAL);
357 who = kill_who_from_string(swho);
359 return bus_send_error_reply(connection, message, &error, -EINVAL);
362 if (signo <= 0 || signo >= _NSIG)
363 return bus_send_error_reply(connection, message, &error, -EINVAL);
365 r = session_kill(s, who, signo);
367 return bus_send_error_reply(connection, message, NULL, r);
369 reply = dbus_message_new_method_return(message);
373 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "TakeControl")) {
377 if (!dbus_message_get_args(
380 DBUS_TYPE_BOOLEAN, &force,
382 return bus_send_error_reply(connection, message, &error, -EINVAL);
384 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
385 if (ul == (unsigned long) -1)
386 return bus_send_error_reply(connection, message, &error, -EIO);
388 if (ul != 0 && (force || ul != s->user->uid))
389 return bus_send_error_reply(connection, message, NULL, -EPERM);
391 r = session_set_controller(s, bus_message_get_sender_with_fallback(message), force);
393 return bus_send_error_reply(connection, message, NULL, r);
395 reply = dbus_message_new_method_return(message);
399 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "ReleaseControl")) {
400 const char *sender = bus_message_get_sender_with_fallback(message);
402 if (!session_is_controller(s, sender))
403 return bus_send_error_reply(connection, message, NULL, -EPERM);
405 session_drop_controller(s);
407 reply = dbus_message_new_method_return(message);
412 const BusBoundProperties bps[] = {
413 { "org.freedesktop.login1.Session", bus_login_session_properties, s },
414 { "org.freedesktop.login1.Session", bus_login_session_user_properties, s->user },
417 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
421 if (!bus_maybe_send_reply(connection, message, reply))
425 return DBUS_HANDLER_RESULT_HANDLED;
428 dbus_error_free(&error);
430 return DBUS_HANDLER_RESULT_NEED_MEMORY;
433 static DBusHandlerResult session_message_handler(
434 DBusConnection *connection,
435 DBusMessage *message,
438 Manager *m = userdata;
442 r = get_session_for_path(m, dbus_message_get_path(message), &s);
446 return DBUS_HANDLER_RESULT_NEED_MEMORY;
452 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session");
453 return bus_send_error_reply(connection, message, &e, r);
456 return bus_send_error_reply(connection, message, NULL, r);
459 return session_message_dispatch(s, connection, message);
462 const DBusObjectPathVTable bus_session_vtable = {
463 .message_function = session_message_handler
466 char *session_bus_path(Session *s) {
467 _cleanup_free_ char *t = NULL;
471 t = bus_path_escape(s->id);
475 return strappend("/org/freedesktop/login1/session/", t);
478 int session_send_signal(Session *s, bool new_session) {
479 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
480 _cleanup_free_ char *p = NULL;
484 m = dbus_message_new_signal("/org/freedesktop/login1",
485 "org.freedesktop.login1.Manager",
486 new_session ? "SessionNew" : "SessionRemoved");
491 p = session_bus_path(s);
495 if (!dbus_message_append_args(
497 DBUS_TYPE_STRING, &s->id,
498 DBUS_TYPE_OBJECT_PATH, &p,
502 if (!dbus_connection_send(s->manager->bus, m, NULL))
508 int session_send_changed(Session *s, const char *properties) {
509 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
510 _cleanup_free_ char *p = NULL;
517 p = session_bus_path(s);
521 m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
525 if (!dbus_connection_send(s->manager->bus, m, NULL))
531 int session_send_lock(Session *s, bool lock) {
532 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
534 _cleanup_free_ char *p = NULL;
538 p = session_bus_path(s);
542 m = dbus_message_new_signal(p, "org.freedesktop.login1.Session", lock ? "Lock" : "Unlock");
547 b = dbus_connection_send(s->manager->bus, m, NULL);
554 int session_send_lock_all(Manager *m, bool lock) {
561 HASHMAP_FOREACH(session, m->sessions, i) {
564 k = session_send_lock(session, lock);
572 int session_send_create_reply(Session *s, DBusError *error) {
573 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
577 if (!s->create_message)
580 /* This is called after the session scope was successfully
581 * created, and finishes where bus_manager_create_session()
587 dbus_error_init(&buffer);
589 if (!dbus_error_is_set(error)) {
590 dbus_set_error_const(&buffer, DBUS_ERROR_INVALID_ARGS, "Invalid Arguments");
594 reply = dbus_message_new_error(s->create_message, error->name, error->message);
595 dbus_error_free(&buffer);
600 _cleanup_close_ int fifo_fd = -1;
601 _cleanup_free_ char *path = NULL;
606 fifo_fd = session_create_fifo(s);
608 log_error("Failed to create fifo: %s", strerror(-fifo_fd));
612 path = session_bus_path(s);
616 reply = dbus_message_new_method_return(s->create_message);
620 cseat = s->seat ? s->seat->id : "";
624 if (!dbus_message_append_args(
626 DBUS_TYPE_STRING, &s->id,
627 DBUS_TYPE_OBJECT_PATH, &path,
628 DBUS_TYPE_STRING, &s->user->runtime_path,
629 DBUS_TYPE_UNIX_FD, &fifo_fd,
630 DBUS_TYPE_STRING, &cseat,
631 DBUS_TYPE_UINT32, &vtnr,
632 DBUS_TYPE_BOOLEAN, &exists,
637 /* Update the state file before we notify the client about the
641 if (!dbus_connection_send(s->manager->bus, reply, NULL))
644 dbus_message_unref(s->create_message);
645 s->create_message = NULL;