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 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 (!dbus_connection_send(connection, reply, NULL))
310 dbus_message_unref(reply);
313 return DBUS_HANDLER_RESULT_HANDLED;
317 dbus_message_unref(reply);
319 dbus_error_free(&error);
321 return DBUS_HANDLER_RESULT_NEED_MEMORY;
324 static DBusHandlerResult user_message_handler(
325 DBusConnection *connection,
326 DBusMessage *message,
329 Manager *m = userdata;
333 r = get_user_for_path(m, dbus_message_get_path(message), &u);
337 return DBUS_HANDLER_RESULT_NEED_MEMORY;
343 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown user");
344 return bus_send_error_reply(connection, message, &e, r);
347 return bus_send_error_reply(connection, message, NULL, r);
350 return user_message_dispatch(u, connection, message);
353 const DBusObjectPathVTable bus_user_vtable = {
354 .message_function = user_message_handler
357 char *user_bus_path(User *u) {
362 if (asprintf(&s, "/org/freedesktop/login1/user/%llu", (unsigned long long) u->uid) < 0)
368 int user_send_signal(User *u, bool new_user) {
376 m = dbus_message_new_signal("/org/freedesktop/login1",
377 "org.freedesktop.login1.Manager",
378 new_user ? "UserNew" : "UserRemoved");
383 p = user_bus_path(u);
389 if (!dbus_message_append_args(
391 DBUS_TYPE_UINT32, &uid,
392 DBUS_TYPE_OBJECT_PATH, &p,
396 if (!dbus_connection_send(u->manager->bus, m, NULL))
402 dbus_message_unref(m);
408 int user_send_changed(User *u, const char *properties) {
418 p = user_bus_path(u);
422 m = bus_properties_changed_new(p, "org.freedesktop.login1.User", properties);
426 if (!dbus_connection_send(u->manager->bus, m, NULL))
433 dbus_message_unref(m);