]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-user-dbus.c
localed: mark bus properties as change emitting
[thirdparty/systemd.git] / src / login / logind-user-dbus.c
CommitLineData
3f49d45a
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
3f49d45a
LP
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
3f49d45a 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
3f49d45a
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <errno.h>
a185c5aa 23#include <string.h>
3f49d45a
LP
24
25#include "logind.h"
26#include "logind-user.h"
27#include "dbus-common.h"
28
29#define BUS_USER_INTERFACE \
30 " <interface name=\"org.freedesktop.login1.User\">\n" \
31 " <method name=\"Terminate\"/>\n" \
de07ab16
LP
32 " <method name=\"Kill\">\n" \
33 " <arg name=\"signal\" type=\"s\"/>\n" \
34 " </method>\n" \
3f49d45a
LP
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" \
a185c5aa
LP
38 " <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \
39 " <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
3f49d45a 40 " <property name=\"RuntimePath\" type=\"s\" access=\"read\"/>\n" \
a185c5aa 41 " <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \
9444b1f2 42 " <property name=\"Slice\" type=\"s\" access=\"read\"/>\n" \
3f49d45a
LP
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" \
a185c5aa
LP
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" \
9444b1f2 49 " </interface>\n"
3f49d45a
LP
50
51#define INTROSPECTION \
52 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
53 "<node>\n" \
54 BUS_USER_INTERFACE \
55 BUS_PROPERTIES_INTERFACE \
56 BUS_PEER_INTERFACE \
57 BUS_INTROSPECTABLE_INTERFACE \
58 "</node>\n"
59
60#define INTERFACES_LIST \
61 BUS_GENERIC_INTERFACES_LIST \
62 "org.freedesktop.login1.User\0"
63
64static int bus_user_append_display(DBusMessageIter *i, const char *property, void *data) {
65 DBusMessageIter sub;
66 User *u = data;
67 const char *id, *path;
7fd1b19b 68 _cleanup_free_ char *p = NULL;
3f49d45a
LP
69
70 assert(i);
71 assert(property);
72 assert(u);
73
74 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
75 return -ENOMEM;
76
77 if (u->display) {
78 id = u->display->id;
79 path = p = session_bus_path(u->display);
80
81 if (!p)
82 return -ENOMEM;
83 } else {
84 id = "";
85 path = "/";
86 }
87
88 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
4654e558 89 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path))
3f49d45a 90 return -ENOMEM;
3f49d45a
LP
91
92 if (!dbus_message_iter_close_container(i, &sub))
93 return -ENOMEM;
94
95 return 0;
96}
97
98static int bus_user_append_state(DBusMessageIter *i, const char *property, void *data) {
99 User *u = data;
100 const char *state;
101
102 assert(i);
103 assert(property);
104 assert(u);
105
106 state = user_state_to_string(user_get_state(u));
107
108 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
109 return -ENOMEM;
110
111 return 0;
112}
113
114static int bus_user_append_sessions(DBusMessageIter *i, const char *property, void *data) {
115 DBusMessageIter sub, sub2;
116 User *u = data;
117 Session *session;
118
119 assert(i);
120 assert(property);
121 assert(u);
122
dec15e92 123 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(so)", &sub))
3f49d45a
LP
124 return -ENOMEM;
125
126 LIST_FOREACH(sessions_by_user, session, u->sessions) {
127 char *p;
128
129 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
130 return -ENOMEM;
131
132 p = session_bus_path(session);
133 if (!p)
134 return -ENOMEM;
135
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)) {
138 free(p);
139 return -ENOMEM;
140 }
141
142 free(p);
143
144 if (!dbus_message_iter_close_container(&sub, &sub2))
145 return -ENOMEM;
146 }
147
148 if (!dbus_message_iter_close_container(i, &sub))
149 return -ENOMEM;
150
151 return 0;
152}
153
a185c5aa
LP
154static int bus_user_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
155 User *u = data;
77527da0 156 dbus_bool_t b;
a185c5aa
LP
157
158 assert(i);
159 assert(property);
160 assert(u);
161
77527da0
LP
162 b = user_get_idle_hint(u, NULL) > 0;
163
a185c5aa
LP
164 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
165 return -ENOMEM;
166
167 return 0;
168}
169
170static int bus_user_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
171 User *u = data;
172 dual_timestamp t;
173 uint64_t k;
174
175 assert(i);
176 assert(property);
177 assert(u);
178
179 user_get_idle_hint(u, &t);
180 k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
181
182 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &k))
183 return -ENOMEM;
184
185 return 0;
186}
187
3f49d45a
LP
188static int get_user_for_path(Manager *m, const char *path, User **_u) {
189 User *u;
190 unsigned long lu;
191 int r;
192
193 assert(m);
194 assert(path);
195 assert(_u);
196
9444b1f2 197 if (!startswith(path, "/org/freedesktop/login1/user/_"))
3f49d45a
LP
198 return -EINVAL;
199
9444b1f2 200 r = safe_atolu(path + 30, &lu);
3f49d45a
LP
201 if (r < 0)
202 return r;
203
204 u = hashmap_get(m->users, ULONG_TO_PTR(lu));
205 if (!u)
206 return -ENOENT;
207
208 *_u = u;
209 return 0;
210}
211
d200735e
MS
212static 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 },
d200735e 219 { "Service", bus_property_append_string, "s", offsetof(User, service), true },
9444b1f2 220 { "Slice", bus_property_append_string, "s", offsetof(User, slice), true },
d200735e
MS
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 },
227 { NULL, }
228};
229
3f49d45a
LP
230static DBusHandlerResult user_message_dispatch(
231 User *u,
232 DBusConnection *connection,
233 DBusMessage *message) {
234
a185c5aa 235 DBusError error;
ce0fc5f5 236 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
a185c5aa
LP
237 int r;
238
3f49d45a
LP
239 assert(u);
240 assert(connection);
241 assert(message);
242
a185c5aa
LP
243 if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Terminate")) {
244
245 r = user_stop(u);
246 if (r < 0)
247 return bus_send_error_reply(connection, message, NULL, r);
248
249 reply = dbus_message_new_method_return(message);
250 if (!reply)
251 goto oom;
de07ab16
LP
252 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Kill")) {
253 int32_t signo;
254
255 if (!dbus_message_get_args(
256 message,
257 &error,
258 DBUS_TYPE_INT32, &signo,
259 DBUS_TYPE_INVALID))
260 return bus_send_error_reply(connection, message, &error, -EINVAL);
261
262 if (signo <= 0 || signo >= _NSIG)
263 return bus_send_error_reply(connection, message, &error, -EINVAL);
264
265 r = user_kill(u, signo);
266 if (r < 0)
267 return bus_send_error_reply(connection, message, NULL, r);
268
269 reply = dbus_message_new_method_return(message);
270 if (!reply)
271 goto oom;
272
d200735e
MS
273 } else {
274 const BusBoundProperties bps[] = {
275 { "org.freedesktop.login1.User", bus_login_user_properties, u },
276 { NULL, }
277 };
278
279 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
280 }
a185c5aa
LP
281
282 if (reply) {
c6a818c8 283 if (!bus_maybe_send_reply(connection, message, reply))
a185c5aa 284 goto oom;
a185c5aa
LP
285 }
286
287 return DBUS_HANDLER_RESULT_HANDLED;
288
289oom:
a185c5aa
LP
290 dbus_error_free(&error);
291
292 return DBUS_HANDLER_RESULT_NEED_MEMORY;
3f49d45a
LP
293}
294
295static DBusHandlerResult user_message_handler(
296 DBusConnection *connection,
297 DBusMessage *message,
298 void *userdata) {
299
300 Manager *m = userdata;
301 User *u;
302 int r;
303
304 r = get_user_for_path(m, dbus_message_get_path(message), &u);
305 if (r < 0) {
306
307 if (r == -ENOMEM)
308 return DBUS_HANDLER_RESULT_NEED_MEMORY;
309
310 if (r == -ENOENT) {
311 DBusError e;
312
313 dbus_error_init(&e);
314 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown user");
315 return bus_send_error_reply(connection, message, &e, r);
316 }
317
318 return bus_send_error_reply(connection, message, NULL, r);
319 }
320
321 return user_message_dispatch(u, connection, message);
322}
323
324const DBusObjectPathVTable bus_user_vtable = {
325 .message_function = user_message_handler
326};
327
328char *user_bus_path(User *u) {
329 char *s;
330
331 assert(u);
332
9444b1f2 333 if (asprintf(&s, "/org/freedesktop/login1/user/_%llu", (unsigned long long) u->uid) < 0)
3f49d45a
LP
334 return NULL;
335
336 return s;
337}
da119395
LP
338
339int user_send_signal(User *u, bool new_user) {
ce0fc5f5 340 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
ce0fc5f5 341 _cleanup_free_ char *p = NULL;
da119395
LP
342 uint32_t uid;
343
344 assert(u);
345
346 m = dbus_message_new_signal("/org/freedesktop/login1",
347 "org.freedesktop.login1.Manager",
348 new_user ? "UserNew" : "UserRemoved");
349
350 if (!m)
351 return -ENOMEM;
352
353 p = user_bus_path(u);
354 if (!p)
4654e558 355 return -ENOMEM;
da119395
LP
356
357 uid = u->uid;
358
359 if (!dbus_message_append_args(
360 m,
361 DBUS_TYPE_UINT32, &uid,
362 DBUS_TYPE_OBJECT_PATH, &p,
363 DBUS_TYPE_INVALID))
4654e558 364 return -ENOMEM;
da119395
LP
365
366 if (!dbus_connection_send(u->manager->bus, m, NULL))
4654e558 367 return -ENOMEM;
da119395 368
4654e558 369 return 0;
da119395 370}
9418f147
LP
371
372int user_send_changed(User *u, const char *properties) {
ce0fc5f5 373 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
ce0fc5f5 374 _cleanup_free_ char *p = NULL;
9418f147
LP
375
376 assert(u);
377
ed18b08b
LP
378 if (!u->started)
379 return 0;
380
9418f147
LP
381 p = user_bus_path(u);
382 if (!p)
383 return -ENOMEM;
384
385 m = bus_properties_changed_new(p, "org.freedesktop.login1.User", properties);
386 if (!m)
4654e558 387 return -ENOMEM;
9418f147
LP
388
389 if (!dbus_connection_send(u->manager->bus, m, NULL))
4654e558 390 return -ENOMEM;
9418f147 391
4654e558 392 return 0;
9418f147 393}