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=\"Slice\" type=\"s\" access=\"read\"/>\n" \
44 " <property name=\"Display\" type=\"(so)\" access=\"read\"/>\n" \
45 " <property name=\"State\" type=\"s\" access=\"read\"/>\n" \
46 " <property name=\"Sessions\" type=\"a(so)\" access=\"read\"/>\n" \
47 " <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
48 " <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
49 " <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
52 #define INTROSPECTION \
53 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
56 BUS_PROPERTIES_INTERFACE \
58 BUS_INTROSPECTABLE_INTERFACE \
61 #define INTERFACES_LIST \
62 BUS_GENERIC_INTERFACES_LIST \
63 "org.freedesktop.login1.User\0"
65 static int bus_user_append_display(DBusMessageIter *i, const char *property, void *data) {
68 const char *id, *path;
69 _cleanup_free_ char *p = NULL;
75 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
80 path = p = session_bus_path(u->display);
89 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
90 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path))
93 if (!dbus_message_iter_close_container(i, &sub))
99 static int bus_user_append_state(DBusMessageIter *i, const char *property, void *data) {
107 state = user_state_to_string(user_get_state(u));
109 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
115 static int bus_user_append_sessions(DBusMessageIter *i, const char *property, void *data) {
116 DBusMessageIter sub, sub2;
124 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(so)", &sub))
127 LIST_FOREACH(sessions_by_user, session, u->sessions) {
130 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
133 p = session_bus_path(session);
137 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
138 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
145 if (!dbus_message_iter_close_container(&sub, &sub2))
149 if (!dbus_message_iter_close_container(i, &sub))
155 static int bus_user_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
163 b = user_get_idle_hint(u, NULL) > 0;
165 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
171 static int bus_user_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
180 user_get_idle_hint(u, &t);
181 k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
183 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &k))
189 static int bus_user_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
191 _cleanup_free_ char *t = NULL;
199 r = cg_join_spec(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &t);
203 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
204 return success ? 0 : -ENOMEM;
207 static int get_user_for_path(Manager *m, const char *path, User **_u) {
216 if (!startswith(path, "/org/freedesktop/login1/user/_"))
219 r = safe_atolu(path + 30, &lu);
223 u = hashmap_get(m->users, ULONG_TO_PTR(lu));
231 static const BusProperty bus_login_user_properties[] = {
232 { "UID", bus_property_append_uid, "u", offsetof(User, uid) },
233 { "GID", bus_property_append_gid, "u", offsetof(User, gid) },
234 { "Name", bus_property_append_string, "s", offsetof(User, name), true },
235 { "Timestamp", bus_property_append_usec, "t", offsetof(User, timestamp.realtime) },
236 { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(User, timestamp.monotonic) },
237 { "RuntimePath", bus_property_append_string, "s", offsetof(User, runtime_path), true },
238 { "DefaultControlGroup", bus_user_append_default_cgroup, "s", 0 },
239 { "Service", bus_property_append_string, "s", offsetof(User, service), true },
240 { "Slice", bus_property_append_string, "s", offsetof(User, slice), true },
241 { "Display", bus_user_append_display, "(so)", 0 },
242 { "State", bus_user_append_state, "s", 0 },
243 { "Sessions", bus_user_append_sessions, "a(so)", 0 },
244 { "IdleHint", bus_user_append_idle_hint, "b", 0 },
245 { "IdleSinceHint", bus_user_append_idle_hint_since, "t", 0 },
246 { "IdleSinceHintMonotonic", bus_user_append_idle_hint_since, "t", 0 },
250 static DBusHandlerResult user_message_dispatch(
252 DBusConnection *connection,
253 DBusMessage *message) {
256 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
263 if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Terminate")) {
267 return bus_send_error_reply(connection, message, NULL, r);
269 reply = dbus_message_new_method_return(message);
272 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Kill")) {
275 if (!dbus_message_get_args(
278 DBUS_TYPE_INT32, &signo,
280 return bus_send_error_reply(connection, message, &error, -EINVAL);
282 if (signo <= 0 || signo >= _NSIG)
283 return bus_send_error_reply(connection, message, &error, -EINVAL);
285 r = user_kill(u, signo);
287 return bus_send_error_reply(connection, message, NULL, r);
289 reply = dbus_message_new_method_return(message);
294 const BusBoundProperties bps[] = {
295 { "org.freedesktop.login1.User", bus_login_user_properties, u },
299 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
303 if (!bus_maybe_send_reply(connection, message, reply))
307 return DBUS_HANDLER_RESULT_HANDLED;
310 dbus_error_free(&error);
312 return DBUS_HANDLER_RESULT_NEED_MEMORY;
315 static DBusHandlerResult user_message_handler(
316 DBusConnection *connection,
317 DBusMessage *message,
320 Manager *m = userdata;
324 r = get_user_for_path(m, dbus_message_get_path(message), &u);
328 return DBUS_HANDLER_RESULT_NEED_MEMORY;
334 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown user");
335 return bus_send_error_reply(connection, message, &e, r);
338 return bus_send_error_reply(connection, message, NULL, r);
341 return user_message_dispatch(u, connection, message);
344 const DBusObjectPathVTable bus_user_vtable = {
345 .message_function = user_message_handler
348 char *user_bus_path(User *u) {
353 if (asprintf(&s, "/org/freedesktop/login1/user/_%llu", (unsigned long long) u->uid) < 0)
359 int user_send_signal(User *u, bool new_user) {
360 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
361 _cleanup_free_ char *p = NULL;
366 m = dbus_message_new_signal("/org/freedesktop/login1",
367 "org.freedesktop.login1.Manager",
368 new_user ? "UserNew" : "UserRemoved");
373 p = user_bus_path(u);
379 if (!dbus_message_append_args(
381 DBUS_TYPE_UINT32, &uid,
382 DBUS_TYPE_OBJECT_PATH, &p,
386 if (!dbus_connection_send(u->manager->bus, m, NULL))
392 int user_send_changed(User *u, const char *properties) {
393 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
394 _cleanup_free_ char *p = NULL;
401 p = user_bus_path(u);
405 m = bus_properties_changed_new(p, "org.freedesktop.login1.User", properties);
409 if (!dbus_connection_send(u->manager->bus, m, NULL))