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=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
51 " <property name=\"VTNr\" type=\"u\" access=\"read\"/>\n" \
52 " <property name=\"Seat\" type=\"(so)\" access=\"read\"/>\n" \
53 " <property name=\"TTY\" type=\"s\" access=\"read\"/>\n" \
54 " <property name=\"Display\" type=\"s\" access=\"read\"/>\n" \
55 " <property name=\"Remote\" type=\"b\" access=\"read\"/>\n" \
56 " <property name=\"RemoteHost\" type=\"s\" access=\"read\"/>\n" \
57 " <property name=\"RemoteUser\" type=\"s\" access=\"read\"/>\n" \
58 " <property name=\"Service\" 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=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
66 " <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
67 " <property name=\"KillProcesses\" type=\"b\" access=\"read\"/>\n" \
68 " <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
69 " <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
70 " <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
73 #define INTROSPECTION \
74 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
76 BUS_SESSION_INTERFACE \
77 BUS_PROPERTIES_INTERFACE \
79 BUS_INTROSPECTABLE_INTERFACE \
82 #define INTERFACES_LIST \
83 BUS_GENERIC_INTERFACES_LIST \
84 "org.freedesktop.login1.Session\0"
86 static int bus_session_append_seat(DBusMessageIter
*i
, const char *property
, void *data
) {
89 const char *id
, *path
;
96 if (!dbus_message_iter_open_container(i
, DBUS_TYPE_STRUCT
, NULL
, &sub
))
101 path
= p
= seat_bus_path(s
->seat
);
110 if (!dbus_message_iter_append_basic(&sub
, DBUS_TYPE_STRING
, &id
) ||
111 !dbus_message_iter_append_basic(&sub
, DBUS_TYPE_OBJECT_PATH
, &path
))
114 if (!dbus_message_iter_close_container(i
, &sub
))
120 static int bus_session_append_user(DBusMessageIter
*i
, const char *property
, void *data
) {
123 _cleanup_free_
char *p
= NULL
;
129 if (!dbus_message_iter_open_container(i
, DBUS_TYPE_STRUCT
, NULL
, &sub
))
132 p
= user_bus_path(u
);
136 if (!dbus_message_iter_append_basic(&sub
, DBUS_TYPE_UINT32
, &u
->uid
) ||
137 !dbus_message_iter_append_basic(&sub
, DBUS_TYPE_OBJECT_PATH
, &p
))
140 if (!dbus_message_iter_close_container(i
, &sub
))
146 static int bus_session_append_active(DBusMessageIter
*i
, const char *property
, void *data
) {
154 b
= session_is_active(s
);
155 if (!dbus_message_iter_append_basic(i
, DBUS_TYPE_BOOLEAN
, &b
))
161 static int bus_session_append_idle_hint(DBusMessageIter
*i
, const char *property
, void *data
) {
169 b
= session_get_idle_hint(s
, NULL
) > 0;
170 if (!dbus_message_iter_append_basic(i
, DBUS_TYPE_BOOLEAN
, &b
))
176 static int bus_session_append_idle_hint_since(DBusMessageIter
*i
, const char *property
, void *data
) {
186 r
= session_get_idle_hint(s
, &t
);
190 u
= streq(property
, "IdleSinceHint") ? t
.realtime
: t
.monotonic
;
192 if (!dbus_message_iter_append_basic(i
, DBUS_TYPE_UINT64
, &u
))
198 static int bus_session_append_default_cgroup(DBusMessageIter
*i
, const char *property
, void *data
) {
200 _cleanup_free_
char *t
= NULL
;
208 r
= cg_join_spec(SYSTEMD_CGROUP_CONTROLLER
, s
->cgroup_path
, &t
);
212 success
= dbus_message_iter_append_basic(i
, DBUS_TYPE_STRING
, &t
);
213 return success
? 0 : -ENOMEM
;
216 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type
, session_type
, SessionType
);
217 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_class
, session_class
, SessionClass
);
219 static int bus_session_append_state(DBusMessageIter
*i
, const char *property
, void *data
) {
227 state
= session_state_to_string(session_get_state(s
));
229 if (!dbus_message_iter_append_basic(i
, DBUS_TYPE_STRING
, &state
))
235 static int get_session_for_path(Manager
*m
, const char *path
, Session
**_s
) {
243 if (!startswith(path
, "/org/freedesktop/login1/session/"))
246 id
= bus_path_unescape(path
+ 32);
250 s
= hashmap_get(m
->sessions
, id
);
260 static const BusProperty bus_login_session_properties
[] = {
261 { "Id", bus_property_append_string
, "s", offsetof(Session
, id
), true },
262 { "Timestamp", bus_property_append_usec
, "t", offsetof(Session
, timestamp
.realtime
) },
263 { "TimestampMonotonic", bus_property_append_usec
, "t", offsetof(Session
, timestamp
.monotonic
) },
264 { "DefaultControlGroup", bus_session_append_default_cgroup
, "s", 0, },
265 { "VTNr", bus_property_append_uint32
, "u", offsetof(Session
, vtnr
) },
266 { "Seat", bus_session_append_seat
, "(so)", 0 },
267 { "TTY", bus_property_append_string
, "s", offsetof(Session
, tty
), true },
268 { "Display", bus_property_append_string
, "s", offsetof(Session
, display
), true },
269 { "Remote", bus_property_append_bool
, "b", offsetof(Session
, remote
) },
270 { "RemoteUser", bus_property_append_string
, "s", offsetof(Session
, remote_user
), true },
271 { "RemoteHost", bus_property_append_string
, "s", offsetof(Session
, remote_host
), true },
272 { "Service", bus_property_append_string
, "s", offsetof(Session
, service
), true },
273 { "Leader", bus_property_append_pid
, "u", offsetof(Session
, leader
) },
274 { "Audit", bus_property_append_uint32
, "u", offsetof(Session
, audit_id
) },
275 { "Type", bus_session_append_type
, "s", offsetof(Session
, type
) },
276 { "Class", bus_session_append_class
, "s", offsetof(Session
, class) },
277 { "Active", bus_session_append_active
, "b", 0 },
278 { "State", bus_session_append_state
, "s", 0 },
279 { "Controllers", bus_property_append_strv
, "as", offsetof(Session
, controllers
), true },
280 { "ResetControllers", bus_property_append_strv
, "as", offsetof(Session
, reset_controllers
), true },
281 { "KillProcesses", bus_property_append_bool
, "b", offsetof(Session
, kill_processes
) },
282 { "IdleHint", bus_session_append_idle_hint
, "b", 0 },
283 { "IdleSinceHint", bus_session_append_idle_hint_since
, "t", 0 },
284 { "IdleSinceHintMonotonic", bus_session_append_idle_hint_since
, "t", 0 },
288 static const BusProperty bus_login_session_user_properties
[] = {
289 { "User", bus_session_append_user
, "(uo)", 0 },
290 { "Name", bus_property_append_string
, "s", offsetof(User
, name
), true },
294 static DBusHandlerResult
session_message_dispatch(
296 DBusConnection
*connection
,
297 DBusMessage
*message
) {
300 _cleanup_dbus_message_unref_ DBusMessage
*reply
= NULL
;
307 dbus_error_init(&error
);
309 if (dbus_message_is_method_call(message
, "org.freedesktop.login1.Session", "Terminate")) {
313 return bus_send_error_reply(connection
, message
, NULL
, r
);
315 reply
= dbus_message_new_method_return(message
);
319 } else if (dbus_message_is_method_call(message
, "org.freedesktop.login1.Session", "Activate")) {
321 r
= session_activate(s
);
323 return bus_send_error_reply(connection
, message
, NULL
, r
);
325 reply
= dbus_message_new_method_return(message
);
329 } else if (dbus_message_is_method_call(message
, "org.freedesktop.login1.Session", "Lock") ||
330 dbus_message_is_method_call(message
, "org.freedesktop.login1.Session", "Unlock")) {
332 if (session_send_lock(s
, streq(dbus_message_get_member(message
), "Lock")) < 0)
335 reply
= dbus_message_new_method_return(message
);
339 } else if (dbus_message_is_method_call(message
, "org.freedesktop.login1.Session", "SetIdleHint")) {
343 if (!dbus_message_get_args(
346 DBUS_TYPE_BOOLEAN
, &b
,
348 return bus_send_error_reply(connection
, message
, &error
, -EINVAL
);
350 ul
= dbus_bus_get_unix_user(connection
, dbus_message_get_sender(message
), &error
);
351 if (ul
== (unsigned long) -1)
352 return bus_send_error_reply(connection
, message
, &error
, -EIO
);
354 if (ul
!= 0 && ul
!= s
->user
->uid
)
355 return bus_send_error_reply(connection
, message
, NULL
, -EPERM
);
357 session_set_idle_hint(s
, b
);
359 reply
= dbus_message_new_method_return(message
);
363 } else if (dbus_message_is_method_call(message
, "org.freedesktop.login1.Session", "Kill")) {
368 if (!dbus_message_get_args(
371 DBUS_TYPE_STRING
, &swho
,
372 DBUS_TYPE_INT32
, &signo
,
374 return bus_send_error_reply(connection
, message
, &error
, -EINVAL
);
379 who
= kill_who_from_string(swho
);
381 return bus_send_error_reply(connection
, message
, &error
, -EINVAL
);
384 if (signo
<= 0 || signo
>= _NSIG
)
385 return bus_send_error_reply(connection
, message
, &error
, -EINVAL
);
387 r
= session_kill(s
, who
, signo
);
389 return bus_send_error_reply(connection
, message
, NULL
, r
);
391 reply
= dbus_message_new_method_return(message
);
396 const BusBoundProperties bps
[] = {
397 { "org.freedesktop.login1.Session", bus_login_session_properties
, s
},
398 { "org.freedesktop.login1.Session", bus_login_session_user_properties
, s
->user
},
401 return bus_default_message_handler(connection
, message
, INTROSPECTION
, INTERFACES_LIST
, bps
);
405 if (!bus_maybe_send_reply(connection
, message
, reply
))
409 return DBUS_HANDLER_RESULT_HANDLED
;
412 dbus_error_free(&error
);
414 return DBUS_HANDLER_RESULT_NEED_MEMORY
;
417 static DBusHandlerResult
session_message_handler(
418 DBusConnection
*connection
,
419 DBusMessage
*message
,
422 Manager
*m
= userdata
;
426 r
= get_session_for_path(m
, dbus_message_get_path(message
), &s
);
430 return DBUS_HANDLER_RESULT_NEED_MEMORY
;
436 dbus_set_error_const(&e
, DBUS_ERROR_UNKNOWN_OBJECT
, "Unknown session");
437 return bus_send_error_reply(connection
, message
, &e
, r
);
440 return bus_send_error_reply(connection
, message
, NULL
, r
);
443 return session_message_dispatch(s
, connection
, message
);
446 const DBusObjectPathVTable bus_session_vtable
= {
447 .message_function
= session_message_handler
450 char *session_bus_path(Session
*s
) {
451 _cleanup_free_
char *t
;
455 t
= bus_path_escape(s
->id
);
459 return strappend("/org/freedesktop/login1/session/", t
);
462 int session_send_signal(Session
*s
, bool new_session
) {
463 _cleanup_dbus_message_unref_ DBusMessage
*m
= NULL
;
464 _cleanup_free_
char *p
= NULL
;
468 m
= dbus_message_new_signal("/org/freedesktop/login1",
469 "org.freedesktop.login1.Manager",
470 new_session
? "SessionNew" : "SessionRemoved");
475 p
= session_bus_path(s
);
479 if (!dbus_message_append_args(
481 DBUS_TYPE_STRING
, &s
->id
,
482 DBUS_TYPE_OBJECT_PATH
, &p
,
486 if (!dbus_connection_send(s
->manager
->bus
, m
, NULL
))
492 int session_send_changed(Session
*s
, const char *properties
) {
493 _cleanup_dbus_message_unref_ DBusMessage
*m
= NULL
;
494 _cleanup_free_
char *p
= NULL
;
501 p
= session_bus_path(s
);
505 m
= bus_properties_changed_new(p
, "org.freedesktop.login1.Session", properties
);
509 if (!dbus_connection_send(s
->manager
->bus
, m
, NULL
))
515 int session_send_lock(Session
*s
, bool lock
) {
516 _cleanup_dbus_message_unref_ DBusMessage
*m
= NULL
;
518 _cleanup_free_
char *p
= NULL
;
522 p
= session_bus_path(s
);
526 m
= dbus_message_new_signal(p
, "org.freedesktop.login1.Session", lock
? "Lock" : "Unlock");
531 b
= dbus_connection_send(s
->manager
->bus
, m
, NULL
);
538 int session_send_lock_all(Manager
*m
, bool lock
) {
545 HASHMAP_FOREACH(session
, m
->sessions
, i
) {
548 k
= session_send_lock(session
, lock
);