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 "logind-session-device.h"
28 #include "dbus-common.h"
31 #define BUS_SESSION_INTERFACE \
32 " <interface name=\"org.freedesktop.login1.Session\">\n" \
33 " <method name=\"Terminate\"/>\n" \
34 " <method name=\"Activate\"/>\n" \
35 " <method name=\"Lock\"/>\n" \
36 " <method name=\"Unlock\"/>\n" \
37 " <method name=\"SetIdleHint\">\n" \
38 " <arg name=\"b\" type=\"b\"/>\n" \
40 " <method name=\"Kill\">\n" \
41 " <arg name=\"who\" type=\"s\"/>\n" \
42 " <arg name=\"signal\" type=\"s\"/>\n" \
44 " <method name=\"TakeControl\"/>\n" \
45 " <arg name=\"force\" type=\"b\"/>\n" \
47 " <method name=\"ReleaseControl\"/>\n" \
48 " <method name=\"TakeDevice\">\n" \
49 " <arg name=\"major\" type=\"u\" direction=\"in\"/>\n" \
50 " <arg name=\"minor\" type=\"u\" direction=\"in\"/>\n" \
51 " <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n" \
52 " <arg name=\"paused\" type=\"b\" direction=\"out\"/>\n" \
54 " <method name=\"ReleaseDevice\">\n" \
55 " <arg name=\"major\" type=\"u\"/>\n" \
56 " <arg name=\"minor\" type=\"u\"/>\n" \
58 " <method name=\"PauseDeviceComplete\">\n" \
59 " <arg name=\"major\" type=\"u\"/>\n" \
60 " <arg name=\"minor\" type=\"u\"/>\n" \
62 " <signal name=\"PauseDevice\">\n" \
63 " <arg name=\"major\" type=\"u\"/>\n" \
64 " <arg name=\"minor\" type=\"u\"/>\n" \
65 " <arg name=\"type\" type=\"s\"/>\n" \
67 " <signal name=\"ResumeDevice\">\n" \
68 " <arg name=\"major\" type=\"u\"/>\n" \
69 " <arg name=\"minor\" type=\"u\"/>\n" \
70 " <arg name=\"fd\" type=\"h\"/>\n" \
72 " <signal name=\"Lock\"/>\n" \
73 " <signal name=\"Unlock\"/>\n" \
74 " <property name=\"Id\" type=\"s\" access=\"read\"/>\n" \
75 " <property name=\"User\" type=\"(uo)\" access=\"read\"/>\n" \
76 " <property name=\"Name\" type=\"s\" access=\"read\"/>\n" \
77 " <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \
78 " <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
79 " <property name=\"VTNr\" type=\"u\" access=\"read\"/>\n" \
80 " <property name=\"Seat\" type=\"(so)\" access=\"read\"/>\n" \
81 " <property name=\"TTY\" type=\"s\" access=\"read\"/>\n" \
82 " <property name=\"Display\" type=\"s\" access=\"read\"/>\n" \
83 " <property name=\"Remote\" type=\"b\" access=\"read\"/>\n" \
84 " <property name=\"RemoteHost\" type=\"s\" access=\"read\"/>\n" \
85 " <property name=\"RemoteUser\" type=\"s\" access=\"read\"/>\n" \
86 " <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \
87 " <property name=\"Scope\" type=\"s\" access=\"read\"/>\n" \
88 " <property name=\"Leader\" type=\"u\" access=\"read\"/>\n" \
89 " <property name=\"Audit\" type=\"u\" access=\"read\"/>\n" \
90 " <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \
91 " <property name=\"Class\" type=\"s\" access=\"read\"/>\n" \
92 " <property name=\"Active\" type=\"b\" access=\"read\"/>\n" \
93 " <property name=\"State\" type=\"s\" access=\"read\"/>\n" \
94 " <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
95 " <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
96 " <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
99 #define INTROSPECTION \
100 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
102 BUS_SESSION_INTERFACE \
103 BUS_PROPERTIES_INTERFACE \
105 BUS_INTROSPECTABLE_INTERFACE \
108 #define INTERFACES_LIST \
109 BUS_GENERIC_INTERFACES_LIST \
110 "org.freedesktop.login1.Session\0"
112 static int bus_session_append_seat(DBusMessageIter *i, const char *property, void *data) {
115 const char *id, *path;
122 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
127 path = p = seat_bus_path(s->seat);
136 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
137 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path))
140 if (!dbus_message_iter_close_container(i, &sub))
146 static int bus_session_append_user(DBusMessageIter *i, const char *property, void *data) {
149 _cleanup_free_ char *p = NULL;
155 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
158 p = user_bus_path(u);
162 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->uid) ||
163 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
166 if (!dbus_message_iter_close_container(i, &sub))
172 static int bus_session_append_active(DBusMessageIter *i, const char *property, void *data) {
180 b = session_is_active(s);
181 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
187 static int bus_session_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
195 b = session_get_idle_hint(s, NULL) > 0;
196 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
202 static int bus_session_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
212 r = session_get_idle_hint(s, &t);
216 u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
218 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
224 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType);
225 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_class, session_class, SessionClass);
227 static int bus_session_append_state(DBusMessageIter *i, const char *property, void *data) {
235 state = session_state_to_string(session_get_state(s));
237 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
243 static int get_session_for_path(Manager *m, const char *path, Session **_s) {
244 _cleanup_free_ char *id = NULL;
251 if (!startswith(path, "/org/freedesktop/login1/session/"))
254 id = bus_path_unescape(path + 32);
258 s = hashmap_get(m->sessions, id);
266 static const BusProperty bus_login_session_properties[] = {
267 { "Id", bus_property_append_string, "s", offsetof(Session, id), true },
268 { "Timestamp", bus_property_append_usec, "t", offsetof(Session, timestamp.realtime) },
269 { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(Session, timestamp.monotonic) },
270 { "VTNr", bus_property_append_uint32, "u", offsetof(Session, vtnr) },
271 { "Seat", bus_session_append_seat, "(so)", 0 },
272 { "TTY", bus_property_append_string, "s", offsetof(Session, tty), true },
273 { "Display", bus_property_append_string, "s", offsetof(Session, display), true },
274 { "Remote", bus_property_append_bool, "b", offsetof(Session, remote) },
275 { "RemoteUser", bus_property_append_string, "s", offsetof(Session, remote_user), true },
276 { "RemoteHost", bus_property_append_string, "s", offsetof(Session, remote_host), true },
277 { "Service", bus_property_append_string, "s", offsetof(Session, service), true },
278 { "Scope", bus_property_append_string, "s", offsetof(Session, scope), true },
279 { "Leader", bus_property_append_pid, "u", offsetof(Session, leader) },
280 { "Audit", bus_property_append_uint32, "u", offsetof(Session, audit_id) },
281 { "Type", bus_session_append_type, "s", offsetof(Session, type) },
282 { "Class", bus_session_append_class, "s", offsetof(Session, class) },
283 { "Active", bus_session_append_active, "b", 0 },
284 { "State", bus_session_append_state, "s", 0 },
285 { "IdleHint", bus_session_append_idle_hint, "b", 0 },
286 { "IdleSinceHint", bus_session_append_idle_hint_since, "t", 0 },
287 { "IdleSinceHintMonotonic", bus_session_append_idle_hint_since, "t", 0 },
291 static const BusProperty bus_login_session_user_properties[] = {
292 { "User", bus_session_append_user, "(uo)", 0 },
293 { "Name", bus_property_append_string, "s", offsetof(User, name), true },
297 static DBusHandlerResult session_message_dispatch(
299 DBusConnection *connection,
300 DBusMessage *message) {
303 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
310 dbus_error_init(&error);
312 if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Terminate")) {
316 return bus_send_error_reply(connection, message, NULL, r);
318 reply = dbus_message_new_method_return(message);
322 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Activate")) {
324 r = session_activate(s);
326 return bus_send_error_reply(connection, message, NULL, r);
328 reply = dbus_message_new_method_return(message);
332 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Lock") ||
333 dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Unlock")) {
335 if (session_send_lock(s, streq(dbus_message_get_member(message), "Lock")) < 0)
338 reply = dbus_message_new_method_return(message);
342 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "SetIdleHint")) {
346 if (!dbus_message_get_args(
349 DBUS_TYPE_BOOLEAN, &b,
351 return bus_send_error_reply(connection, message, &error, -EINVAL);
353 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
354 if (ul == (unsigned long) -1)
355 return bus_send_error_reply(connection, message, &error, -EIO);
357 if (ul != 0 && ul != s->user->uid)
358 return bus_send_error_reply(connection, message, NULL, -EPERM);
360 session_set_idle_hint(s, b);
362 reply = dbus_message_new_method_return(message);
366 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Kill")) {
371 if (!dbus_message_get_args(
374 DBUS_TYPE_STRING, &swho,
375 DBUS_TYPE_INT32, &signo,
377 return bus_send_error_reply(connection, message, &error, -EINVAL);
382 who = kill_who_from_string(swho);
384 return bus_send_error_reply(connection, message, &error, -EINVAL);
387 if (signo <= 0 || signo >= _NSIG)
388 return bus_send_error_reply(connection, message, &error, -EINVAL);
390 r = session_kill(s, who, signo);
392 return bus_send_error_reply(connection, message, NULL, r);
394 reply = dbus_message_new_method_return(message);
398 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "TakeControl")) {
402 if (!dbus_message_get_args(
405 DBUS_TYPE_BOOLEAN, &force,
407 return bus_send_error_reply(connection, message, &error, -EINVAL);
409 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
410 if (ul == (unsigned long) -1)
411 return bus_send_error_reply(connection, message, &error, -EIO);
413 if (ul != 0 && (force || ul != s->user->uid))
414 return bus_send_error_reply(connection, message, NULL, -EPERM);
416 r = session_set_controller(s, bus_message_get_sender_with_fallback(message), force);
418 return bus_send_error_reply(connection, message, NULL, r);
420 reply = dbus_message_new_method_return(message);
424 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "ReleaseControl")) {
425 const char *sender = bus_message_get_sender_with_fallback(message);
427 if (!session_is_controller(s, sender))
428 return bus_send_error_reply(connection, message, NULL, -EPERM);
430 session_drop_controller(s);
432 reply = dbus_message_new_method_return(message);
436 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "TakeDevice")) {
440 uint32_t major, minor;
443 if (!session_is_controller(s, bus_message_get_sender_with_fallback(message)))
444 return bus_send_error_reply(connection, message, NULL, -EPERM);
446 if (!dbus_message_get_args(
449 DBUS_TYPE_UINT32, &major,
450 DBUS_TYPE_UINT32, &minor,
452 return bus_send_error_reply(connection, message, &error, -EINVAL);
454 dev = makedev(major, minor);
455 assert_cc(sizeof(unsigned long) >= sizeof(dev_t));
457 sd = hashmap_get(s->devices, ULONG_TO_PTR((unsigned long)dev));
459 /* We don't allow retrieving a device multiple times.
460 * The related ReleaseDevice call is not ref-counted.
461 * The caller should use dup() if it requires more than
462 * one fd (it would be functionally equivalent). */
463 return bus_send_error_reply(connection, message, &error, -EBUSY);
466 r = session_device_new(s, dev, &sd);
468 return bus_send_error_reply(connection, message, NULL, r);
470 reply = dbus_message_new_method_return(message);
472 session_device_free(sd);
476 paused = !sd->active;
477 b = dbus_message_append_args(
479 DBUS_TYPE_UNIX_FD, &sd->fd,
480 DBUS_TYPE_BOOLEAN, &paused,
483 session_device_free(sd);
484 return bus_send_error_reply(connection, message, NULL, -ENOMEM);
487 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "ReleaseDevice")) {
489 uint32_t major, minor;
491 if (!session_is_controller(s, bus_message_get_sender_with_fallback(message)))
492 return bus_send_error_reply(connection, message, NULL, -EPERM);
494 if (!dbus_message_get_args(
497 DBUS_TYPE_UINT32, &major,
498 DBUS_TYPE_UINT32, &minor,
500 return bus_send_error_reply(connection, message, &error, -EINVAL);
502 sd = hashmap_get(s->devices, ULONG_TO_PTR((unsigned long)makedev(major, minor)));
504 return bus_send_error_reply(connection, message, NULL, -ENODEV);
506 session_device_free(sd);
508 reply = dbus_message_new_method_return(message);
512 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "PauseDeviceComplete")) {
514 uint32_t major, minor;
516 if (!session_is_controller(s, bus_message_get_sender_with_fallback(message)))
517 return bus_send_error_reply(connection, message, NULL, -EPERM);
519 if (!dbus_message_get_args(
522 DBUS_TYPE_UINT32, &major,
523 DBUS_TYPE_UINT32, &minor,
525 return bus_send_error_reply(connection, message, &error, -EINVAL);
527 sd = hashmap_get(s->devices, ULONG_TO_PTR((unsigned long)makedev(major, minor)));
529 return bus_send_error_reply(connection, message, NULL, -ENODEV);
531 session_device_complete_pause(sd);
533 reply = dbus_message_new_method_return(message);
538 const BusBoundProperties bps[] = {
539 { "org.freedesktop.login1.Session", bus_login_session_properties, s },
540 { "org.freedesktop.login1.Session", bus_login_session_user_properties, s->user },
543 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
547 if (!bus_maybe_send_reply(connection, message, reply))
551 return DBUS_HANDLER_RESULT_HANDLED;
554 dbus_error_free(&error);
556 return DBUS_HANDLER_RESULT_NEED_MEMORY;
559 static DBusHandlerResult session_message_handler(
560 DBusConnection *connection,
561 DBusMessage *message,
564 Manager *m = userdata;
568 r = get_session_for_path(m, dbus_message_get_path(message), &s);
572 return DBUS_HANDLER_RESULT_NEED_MEMORY;
578 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session");
579 return bus_send_error_reply(connection, message, &e, r);
582 return bus_send_error_reply(connection, message, NULL, r);
585 return session_message_dispatch(s, connection, message);
588 const DBusObjectPathVTable bus_session_vtable = {
589 .message_function = session_message_handler
592 char *session_bus_path(Session *s) {
593 _cleanup_free_ char *t = NULL;
597 t = bus_path_escape(s->id);
601 return strappend("/org/freedesktop/login1/session/", t);
604 int session_send_signal(Session *s, bool new_session) {
605 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
606 _cleanup_free_ char *p = NULL;
610 m = dbus_message_new_signal("/org/freedesktop/login1",
611 "org.freedesktop.login1.Manager",
612 new_session ? "SessionNew" : "SessionRemoved");
617 p = session_bus_path(s);
621 if (!dbus_message_append_args(
623 DBUS_TYPE_STRING, &s->id,
624 DBUS_TYPE_OBJECT_PATH, &p,
628 if (!dbus_connection_send(s->manager->bus, m, NULL))
634 int session_send_changed(Session *s, const char *properties) {
635 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
636 _cleanup_free_ char *p = NULL;
643 p = session_bus_path(s);
647 m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
651 if (!dbus_connection_send(s->manager->bus, m, NULL))
657 int session_send_lock(Session *s, bool lock) {
658 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
660 _cleanup_free_ char *p = NULL;
664 p = session_bus_path(s);
668 m = dbus_message_new_signal(p, "org.freedesktop.login1.Session", lock ? "Lock" : "Unlock");
673 b = dbus_connection_send(s->manager->bus, m, NULL);
680 int session_send_lock_all(Manager *m, bool lock) {
687 HASHMAP_FOREACH(session, m->sessions, i) {
690 k = session_send_lock(session, lock);
698 int session_send_create_reply(Session *s, DBusError *error) {
699 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
703 if (!s->create_message)
706 /* This is called after the session scope was successfully
707 * created, and finishes where bus_manager_create_session()
713 dbus_error_init(&buffer);
715 if (!dbus_error_is_set(error)) {
716 dbus_set_error_const(&buffer, DBUS_ERROR_INVALID_ARGS, "Invalid Arguments");
720 reply = dbus_message_new_error(s->create_message, error->name, error->message);
721 dbus_error_free(&buffer);
726 _cleanup_close_ int fifo_fd = -1;
727 _cleanup_free_ char *path = NULL;
732 fifo_fd = session_create_fifo(s);
734 log_error("Failed to create fifo: %s", strerror(-fifo_fd));
738 path = session_bus_path(s);
742 reply = dbus_message_new_method_return(s->create_message);
746 cseat = s->seat ? s->seat->id : "";
750 if (!dbus_message_append_args(
752 DBUS_TYPE_STRING, &s->id,
753 DBUS_TYPE_OBJECT_PATH, &path,
754 DBUS_TYPE_STRING, &s->user->runtime_path,
755 DBUS_TYPE_UNIX_FD, &fifo_fd,
756 DBUS_TYPE_STRING, &cseat,
757 DBUS_TYPE_UINT32, &vtnr,
758 DBUS_TYPE_BOOLEAN, &exists,
763 /* Update the state file before we notify the client about the
767 if (!dbus_connection_send(s->manager->bus, reply, NULL))
770 dbus_message_unref(s->create_message);
771 s->create_message = NULL;