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=\"DefaultControlGroup\" 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;
68 char _cleanup_free_ *p = NULL;
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))
92 if (!dbus_message_iter_close_container(i, &sub))
98 static int bus_user_append_state(DBusMessageIter *i, const char *property, void *data) {
106 state = user_state_to_string(user_get_state(u));
108 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
114 static int bus_user_append_sessions(DBusMessageIter *i, const char *property, void *data) {
115 DBusMessageIter sub, sub2;
123 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(so)", &sub))
126 LIST_FOREACH(sessions_by_user, session, u->sessions) {
129 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
132 p = session_bus_path(session);
136 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
137 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
144 if (!dbus_message_iter_close_container(&sub, &sub2))
148 if (!dbus_message_iter_close_container(i, &sub))
154 static int bus_user_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
162 b = user_get_idle_hint(u, NULL) > 0;
164 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
170 static int bus_user_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
179 user_get_idle_hint(u, &t);
180 k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
182 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &k))
188 static int bus_user_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
190 char _cleanup_free_ *t = NULL;
198 r = cg_join_spec(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &t);
202 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
203 return success ? 0 : -ENOMEM;
206 static int get_user_for_path(Manager *m, const char *path, User **_u) {
215 if (!startswith(path, "/org/freedesktop/login1/user/"))
218 r = safe_atolu(path + 29, &lu);
222 u = hashmap_get(m->users, ULONG_TO_PTR(lu));
230 static const BusProperty bus_login_user_properties[] = {
231 { "UID", bus_property_append_uid, "u", offsetof(User, uid) },
232 { "GID", bus_property_append_gid, "u", offsetof(User, gid) },
233 { "Name", bus_property_append_string, "s", offsetof(User, name), true },
234 { "Timestamp", bus_property_append_usec, "t", offsetof(User, timestamp.realtime) },
235 { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(User, timestamp.monotonic) },
236 { "RuntimePath", bus_property_append_string, "s", offsetof(User, runtime_path), true },
237 { "DefaultControlGroup", bus_user_append_default_cgroup, "s", 0 },
238 { "Service", bus_property_append_string, "s", offsetof(User, service), true },
239 { "Display", bus_user_append_display, "(so)", 0 },
240 { "State", bus_user_append_state, "s", 0 },
241 { "Sessions", bus_user_append_sessions, "a(so)", 0 },
242 { "IdleHint", bus_user_append_idle_hint, "b", 0 },
243 { "IdleSinceHint", bus_user_append_idle_hint_since, "t", 0 },
244 { "IdleSinceHintMonotonic", bus_user_append_idle_hint_since, "t", 0 },
248 static DBusHandlerResult user_message_dispatch(
250 DBusConnection *connection,
251 DBusMessage *message) {
254 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
261 if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Terminate")) {
265 return bus_send_error_reply(connection, message, NULL, r);
267 reply = dbus_message_new_method_return(message);
270 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Kill")) {
273 if (!dbus_message_get_args(
276 DBUS_TYPE_INT32, &signo,
278 return bus_send_error_reply(connection, message, &error, -EINVAL);
280 if (signo <= 0 || signo >= _NSIG)
281 return bus_send_error_reply(connection, message, &error, -EINVAL);
283 r = user_kill(u, signo);
285 return bus_send_error_reply(connection, message, NULL, r);
287 reply = dbus_message_new_method_return(message);
292 const BusBoundProperties bps[] = {
293 { "org.freedesktop.login1.User", bus_login_user_properties, u },
297 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
301 if (!bus_maybe_send_reply(connection, message, reply))
305 return DBUS_HANDLER_RESULT_HANDLED;
308 dbus_error_free(&error);
310 return DBUS_HANDLER_RESULT_NEED_MEMORY;
313 static DBusHandlerResult user_message_handler(
314 DBusConnection *connection,
315 DBusMessage *message,
318 Manager *m = userdata;
322 r = get_user_for_path(m, dbus_message_get_path(message), &u);
326 return DBUS_HANDLER_RESULT_NEED_MEMORY;
332 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown user");
333 return bus_send_error_reply(connection, message, &e, r);
336 return bus_send_error_reply(connection, message, NULL, r);
339 return user_message_dispatch(u, connection, message);
342 const DBusObjectPathVTable bus_user_vtable = {
343 .message_function = user_message_handler
346 char *user_bus_path(User *u) {
351 if (asprintf(&s, "/org/freedesktop/login1/user/%llu", (unsigned long long) u->uid) < 0)
357 int user_send_signal(User *u, bool new_user) {
358 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
359 _cleanup_free_ char *p = NULL;
364 m = dbus_message_new_signal("/org/freedesktop/login1",
365 "org.freedesktop.login1.Manager",
366 new_user ? "UserNew" : "UserRemoved");
371 p = user_bus_path(u);
377 if (!dbus_message_append_args(
379 DBUS_TYPE_UINT32, &uid,
380 DBUS_TYPE_OBJECT_PATH, &p,
384 if (!dbus_connection_send(u->manager->bus, m, NULL))
390 int user_send_changed(User *u, const char *properties) {
391 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
392 _cleanup_free_ char *p = NULL;
399 p = user_bus_path(u);
403 m = bus_properties_changed_new(p, "org.freedesktop.login1.User", properties);
407 if (!dbus_connection_send(u->manager->bus, m, NULL))