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-user.h"
27 #include "dbus-common.h"
29 #define BUS_USER_INTERFACE \
30 " <interface name=\"org.freedesktop.login1.User\">\n" \
31 " <method name=\"Terminate\"/>\n" \
32 " <method name=\"Kill\">\n" \
33 " <arg name=\"signal\" type=\"s\"/>\n" \
35 " <property name=\"UID\" type=\"u\" access=\"read\"/>\n" \
36 " <property name=\"GID\" type=\"u\" access=\"read\"/>\n" \
37 " <property name=\"Name\" type=\"s\" access=\"read\"/>\n" \
38 " <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \
39 " <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
40 " <property name=\"RuntimePath\" type=\"s\" access=\"read\"/>\n" \
41 " <property name=\"ControlGroupPath\" type=\"s\" access=\"read\"/>\n" \
42 " <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \
43 " <property name=\"Display\" type=\"(so)\" access=\"read\"/>\n" \
44 " <property name=\"State\" type=\"s\" access=\"read\"/>\n" \
45 " <property name=\"Sessions\" type=\"a(so)\" access=\"read\"/>\n" \
46 " <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
47 " <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
48 " <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
51 #define INTROSPECTION \
52 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
55 BUS_PROPERTIES_INTERFACE \
57 BUS_INTROSPECTABLE_INTERFACE \
60 #define INTERFACES_LIST \
61 BUS_GENERIC_INTERFACES_LIST \
62 "org.freedesktop.login1.User\0"
64 static int bus_user_append_display(DBusMessageIter
*i
, const char *property
, void *data
) {
67 const char *id
, *path
;
74 if (!dbus_message_iter_open_container(i
, DBUS_TYPE_STRUCT
, NULL
, &sub
))
79 path
= p
= session_bus_path(u
->display
);
88 if (!dbus_message_iter_append_basic(&sub
, DBUS_TYPE_STRING
, &id
) ||
89 !dbus_message_iter_append_basic(&sub
, DBUS_TYPE_OBJECT_PATH
, &path
)) {
96 if (!dbus_message_iter_close_container(i
, &sub
))
102 static int bus_user_append_state(DBusMessageIter
*i
, const char *property
, void *data
) {
110 state
= user_state_to_string(user_get_state(u
));
112 if (!dbus_message_iter_append_basic(i
, DBUS_TYPE_STRING
, &state
))
118 static int bus_user_append_sessions(DBusMessageIter
*i
, const char *property
, void *data
) {
119 DBusMessageIter sub
, sub2
;
127 if (!dbus_message_iter_open_container(i
, DBUS_TYPE_ARRAY
, "(so)", &sub
))
130 LIST_FOREACH(sessions_by_user
, session
, u
->sessions
) {
133 if (!dbus_message_iter_open_container(&sub
, DBUS_TYPE_STRUCT
, NULL
, &sub2
))
136 p
= session_bus_path(session
);
140 if (!dbus_message_iter_append_basic(&sub2
, DBUS_TYPE_STRING
, &session
->id
) ||
141 !dbus_message_iter_append_basic(&sub2
, DBUS_TYPE_OBJECT_PATH
, &p
)) {
148 if (!dbus_message_iter_close_container(&sub
, &sub2
))
152 if (!dbus_message_iter_close_container(i
, &sub
))
158 static int bus_user_append_idle_hint(DBusMessageIter
*i
, const char *property
, void *data
) {
166 b
= user_get_idle_hint(u
, NULL
) > 0;
168 if (!dbus_message_iter_append_basic(i
, DBUS_TYPE_BOOLEAN
, &b
))
174 static int bus_user_append_idle_hint_since(DBusMessageIter
*i
, const char *property
, void *data
) {
183 user_get_idle_hint(u
, &t
);
184 k
= streq(property
, "IdleSinceHint") ? t
.realtime
: t
.monotonic
;
186 if (!dbus_message_iter_append_basic(i
, DBUS_TYPE_UINT64
, &k
))
192 static int get_user_for_path(Manager
*m
, const char *path
, User
**_u
) {
201 if (!startswith(path
, "/org/freedesktop/login1/user/"))
204 r
= safe_atolu(path
+ 29, &lu
);
208 u
= hashmap_get(m
->users
, ULONG_TO_PTR(lu
));
216 static const BusProperty bus_login_user_properties
[] = {
217 { "UID", bus_property_append_uid
, "u", offsetof(User
, uid
) },
218 { "GID", bus_property_append_gid
, "u", offsetof(User
, gid
) },
219 { "Name", bus_property_append_string
, "s", offsetof(User
, name
), true },
220 { "Timestamp", bus_property_append_usec
, "t", offsetof(User
, timestamp
.realtime
) },
221 { "TimestampMonotonic", bus_property_append_usec
, "t", offsetof(User
, timestamp
.monotonic
) },
222 { "RuntimePath", bus_property_append_string
, "s", offsetof(User
, runtime_path
), true },
223 { "ControlGroupPath", bus_property_append_string
, "s", offsetof(User
, cgroup_path
), true },
224 { "Service", bus_property_append_string
, "s", offsetof(User
, service
), true },
225 { "Display", bus_user_append_display
, "(so)", 0 },
226 { "State", bus_user_append_state
, "s", 0 },
227 { "Sessions", bus_user_append_sessions
, "a(so)", 0 },
228 { "IdleHint", bus_user_append_idle_hint
, "b", 0 },
229 { "IdleSinceHint", bus_user_append_idle_hint_since
, "t", 0 },
230 { "IdleSinceHintMonotonic", bus_user_append_idle_hint_since
, "t", 0 },
234 static DBusHandlerResult
user_message_dispatch(
236 DBusConnection
*connection
,
237 DBusMessage
*message
) {
240 DBusMessage
*reply
= NULL
;
247 if (dbus_message_is_method_call(message
, "org.freedesktop.login1.User", "Terminate")) {
251 return bus_send_error_reply(connection
, message
, NULL
, r
);
253 reply
= dbus_message_new_method_return(message
);
256 } else if (dbus_message_is_method_call(message
, "org.freedesktop.login1.User", "Kill")) {
259 if (!dbus_message_get_args(
262 DBUS_TYPE_INT32
, &signo
,
264 return bus_send_error_reply(connection
, message
, &error
, -EINVAL
);
266 if (signo
<= 0 || signo
>= _NSIG
)
267 return bus_send_error_reply(connection
, message
, &error
, -EINVAL
);
269 r
= user_kill(u
, signo
);
271 return bus_send_error_reply(connection
, message
, NULL
, r
);
273 reply
= dbus_message_new_method_return(message
);
278 const BusBoundProperties bps
[] = {
279 { "org.freedesktop.login1.User", bus_login_user_properties
, u
},
283 return bus_default_message_handler(connection
, message
, INTROSPECTION
, INTERFACES_LIST
, bps
);
287 if (!dbus_connection_send(connection
, reply
, NULL
))
290 dbus_message_unref(reply
);
293 return DBUS_HANDLER_RESULT_HANDLED
;
297 dbus_message_unref(reply
);
299 dbus_error_free(&error
);
301 return DBUS_HANDLER_RESULT_NEED_MEMORY
;
304 static DBusHandlerResult
user_message_handler(
305 DBusConnection
*connection
,
306 DBusMessage
*message
,
309 Manager
*m
= userdata
;
313 r
= get_user_for_path(m
, dbus_message_get_path(message
), &u
);
317 return DBUS_HANDLER_RESULT_NEED_MEMORY
;
323 dbus_set_error_const(&e
, DBUS_ERROR_UNKNOWN_OBJECT
, "Unknown user");
324 return bus_send_error_reply(connection
, message
, &e
, r
);
327 return bus_send_error_reply(connection
, message
, NULL
, r
);
330 return user_message_dispatch(u
, connection
, message
);
333 const DBusObjectPathVTable bus_user_vtable
= {
334 .message_function
= user_message_handler
337 char *user_bus_path(User
*u
) {
342 if (asprintf(&s
, "/org/freedesktop/login1/user/%llu", (unsigned long long) u
->uid
) < 0)
348 int user_send_signal(User
*u
, bool new_user
) {
356 m
= dbus_message_new_signal("/org/freedesktop/login1",
357 "org.freedesktop.login1.Manager",
358 new_user
? "UserNew" : "UserRemoved");
363 p
= user_bus_path(u
);
369 if (!dbus_message_append_args(
371 DBUS_TYPE_UINT32
, &uid
,
372 DBUS_TYPE_OBJECT_PATH
, &p
,
376 if (!dbus_connection_send(u
->manager
->bus
, m
, NULL
))
382 dbus_message_unref(m
);
388 int user_send_changed(User
*u
, const char *properties
) {
398 p
= user_bus_path(u
);
402 m
= bus_properties_changed_new(p
, "org.freedesktop.login1.User", properties
);
406 if (!dbus_connection_send(u
->manager
->bus
, m
, NULL
))
413 dbus_message_unref(m
);