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;
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 bus_user_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
202 r = cg_join_spec(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &t);
206 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
209 return success ? 0 : -ENOMEM;
212 static int get_user_for_path(Manager *m, const char *path, User **_u) {
221 if (!startswith(path, "/org/freedesktop/login1/user/"))
224 r = safe_atolu(path + 29, &lu);
228 u = hashmap_get(m->users, ULONG_TO_PTR(lu));
236 static const BusProperty bus_login_user_properties[] = {
237 { "UID", bus_property_append_uid, "u", offsetof(User, uid) },
238 { "GID", bus_property_append_gid, "u", offsetof(User, gid) },
239 { "Name", bus_property_append_string, "s", offsetof(User, name), true },
240 { "Timestamp", bus_property_append_usec, "t", offsetof(User, timestamp.realtime) },
241 { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(User, timestamp.monotonic) },
242 { "RuntimePath", bus_property_append_string, "s", offsetof(User, runtime_path), true },
243 { "DefaultControlGroup", bus_user_append_default_cgroup, "s", 0 },
244 { "Service", bus_property_append_string, "s", offsetof(User, service), true },
245 { "Display", bus_user_append_display, "(so)", 0 },
246 { "State", bus_user_append_state, "s", 0 },
247 { "Sessions", bus_user_append_sessions, "a(so)", 0 },
248 { "IdleHint", bus_user_append_idle_hint, "b", 0 },
249 { "IdleSinceHint", bus_user_append_idle_hint_since, "t", 0 },
250 { "IdleSinceHintMonotonic", bus_user_append_idle_hint_since, "t", 0 },
254 static DBusHandlerResult user_message_dispatch(
256 DBusConnection *connection,
257 DBusMessage *message) {
260 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
267 if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Terminate")) {
271 return bus_send_error_reply(connection, message, NULL, r);
273 reply = dbus_message_new_method_return(message);
276 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Kill")) {
279 if (!dbus_message_get_args(
282 DBUS_TYPE_INT32, &signo,
284 return bus_send_error_reply(connection, message, &error, -EINVAL);
286 if (signo <= 0 || signo >= _NSIG)
287 return bus_send_error_reply(connection, message, &error, -EINVAL);
289 r = user_kill(u, signo);
291 return bus_send_error_reply(connection, message, NULL, r);
293 reply = dbus_message_new_method_return(message);
298 const BusBoundProperties bps[] = {
299 { "org.freedesktop.login1.User", bus_login_user_properties, u },
303 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
307 if (!bus_maybe_send_reply(connection, message, reply))
311 return DBUS_HANDLER_RESULT_HANDLED;
314 dbus_error_free(&error);
316 return DBUS_HANDLER_RESULT_NEED_MEMORY;
319 static DBusHandlerResult user_message_handler(
320 DBusConnection *connection,
321 DBusMessage *message,
324 Manager *m = userdata;
328 r = get_user_for_path(m, dbus_message_get_path(message), &u);
332 return DBUS_HANDLER_RESULT_NEED_MEMORY;
338 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown user");
339 return bus_send_error_reply(connection, message, &e, r);
342 return bus_send_error_reply(connection, message, NULL, r);
345 return user_message_dispatch(u, connection, message);
348 const DBusObjectPathVTable bus_user_vtable = {
349 .message_function = user_message_handler
352 char *user_bus_path(User *u) {
357 if (asprintf(&s, "/org/freedesktop/login1/user/%llu", (unsigned long long) u->uid) < 0)
363 int user_send_signal(User *u, bool new_user) {
364 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
366 _cleanup_free_ char *p = NULL;
371 m = dbus_message_new_signal("/org/freedesktop/login1",
372 "org.freedesktop.login1.Manager",
373 new_user ? "UserNew" : "UserRemoved");
378 p = user_bus_path(u);
384 if (!dbus_message_append_args(
386 DBUS_TYPE_UINT32, &uid,
387 DBUS_TYPE_OBJECT_PATH, &p,
391 if (!dbus_connection_send(u->manager->bus, m, NULL))
400 int user_send_changed(User *u, const char *properties) {
401 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
403 _cleanup_free_ char *p = NULL;
410 p = user_bus_path(u);
414 m = bus_properties_changed_new(p, "org.freedesktop.login1.User", properties);
418 if (!dbus_connection_send(u->manager->bus, m, NULL))