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=\"VTNr\" type=\"u\" access=\"read\"/>\n" \
51 " <property name=\"Seat\" type=\"(so)\" access=\"read\"/>\n" \
52 " <property name=\"TTY\" type=\"s\" access=\"read\"/>\n" \
53 " <property name=\"Display\" type=\"s\" access=\"read\"/>\n" \
54 " <property name=\"Remote\" type=\"b\" access=\"read\"/>\n" \
55 " <property name=\"RemoteHost\" type=\"s\" access=\"read\"/>\n" \
56 " <property name=\"RemoteUser\" type=\"s\" access=\"read\"/>\n" \
57 " <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \
58 " <property name=\"Scope\" 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=\"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))
111 if (!dbus_message_iter_close_container(i, &sub))
117 static int bus_session_append_user(DBusMessageIter *i, const char *property, void *data) {
120 _cleanup_free_ char *p = NULL;
126 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
129 p = user_bus_path(u);
133 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->uid) ||
134 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
137 if (!dbus_message_iter_close_container(i, &sub))
143 static int bus_session_append_active(DBusMessageIter *i, const char *property, void *data) {
151 b = session_is_active(s);
152 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
158 static int bus_session_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
166 b = session_get_idle_hint(s, NULL) > 0;
167 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
173 static int bus_session_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
183 r = session_get_idle_hint(s, &t);
187 u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
189 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
195 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType);
196 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_class, session_class, SessionClass);
198 static int bus_session_append_state(DBusMessageIter *i, const char *property, void *data) {
206 state = session_state_to_string(session_get_state(s));
208 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
214 static int get_session_for_path(Manager *m, const char *path, Session **_s) {
215 _cleanup_free_ char *id = NULL;
222 if (!startswith(path, "/org/freedesktop/login1/session/"))
225 id = bus_path_unescape(path + 32);
229 s = hashmap_get(m->sessions, id);
237 static const BusProperty bus_login_session_properties[] = {
238 { "Id", bus_property_append_string, "s", offsetof(Session, id), true },
239 { "Timestamp", bus_property_append_usec, "t", offsetof(Session, timestamp.realtime) },
240 { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(Session, timestamp.monotonic) },
241 { "VTNr", bus_property_append_uint32, "u", offsetof(Session, vtnr) },
242 { "Seat", bus_session_append_seat, "(so)", 0 },
243 { "TTY", bus_property_append_string, "s", offsetof(Session, tty), true },
244 { "Display", bus_property_append_string, "s", offsetof(Session, display), true },
245 { "Remote", bus_property_append_bool, "b", offsetof(Session, remote) },
246 { "RemoteUser", bus_property_append_string, "s", offsetof(Session, remote_user), true },
247 { "RemoteHost", bus_property_append_string, "s", offsetof(Session, remote_host), true },
248 { "Service", bus_property_append_string, "s", offsetof(Session, service), true },
249 { "Scope", bus_property_append_string, "s", offsetof(Session, scope), true },
250 { "Leader", bus_property_append_pid, "u", offsetof(Session, leader) },
251 { "Audit", bus_property_append_uint32, "u", offsetof(Session, audit_id) },
252 { "Type", bus_session_append_type, "s", offsetof(Session, type) },
253 { "Class", bus_session_append_class, "s", offsetof(Session, class) },
254 { "Active", bus_session_append_active, "b", 0 },
255 { "State", bus_session_append_state, "s", 0 },
256 { "IdleHint", bus_session_append_idle_hint, "b", 0 },
257 { "IdleSinceHint", bus_session_append_idle_hint_since, "t", 0 },
258 { "IdleSinceHintMonotonic", bus_session_append_idle_hint_since, "t", 0 },
262 static const BusProperty bus_login_session_user_properties[] = {
263 { "User", bus_session_append_user, "(uo)", 0 },
264 { "Name", bus_property_append_string, "s", offsetof(User, name), true },
268 static DBusHandlerResult session_message_dispatch(
270 DBusConnection *connection,
271 DBusMessage *message) {
274 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
281 dbus_error_init(&error);
283 if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Terminate")) {
287 return bus_send_error_reply(connection, message, NULL, r);
289 reply = dbus_message_new_method_return(message);
293 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Activate")) {
295 r = session_activate(s);
297 return bus_send_error_reply(connection, message, NULL, r);
299 reply = dbus_message_new_method_return(message);
303 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Lock") ||
304 dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Unlock")) {
306 if (session_send_lock(s, streq(dbus_message_get_member(message), "Lock")) < 0)
309 reply = dbus_message_new_method_return(message);
313 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "SetIdleHint")) {
317 if (!dbus_message_get_args(
320 DBUS_TYPE_BOOLEAN, &b,
322 return bus_send_error_reply(connection, message, &error, -EINVAL);
324 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
325 if (ul == (unsigned long) -1)
326 return bus_send_error_reply(connection, message, &error, -EIO);
328 if (ul != 0 && ul != s->user->uid)
329 return bus_send_error_reply(connection, message, NULL, -EPERM);
331 session_set_idle_hint(s, b);
333 reply = dbus_message_new_method_return(message);
337 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Kill")) {
342 if (!dbus_message_get_args(
345 DBUS_TYPE_STRING, &swho,
346 DBUS_TYPE_INT32, &signo,
348 return bus_send_error_reply(connection, message, &error, -EINVAL);
353 who = kill_who_from_string(swho);
355 return bus_send_error_reply(connection, message, &error, -EINVAL);
358 if (signo <= 0 || signo >= _NSIG)
359 return bus_send_error_reply(connection, message, &error, -EINVAL);
361 r = session_kill(s, who, signo);
363 return bus_send_error_reply(connection, message, NULL, r);
365 reply = dbus_message_new_method_return(message);
370 const BusBoundProperties bps[] = {
371 { "org.freedesktop.login1.Session", bus_login_session_properties, s },
372 { "org.freedesktop.login1.Session", bus_login_session_user_properties, s->user },
375 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
379 if (!bus_maybe_send_reply(connection, message, reply))
383 return DBUS_HANDLER_RESULT_HANDLED;
386 dbus_error_free(&error);
388 return DBUS_HANDLER_RESULT_NEED_MEMORY;
391 static DBusHandlerResult session_message_handler(
392 DBusConnection *connection,
393 DBusMessage *message,
396 Manager *m = userdata;
400 r = get_session_for_path(m, dbus_message_get_path(message), &s);
404 return DBUS_HANDLER_RESULT_NEED_MEMORY;
410 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session");
411 return bus_send_error_reply(connection, message, &e, r);
414 return bus_send_error_reply(connection, message, NULL, r);
417 return session_message_dispatch(s, connection, message);
420 const DBusObjectPathVTable bus_session_vtable = {
421 .message_function = session_message_handler
424 char *session_bus_path(Session *s) {
425 _cleanup_free_ char *t = NULL;
429 t = bus_path_escape(s->id);
433 return strappend("/org/freedesktop/login1/session/", t);
436 int session_send_signal(Session *s, bool new_session) {
437 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
438 _cleanup_free_ char *p = NULL;
442 m = dbus_message_new_signal("/org/freedesktop/login1",
443 "org.freedesktop.login1.Manager",
444 new_session ? "SessionNew" : "SessionRemoved");
449 p = session_bus_path(s);
453 if (!dbus_message_append_args(
455 DBUS_TYPE_STRING, &s->id,
456 DBUS_TYPE_OBJECT_PATH, &p,
460 if (!dbus_connection_send(s->manager->bus, m, NULL))
466 int session_send_changed(Session *s, const char *properties) {
467 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
468 _cleanup_free_ char *p = NULL;
475 p = session_bus_path(s);
479 m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
483 if (!dbus_connection_send(s->manager->bus, m, NULL))
489 int session_send_lock(Session *s, bool lock) {
490 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
492 _cleanup_free_ char *p = NULL;
496 p = session_bus_path(s);
500 m = dbus_message_new_signal(p, "org.freedesktop.login1.Session", lock ? "Lock" : "Unlock");
505 b = dbus_connection_send(s->manager->bus, m, NULL);
512 int session_send_lock_all(Manager *m, bool lock) {
519 HASHMAP_FOREACH(session, m->sessions, i) {
522 k = session_send_lock(session, lock);
530 int session_send_create_reply(Session *s, DBusError *error) {
531 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
535 if (!s->create_message)
541 dbus_error_init(&buffer);
543 if (!dbus_error_is_set(error)) {
544 dbus_set_error_const(&buffer, DBUS_ERROR_INVALID_ARGS, "Invalid Arguments");
548 reply = dbus_message_new_error(s->create_message, error->name, error->message);
549 dbus_error_free(&buffer);
554 _cleanup_close_ int fifo_fd = -1;
555 _cleanup_free_ char *path = NULL;
560 fifo_fd = session_create_fifo(s);
562 log_error("Failed to create fifo: %s", strerror(-fifo_fd));
566 path = session_bus_path(s);
570 reply = dbus_message_new_method_return(s->create_message);
574 cseat = s->seat ? s->seat->id : "";
578 if (!dbus_message_append_args(
580 DBUS_TYPE_STRING, &s->id,
581 DBUS_TYPE_OBJECT_PATH, &path,
582 DBUS_TYPE_STRING, &s->user->runtime_path,
583 DBUS_TYPE_UNIX_FD, &fifo_fd,
584 DBUS_TYPE_STRING, &cseat,
585 DBUS_TYPE_UINT32, &vtnr,
586 DBUS_TYPE_BOOLEAN, &exists,
591 if (!dbus_connection_send(s->manager->bus, reply, NULL))
594 dbus_message_unref(s->create_message);
595 s->create_message = NULL;