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=\"Service\" type=\"s\" access=\"read\"/>\n" \
42 " <property name=\"Slice\" 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 _cleanup_free_ char *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 get_user_for_path(Manager *m, const char *path, User **_u) {
197 if (!startswith(path, "/org/freedesktop/login1/user/_"))
200 r = safe_atolu(path + 30, &lu);
204 u = hashmap_get(m->users, ULONG_TO_PTR(lu));
212 static const BusProperty bus_login_user_properties[] = {
213 { "UID", bus_property_append_uid, "u", offsetof(User, uid) },
214 { "GID", bus_property_append_gid, "u", offsetof(User, gid) },
215 { "Name", bus_property_append_string, "s", offsetof(User, name), true },
216 { "Timestamp", bus_property_append_usec, "t", offsetof(User, timestamp.realtime) },
217 { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(User, timestamp.monotonic) },
218 { "RuntimePath", bus_property_append_string, "s", offsetof(User, runtime_path), true },
219 { "Service", bus_property_append_string, "s", offsetof(User, service), true },
220 { "Slice", bus_property_append_string, "s", offsetof(User, slice), true },
221 { "Display", bus_user_append_display, "(so)", 0 },
222 { "State", bus_user_append_state, "s", 0 },
223 { "Sessions", bus_user_append_sessions, "a(so)", 0 },
224 { "IdleHint", bus_user_append_idle_hint, "b", 0 },
225 { "IdleSinceHint", bus_user_append_idle_hint_since, "t", 0 },
226 { "IdleSinceHintMonotonic", bus_user_append_idle_hint_since, "t", 0 },
230 static DBusHandlerResult user_message_dispatch(
232 DBusConnection *connection,
233 DBusMessage *message) {
236 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
243 if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Terminate")) {
247 return bus_send_error_reply(connection, message, NULL, r);
249 reply = dbus_message_new_method_return(message);
252 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Kill")) {
255 if (!dbus_message_get_args(
258 DBUS_TYPE_INT32, &signo,
260 return bus_send_error_reply(connection, message, &error, -EINVAL);
262 if (signo <= 0 || signo >= _NSIG)
263 return bus_send_error_reply(connection, message, &error, -EINVAL);
265 r = user_kill(u, signo);
267 return bus_send_error_reply(connection, message, NULL, r);
269 reply = dbus_message_new_method_return(message);
274 const BusBoundProperties bps[] = {
275 { "org.freedesktop.login1.User", bus_login_user_properties, u },
279 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
283 if (!bus_maybe_send_reply(connection, message, reply))
287 return DBUS_HANDLER_RESULT_HANDLED;
290 dbus_error_free(&error);
292 return DBUS_HANDLER_RESULT_NEED_MEMORY;
295 static DBusHandlerResult user_message_handler(
296 DBusConnection *connection,
297 DBusMessage *message,
300 Manager *m = userdata;
304 r = get_user_for_path(m, dbus_message_get_path(message), &u);
308 return DBUS_HANDLER_RESULT_NEED_MEMORY;
314 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown user");
315 return bus_send_error_reply(connection, message, &e, r);
318 return bus_send_error_reply(connection, message, NULL, r);
321 return user_message_dispatch(u, connection, message);
324 const DBusObjectPathVTable bus_user_vtable = {
325 .message_function = user_message_handler
328 char *user_bus_path(User *u) {
333 if (asprintf(&s, "/org/freedesktop/login1/user/_%llu", (unsigned long long) u->uid) < 0)
339 int user_send_signal(User *u, bool new_user) {
340 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
341 _cleanup_free_ char *p = NULL;
346 m = dbus_message_new_signal("/org/freedesktop/login1",
347 "org.freedesktop.login1.Manager",
348 new_user ? "UserNew" : "UserRemoved");
353 p = user_bus_path(u);
359 if (!dbus_message_append_args(
361 DBUS_TYPE_UINT32, &uid,
362 DBUS_TYPE_OBJECT_PATH, &p,
366 if (!dbus_connection_send(u->manager->bus, m, NULL))
372 int user_send_changed(User *u, const char *properties) {
373 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
374 _cleanup_free_ char *p = NULL;
381 p = user_bus_path(u);
385 m = bus_properties_changed_new(p, "org.freedesktop.login1.User", properties);
389 if (!dbus_connection_send(u->manager->bus, m, NULL))