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=\"KillProcesses\" type=\"b\" access=\"read\"/>\n" \
66 " <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
67 " <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
68 " <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
71 #define INTROSPECTION \
72 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
74 BUS_SESSION_INTERFACE \
75 BUS_PROPERTIES_INTERFACE \
77 BUS_INTROSPECTABLE_INTERFACE \
80 #define INTERFACES_LIST \
81 BUS_GENERIC_INTERFACES_LIST \
82 "org.freedesktop.login1.Session\0"
84 static int bus_session_append_seat(DBusMessageIter *i, const char *property, void *data) {
87 const char *id, *path;
94 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
99 path = p = seat_bus_path(s->seat);
108 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
109 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path))
112 if (!dbus_message_iter_close_container(i, &sub))
118 static int bus_session_append_user(DBusMessageIter *i, const char *property, void *data) {
121 _cleanup_free_ char *p = NULL;
127 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
130 p = user_bus_path(u);
134 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->uid) ||
135 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
138 if (!dbus_message_iter_close_container(i, &sub))
144 static int bus_session_append_active(DBusMessageIter *i, const char *property, void *data) {
152 b = session_is_active(s);
153 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
159 static int bus_session_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
167 b = session_get_idle_hint(s, NULL) > 0;
168 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
174 static int bus_session_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
184 r = session_get_idle_hint(s, &t);
188 u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
190 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
196 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType);
197 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_class, session_class, SessionClass);
199 static int bus_session_append_state(DBusMessageIter *i, const char *property, void *data) {
207 state = session_state_to_string(session_get_state(s));
209 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
215 static int get_session_for_path(Manager *m, const char *path, Session **_s) {
216 _cleanup_free_ char *id = NULL;
223 if (!startswith(path, "/org/freedesktop/login1/session/"))
226 id = bus_path_unescape(path + 32);
230 s = hashmap_get(m->sessions, id);
238 static const BusProperty bus_login_session_properties[] = {
239 { "Id", bus_property_append_string, "s", offsetof(Session, id), true },
240 { "Timestamp", bus_property_append_usec, "t", offsetof(Session, timestamp.realtime) },
241 { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(Session, timestamp.monotonic) },
242 { "VTNr", bus_property_append_uint32, "u", offsetof(Session, vtnr) },
243 { "Seat", bus_session_append_seat, "(so)", 0 },
244 { "TTY", bus_property_append_string, "s", offsetof(Session, tty), true },
245 { "Display", bus_property_append_string, "s", offsetof(Session, display), true },
246 { "Remote", bus_property_append_bool, "b", offsetof(Session, remote) },
247 { "RemoteUser", bus_property_append_string, "s", offsetof(Session, remote_user), true },
248 { "RemoteHost", bus_property_append_string, "s", offsetof(Session, remote_host), true },
249 { "Service", bus_property_append_string, "s", offsetof(Session, service), true },
250 { "Scope", bus_property_append_string, "s", offsetof(Session, scope), true },
251 { "Leader", bus_property_append_pid, "u", offsetof(Session, leader) },
252 { "Audit", bus_property_append_uint32, "u", offsetof(Session, audit_id) },
253 { "Type", bus_session_append_type, "s", offsetof(Session, type) },
254 { "Class", bus_session_append_class, "s", offsetof(Session, class) },
255 { "Active", bus_session_append_active, "b", 0 },
256 { "State", bus_session_append_state, "s", 0 },
257 { "IdleHint", bus_session_append_idle_hint, "b", 0 },
258 { "IdleSinceHint", bus_session_append_idle_hint_since, "t", 0 },
259 { "IdleSinceHintMonotonic", bus_session_append_idle_hint_since, "t", 0 },
263 static const BusProperty bus_login_session_user_properties[] = {
264 { "User", bus_session_append_user, "(uo)", 0 },
265 { "Name", bus_property_append_string, "s", offsetof(User, name), true },
269 static DBusHandlerResult session_message_dispatch(
271 DBusConnection *connection,
272 DBusMessage *message) {
275 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
282 dbus_error_init(&error);
284 if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Terminate")) {
288 return bus_send_error_reply(connection, message, NULL, r);
290 reply = dbus_message_new_method_return(message);
294 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Activate")) {
296 r = session_activate(s);
298 return bus_send_error_reply(connection, message, NULL, r);
300 reply = dbus_message_new_method_return(message);
304 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Lock") ||
305 dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Unlock")) {
307 if (session_send_lock(s, streq(dbus_message_get_member(message), "Lock")) < 0)
310 reply = dbus_message_new_method_return(message);
314 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "SetIdleHint")) {
318 if (!dbus_message_get_args(
321 DBUS_TYPE_BOOLEAN, &b,
323 return bus_send_error_reply(connection, message, &error, -EINVAL);
325 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
326 if (ul == (unsigned long) -1)
327 return bus_send_error_reply(connection, message, &error, -EIO);
329 if (ul != 0 && ul != s->user->uid)
330 return bus_send_error_reply(connection, message, NULL, -EPERM);
332 session_set_idle_hint(s, b);
334 reply = dbus_message_new_method_return(message);
338 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Kill")) {
343 if (!dbus_message_get_args(
346 DBUS_TYPE_STRING, &swho,
347 DBUS_TYPE_INT32, &signo,
349 return bus_send_error_reply(connection, message, &error, -EINVAL);
354 who = kill_who_from_string(swho);
356 return bus_send_error_reply(connection, message, &error, -EINVAL);
359 if (signo <= 0 || signo >= _NSIG)
360 return bus_send_error_reply(connection, message, &error, -EINVAL);
362 r = session_kill(s, who, signo);
364 return bus_send_error_reply(connection, message, NULL, r);
366 reply = dbus_message_new_method_return(message);
371 const BusBoundProperties bps[] = {
372 { "org.freedesktop.login1.Session", bus_login_session_properties, s },
373 { "org.freedesktop.login1.Session", bus_login_session_user_properties, s->user },
376 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
380 if (!bus_maybe_send_reply(connection, message, reply))
384 return DBUS_HANDLER_RESULT_HANDLED;
387 dbus_error_free(&error);
389 return DBUS_HANDLER_RESULT_NEED_MEMORY;
392 static DBusHandlerResult session_message_handler(
393 DBusConnection *connection,
394 DBusMessage *message,
397 Manager *m = userdata;
401 r = get_session_for_path(m, dbus_message_get_path(message), &s);
405 return DBUS_HANDLER_RESULT_NEED_MEMORY;
411 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session");
412 return bus_send_error_reply(connection, message, &e, r);
415 return bus_send_error_reply(connection, message, NULL, r);
418 return session_message_dispatch(s, connection, message);
421 const DBusObjectPathVTable bus_session_vtable = {
422 .message_function = session_message_handler
425 char *session_bus_path(Session *s) {
426 _cleanup_free_ char *t = NULL;
430 t = bus_path_escape(s->id);
434 return strappend("/org/freedesktop/login1/session/", t);
437 int session_send_signal(Session *s, bool new_session) {
438 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
439 _cleanup_free_ char *p = NULL;
443 m = dbus_message_new_signal("/org/freedesktop/login1",
444 "org.freedesktop.login1.Manager",
445 new_session ? "SessionNew" : "SessionRemoved");
450 p = session_bus_path(s);
454 if (!dbus_message_append_args(
456 DBUS_TYPE_STRING, &s->id,
457 DBUS_TYPE_OBJECT_PATH, &p,
461 if (!dbus_connection_send(s->manager->bus, m, NULL))
467 int session_send_changed(Session *s, const char *properties) {
468 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
469 _cleanup_free_ char *p = NULL;
476 p = session_bus_path(s);
480 m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
484 if (!dbus_connection_send(s->manager->bus, m, NULL))
490 int session_send_lock(Session *s, bool lock) {
491 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
493 _cleanup_free_ char *p = NULL;
497 p = session_bus_path(s);
501 m = dbus_message_new_signal(p, "org.freedesktop.login1.Session", lock ? "Lock" : "Unlock");
506 b = dbus_connection_send(s->manager->bus, m, NULL);
513 int session_send_lock_all(Manager *m, bool lock) {
520 HASHMAP_FOREACH(session, m->sessions, i) {
523 k = session_send_lock(session, lock);
531 int session_send_create_reply(Session *s, DBusError *error) {
532 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
536 if (!s->create_message)
542 dbus_error_init(&buffer);
544 if (!dbus_error_is_set(error)) {
545 dbus_set_error_const(&buffer, DBUS_ERROR_INVALID_ARGS, "Invalid Arguments");
549 reply = dbus_message_new_error(s->create_message, error->name, error->message);
550 dbus_error_free(&buffer);
555 _cleanup_close_ int fifo_fd = -1;
556 _cleanup_free_ char *path = NULL;
561 fifo_fd = session_create_fifo(s);
563 log_error("Failed to create fifo: %s", strerror(-fifo_fd));
567 path = session_bus_path(s);
571 reply = dbus_message_new_method_return(s->create_message);
575 cseat = s->seat ? s->seat->id : "";
579 if (!dbus_message_append_args(
581 DBUS_TYPE_STRING, &s->id,
582 DBUS_TYPE_OBJECT_PATH, &path,
583 DBUS_TYPE_STRING, &s->user->runtime_path,
584 DBUS_TYPE_UNIX_FD, &fifo_fd,
585 DBUS_TYPE_STRING, &cseat,
586 DBUS_TYPE_UINT32, &vtnr,
587 DBUS_TYPE_BOOLEAN, &exists,
592 if (!dbus_connection_send(s->manager->bus, reply, NULL))
595 dbus_message_unref(s->create_message);
596 s->create_message = NULL;