]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-user-dbus.c
license: LGPL-2.1+ -> LGPL-2.1-or-later
[thirdparty/systemd.git] / src / login / logind-user-dbus.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4
5 #include "alloc-util.h"
6 #include "bus-get-properties.h"
7 #include "bus-polkit.h"
8 #include "bus-util.h"
9 #include "format-util.h"
10 #include "logind-dbus.h"
11 #include "logind-session-dbus.h"
12 #include "logind-user-dbus.h"
13 #include "logind-user.h"
14 #include "logind.h"
15 #include "missing_capability.h"
16 #include "signal-util.h"
17 #include "strv.h"
18 #include "user-util.h"
19
20 static int property_get_uid(
21 sd_bus *bus,
22 const char *path,
23 const char *interface,
24 const char *property,
25 sd_bus_message *reply,
26 void *userdata,
27 sd_bus_error *error) {
28
29 User *u = userdata;
30
31 assert(bus);
32 assert(reply);
33 assert(u);
34
35 return sd_bus_message_append(reply, "u", (uint32_t) u->user_record->uid);
36 }
37
38 static int property_get_gid(
39 sd_bus *bus,
40 const char *path,
41 const char *interface,
42 const char *property,
43 sd_bus_message *reply,
44 void *userdata,
45 sd_bus_error *error) {
46
47 User *u = userdata;
48
49 assert(bus);
50 assert(reply);
51 assert(u);
52
53 return sd_bus_message_append(reply, "u", (uint32_t) u->user_record->gid);
54 }
55
56 static int property_get_name(
57 sd_bus *bus,
58 const char *path,
59 const char *interface,
60 const char *property,
61 sd_bus_message *reply,
62 void *userdata,
63 sd_bus_error *error) {
64
65 User *u = userdata;
66
67 assert(bus);
68 assert(reply);
69 assert(u);
70
71 return sd_bus_message_append(reply, "s", u->user_record->user_name);
72 }
73
74 static BUS_DEFINE_PROPERTY_GET2(property_get_state, "s", User, user_get_state, user_state_to_string);
75
76 static int property_get_display(
77 sd_bus *bus,
78 const char *path,
79 const char *interface,
80 const char *property,
81 sd_bus_message *reply,
82 void *userdata,
83 sd_bus_error *error) {
84
85 _cleanup_free_ char *p = NULL;
86 User *u = userdata;
87
88 assert(bus);
89 assert(reply);
90 assert(u);
91
92 p = u->display ? session_bus_path(u->display) : strdup("/");
93 if (!p)
94 return -ENOMEM;
95
96 return sd_bus_message_append(reply, "(so)", u->display ? u->display->id : "", p);
97 }
98
99 static int property_get_sessions(
100 sd_bus *bus,
101 const char *path,
102 const char *interface,
103 const char *property,
104 sd_bus_message *reply,
105 void *userdata,
106 sd_bus_error *error) {
107
108 User *u = userdata;
109 Session *session;
110 int r;
111
112 assert(bus);
113 assert(reply);
114 assert(u);
115
116 r = sd_bus_message_open_container(reply, 'a', "(so)");
117 if (r < 0)
118 return r;
119
120 LIST_FOREACH(sessions_by_user, session, u->sessions) {
121 _cleanup_free_ char *p = NULL;
122
123 p = session_bus_path(session);
124 if (!p)
125 return -ENOMEM;
126
127 r = sd_bus_message_append(reply, "(so)", session->id, p);
128 if (r < 0)
129 return r;
130
131 }
132
133 return sd_bus_message_close_container(reply);
134 }
135
136 static int property_get_idle_hint(
137 sd_bus *bus,
138 const char *path,
139 const char *interface,
140 const char *property,
141 sd_bus_message *reply,
142 void *userdata,
143 sd_bus_error *error) {
144
145 User *u = userdata;
146
147 assert(bus);
148 assert(reply);
149 assert(u);
150
151 return sd_bus_message_append(reply, "b", user_get_idle_hint(u, NULL) > 0);
152 }
153
154 static int property_get_idle_since_hint(
155 sd_bus *bus,
156 const char *path,
157 const char *interface,
158 const char *property,
159 sd_bus_message *reply,
160 void *userdata,
161 sd_bus_error *error) {
162
163 User *u = userdata;
164 dual_timestamp t = DUAL_TIMESTAMP_NULL;
165 uint64_t k;
166
167 assert(bus);
168 assert(reply);
169 assert(u);
170
171 (void) user_get_idle_hint(u, &t);
172 k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
173
174 return sd_bus_message_append(reply, "t", k);
175 }
176
177 static int property_get_linger(
178 sd_bus *bus,
179 const char *path,
180 const char *interface,
181 const char *property,
182 sd_bus_message *reply,
183 void *userdata,
184 sd_bus_error *error) {
185
186 User *u = userdata;
187 int r;
188
189 assert(bus);
190 assert(reply);
191 assert(u);
192
193 r = user_check_linger_file(u);
194
195 return sd_bus_message_append(reply, "b", r > 0);
196 }
197
198 int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
199 User *u = userdata;
200 int r;
201
202 assert(message);
203 assert(u);
204
205 r = bus_verify_polkit_async(
206 message,
207 CAP_KILL,
208 "org.freedesktop.login1.manage",
209 NULL,
210 false,
211 u->user_record->uid,
212 &u->manager->polkit_registry,
213 error);
214 if (r < 0)
215 return r;
216 if (r == 0)
217 return 1; /* Will call us back */
218
219 r = user_stop(u, /* force */ true);
220 if (r < 0)
221 return r;
222
223 return sd_bus_reply_method_return(message, NULL);
224 }
225
226 int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) {
227 User *u = userdata;
228 int32_t signo;
229 int r;
230
231 assert(message);
232 assert(u);
233
234 r = bus_verify_polkit_async(
235 message,
236 CAP_KILL,
237 "org.freedesktop.login1.manage",
238 NULL,
239 false,
240 u->user_record->uid,
241 &u->manager->polkit_registry,
242 error);
243 if (r < 0)
244 return r;
245 if (r == 0)
246 return 1; /* Will call us back */
247
248 r = sd_bus_message_read(message, "i", &signo);
249 if (r < 0)
250 return r;
251
252 if (!SIGNAL_VALID(signo))
253 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
254
255 r = user_kill(u, signo);
256 if (r < 0)
257 return r;
258
259 return sd_bus_reply_method_return(message, NULL);
260 }
261
262 static int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
263 Manager *m = userdata;
264 uid_t uid;
265 User *user;
266 int r;
267
268 assert(bus);
269 assert(path);
270 assert(interface);
271 assert(found);
272 assert(m);
273
274 if (streq(path, "/org/freedesktop/login1/user/self")) {
275 sd_bus_message *message;
276
277 message = sd_bus_get_current_message(bus);
278
279 r = manager_get_user_from_creds(m, message, UID_INVALID, error, &user);
280 if (r == -ENXIO) {
281 sd_bus_error_free(error);
282 return 0;
283 }
284 if (r < 0)
285 return r;
286 } else {
287 const char *p;
288
289 p = startswith(path, "/org/freedesktop/login1/user/_");
290 if (!p)
291 return 0;
292
293 r = parse_uid(p, &uid);
294 if (r < 0)
295 return 0;
296
297 user = hashmap_get(m->users, UID_TO_PTR(uid));
298 if (!user)
299 return 0;
300 }
301
302 *found = user;
303 return 1;
304 }
305
306 char *user_bus_path(User *u) {
307 char *s;
308
309 assert(u);
310
311 if (asprintf(&s, "/org/freedesktop/login1/user/_"UID_FMT, u->user_record->uid) < 0)
312 return NULL;
313
314 return s;
315 }
316
317 static int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
318 _cleanup_strv_free_ char **l = NULL;
319 sd_bus_message *message;
320 Manager *m = userdata;
321 User *user;
322 int r;
323
324 assert(bus);
325 assert(path);
326 assert(nodes);
327
328 HASHMAP_FOREACH(user, m->users) {
329 char *p;
330
331 p = user_bus_path(user);
332 if (!p)
333 return -ENOMEM;
334
335 r = strv_consume(&l, p);
336 if (r < 0)
337 return r;
338 }
339
340 message = sd_bus_get_current_message(bus);
341 if (message) {
342 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
343
344 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
345 if (r >= 0) {
346 uid_t uid;
347
348 r = sd_bus_creds_get_owner_uid(creds, &uid);
349 if (r >= 0) {
350 user = hashmap_get(m->users, UID_TO_PTR(uid));
351 if (user) {
352 r = strv_extend(&l, "/org/freedesktop/login1/user/self");
353 if (r < 0)
354 return r;
355 }
356 }
357 }
358 }
359
360 *nodes = TAKE_PTR(l);
361
362 return 1;
363 }
364
365 static const sd_bus_vtable user_vtable[] = {
366 SD_BUS_VTABLE_START(0),
367
368 SD_BUS_PROPERTY("UID", "u", property_get_uid, 0, SD_BUS_VTABLE_PROPERTY_CONST),
369 SD_BUS_PROPERTY("GID", "u", property_get_gid, 0, SD_BUS_VTABLE_PROPERTY_CONST),
370 SD_BUS_PROPERTY("Name", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST),
371 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(User, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
372 SD_BUS_PROPERTY("RuntimePath", "s", NULL, offsetof(User, runtime_path), SD_BUS_VTABLE_PROPERTY_CONST),
373 SD_BUS_PROPERTY("Service", "s", NULL, offsetof(User, service), SD_BUS_VTABLE_PROPERTY_CONST),
374 SD_BUS_PROPERTY("Slice", "s", NULL, offsetof(User, slice), SD_BUS_VTABLE_PROPERTY_CONST),
375 SD_BUS_PROPERTY("Display", "(so)", property_get_display, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
376 SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
377 SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0),
378 SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
379 SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
380 SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
381 SD_BUS_PROPERTY("Linger", "b", property_get_linger, 0, 0),
382
383 SD_BUS_METHOD("Terminate", NULL, NULL, bus_user_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
384 SD_BUS_METHOD_WITH_NAMES("Kill",
385 "i",
386 SD_BUS_PARAM(signal_number),
387 NULL,,
388 bus_user_method_kill,
389 SD_BUS_VTABLE_UNPRIVILEGED),
390
391 SD_BUS_VTABLE_END
392 };
393
394 const BusObjectImplementation user_object = {
395 "/org/freedesktop/login1/user",
396 "org.freedesktop.login1.User",
397 .fallback_vtables = BUS_FALLBACK_VTABLES({user_vtable, user_object_find}),
398 .node_enumerator = user_node_enumerator,
399 };
400
401 int user_send_signal(User *u, bool new_user) {
402 _cleanup_free_ char *p = NULL;
403
404 assert(u);
405
406 p = user_bus_path(u);
407 if (!p)
408 return -ENOMEM;
409
410 return sd_bus_emit_signal(
411 u->manager->bus,
412 "/org/freedesktop/login1",
413 "org.freedesktop.login1.Manager",
414 new_user ? "UserNew" : "UserRemoved",
415 "uo", (uint32_t) u->user_record->uid, p);
416 }
417
418 int user_send_changed(User *u, const char *properties, ...) {
419 _cleanup_free_ char *p = NULL;
420 char **l;
421
422 assert(u);
423
424 if (!u->started)
425 return 0;
426
427 p = user_bus_path(u);
428 if (!p)
429 return -ENOMEM;
430
431 l = strv_from_stdarg_alloca(properties);
432
433 return sd_bus_emit_properties_changed_strv(u->manager->bus, p, "org.freedesktop.login1.User", l);
434 }