]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-user-dbus.c
logind: Make more use of cleanup macros
[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" \
b69d29ce 41 " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
a185c5aa 42 " <property name=\"Service\" 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" \
3f49d45a
LP
49 " </interface>\n" \
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;
68 char *p = NULL;
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) ||
89 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) {
90 free(p);
91 return -ENOMEM;
92 }
93
94 free(p);
95
96 if (!dbus_message_iter_close_container(i, &sub))
97 return -ENOMEM;
98
99 return 0;
100}
101
102static int bus_user_append_state(DBusMessageIter *i, const char *property, void *data) {
103 User *u = data;
104 const char *state;
105
106 assert(i);
107 assert(property);
108 assert(u);
109
110 state = user_state_to_string(user_get_state(u));
111
112 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
113 return -ENOMEM;
114
115 return 0;
116}
117
118static int bus_user_append_sessions(DBusMessageIter *i, const char *property, void *data) {
119 DBusMessageIter sub, sub2;
120 User *u = data;
121 Session *session;
122
123 assert(i);
124 assert(property);
125 assert(u);
126
dec15e92 127 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(so)", &sub))
3f49d45a
LP
128 return -ENOMEM;
129
130 LIST_FOREACH(sessions_by_user, session, u->sessions) {
131 char *p;
132
133 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
134 return -ENOMEM;
135
136 p = session_bus_path(session);
137 if (!p)
138 return -ENOMEM;
139
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)) {
142 free(p);
143 return -ENOMEM;
144 }
145
146 free(p);
147
148 if (!dbus_message_iter_close_container(&sub, &sub2))
149 return -ENOMEM;
150 }
151
152 if (!dbus_message_iter_close_container(i, &sub))
153 return -ENOMEM;
154
155 return 0;
156}
157
a185c5aa
LP
158static int bus_user_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
159 User *u = data;
77527da0 160 dbus_bool_t b;
a185c5aa
LP
161
162 assert(i);
163 assert(property);
164 assert(u);
165
77527da0
LP
166 b = user_get_idle_hint(u, NULL) > 0;
167
a185c5aa
LP
168 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
169 return -ENOMEM;
170
171 return 0;
172}
173
174static int bus_user_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
175 User *u = data;
176 dual_timestamp t;
177 uint64_t k;
178
179 assert(i);
180 assert(property);
181 assert(u);
182
183 user_get_idle_hint(u, &t);
184 k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
185
186 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &k))
187 return -ENOMEM;
188
189 return 0;
190}
191
9e13dbae 192static int bus_user_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
b69d29ce
LP
193 User *u = data;
194 char *t;
195 int r;
196 bool success;
197
198 assert(i);
199 assert(property);
200 assert(u);
201
202 r = cg_join_spec(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &t);
203 if (r < 0)
204 return r;
205
206 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
207 free(t);
208
209 return success ? 0 : -ENOMEM;
210}
211
3f49d45a
LP
212static int get_user_for_path(Manager *m, const char *path, User **_u) {
213 User *u;
214 unsigned long lu;
215 int r;
216
217 assert(m);
218 assert(path);
219 assert(_u);
220
221 if (!startswith(path, "/org/freedesktop/login1/user/"))
222 return -EINVAL;
223
224 r = safe_atolu(path + 29, &lu);
225 if (r < 0)
226 return r;
227
228 u = hashmap_get(m->users, ULONG_TO_PTR(lu));
229 if (!u)
230 return -ENOENT;
231
232 *_u = u;
233 return 0;
234}
235
d200735e
MS
236static 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 },
b69d29ce 243 { "DefaultControlGroup", bus_user_append_default_cgroup, "s", 0 },
d200735e
MS
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 },
251 { NULL, }
252};
253
3f49d45a
LP
254static DBusHandlerResult user_message_dispatch(
255 User *u,
256 DBusConnection *connection,
257 DBusMessage *message) {
258
a185c5aa 259 DBusError error;
ce0fc5f5 260 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
a185c5aa
LP
261 int r;
262
3f49d45a
LP
263 assert(u);
264 assert(connection);
265 assert(message);
266
a185c5aa
LP
267 if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Terminate")) {
268
269 r = user_stop(u);
270 if (r < 0)
271 return bus_send_error_reply(connection, message, NULL, r);
272
273 reply = dbus_message_new_method_return(message);
274 if (!reply)
275 goto oom;
de07ab16
LP
276 } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Kill")) {
277 int32_t signo;
278
279 if (!dbus_message_get_args(
280 message,
281 &error,
282 DBUS_TYPE_INT32, &signo,
283 DBUS_TYPE_INVALID))
284 return bus_send_error_reply(connection, message, &error, -EINVAL);
285
286 if (signo <= 0 || signo >= _NSIG)
287 return bus_send_error_reply(connection, message, &error, -EINVAL);
288
289 r = user_kill(u, signo);
290 if (r < 0)
291 return bus_send_error_reply(connection, message, NULL, r);
292
293 reply = dbus_message_new_method_return(message);
294 if (!reply)
295 goto oom;
296
d200735e
MS
297 } else {
298 const BusBoundProperties bps[] = {
299 { "org.freedesktop.login1.User", bus_login_user_properties, u },
300 { NULL, }
301 };
302
303 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
304 }
a185c5aa
LP
305
306 if (reply) {
c6a818c8 307 if (!bus_maybe_send_reply(connection, message, reply))
a185c5aa 308 goto oom;
a185c5aa
LP
309 }
310
311 return DBUS_HANDLER_RESULT_HANDLED;
312
313oom:
a185c5aa
LP
314 dbus_error_free(&error);
315
316 return DBUS_HANDLER_RESULT_NEED_MEMORY;
3f49d45a
LP
317}
318
319static DBusHandlerResult user_message_handler(
320 DBusConnection *connection,
321 DBusMessage *message,
322 void *userdata) {
323
324 Manager *m = userdata;
325 User *u;
326 int r;
327
328 r = get_user_for_path(m, dbus_message_get_path(message), &u);
329 if (r < 0) {
330
331 if (r == -ENOMEM)
332 return DBUS_HANDLER_RESULT_NEED_MEMORY;
333
334 if (r == -ENOENT) {
335 DBusError e;
336
337 dbus_error_init(&e);
338 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown user");
339 return bus_send_error_reply(connection, message, &e, r);
340 }
341
342 return bus_send_error_reply(connection, message, NULL, r);
343 }
344
345 return user_message_dispatch(u, connection, message);
346}
347
348const DBusObjectPathVTable bus_user_vtable = {
349 .message_function = user_message_handler
350};
351
352char *user_bus_path(User *u) {
353 char *s;
354
355 assert(u);
356
357 if (asprintf(&s, "/org/freedesktop/login1/user/%llu", (unsigned long long) u->uid) < 0)
358 return NULL;
359
360 return s;
361}
da119395
LP
362
363int user_send_signal(User *u, bool new_user) {
ce0fc5f5 364 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
da119395 365 int r = -ENOMEM;
ce0fc5f5 366 _cleanup_free_ char *p = NULL;
da119395
LP
367 uint32_t uid;
368
369 assert(u);
370
371 m = dbus_message_new_signal("/org/freedesktop/login1",
372 "org.freedesktop.login1.Manager",
373 new_user ? "UserNew" : "UserRemoved");
374
375 if (!m)
376 return -ENOMEM;
377
378 p = user_bus_path(u);
379 if (!p)
380 goto finish;
381
382 uid = u->uid;
383
384 if (!dbus_message_append_args(
385 m,
386 DBUS_TYPE_UINT32, &uid,
387 DBUS_TYPE_OBJECT_PATH, &p,
388 DBUS_TYPE_INVALID))
389 goto finish;
390
391 if (!dbus_connection_send(u->manager->bus, m, NULL))
392 goto finish;
393
394 r = 0;
395
396finish:
da119395
LP
397 return r;
398}
9418f147
LP
399
400int user_send_changed(User *u, const char *properties) {
ce0fc5f5 401 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
9418f147 402 int r = -ENOMEM;
ce0fc5f5 403 _cleanup_free_ char *p = NULL;
9418f147
LP
404
405 assert(u);
406
ed18b08b
LP
407 if (!u->started)
408 return 0;
409
9418f147
LP
410 p = user_bus_path(u);
411 if (!p)
412 return -ENOMEM;
413
414 m = bus_properties_changed_new(p, "org.freedesktop.login1.User", properties);
415 if (!m)
416 goto finish;
417
418 if (!dbus_connection_send(u->manager->bus, m, NULL))
419 goto finish;
420
421 r = 0;
422
423finish:
9418f147
LP
424 return r;
425}