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 sd = hashmap_get(s->devices, &dev);
457 /* We don't allow retrieving a device multiple times.
458 * The related ReleaseDevice call is not ref-counted.
459 * The caller should use dup() if it requires more than
460 * one fd (it would be functionally equivalent). */
461 return bus_send_error_reply(connection, message, &error, -EBUSY);
464 r = session_device_new(s, dev, &sd);
466 return bus_send_error_reply(connection, message, NULL, r);
468 reply = dbus_message_new_method_return(message);
470 session_device_free(sd);
474 paused = !sd->active;
475 b = dbus_message_append_args(
477 DBUS_TYPE_UNIX_FD, &sd->fd,
478 DBUS_TYPE_BOOLEAN, &paused,
481 session_device_free(sd);
482 return bus_send_error_reply(connection, message, NULL, -ENOMEM);
485 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "ReleaseDevice")) {
487 uint32_t major, minor;
490 if (!session_is_controller(s, bus_message_get_sender_with_fallback(message)))
491 return bus_send_error_reply(connection, message, NULL, -EPERM);
493 if (!dbus_message_get_args(
496 DBUS_TYPE_UINT32, &major,
497 DBUS_TYPE_UINT32, &minor,
499 return bus_send_error_reply(connection, message, &error, -EINVAL);
501 dev = makedev(major, minor);
502 sd = hashmap_get(s->devices, &dev);
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;
517 if (!session_is_controller(s, bus_message_get_sender_with_fallback(message)))
518 return bus_send_error_reply(connection, message, NULL, -EPERM);
520 if (!dbus_message_get_args(
523 DBUS_TYPE_UINT32, &major,
524 DBUS_TYPE_UINT32, &minor,
526 return bus_send_error_reply(connection, message, &error, -EINVAL);
528 dev = makedev(major, minor);
529 sd = hashmap_get(s->devices, &dev);
531 return bus_send_error_reply(connection, message, NULL, -ENODEV);
533 session_device_complete_pause(sd);
535 reply = dbus_message_new_method_return(message);
540 const BusBoundProperties bps[] = {
541 { "org.freedesktop.login1.Session", bus_login_session_properties, s },
542 { "org.freedesktop.login1.Session", bus_login_session_user_properties, s->user },
545 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
549 if (!bus_maybe_send_reply(connection, message, reply))
553 return DBUS_HANDLER_RESULT_HANDLED;
556 dbus_error_free(&error);
558 return DBUS_HANDLER_RESULT_NEED_MEMORY;
561 static DBusHandlerResult session_message_handler(
562 DBusConnection *connection,
563 DBusMessage *message,
566 Manager *m = userdata;
570 r = get_session_for_path(m, dbus_message_get_path(message), &s);
574 return DBUS_HANDLER_RESULT_NEED_MEMORY;
580 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session");
581 return bus_send_error_reply(connection, message, &e, r);
584 return bus_send_error_reply(connection, message, NULL, r);
587 return session_message_dispatch(s, connection, message);
590 const DBusObjectPathVTable bus_session_vtable = {
591 .message_function = session_message_handler
594 char *session_bus_path(Session *s) {
595 _cleanup_free_ char *t = NULL;
599 t = bus_path_escape(s->id);
603 return strappend("/org/freedesktop/login1/session/", t);
606 int session_send_signal(Session *s, bool new_session) {
607 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
608 _cleanup_free_ char *p = NULL;
612 m = dbus_message_new_signal("/org/freedesktop/login1",
613 "org.freedesktop.login1.Manager",
614 new_session ? "SessionNew" : "SessionRemoved");
619 p = session_bus_path(s);
623 if (!dbus_message_append_args(
625 DBUS_TYPE_STRING, &s->id,
626 DBUS_TYPE_OBJECT_PATH, &p,
630 if (!dbus_connection_send(s->manager->bus, m, NULL))
636 int session_send_changed(Session *s, const char *properties) {
637 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
638 _cleanup_free_ char *p = NULL;
645 p = session_bus_path(s);
649 m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
653 if (!dbus_connection_send(s->manager->bus, m, NULL))
659 int session_send_lock(Session *s, bool lock) {
660 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
662 _cleanup_free_ char *p = NULL;
666 p = session_bus_path(s);
670 m = dbus_message_new_signal(p, "org.freedesktop.login1.Session", lock ? "Lock" : "Unlock");
675 b = dbus_connection_send(s->manager->bus, m, NULL);
682 int session_send_lock_all(Manager *m, bool lock) {
689 HASHMAP_FOREACH(session, m->sessions, i) {
692 k = session_send_lock(session, lock);
700 int session_send_create_reply(Session *s, DBusError *error) {
701 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
705 if (!s->create_message)
708 /* This is called after the session scope was successfully
709 * created, and finishes where bus_manager_create_session()
715 dbus_error_init(&buffer);
717 if (!dbus_error_is_set(error)) {
718 dbus_set_error_const(&buffer, DBUS_ERROR_INVALID_ARGS, "Invalid Arguments");
722 reply = dbus_message_new_error(s->create_message, error->name, error->message);
723 dbus_error_free(&buffer);
728 _cleanup_close_ int fifo_fd = -1;
729 _cleanup_free_ char *path = NULL;
734 fifo_fd = session_create_fifo(s);
736 log_error("Failed to create fifo: %s", strerror(-fifo_fd));
740 path = session_bus_path(s);
744 reply = dbus_message_new_method_return(s->create_message);
748 cseat = s->seat ? s->seat->id : "";
752 if (!dbus_message_append_args(
754 DBUS_TYPE_STRING, &s->id,
755 DBUS_TYPE_OBJECT_PATH, &path,
756 DBUS_TYPE_STRING, &s->user->runtime_path,
757 DBUS_TYPE_UNIX_FD, &fifo_fd,
758 DBUS_TYPE_STRING, &cseat,
759 DBUS_TYPE_UINT32, &vtnr,
760 DBUS_TYPE_BOOLEAN, &exists,
765 /* Update the state file before we notify the client about the
769 if (!dbus_connection_send(s->manager->bus, reply, NULL))
772 dbus_message_unref(s->create_message);
773 s->create_message = NULL;