]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
40ca29a1 | 2 | /*** |
40ca29a1 | 3 | Copyright 2013 Lennart Poettering |
40ca29a1 LP |
4 | ***/ |
5 | ||
a8fbdf54 TA |
6 | #include <errno.h> |
7 | #include <fcntl.h> | |
8 | #include <inttypes.h> | |
9 | #include <stdio.h> | |
10 | #include <stdlib.h> | |
11 | #include <string.h> | |
12 | #include <sys/ioctl.h> | |
13 | #include <sys/resource.h> | |
0c842e0a | 14 | #include <sys/socket.h> |
a8fbdf54 | 15 | #include <unistd.h> |
0c842e0a | 16 | |
a8fbdf54 | 17 | #include "sd-bus-protocol.h" |
4f5dd394 | 18 | #include "sd-bus.h" |
ebd011d9 LP |
19 | #include "sd-daemon.h" |
20 | #include "sd-event.h" | |
a8fbdf54 | 21 | #include "sd-id128.h" |
d53d9474 | 22 | |
b5efdb8a | 23 | #include "alloc-util.h" |
d53d9474 LP |
24 | #include "bus-internal.h" |
25 | #include "bus-label.h" | |
26 | #include "bus-message.h" | |
3ffd4af2 | 27 | #include "bus-util.h" |
52610b02 | 28 | #include "cap-list.h" |
21771f33 | 29 | #include "cgroup-util.h" |
40ca29a1 | 30 | #include "def.h" |
4f5dd394 | 31 | #include "escape.h" |
3ffd4af2 | 32 | #include "fd-util.h" |
2bba9a57 | 33 | #include "missing.h" |
21771f33 | 34 | #include "mount-util.h" |
73186d53 | 35 | #include "nsflags.h" |
6bedfcbb | 36 | #include "parse-util.h" |
ee104e11 | 37 | #include "proc-cmdline.h" |
78f22b97 | 38 | #include "rlimit-util.h" |
15a5e950 | 39 | #include "stdio-util.h" |
d53d9474 | 40 | #include "strv.h" |
ee104e11 | 41 | #include "user-util.h" |
40ca29a1 | 42 | |
19070062 | 43 | static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { |
40ca29a1 LP |
44 | sd_event *e = userdata; |
45 | ||
40ca29a1 LP |
46 | assert(m); |
47 | assert(e); | |
48 | ||
19070062 | 49 | sd_bus_close(sd_bus_message_get_bus(m)); |
6203e07a | 50 | sd_event_exit(e, 0); |
b27adf35 | 51 | |
40ca29a1 LP |
52 | return 1; |
53 | } | |
54 | ||
6203e07a | 55 | int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) { |
75152a4d | 56 | const char *match; |
11846aa7 | 57 | const char *unique; |
40ca29a1 LP |
58 | int r; |
59 | ||
60 | assert(e); | |
61 | assert(bus); | |
62 | assert(name); | |
63 | ||
6203e07a LP |
64 | /* We unregister the name here and then wait for the |
65 | * NameOwnerChanged signal for this event to arrive before we | |
66 | * quit. We do this in order to make sure that any queued | |
67 | * requests are still processed before we really exit. */ | |
68 | ||
11846aa7 | 69 | r = sd_bus_get_unique_name(bus, &unique); |
40ca29a1 LP |
70 | if (r < 0) |
71 | return r; | |
72 | ||
75152a4d LP |
73 | match = strjoina( |
74 | "sender='org.freedesktop.DBus'," | |
75 | "type='signal'," | |
76 | "interface='org.freedesktop.DBus'," | |
77 | "member='NameOwnerChanged'," | |
78 | "path='/org/freedesktop/DBus'," | |
79 | "arg0='", name, "',", | |
80 | "arg1='", unique, "',", | |
81 | "arg2=''"); | |
82 | ||
83 | r = sd_bus_add_match_async(bus, NULL, match, name_owner_change_callback, NULL, e); | |
40ca29a1 LP |
84 | if (r < 0) |
85 | return r; | |
86 | ||
75152a4d | 87 | r = sd_bus_release_name_async(bus, NULL, name, NULL, NULL); |
40ca29a1 LP |
88 | if (r < 0) |
89 | return r; | |
90 | ||
40ca29a1 LP |
91 | return 0; |
92 | } | |
93 | ||
37224a5f LP |
94 | int bus_event_loop_with_idle( |
95 | sd_event *e, | |
96 | sd_bus *bus, | |
97 | const char *name, | |
98 | usec_t timeout, | |
99 | check_idle_t check_idle, | |
100 | void *userdata) { | |
40ca29a1 | 101 | bool exiting = false; |
6203e07a | 102 | int r, code; |
40ca29a1 LP |
103 | |
104 | assert(e); | |
105 | assert(bus); | |
106 | assert(name); | |
107 | ||
108 | for (;;) { | |
37224a5f LP |
109 | bool idle; |
110 | ||
40ca29a1 LP |
111 | r = sd_event_get_state(e); |
112 | if (r < 0) | |
113 | return r; | |
40ca29a1 LP |
114 | if (r == SD_EVENT_FINISHED) |
115 | break; | |
116 | ||
37224a5f LP |
117 | if (check_idle) |
118 | idle = check_idle(userdata); | |
119 | else | |
120 | idle = true; | |
121 | ||
122 | r = sd_event_run(e, exiting || !idle ? (uint64_t) -1 : timeout); | |
40ca29a1 LP |
123 | if (r < 0) |
124 | return r; | |
125 | ||
a8ba6cd1 | 126 | if (r == 0 && !exiting && idle) { |
b27adf35 LP |
127 | |
128 | r = sd_bus_try_close(bus); | |
129 | if (r == -EBUSY) | |
130 | continue; | |
131 | ||
430e21c2 LP |
132 | /* Fallback for dbus1 connections: we |
133 | * unregister the name and wait for the | |
134 | * response to come through for it */ | |
15411c0c | 135 | if (r == -EOPNOTSUPP) { |
430e21c2 LP |
136 | |
137 | /* Inform the service manager that we | |
138 | * are going down, so that it will | |
139 | * queue all further start requests, | |
140 | * instead of assuming we are already | |
141 | * running. */ | |
142 | sd_notify(false, "STOPPING=1"); | |
b27adf35 LP |
143 | |
144 | r = bus_async_unregister_and_exit(e, bus, name); | |
145 | if (r < 0) | |
146 | return r; | |
147 | ||
148 | exiting = true; | |
149 | continue; | |
150 | } | |
151 | ||
40ca29a1 LP |
152 | if (r < 0) |
153 | return r; | |
154 | ||
b27adf35 LP |
155 | sd_event_exit(e, 0); |
156 | break; | |
40ca29a1 LP |
157 | } |
158 | } | |
159 | ||
6203e07a LP |
160 | r = sd_event_get_exit_code(e, &code); |
161 | if (r < 0) | |
162 | return r; | |
163 | ||
164 | return code; | |
40ca29a1 LP |
165 | } |
166 | ||
5fd38859 | 167 | int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) { |
4afd3348 | 168 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *rep = NULL; |
5fd38859 DH |
169 | int r, has_owner = 0; |
170 | ||
171 | assert(c); | |
172 | assert(name); | |
173 | ||
174 | r = sd_bus_call_method(c, | |
175 | "org.freedesktop.DBus", | |
176 | "/org/freedesktop/dbus", | |
177 | "org.freedesktop.DBus", | |
178 | "NameHasOwner", | |
179 | error, | |
180 | &rep, | |
181 | "s", | |
182 | name); | |
183 | if (r < 0) | |
184 | return r; | |
185 | ||
186 | r = sd_bus_message_read_basic(rep, 'b', &has_owner); | |
187 | if (r < 0) | |
188 | return sd_bus_error_set_errno(error, r); | |
189 | ||
190 | return has_owner; | |
191 | } | |
192 | ||
c529695e | 193 | static int check_good_user(sd_bus_message *m, uid_t good_user) { |
4afd3348 | 194 | _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; |
c529695e LP |
195 | uid_t sender_uid; |
196 | int r; | |
197 | ||
198 | assert(m); | |
199 | ||
200 | if (good_user == UID_INVALID) | |
201 | return 0; | |
202 | ||
203 | r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds); | |
204 | if (r < 0) | |
205 | return r; | |
206 | ||
0f514420 LP |
207 | /* Don't trust augmented credentials for authorization */ |
208 | assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM); | |
209 | ||
c529695e LP |
210 | r = sd_bus_creds_get_euid(creds, &sender_uid); |
211 | if (r < 0) | |
212 | return r; | |
213 | ||
214 | return sender_uid == good_user; | |
215 | } | |
216 | ||
ceb24229 | 217 | int bus_test_polkit( |
f3885791 | 218 | sd_bus_message *call, |
def9a7aa | 219 | int capability, |
40ca29a1 | 220 | const char *action, |
403ed0e5 | 221 | const char **details, |
c529695e | 222 | uid_t good_user, |
40ca29a1 LP |
223 | bool *_challenge, |
224 | sd_bus_error *e) { | |
225 | ||
40ca29a1 LP |
226 | int r; |
227 | ||
f3885791 | 228 | assert(call); |
40ca29a1 LP |
229 | assert(action); |
230 | ||
ceb24229 LP |
231 | /* Tests non-interactively! */ |
232 | ||
c529695e LP |
233 | r = check_good_user(call, good_user); |
234 | if (r != 0) | |
235 | return r; | |
236 | ||
f3885791 | 237 | r = sd_bus_query_sender_privilege(call, capability); |
5b12334d LP |
238 | if (r < 0) |
239 | return r; | |
f3885791 | 240 | else if (r > 0) |
40ca29a1 | 241 | return 1; |
349cc4a5 | 242 | #if ENABLE_POLKIT |
40ca29a1 | 243 | else { |
4afd3348 LP |
244 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL; |
245 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
ceb24229 | 246 | int authorized = false, challenge = false; |
403ed0e5 | 247 | const char *sender, **k, **v; |
5b12334d | 248 | |
f3885791 | 249 | sender = sd_bus_message_get_sender(call); |
5b12334d LP |
250 | if (!sender) |
251 | return -EBADMSG; | |
40ca29a1 | 252 | |
403ed0e5 | 253 | r = sd_bus_message_new_method_call( |
f3885791 | 254 | call->bus, |
403ed0e5 | 255 | &request, |
40ca29a1 LP |
256 | "org.freedesktop.PolicyKit1", |
257 | "/org/freedesktop/PolicyKit1/Authority", | |
258 | "org.freedesktop.PolicyKit1.Authority", | |
403ed0e5 MC |
259 | "CheckAuthorization"); |
260 | if (r < 0) | |
261 | return r; | |
262 | ||
263 | r = sd_bus_message_append( | |
264 | request, | |
265 | "(sa{sv})s", | |
40ca29a1 | 266 | "system-bus-name", 1, "name", "s", sender, |
403ed0e5 MC |
267 | action); |
268 | if (r < 0) | |
269 | return r; | |
270 | ||
271 | r = sd_bus_message_open_container(request, 'a', "{ss}"); | |
272 | if (r < 0) | |
273 | return r; | |
40ca29a1 | 274 | |
403ed0e5 MC |
275 | STRV_FOREACH_PAIR(k, v, details) { |
276 | r = sd_bus_message_append(request, "{ss}", *k, *v); | |
277 | if (r < 0) | |
278 | return r; | |
279 | } | |
280 | ||
281 | r = sd_bus_message_close_container(request); | |
282 | if (r < 0) | |
283 | return r; | |
284 | ||
285 | r = sd_bus_message_append(request, "us", 0, NULL); | |
286 | if (r < 0) | |
287 | return r; | |
288 | ||
289 | r = sd_bus_call(call->bus, request, 0, e, &reply); | |
40ca29a1 LP |
290 | if (r < 0) { |
291 | /* Treat no PK available as access denied */ | |
292 | if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) { | |
293 | sd_bus_error_free(e); | |
294 | return -EACCES; | |
295 | } | |
296 | ||
297 | return r; | |
298 | } | |
299 | ||
313333b4 | 300 | r = sd_bus_message_enter_container(reply, 'r', "bba{ss}"); |
2b49a470 TA |
301 | if (r < 0) |
302 | return r; | |
303 | ||
304 | r = sd_bus_message_read(reply, "bb", &authorized, &challenge); | |
305 | if (r < 0) | |
306 | return r; | |
40ca29a1 LP |
307 | |
308 | if (authorized) | |
309 | return 1; | |
310 | ||
311 | if (_challenge) { | |
312 | *_challenge = challenge; | |
313 | return 0; | |
314 | } | |
315 | } | |
316 | #endif | |
317 | ||
318 | return -EACCES; | |
319 | } | |
320 | ||
349cc4a5 | 321 | #if ENABLE_POLKIT |
40ca29a1 LP |
322 | |
323 | typedef struct AsyncPolkitQuery { | |
324 | sd_bus_message *request, *reply; | |
325 | sd_bus_message_handler_t callback; | |
326 | void *userdata; | |
19befb2d | 327 | sd_bus_slot *slot; |
ebcf1f97 | 328 | Hashmap *registry; |
40ca29a1 LP |
329 | } AsyncPolkitQuery; |
330 | ||
19befb2d | 331 | static void async_polkit_query_free(AsyncPolkitQuery *q) { |
ebcf1f97 LP |
332 | |
333 | if (!q) | |
334 | return; | |
335 | ||
19befb2d | 336 | sd_bus_slot_unref(q->slot); |
ebcf1f97 LP |
337 | |
338 | if (q->registry && q->request) | |
339 | hashmap_remove(q->registry, q->request); | |
340 | ||
341 | sd_bus_message_unref(q->request); | |
342 | sd_bus_message_unref(q->reply); | |
343 | ||
344 | free(q); | |
345 | } | |
346 | ||
19070062 | 347 | static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) { |
4afd3348 | 348 | _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; |
ebcf1f97 | 349 | AsyncPolkitQuery *q = userdata; |
40ca29a1 LP |
350 | int r; |
351 | ||
40ca29a1 LP |
352 | assert(reply); |
353 | assert(q); | |
354 | ||
19befb2d | 355 | q->slot = sd_bus_slot_unref(q->slot); |
40ca29a1 | 356 | q->reply = sd_bus_message_ref(reply); |
40ca29a1 | 357 | |
ebcf1f97 LP |
358 | r = sd_bus_message_rewind(q->request, true); |
359 | if (r < 0) { | |
360 | r = sd_bus_reply_method_errno(q->request, r, NULL); | |
361 | goto finish; | |
362 | } | |
40ca29a1 | 363 | |
19070062 | 364 | r = q->callback(q->request, q->userdata, &error_buffer); |
ebcf1f97 | 365 | r = bus_maybe_reply_error(q->request, r, &error_buffer); |
40ca29a1 | 366 | |
ebcf1f97 | 367 | finish: |
19befb2d LP |
368 | async_polkit_query_free(q); |
369 | ||
ebcf1f97 | 370 | return r; |
40ca29a1 LP |
371 | } |
372 | ||
373 | #endif | |
374 | ||
375 | int bus_verify_polkit_async( | |
f3885791 | 376 | sd_bus_message *call, |
def9a7aa | 377 | int capability, |
40ca29a1 | 378 | const char *action, |
403ed0e5 | 379 | const char **details, |
40ca29a1 | 380 | bool interactive, |
c529695e | 381 | uid_t good_user, |
f3885791 LP |
382 | Hashmap **registry, |
383 | sd_bus_error *error) { | |
40ca29a1 | 384 | |
349cc4a5 | 385 | #if ENABLE_POLKIT |
4afd3348 | 386 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL; |
40ca29a1 | 387 | AsyncPolkitQuery *q; |
403ed0e5 | 388 | const char *sender, **k, **v; |
f3885791 LP |
389 | sd_bus_message_handler_t callback; |
390 | void *userdata; | |
b911eb15 | 391 | int c; |
5b12334d | 392 | #endif |
40ca29a1 LP |
393 | int r; |
394 | ||
f3885791 | 395 | assert(call); |
40ca29a1 | 396 | assert(action); |
f3885791 | 397 | assert(registry); |
40ca29a1 | 398 | |
c529695e LP |
399 | r = check_good_user(call, good_user); |
400 | if (r != 0) | |
401 | return r; | |
402 | ||
349cc4a5 | 403 | #if ENABLE_POLKIT |
f3885791 | 404 | q = hashmap_get(*registry, call); |
40ca29a1 | 405 | if (q) { |
102d8f81 | 406 | int authorized, challenge; |
40ca29a1 LP |
407 | |
408 | /* This is the second invocation of this function, and | |
409 | * there's already a response from polkit, let's | |
410 | * process it */ | |
411 | assert(q->reply); | |
412 | ||
413 | if (sd_bus_message_is_method_error(q->reply, NULL)) { | |
414 | const sd_bus_error *e; | |
415 | ||
ebcf1f97 | 416 | /* Copy error from polkit reply */ |
40ca29a1 LP |
417 | e = sd_bus_message_get_error(q->reply); |
418 | sd_bus_error_copy(error, e); | |
40ca29a1 | 419 | |
ebcf1f97 LP |
420 | /* Treat no PK available as access denied */ |
421 | if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) | |
422 | return -EACCES; | |
423 | ||
5958d089 | 424 | return -sd_bus_error_get_errno(e); |
40ca29a1 LP |
425 | } |
426 | ||
427 | r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}"); | |
428 | if (r >= 0) | |
429 | r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge); | |
430 | ||
40ca29a1 LP |
431 | if (r < 0) |
432 | return r; | |
433 | ||
434 | if (authorized) | |
435 | return 1; | |
436 | ||
f2288cc6 LP |
437 | if (challenge) |
438 | return sd_bus_error_set(error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required."); | |
439 | ||
40ca29a1 LP |
440 | return -EACCES; |
441 | } | |
442 | #endif | |
443 | ||
f3885791 | 444 | r = sd_bus_query_sender_privilege(call, capability); |
5b12334d LP |
445 | if (r < 0) |
446 | return r; | |
f3885791 | 447 | else if (r > 0) |
40ca29a1 | 448 | return 1; |
5b12334d | 449 | |
349cc4a5 | 450 | #if ENABLE_POLKIT |
f3885791 LP |
451 | if (sd_bus_get_current_message(call->bus) != call) |
452 | return -EINVAL; | |
453 | ||
454 | callback = sd_bus_get_current_handler(call->bus); | |
455 | if (!callback) | |
456 | return -EINVAL; | |
457 | ||
458 | userdata = sd_bus_get_current_userdata(call->bus); | |
459 | ||
460 | sender = sd_bus_message_get_sender(call); | |
5b12334d LP |
461 | if (!sender) |
462 | return -EBADMSG; | |
40ca29a1 | 463 | |
b911eb15 LP |
464 | c = sd_bus_message_get_allow_interactive_authorization(call); |
465 | if (c < 0) | |
466 | return c; | |
467 | if (c > 0) | |
468 | interactive = true; | |
469 | ||
d5099efc | 470 | r = hashmap_ensure_allocated(registry, NULL); |
40ca29a1 LP |
471 | if (r < 0) |
472 | return r; | |
473 | ||
474 | r = sd_bus_message_new_method_call( | |
f3885791 | 475 | call->bus, |
151b9b96 | 476 | &pk, |
40ca29a1 LP |
477 | "org.freedesktop.PolicyKit1", |
478 | "/org/freedesktop/PolicyKit1/Authority", | |
479 | "org.freedesktop.PolicyKit1.Authority", | |
151b9b96 | 480 | "CheckAuthorization"); |
40ca29a1 LP |
481 | if (r < 0) |
482 | return r; | |
483 | ||
484 | r = sd_bus_message_append( | |
485 | pk, | |
403ed0e5 | 486 | "(sa{sv})s", |
40ca29a1 | 487 | "system-bus-name", 1, "name", "s", sender, |
403ed0e5 MC |
488 | action); |
489 | if (r < 0) | |
490 | return r; | |
491 | ||
492 | r = sd_bus_message_open_container(pk, 'a', "{ss}"); | |
493 | if (r < 0) | |
494 | return r; | |
495 | ||
496 | STRV_FOREACH_PAIR(k, v, details) { | |
497 | r = sd_bus_message_append(pk, "{ss}", *k, *v); | |
498 | if (r < 0) | |
499 | return r; | |
500 | } | |
501 | ||
502 | r = sd_bus_message_close_container(pk); | |
503 | if (r < 0) | |
504 | return r; | |
505 | ||
506 | r = sd_bus_message_append(pk, "us", !!interactive, NULL); | |
40ca29a1 LP |
507 | if (r < 0) |
508 | return r; | |
509 | ||
510 | q = new0(AsyncPolkitQuery, 1); | |
511 | if (!q) | |
512 | return -ENOMEM; | |
513 | ||
f3885791 | 514 | q->request = sd_bus_message_ref(call); |
40ca29a1 LP |
515 | q->callback = callback; |
516 | q->userdata = userdata; | |
517 | ||
f3885791 | 518 | r = hashmap_put(*registry, call, q); |
40ca29a1 | 519 | if (r < 0) { |
19befb2d | 520 | async_polkit_query_free(q); |
40ca29a1 LP |
521 | return r; |
522 | } | |
523 | ||
ebcf1f97 LP |
524 | q->registry = *registry; |
525 | ||
f3885791 | 526 | r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0); |
ebcf1f97 | 527 | if (r < 0) { |
19befb2d | 528 | async_polkit_query_free(q); |
40ca29a1 | 529 | return r; |
ebcf1f97 | 530 | } |
40ca29a1 LP |
531 | |
532 | return 0; | |
533 | #endif | |
534 | ||
535 | return -EACCES; | |
536 | } | |
537 | ||
36e34057 | 538 | void bus_verify_polkit_async_registry_free(Hashmap *registry) { |
349cc4a5 | 539 | #if ENABLE_POLKIT |
224b0e7a | 540 | hashmap_free_with_destructor(registry, async_polkit_query_free); |
40ca29a1 LP |
541 | #endif |
542 | } | |
0c842e0a | 543 | |
718db961 | 544 | int bus_check_peercred(sd_bus *c) { |
0c842e0a | 545 | struct ucred ucred; |
3e641e36 | 546 | int fd, r; |
0c842e0a TG |
547 | |
548 | assert(c); | |
549 | ||
550 | fd = sd_bus_get_fd(c); | |
0f8bd8de LP |
551 | if (fd < 0) |
552 | return fd; | |
0c842e0a | 553 | |
3e641e36 LP |
554 | r = getpeercred(fd, &ucred); |
555 | if (r < 0) | |
556 | return r; | |
0c842e0a TG |
557 | |
558 | if (ucred.uid != 0 && ucred.uid != geteuid()) | |
559 | return -EPERM; | |
560 | ||
561 | return 1; | |
562 | } | |
563 | ||
266f3e26 | 564 | int bus_connect_system_systemd(sd_bus **_bus) { |
4afd3348 | 565 | _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; |
0c842e0a | 566 | int r; |
0c842e0a TG |
567 | |
568 | assert(_bus); | |
569 | ||
0f8bd8de | 570 | if (geteuid() != 0) |
266f3e26 | 571 | return sd_bus_default_system(_bus); |
a1da8583 | 572 | |
a132bef0 ZJS |
573 | /* If we are root then let's talk directly to the system |
574 | * instance, instead of going via the bus */ | |
a6aa8912 LP |
575 | |
576 | r = sd_bus_new(&bus); | |
0f8bd8de LP |
577 | if (r < 0) |
578 | return r; | |
a1da8583 | 579 | |
a6aa8912 LP |
580 | r = sd_bus_set_address(bus, "unix:path=/run/systemd/private"); |
581 | if (r < 0) | |
582 | return r; | |
583 | ||
584 | r = sd_bus_start(bus); | |
585 | if (r < 0) | |
266f3e26 | 586 | return sd_bus_default_system(_bus); |
a6aa8912 | 587 | |
0f8bd8de | 588 | r = bus_check_peercred(bus); |
a1da8583 TG |
589 | if (r < 0) |
590 | return r; | |
591 | ||
1cc6c93a | 592 | *_bus = TAKE_PTR(bus); |
0f8bd8de | 593 | |
a1da8583 TG |
594 | return 0; |
595 | } | |
596 | ||
266f3e26 | 597 | int bus_connect_user_systemd(sd_bus **_bus) { |
4afd3348 | 598 | _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; |
a6aa8912 | 599 | _cleanup_free_ char *ee = NULL; |
41dd15e4 LP |
600 | const char *e; |
601 | int r; | |
602 | ||
41dd15e4 LP |
603 | assert(_bus); |
604 | ||
605 | e = secure_getenv("XDG_RUNTIME_DIR"); | |
537220d9 | 606 | if (!e) |
266f3e26 | 607 | return sd_bus_default_user(_bus); |
537220d9 | 608 | |
a6aa8912 LP |
609 | ee = bus_address_escape(e); |
610 | if (!ee) | |
537220d9 | 611 | return -ENOMEM; |
41dd15e4 LP |
612 | |
613 | r = sd_bus_new(&bus); | |
614 | if (r < 0) | |
615 | return r; | |
616 | ||
605405c6 | 617 | bus->address = strjoin("unix:path=", ee, "/systemd/private"); |
a6aa8912 LP |
618 | if (!bus->address) |
619 | return -ENOMEM; | |
41dd15e4 LP |
620 | |
621 | r = sd_bus_start(bus); | |
622 | if (r < 0) | |
266f3e26 | 623 | return sd_bus_default_user(_bus); |
41dd15e4 LP |
624 | |
625 | r = bus_check_peercred(bus); | |
626 | if (r < 0) | |
627 | return r; | |
628 | ||
1cc6c93a | 629 | *_bus = TAKE_PTR(bus); |
41dd15e4 LP |
630 | |
631 | return 0; | |
632 | } | |
633 | ||
4f9a9105 ZJS |
634 | #define print_property(name, fmt, ...) \ |
635 | do { \ | |
636 | if (value) \ | |
637 | printf(fmt "\n", __VA_ARGS__); \ | |
638 | else \ | |
639 | printf("%s=" fmt "\n", name, __VA_ARGS__); \ | |
508f63b4 | 640 | } while (0) |
4f9a9105 | 641 | |
07636114 | 642 | int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all) { |
a1da8583 TG |
643 | char type; |
644 | const char *contents; | |
9f6eb1cd | 645 | int r; |
a1da8583 TG |
646 | |
647 | assert(name); | |
07636114 | 648 | assert(m); |
a1da8583 | 649 | |
07636114 | 650 | r = sd_bus_message_peek_type(m, &type, &contents); |
9f6eb1cd KS |
651 | if (r < 0) |
652 | return r; | |
a1da8583 TG |
653 | |
654 | switch (type) { | |
655 | ||
656 | case SD_BUS_TYPE_STRING: { | |
657 | const char *s; | |
9f6eb1cd | 658 | |
07636114 | 659 | r = sd_bus_message_read_basic(m, type, &s); |
9f6eb1cd KS |
660 | if (r < 0) |
661 | return r; | |
a1da8583 | 662 | |
5993d46a ZJS |
663 | if (all || !isempty(s)) { |
664 | bool good; | |
665 | ||
666 | /* This property has a single value, so we need to take | |
667 | * care not to print a new line, everything else is OK. */ | |
668 | good = !strchr(s, '\n'); | |
669 | print_property(name, "%s", good ? s : "[unprintable]"); | |
670 | } | |
a1da8583 TG |
671 | |
672 | return 1; | |
673 | } | |
674 | ||
675 | case SD_BUS_TYPE_BOOLEAN: { | |
c2fa048c | 676 | int b; |
a1da8583 | 677 | |
07636114 | 678 | r = sd_bus_message_read_basic(m, type, &b); |
9f6eb1cd KS |
679 | if (r < 0) |
680 | return r; | |
681 | ||
4f9a9105 | 682 | print_property(name, "%s", yes_no(b)); |
a1da8583 TG |
683 | |
684 | return 1; | |
685 | } | |
686 | ||
687 | case SD_BUS_TYPE_UINT64: { | |
688 | uint64_t u; | |
689 | ||
07636114 | 690 | r = sd_bus_message_read_basic(m, type, &u); |
9f6eb1cd KS |
691 | if (r < 0) |
692 | return r; | |
a1da8583 TG |
693 | |
694 | /* Yes, heuristics! But we can change this check | |
695 | * should it turn out to not be sufficient */ | |
696 | ||
ead0adb1 YW |
697 | if (endswith(name, "Timestamp") || |
698 | STR_IN_SET(name, "NextElapseUSecRealtime", "LastTriggerUSec", "TimeUSec", "RTCTimeUSec")) { | |
4d9685be ZJS |
699 | char timestamp[FORMAT_TIMESTAMP_MAX]; |
700 | const char *t; | |
a1da8583 TG |
701 | |
702 | t = format_timestamp(timestamp, sizeof(timestamp), u); | |
703 | if (t || all) | |
4f9a9105 | 704 | print_property(name, "%s", strempty(t)); |
a1da8583 TG |
705 | |
706 | } else if (strstr(name, "USec")) { | |
707 | char timespan[FORMAT_TIMESPAN_MAX]; | |
708 | ||
4f9a9105 | 709 | print_property(name, "%s", format_timespan(timespan, sizeof(timespan), u, 0)); |
73186d53 DH |
710 | } else if (streq(name, "RestrictNamespaces")) { |
711 | _cleanup_free_ char *s = NULL; | |
ae978b9f | 712 | const char *result; |
73186d53 DH |
713 | |
714 | if ((u & NAMESPACE_FLAGS_ALL) == 0) | |
715 | result = "yes"; | |
716 | else if ((u & NAMESPACE_FLAGS_ALL) == NAMESPACE_FLAGS_ALL) | |
717 | result = "no"; | |
718 | else { | |
86c2a9f1 | 719 | r = namespace_flags_to_string(u, &s); |
73186d53 DH |
720 | if (r < 0) |
721 | return r; | |
722 | ||
723 | result = s; | |
724 | } | |
725 | ||
726 | print_property(name, "%s", result); | |
21771f33 YW |
727 | |
728 | } else if (streq(name, "MountFlags")) { | |
ae978b9f | 729 | const char *result; |
21771f33 YW |
730 | |
731 | result = mount_propagation_flags_to_string(u); | |
732 | if (!result) | |
733 | return -EINVAL; | |
734 | ||
735 | print_property(name, "%s", result); | |
736 | ||
52610b02 YW |
737 | } else if (STR_IN_SET(name, "CapabilityBoundingSet", "AmbientCapabilities")) { |
738 | _cleanup_free_ char *s = NULL; | |
739 | ||
740 | r = capability_set_to_string_alloc(u, &s); | |
741 | if (r < 0) | |
742 | return r; | |
743 | ||
744 | print_property(name, "%s", s); | |
745 | ||
21771f33 YW |
746 | } else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) || |
747 | (STR_IN_SET(name, "CPUShares", "StartupCPUShares") && u == CGROUP_CPU_SHARES_INVALID) || | |
748 | (STR_IN_SET(name, "BlockIOWeight", "StartupBlockIOWeight") && u == CGROUP_BLKIO_WEIGHT_INVALID) || | |
749 | (STR_IN_SET(name, "MemoryCurrent", "TasksCurrent") && u == (uint64_t) -1) || | |
750 | (endswith(name, "NSec") && u == (uint64_t) -1)) | |
751 | ||
752 | print_property(name, "%s", "[not set]"); | |
753 | ||
754 | else if ((STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) || | |
755 | (STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == (uint64_t) -1) || | |
756 | (startswith(name, "Limit") && u == (uint64_t) -1) || | |
757 | (startswith(name, "DefaultLimit") && u == (uint64_t) -1)) | |
758 | ||
759 | print_property(name, "%s", "infinity"); | |
760 | else | |
4f9a9105 | 761 | print_property(name, "%"PRIu64, u); |
a1da8583 TG |
762 | |
763 | return 1; | |
764 | } | |
765 | ||
d7161865 DM |
766 | case SD_BUS_TYPE_INT64: { |
767 | int64_t i; | |
768 | ||
07636114 | 769 | r = sd_bus_message_read_basic(m, type, &i); |
d7161865 DM |
770 | if (r < 0) |
771 | return r; | |
772 | ||
4f9a9105 | 773 | print_property(name, "%"PRIi64, i); |
d7161865 DM |
774 | |
775 | return 1; | |
776 | } | |
777 | ||
a1da8583 TG |
778 | case SD_BUS_TYPE_UINT32: { |
779 | uint32_t u; | |
780 | ||
07636114 | 781 | r = sd_bus_message_read_basic(m, type, &u); |
9f6eb1cd KS |
782 | if (r < 0) |
783 | return r; | |
a1da8583 TG |
784 | |
785 | if (strstr(name, "UMask") || strstr(name, "Mode")) | |
4f9a9105 | 786 | print_property(name, "%04o", u); |
c533658a ZJS |
787 | else if (streq(name, "UID")) { |
788 | if (u == UID_INVALID) | |
789 | print_property(name, "%s", "[not set]"); | |
790 | else | |
791 | print_property(name, "%"PRIu32, u); | |
792 | } else if (streq(name, "GID")) { | |
793 | if (u == GID_INVALID) | |
794 | print_property(name, "%s", "[not set]"); | |
795 | else | |
796 | print_property(name, "%"PRIu32, u); | |
797 | } else | |
4f9a9105 | 798 | print_property(name, "%"PRIu32, u); |
a1da8583 TG |
799 | |
800 | return 1; | |
801 | } | |
802 | ||
803 | case SD_BUS_TYPE_INT32: { | |
804 | int32_t i; | |
805 | ||
07636114 | 806 | r = sd_bus_message_read_basic(m, type, &i); |
9f6eb1cd KS |
807 | if (r < 0) |
808 | return r; | |
a1da8583 | 809 | |
4f9a9105 | 810 | print_property(name, "%"PRIi32, i); |
a1da8583 TG |
811 | return 1; |
812 | } | |
813 | ||
814 | case SD_BUS_TYPE_DOUBLE: { | |
815 | double d; | |
816 | ||
07636114 | 817 | r = sd_bus_message_read_basic(m, type, &d); |
9f6eb1cd KS |
818 | if (r < 0) |
819 | return r; | |
a1da8583 | 820 | |
4f9a9105 | 821 | print_property(name, "%g", d); |
a1da8583 TG |
822 | return 1; |
823 | } | |
824 | ||
825 | case SD_BUS_TYPE_ARRAY: | |
a1da8583 | 826 | if (streq(contents, "s")) { |
261afec5 MAP |
827 | bool first = true; |
828 | const char *str; | |
a1da8583 | 829 | |
07636114 | 830 | r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, contents); |
9f6eb1cd KS |
831 | if (r < 0) |
832 | return r; | |
833 | ||
07636114 | 834 | while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &str)) > 0) { |
5993d46a ZJS |
835 | bool good; |
836 | ||
4f9a9105 | 837 | if (first && !value) |
261afec5 MAP |
838 | printf("%s=", name); |
839 | ||
ed88a900 | 840 | /* This property has multiple space-separated values, so |
ead6bd25 | 841 | * neither spaces nor newlines can be allowed in a value. */ |
5993d46a ZJS |
842 | good = str[strcspn(str, " \n")] == '\0'; |
843 | ||
844 | printf("%s%s", first ? "" : " ", good ? str : "[unprintable]"); | |
261afec5 MAP |
845 | |
846 | first = false; | |
847 | } | |
9f6eb1cd KS |
848 | if (r < 0) |
849 | return r; | |
a1da8583 | 850 | |
4f9a9105 | 851 | if (first && all && !value) |
a1da8583 | 852 | printf("%s=", name); |
261afec5 | 853 | if (!first || all) |
a1da8583 | 854 | puts(""); |
a1da8583 | 855 | |
07636114 | 856 | r = sd_bus_message_exit_container(m); |
9f6eb1cd KS |
857 | if (r < 0) |
858 | return r; | |
a1da8583 TG |
859 | |
860 | return 1; | |
861 | ||
862 | } else if (streq(contents, "y")) { | |
863 | const uint8_t *u; | |
864 | size_t n; | |
865 | ||
07636114 | 866 | r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, (const void**) &u, &n); |
9f6eb1cd KS |
867 | if (r < 0) |
868 | return r; | |
869 | ||
a1da8583 TG |
870 | if (all || n > 0) { |
871 | unsigned int i; | |
872 | ||
4f9a9105 ZJS |
873 | if (!value) |
874 | printf("%s=", name); | |
a1da8583 TG |
875 | |
876 | for (i = 0; i < n; i++) | |
877 | printf("%02x", u[i]); | |
878 | ||
879 | puts(""); | |
880 | } | |
881 | ||
882 | return 1; | |
883 | ||
884 | } else if (streq(contents, "u")) { | |
885 | uint32_t *u; | |
886 | size_t n; | |
887 | ||
07636114 | 888 | r = sd_bus_message_read_array(m, SD_BUS_TYPE_UINT32, (const void**) &u, &n); |
9f6eb1cd KS |
889 | if (r < 0) |
890 | return r; | |
891 | ||
a1da8583 TG |
892 | if (all || n > 0) { |
893 | unsigned int i; | |
894 | ||
4f9a9105 ZJS |
895 | if (!value) |
896 | printf("%s=", name); | |
a1da8583 TG |
897 | |
898 | for (i = 0; i < n; i++) | |
899 | printf("%08x", u[i]); | |
900 | ||
901 | puts(""); | |
902 | } | |
903 | ||
904 | return 1; | |
905 | } | |
906 | ||
907 | break; | |
908 | } | |
909 | ||
910 | return 0; | |
911 | } | |
d21ed1ea | 912 | |
07636114 YW |
913 | int bus_message_print_all_properties( |
914 | sd_bus_message *m, | |
915 | bus_message_print_t func, | |
916 | char **filter, | |
917 | bool value, | |
918 | bool all, | |
919 | Set **found_properties) { | |
ffc06c35 | 920 | |
07636114 | 921 | int r; |
9f6eb1cd | 922 | |
07636114 | 923 | assert(m); |
ffc06c35 | 924 | |
07636114 | 925 | r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}"); |
ffc06c35 KS |
926 | if (r < 0) |
927 | return r; | |
928 | ||
07636114 | 929 | while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { |
ffc06c35 | 930 | const char *name; |
ffc06c35 | 931 | const char *contents; |
ffc06c35 | 932 | |
07636114 | 933 | r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name); |
ffc06c35 KS |
934 | if (r < 0) |
935 | return r; | |
936 | ||
07636114 YW |
937 | if (found_properties) { |
938 | r = set_ensure_allocated(found_properties, &string_hash_ops); | |
939 | if (r < 0) | |
940 | return log_oom(); | |
941 | ||
942 | r = set_put(*found_properties, name); | |
943 | if (r < 0 && r != EEXIST) | |
944 | return log_oom(); | |
945 | } | |
946 | ||
9f6eb1cd | 947 | if (!filter || strv_find(filter, name)) { |
07636114 | 948 | r = sd_bus_message_peek_type(m, NULL, &contents); |
9f6eb1cd KS |
949 | if (r < 0) |
950 | return r; | |
951 | ||
07636114 | 952 | r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents); |
9f6eb1cd KS |
953 | if (r < 0) |
954 | return r; | |
ffc06c35 | 955 | |
07636114 YW |
956 | if (func) |
957 | r = func(name, m, value, all); | |
958 | if (!func || r == 0) | |
959 | r = bus_print_property(name, m, value, all); | |
9f6eb1cd KS |
960 | if (r < 0) |
961 | return r; | |
27e72d6b SP |
962 | if (r == 0) { |
963 | if (all) | |
964 | printf("%s=[unprintable]\n", name); | |
965 | /* skip what we didn't read */ | |
07636114 | 966 | r = sd_bus_message_skip(m, contents); |
27e72d6b SP |
967 | if (r < 0) |
968 | return r; | |
969 | } | |
9f6eb1cd | 970 | |
07636114 | 971 | r = sd_bus_message_exit_container(m); |
9f6eb1cd KS |
972 | if (r < 0) |
973 | return r; | |
974 | } else { | |
07636114 | 975 | r = sd_bus_message_skip(m, "v"); |
9f6eb1cd KS |
976 | if (r < 0) |
977 | return r; | |
978 | } | |
979 | ||
07636114 | 980 | r = sd_bus_message_exit_container(m); |
ffc06c35 KS |
981 | if (r < 0) |
982 | return r; | |
9f6eb1cd KS |
983 | } |
984 | if (r < 0) | |
985 | return r; | |
ffc06c35 | 986 | |
07636114 | 987 | r = sd_bus_message_exit_container(m); |
9f6eb1cd KS |
988 | if (r < 0) |
989 | return r; | |
ffc06c35 | 990 | |
9f6eb1cd KS |
991 | return 0; |
992 | } | |
ffc06c35 | 993 | |
07636114 YW |
994 | int bus_print_all_properties( |
995 | sd_bus *bus, | |
996 | const char *dest, | |
997 | const char *path, | |
998 | bus_message_print_t func, | |
999 | char **filter, | |
1000 | bool value, | |
1001 | bool all, | |
1002 | Set **found_properties) { | |
1003 | ||
1004 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
1005 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
1006 | int r; | |
1007 | ||
1008 | assert(bus); | |
1009 | assert(path); | |
1010 | ||
1011 | r = sd_bus_call_method(bus, | |
1012 | dest, | |
1013 | path, | |
1014 | "org.freedesktop.DBus.Properties", | |
1015 | "GetAll", | |
1016 | &error, | |
1017 | &reply, | |
1018 | "s", ""); | |
1019 | if (r < 0) | |
1020 | return r; | |
1021 | ||
1022 | return bus_message_print_all_properties(reply, func, filter, value, all, found_properties); | |
1023 | } | |
1024 | ||
9f6eb1cd KS |
1025 | int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { |
1026 | sd_id128_t *p = userdata; | |
1027 | const void *v; | |
1028 | size_t n; | |
1029 | int r; | |
ffc06c35 | 1030 | |
9f6eb1cd KS |
1031 | r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n); |
1032 | if (r < 0) | |
1033 | return r; | |
ffc06c35 | 1034 | |
9f6eb1cd KS |
1035 | if (n == 0) |
1036 | *p = SD_ID128_NULL; | |
1037 | else if (n == 16) | |
1038 | memcpy((*p).bytes, v, n); | |
1039 | else | |
1040 | return -EINVAL; | |
ffc06c35 | 1041 | |
9f6eb1cd KS |
1042 | return 0; |
1043 | } | |
ffc06c35 | 1044 | |
a7e4861c | 1045 | static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, unsigned flags, sd_bus_error *error, void *userdata) { |
9f6eb1cd KS |
1046 | char type; |
1047 | int r; | |
ffc06c35 | 1048 | |
9f6eb1cd KS |
1049 | r = sd_bus_message_peek_type(m, &type, NULL); |
1050 | if (r < 0) | |
1051 | return r; | |
ffc06c35 | 1052 | |
9f6eb1cd | 1053 | switch (type) { |
8b3b6f58 | 1054 | |
9f6eb1cd | 1055 | case SD_BUS_TYPE_STRING: { |
f37f8a61 | 1056 | const char **p = userdata; |
8b3b6f58 | 1057 | const char *s; |
ffc06c35 | 1058 | |
9f6eb1cd KS |
1059 | r = sd_bus_message_read_basic(m, type, &s); |
1060 | if (r < 0) | |
8b3b6f58 | 1061 | return r; |
ffc06c35 | 1062 | |
9f6eb1cd | 1063 | if (isempty(s)) |
0b83b8a4 | 1064 | s = NULL; |
ffc06c35 | 1065 | |
a7e4861c | 1066 | if (flags & BUS_MAP_STRDUP) |
f37f8a61 YW |
1067 | return free_and_strdup((char **) userdata, s); |
1068 | ||
1069 | *p = s; | |
1070 | return 0; | |
9f6eb1cd | 1071 | } |
ffc06c35 | 1072 | |
9f6eb1cd | 1073 | case SD_BUS_TYPE_ARRAY: { |
7d6884b6 TA |
1074 | _cleanup_strv_free_ char **l = NULL; |
1075 | char ***p = userdata; | |
ffc06c35 | 1076 | |
9f6eb1cd KS |
1077 | r = bus_message_read_strv_extend(m, &l); |
1078 | if (r < 0) | |
8b3b6f58 | 1079 | return r; |
ffc06c35 | 1080 | |
130d3d22 | 1081 | return strv_free_and_replace(*p, l); |
9f6eb1cd | 1082 | } |
ffc06c35 | 1083 | |
9f6eb1cd | 1084 | case SD_BUS_TYPE_BOOLEAN: { |
a7e4861c | 1085 | int b; |
ffc06c35 | 1086 | |
9f6eb1cd KS |
1087 | r = sd_bus_message_read_basic(m, type, &b); |
1088 | if (r < 0) | |
8b3b6f58 | 1089 | return r; |
ffc06c35 | 1090 | |
a7e4861c | 1091 | if (flags & BUS_MAP_BOOLEAN_AS_BOOL) |
5d904a6a | 1092 | *(bool*) userdata = b; |
a7e4861c | 1093 | else |
5d904a6a | 1094 | *(int*) userdata = b; |
a7e4861c | 1095 | |
8b3b6f58 | 1096 | return 0; |
9f6eb1cd | 1097 | } |
ffc06c35 | 1098 | |
bdf97b8a | 1099 | case SD_BUS_TYPE_INT32: |
9f6eb1cd | 1100 | case SD_BUS_TYPE_UINT32: { |
bdf97b8a | 1101 | uint32_t u, *p = userdata; |
9f6eb1cd KS |
1102 | |
1103 | r = sd_bus_message_read_basic(m, type, &u); | |
1104 | if (r < 0) | |
8b3b6f58 | 1105 | return r; |
ffc06c35 | 1106 | |
9f6eb1cd | 1107 | *p = u; |
8b3b6f58 | 1108 | return 0; |
9f6eb1cd KS |
1109 | } |
1110 | ||
bdf97b8a | 1111 | case SD_BUS_TYPE_INT64: |
9f6eb1cd | 1112 | case SD_BUS_TYPE_UINT64: { |
bdf97b8a | 1113 | uint64_t t, *p = userdata; |
9f6eb1cd KS |
1114 | |
1115 | r = sd_bus_message_read_basic(m, type, &t); | |
1116 | if (r < 0) | |
8b3b6f58 | 1117 | return r; |
ffc06c35 | 1118 | |
9f6eb1cd | 1119 | *p = t; |
8b3b6f58 | 1120 | return 0; |
9f6eb1cd KS |
1121 | } |
1122 | ||
52e045f9 | 1123 | case SD_BUS_TYPE_DOUBLE: { |
8b3b6f58 | 1124 | double d, *p = userdata; |
52e045f9 SS |
1125 | |
1126 | r = sd_bus_message_read_basic(m, type, &d); | |
1127 | if (r < 0) | |
8b3b6f58 | 1128 | return r; |
52e045f9 SS |
1129 | |
1130 | *p = d; | |
8b3b6f58 LP |
1131 | return 0; |
1132 | }} | |
52e045f9 | 1133 | |
8b3b6f58 | 1134 | return -EOPNOTSUPP; |
9f6eb1cd KS |
1135 | } |
1136 | ||
fe506d56 LP |
1137 | int bus_message_map_all_properties( |
1138 | sd_bus_message *m, | |
1139 | const struct bus_properties_map *map, | |
a7e4861c | 1140 | unsigned flags, |
f9e0eefc | 1141 | sd_bus_error *error, |
fe506d56 LP |
1142 | void *userdata) { |
1143 | ||
9f6eb1cd KS |
1144 | int r; |
1145 | ||
aae2b488 | 1146 | assert(m); |
9f6eb1cd KS |
1147 | assert(map); |
1148 | ||
9f6eb1cd KS |
1149 | r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}"); |
1150 | if (r < 0) | |
1151 | return r; | |
1152 | ||
1153 | while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { | |
1154 | const struct bus_properties_map *prop; | |
1155 | const char *member; | |
1156 | const char *contents; | |
1157 | void *v; | |
1158 | unsigned i; | |
1159 | ||
1160 | r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member); | |
ffc06c35 KS |
1161 | if (r < 0) |
1162 | return r; | |
1163 | ||
9f6eb1cd KS |
1164 | for (i = 0, prop = NULL; map[i].member; i++) |
1165 | if (streq(map[i].member, member)) { | |
1166 | prop = &map[i]; | |
1167 | break; | |
1168 | } | |
1169 | ||
1170 | if (prop) { | |
1171 | r = sd_bus_message_peek_type(m, NULL, &contents); | |
1172 | if (r < 0) | |
1173 | return r; | |
1174 | ||
1175 | r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents); | |
1176 | if (r < 0) | |
1177 | return r; | |
1178 | ||
1179 | v = (uint8_t *)userdata + prop->offset; | |
27e72d6b | 1180 | if (map[i].set) |
f9e0eefc | 1181 | r = prop->set(sd_bus_message_get_bus(m), member, m, error, v); |
9f6eb1cd | 1182 | else |
a7e4861c | 1183 | r = map_basic(sd_bus_message_get_bus(m), member, m, flags, error, v); |
2b49a470 TA |
1184 | if (r < 0) |
1185 | return r; | |
9f6eb1cd KS |
1186 | |
1187 | r = sd_bus_message_exit_container(m); | |
1188 | if (r < 0) | |
1189 | return r; | |
1190 | } else { | |
1191 | r = sd_bus_message_skip(m, "v"); | |
1192 | if (r < 0) | |
6c1508b8 | 1193 | return r; |
9f6eb1cd KS |
1194 | } |
1195 | ||
ffc06c35 KS |
1196 | r = sd_bus_message_exit_container(m); |
1197 | if (r < 0) | |
1198 | return r; | |
1199 | } | |
fe506d56 LP |
1200 | if (r < 0) |
1201 | return r; | |
ffc06c35 | 1202 | |
aae2b488 DH |
1203 | return sd_bus_message_exit_container(m); |
1204 | } | |
1205 | ||
fe506d56 LP |
1206 | int bus_message_map_properties_changed( |
1207 | sd_bus_message *m, | |
1208 | const struct bus_properties_map *map, | |
a7e4861c | 1209 | unsigned flags, |
f9e0eefc | 1210 | sd_bus_error *error, |
fe506d56 LP |
1211 | void *userdata) { |
1212 | ||
aae2b488 DH |
1213 | const char *member; |
1214 | int r, invalidated, i; | |
1215 | ||
aae2b488 DH |
1216 | assert(m); |
1217 | assert(map); | |
1218 | ||
a7e4861c | 1219 | r = bus_message_map_all_properties(m, map, flags, error, userdata); |
aae2b488 DH |
1220 | if (r < 0) |
1221 | return r; | |
1222 | ||
1223 | r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s"); | |
1224 | if (r < 0) | |
1225 | return r; | |
1226 | ||
1227 | invalidated = 0; | |
1228 | while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member)) > 0) | |
1229 | for (i = 0; map[i].member; i++) | |
1230 | if (streq(map[i].member, member)) { | |
1231 | ++invalidated; | |
1232 | break; | |
1233 | } | |
fe506d56 LP |
1234 | if (r < 0) |
1235 | return r; | |
aae2b488 DH |
1236 | |
1237 | r = sd_bus_message_exit_container(m); | |
1238 | if (r < 0) | |
1239 | return r; | |
1240 | ||
1241 | return invalidated; | |
1242 | } | |
1243 | ||
fe506d56 LP |
1244 | int bus_map_all_properties( |
1245 | sd_bus *bus, | |
1246 | const char *destination, | |
1247 | const char *path, | |
1248 | const struct bus_properties_map *map, | |
a7e4861c | 1249 | unsigned flags, |
f9e0eefc | 1250 | sd_bus_error *error, |
f37f8a61 | 1251 | sd_bus_message **reply, |
fe506d56 LP |
1252 | void *userdata) { |
1253 | ||
4afd3348 | 1254 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; |
aae2b488 DH |
1255 | int r; |
1256 | ||
1257 | assert(bus); | |
1258 | assert(destination); | |
1259 | assert(path); | |
1260 | assert(map); | |
a7e4861c | 1261 | assert(reply || (flags & BUS_MAP_STRDUP)); |
aae2b488 DH |
1262 | |
1263 | r = sd_bus_call_method( | |
1264 | bus, | |
1265 | destination, | |
1266 | path, | |
1267 | "org.freedesktop.DBus.Properties", | |
1268 | "GetAll", | |
f9e0eefc | 1269 | error, |
aae2b488 DH |
1270 | &m, |
1271 | "s", ""); | |
1272 | if (r < 0) | |
1273 | return r; | |
1274 | ||
a7e4861c | 1275 | r = bus_message_map_all_properties(m, map, flags, error, userdata); |
f37f8a61 YW |
1276 | if (r < 0) |
1277 | return r; | |
1278 | ||
1279 | if (reply) | |
1280 | *reply = sd_bus_message_ref(m); | |
1281 | ||
1282 | return r; | |
ffc06c35 KS |
1283 | } |
1284 | ||
38303498 LP |
1285 | int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **ret) { |
1286 | _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; | |
d21ed1ea LP |
1287 | int r; |
1288 | ||
1289 | assert(transport >= 0); | |
1290 | assert(transport < _BUS_TRANSPORT_MAX); | |
38303498 | 1291 | assert(ret); |
d21ed1ea LP |
1292 | |
1293 | assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL); | |
15411c0c | 1294 | assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP); |
d21ed1ea LP |
1295 | |
1296 | switch (transport) { | |
1297 | ||
1298 | case BUS_TRANSPORT_LOCAL: | |
1299 | if (user) | |
38303498 | 1300 | r = sd_bus_default_user(&bus); |
fb507898 YW |
1301 | else { |
1302 | if (sd_booted() <= 0) { | |
1303 | /* Print a friendly message when the local system is actually not running systemd as PID 1. */ | |
1304 | log_error("System has not been booted with systemd as init system (PID 1). Can't operate."); | |
d21ed1ea | 1305 | |
fb507898 YW |
1306 | return -EHOSTDOWN; |
1307 | } | |
1308 | r = sd_bus_default_system(&bus); | |
1309 | } | |
d21ed1ea LP |
1310 | break; |
1311 | ||
1312 | case BUS_TRANSPORT_REMOTE: | |
38303498 | 1313 | r = sd_bus_open_system_remote(&bus, host); |
41dd15e4 LP |
1314 | break; |
1315 | ||
de33fc62 | 1316 | case BUS_TRANSPORT_MACHINE: |
38303498 | 1317 | r = sd_bus_open_system_machine(&bus, host); |
41dd15e4 LP |
1318 | break; |
1319 | ||
1320 | default: | |
1321 | assert_not_reached("Hmm, unknown transport type."); | |
1322 | } | |
38303498 LP |
1323 | if (r < 0) |
1324 | return r; | |
41dd15e4 | 1325 | |
38303498 LP |
1326 | r = sd_bus_set_exit_on_disconnect(bus, true); |
1327 | if (r < 0) | |
1328 | return r; | |
1329 | ||
1cc6c93a | 1330 | *ret = TAKE_PTR(bus); |
38303498 LP |
1331 | |
1332 | return 0; | |
41dd15e4 LP |
1333 | } |
1334 | ||
266f3e26 | 1335 | int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) { |
41dd15e4 LP |
1336 | int r; |
1337 | ||
1338 | assert(transport >= 0); | |
1339 | assert(transport < _BUS_TRANSPORT_MAX); | |
1340 | assert(bus); | |
1341 | ||
1342 | assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL); | |
15411c0c | 1343 | assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP); |
41dd15e4 LP |
1344 | |
1345 | switch (transport) { | |
1346 | ||
1347 | case BUS_TRANSPORT_LOCAL: | |
1348 | if (user) | |
266f3e26 | 1349 | r = bus_connect_user_systemd(bus); |
fb507898 YW |
1350 | else { |
1351 | if (sd_booted() <= 0) { | |
1352 | /* Print a friendly message when the local system is actually not running systemd as PID 1. */ | |
1353 | log_error("System has not been booted with systemd as init system (PID 1). Can't operate."); | |
41dd15e4 | 1354 | |
fb507898 YW |
1355 | return -EHOSTDOWN; |
1356 | } | |
1357 | r = bus_connect_system_systemd(bus); | |
1358 | } | |
41dd15e4 LP |
1359 | break; |
1360 | ||
1361 | case BUS_TRANSPORT_REMOTE: | |
3db729cb | 1362 | r = sd_bus_open_system_remote(bus, host); |
d21ed1ea LP |
1363 | break; |
1364 | ||
de33fc62 LP |
1365 | case BUS_TRANSPORT_MACHINE: |
1366 | r = sd_bus_open_system_machine(bus, host); | |
d21ed1ea LP |
1367 | break; |
1368 | ||
1369 | default: | |
1370 | assert_not_reached("Hmm, unknown transport type."); | |
1371 | } | |
1372 | ||
1373 | return r; | |
1374 | } | |
e6504030 LP |
1375 | |
1376 | int bus_property_get_bool( | |
1377 | sd_bus *bus, | |
1378 | const char *path, | |
1379 | const char *interface, | |
1380 | const char *property, | |
1381 | sd_bus_message *reply, | |
ebcf1f97 LP |
1382 | void *userdata, |
1383 | sd_bus_error *error) { | |
e6504030 LP |
1384 | |
1385 | int b = *(bool*) userdata; | |
1386 | ||
1387 | return sd_bus_message_append_basic(reply, 'b', &b); | |
1388 | } | |
1389 | ||
43ce15ac JK |
1390 | int bus_property_set_bool( |
1391 | sd_bus *bus, | |
1392 | const char *path, | |
1393 | const char *interface, | |
1394 | const char *property, | |
1395 | sd_bus_message *value, | |
1396 | void *userdata, | |
1397 | sd_bus_error *error) { | |
1398 | ||
1399 | int b, r; | |
1400 | ||
1401 | r = sd_bus_message_read(value, "b", &b); | |
1402 | if (r < 0) | |
1403 | return r; | |
1404 | ||
5d904a6a | 1405 | *(bool*) userdata = b; |
43ce15ac JK |
1406 | return 0; |
1407 | } | |
1408 | ||
766c94ad LP |
1409 | int bus_property_get_id128( |
1410 | sd_bus *bus, | |
1411 | const char *path, | |
1412 | const char *interface, | |
1413 | const char *property, | |
1414 | sd_bus_message *reply, | |
1415 | void *userdata, | |
1416 | sd_bus_error *error) { | |
1417 | ||
1418 | sd_id128_t *id = userdata; | |
1419 | ||
1420 | if (sd_id128_is_null(*id)) /* Add an empty array if the ID is zero */ | |
1421 | return sd_bus_message_append(reply, "ay", 0); | |
1422 | else | |
4b58153d | 1423 | return sd_bus_message_append_array(reply, 'y', id->bytes, 16); |
766c94ad LP |
1424 | } |
1425 | ||
718db961 LP |
1426 | #if __SIZEOF_SIZE_T__ != 8 |
1427 | int bus_property_get_size( | |
e6504030 LP |
1428 | sd_bus *bus, |
1429 | const char *path, | |
1430 | const char *interface, | |
1431 | const char *property, | |
1432 | sd_bus_message *reply, | |
ebcf1f97 LP |
1433 | void *userdata, |
1434 | sd_bus_error *error) { | |
e6504030 | 1435 | |
718db961 | 1436 | uint64_t sz = *(size_t*) userdata; |
e6504030 | 1437 | |
718db961 | 1438 | return sd_bus_message_append_basic(reply, 't', &sz); |
e6504030 | 1439 | } |
718db961 LP |
1440 | #endif |
1441 | ||
1442 | #if __SIZEOF_LONG__ != 8 | |
1443 | int bus_property_get_long( | |
1444 | sd_bus *bus, | |
1445 | const char *path, | |
1446 | const char *interface, | |
1447 | const char *property, | |
1448 | sd_bus_message *reply, | |
ebcf1f97 LP |
1449 | void *userdata, |
1450 | sd_bus_error *error) { | |
718db961 LP |
1451 | |
1452 | int64_t l = *(long*) userdata; | |
1453 | ||
1454 | return sd_bus_message_append_basic(reply, 'x', &l); | |
1455 | } | |
1456 | ||
1457 | int bus_property_get_ulong( | |
1458 | sd_bus *bus, | |
1459 | const char *path, | |
1460 | const char *interface, | |
1461 | const char *property, | |
1462 | sd_bus_message *reply, | |
ebcf1f97 LP |
1463 | void *userdata, |
1464 | sd_bus_error *error) { | |
718db961 LP |
1465 | |
1466 | uint64_t ul = *(unsigned long*) userdata; | |
1467 | ||
1468 | return sd_bus_message_append_basic(reply, 't', &ul); | |
1469 | } | |
1470 | #endif | |
5b30bef8 LP |
1471 | |
1472 | int bus_log_parse_error(int r) { | |
23bbb0de | 1473 | return log_error_errno(r, "Failed to parse bus message: %m"); |
5b30bef8 | 1474 | } |
f459b602 MAP |
1475 | |
1476 | int bus_log_create_error(int r) { | |
23bbb0de | 1477 | return log_error_errno(r, "Failed to create bus message: %m"); |
f459b602 MAP |
1478 | } |
1479 | ||
98a4c30b DH |
1480 | /** |
1481 | * bus_path_encode_unique() - encode unique object path | |
1482 | * @b: bus connection or NULL | |
1483 | * @prefix: object path prefix | |
1484 | * @sender_id: unique-name of client, or NULL | |
1485 | * @external_id: external ID to be chosen by client, or NULL | |
1486 | * @ret_path: storage for encoded object path pointer | |
1487 | * | |
1488 | * Whenever we provide a bus API that allows clients to create and manage | |
1489 | * server-side objects, we need to provide a unique name for these objects. If | |
1490 | * we let the server choose the name, we suffer from a race condition: If a | |
1491 | * client creates an object asynchronously, it cannot destroy that object until | |
1492 | * it received the method reply. It cannot know the name of the new object, | |
1493 | * thus, it cannot destroy it. Furthermore, it enforces a round-trip. | |
1494 | * | |
1495 | * Therefore, many APIs allow the client to choose the unique name for newly | |
1496 | * created objects. There're two problems to solve, though: | |
1497 | * 1) Object names are usually defined via dbus object paths, which are | |
1498 | * usually globally namespaced. Therefore, multiple clients must be able | |
1499 | * to choose unique object names without interference. | |
1500 | * 2) If multiple libraries share the same bus connection, they must be | |
1501 | * able to choose unique object names without interference. | |
1502 | * The first problem is solved easily by prefixing a name with the | |
1503 | * unique-bus-name of a connection. The server side must enforce this and | |
1504 | * reject any other name. The second problem is solved by providing unique | |
1505 | * suffixes from within sd-bus. | |
1506 | * | |
1507 | * This helper allows clients to create unique object-paths. It uses the | |
1508 | * template '/prefix/sender_id/external_id' and returns the new path in | |
1509 | * @ret_path (must be freed by the caller). | |
1510 | * If @sender_id is NULL, the unique-name of @b is used. If @external_id is | |
1511 | * NULL, this function allocates a unique suffix via @b (by requesting a new | |
1512 | * cookie). If both @sender_id and @external_id are given, @b can be passed as | |
1513 | * NULL. | |
1514 | * | |
1515 | * Returns: 0 on success, negative error code on failure. | |
1516 | */ | |
1517 | int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path) { | |
1518 | _cleanup_free_ char *sender_label = NULL, *external_label = NULL; | |
1519 | char external_buf[DECIMAL_STR_MAX(uint64_t)], *p; | |
1520 | int r; | |
1521 | ||
1522 | assert_return(b || (sender_id && external_id), -EINVAL); | |
1523 | assert_return(object_path_is_valid(prefix), -EINVAL); | |
1524 | assert_return(ret_path, -EINVAL); | |
1525 | ||
1526 | if (!sender_id) { | |
1527 | r = sd_bus_get_unique_name(b, &sender_id); | |
1528 | if (r < 0) | |
1529 | return r; | |
1530 | } | |
1531 | ||
1532 | if (!external_id) { | |
1533 | xsprintf(external_buf, "%"PRIu64, ++b->cookie); | |
1534 | external_id = external_buf; | |
1535 | } | |
1536 | ||
1537 | sender_label = bus_label_escape(sender_id); | |
1538 | if (!sender_label) | |
1539 | return -ENOMEM; | |
1540 | ||
1541 | external_label = bus_label_escape(external_id); | |
1542 | if (!external_label) | |
1543 | return -ENOMEM; | |
1544 | ||
605405c6 | 1545 | p = strjoin(prefix, "/", sender_label, "/", external_label); |
98a4c30b DH |
1546 | if (!p) |
1547 | return -ENOMEM; | |
1548 | ||
1549 | *ret_path = p; | |
1550 | return 0; | |
1551 | } | |
1552 | ||
1553 | /** | |
1554 | * bus_path_decode_unique() - decode unique object path | |
1555 | * @path: object path to decode | |
1556 | * @prefix: object path prefix | |
1557 | * @ret_sender: output parameter for sender-id label | |
1558 | * @ret_external: output parameter for external-id label | |
1559 | * | |
1560 | * This does the reverse of bus_path_encode_unique() (see its description for | |
1561 | * details). Both trailing labels, sender-id and external-id, are unescaped and | |
1562 | * returned in the given output parameters (the caller must free them). | |
1563 | * | |
1564 | * Note that this function returns 0 if the path does not match the template | |
1565 | * (see bus_path_encode_unique()), 1 if it matched. | |
1566 | * | |
1567 | * Returns: Negative error code on failure, 0 if the given object path does not | |
1568 | * match the template (return parameters are set to NULL), 1 if it was | |
1569 | * parsed successfully (return parameters contain allocated labels). | |
1570 | */ | |
1571 | int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external) { | |
1572 | const char *p, *q; | |
1573 | char *sender, *external; | |
1574 | ||
1575 | assert(object_path_is_valid(path)); | |
1576 | assert(object_path_is_valid(prefix)); | |
1577 | assert(ret_sender); | |
1578 | assert(ret_external); | |
1579 | ||
1580 | p = object_path_startswith(path, prefix); | |
1581 | if (!p) { | |
1582 | *ret_sender = NULL; | |
1583 | *ret_external = NULL; | |
1584 | return 0; | |
1585 | } | |
1586 | ||
1587 | q = strchr(p, '/'); | |
1588 | if (!q) { | |
1589 | *ret_sender = NULL; | |
1590 | *ret_external = NULL; | |
1591 | return 0; | |
1592 | } | |
1593 | ||
1594 | sender = bus_label_unescape_n(p, q - p); | |
1595 | external = bus_label_unescape(q + 1); | |
1596 | if (!sender || !external) { | |
1597 | free(sender); | |
1598 | free(external); | |
1599 | return -ENOMEM; | |
1600 | } | |
1601 | ||
1602 | *ret_sender = sender; | |
1603 | *ret_external = external; | |
1604 | return 1; | |
1605 | } | |
057171ef | 1606 | |
c9d031c3 EV |
1607 | int bus_property_get_rlimit( |
1608 | sd_bus *bus, | |
1609 | const char *path, | |
1610 | const char *interface, | |
1611 | const char *property, | |
1612 | sd_bus_message *reply, | |
1613 | void *userdata, | |
1614 | sd_bus_error *error) { | |
1615 | ||
6550c24c | 1616 | const char *is_soft; |
c9d031c3 EV |
1617 | struct rlimit *rl; |
1618 | uint64_t u; | |
1619 | rlim_t x; | |
1620 | ||
1621 | assert(bus); | |
1622 | assert(reply); | |
1623 | assert(userdata); | |
1624 | ||
147f6858 | 1625 | is_soft = endswith(property, "Soft"); |
6550c24c | 1626 | |
c9d031c3 EV |
1627 | rl = *(struct rlimit**) userdata; |
1628 | if (rl) | |
147f6858 | 1629 | x = is_soft ? rl->rlim_cur : rl->rlim_max; |
c9d031c3 EV |
1630 | else { |
1631 | struct rlimit buf = {}; | |
6550c24c | 1632 | const char *s, *p; |
c9d031c3 | 1633 | int z; |
147f6858 | 1634 | |
6550c24c | 1635 | /* Chop off "Soft" suffix */ |
147f6858 | 1636 | s = is_soft ? strndupa(property, is_soft - property) : property; |
c9d031c3 | 1637 | |
6550c24c LP |
1638 | /* Skip over any prefix, such as "Default" */ |
1639 | assert_se(p = strstr(s, "Limit")); | |
1640 | ||
1641 | z = rlimit_from_string(p + 5); | |
c9d031c3 EV |
1642 | assert(z >= 0); |
1643 | ||
6550c24c | 1644 | (void) getrlimit(z, &buf); |
147f6858 | 1645 | x = is_soft ? buf.rlim_cur : buf.rlim_max; |
c9d031c3 EV |
1646 | } |
1647 | ||
6550c24c LP |
1648 | /* rlim_t might have different sizes, let's map RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on all |
1649 | * archs */ | |
c9d031c3 EV |
1650 | u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x; |
1651 | ||
1652 | return sd_bus_message_append(reply, "t", u); | |
1653 | } | |
984794ba LP |
1654 | |
1655 | int bus_track_add_name_many(sd_bus_track *t, char **l) { | |
1656 | int r = 0; | |
1657 | char **i; | |
1658 | ||
1659 | assert(t); | |
1660 | ||
1661 | /* Continues adding after failure, and returns the first failure. */ | |
1662 | ||
1663 | STRV_FOREACH(i, l) { | |
1664 | int k; | |
1665 | ||
1666 | k = sd_bus_track_add_name(t, *i); | |
1667 | if (k < 0 && r >= 0) | |
1668 | r = k; | |
1669 | } | |
1670 | ||
1671 | return r; | |
1672 | } | |
d7afd945 | 1673 | |
0ddf50ff | 1674 | int bus_open_system_watch_bind_with_description(sd_bus **ret, const char *description) { |
d7afd945 LP |
1675 | _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; |
1676 | const char *e; | |
1677 | int r; | |
1678 | ||
1679 | assert(ret); | |
1680 | ||
1681 | /* Match like sd_bus_open_system(), but with the "watch_bind" feature and the Connected() signal turned on. */ | |
1682 | ||
1683 | r = sd_bus_new(&bus); | |
1684 | if (r < 0) | |
1685 | return r; | |
1686 | ||
0ddf50ff YW |
1687 | if (description) { |
1688 | r = sd_bus_set_description(bus, description); | |
1689 | if (r < 0) | |
1690 | return r; | |
1691 | } | |
1692 | ||
d7afd945 LP |
1693 | e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS"); |
1694 | if (!e) | |
1695 | e = DEFAULT_SYSTEM_BUS_ADDRESS; | |
1696 | ||
1697 | r = sd_bus_set_address(bus, e); | |
1698 | if (r < 0) | |
1699 | return r; | |
1700 | ||
1701 | r = sd_bus_set_bus_client(bus, true); | |
1702 | if (r < 0) | |
1703 | return r; | |
1704 | ||
1705 | r = sd_bus_set_trusted(bus, true); | |
1706 | if (r < 0) | |
1707 | return r; | |
1708 | ||
1709 | r = sd_bus_negotiate_creds(bus, true, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS); | |
1710 | if (r < 0) | |
1711 | return r; | |
1712 | ||
1713 | r = sd_bus_set_watch_bind(bus, true); | |
1714 | if (r < 0) | |
1715 | return r; | |
1716 | ||
1717 | r = sd_bus_set_connected_signal(bus, true); | |
1718 | if (r < 0) | |
1719 | return r; | |
1720 | ||
1721 | r = sd_bus_start(bus); | |
1722 | if (r < 0) | |
1723 | return r; | |
1724 | ||
1cc6c93a | 1725 | *ret = TAKE_PTR(bus); |
d7afd945 LP |
1726 | |
1727 | return 0; | |
1728 | } | |
906cb2eb YW |
1729 | |
1730 | struct request_name_data { | |
200bc75d ZJS |
1731 | unsigned n_ref; |
1732 | ||
906cb2eb YW |
1733 | const char *name; |
1734 | uint64_t flags; | |
1735 | void *userdata; | |
1736 | }; | |
1737 | ||
200bc75d ZJS |
1738 | static void request_name_destroy_callback(void *userdata) { |
1739 | struct request_name_data *data = userdata; | |
1740 | ||
1741 | assert(data); | |
1742 | assert(data->n_ref > 0); | |
1743 | ||
1744 | log_info("%s n_ref=%u", __func__, data->n_ref); | |
1745 | ||
1746 | data->n_ref--; | |
1747 | if (data->n_ref == 0) | |
1748 | free(data); | |
1749 | } | |
1750 | ||
906cb2eb | 1751 | static int reload_dbus_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { |
200bc75d | 1752 | struct request_name_data *data = userdata; |
906cb2eb YW |
1753 | const sd_bus_error *e; |
1754 | int r; | |
1755 | ||
906cb2eb YW |
1756 | assert(data); |
1757 | assert(data->name); | |
200bc75d | 1758 | assert(data->n_ref > 0); |
906cb2eb YW |
1759 | |
1760 | e = sd_bus_message_get_error(m); | |
1761 | if (e) { | |
1762 | log_error_errno(sd_bus_error_get_errno(e), "Failed to reload DBus configuration: %s", e->message); | |
1763 | return 1; | |
1764 | } | |
1765 | ||
1766 | /* Here, use the default request name handler to avoid an infinite loop of reloading and requesting. */ | |
1767 | r = sd_bus_request_name_async(sd_bus_message_get_bus(m), NULL, data->name, data->flags, NULL, data->userdata); | |
1768 | if (r < 0) | |
1769 | log_error_errno(r, "Failed to request name: %m"); | |
1770 | ||
1771 | return 1; | |
1772 | } | |
1773 | ||
1774 | static int request_name_handler_may_reload_dbus(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { | |
200bc75d | 1775 | struct request_name_data *data = userdata; |
906cb2eb YW |
1776 | uint32_t ret; |
1777 | int r; | |
1778 | ||
1779 | assert(m); | |
200bc75d | 1780 | assert(data); |
906cb2eb YW |
1781 | |
1782 | if (sd_bus_message_is_method_error(m, NULL)) { | |
1783 | const sd_bus_error *e = sd_bus_message_get_error(m); | |
200bc75d | 1784 | _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL; |
906cb2eb YW |
1785 | |
1786 | if (!sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED)) { | |
1787 | log_debug_errno(sd_bus_error_get_errno(e), | |
1788 | "Unable to request name, failing connection: %s", | |
1789 | e->message); | |
1790 | ||
1791 | bus_enter_closing(sd_bus_message_get_bus(m)); | |
1792 | return 1; | |
1793 | } | |
1794 | ||
1795 | log_debug_errno(sd_bus_error_get_errno(e), | |
200bc75d | 1796 | "Unable to request name, will retry after reloading DBus configuration: %s", |
906cb2eb YW |
1797 | e->message); |
1798 | ||
1799 | /* If systemd-timesyncd.service enables DynamicUser= and dbus.service | |
1800 | * started before the dynamic user is realized, then the DBus policy | |
1801 | * about timesyncd has not been enabled yet. So, let's try to reload | |
200bc75d | 1802 | * DBus configuration, and after that request the name again. Note that it |
906cb2eb YW |
1803 | * seems that no privileges are necessary to call the following method. */ |
1804 | ||
1805 | r = sd_bus_call_method_async( | |
1806 | sd_bus_message_get_bus(m), | |
200bc75d | 1807 | &slot, |
906cb2eb YW |
1808 | "org.freedesktop.DBus", |
1809 | "/org/freedesktop/DBus", | |
1810 | "org.freedesktop.DBus", | |
1811 | "ReloadConfig", | |
1812 | reload_dbus_handler, | |
200bc75d | 1813 | data, NULL); |
906cb2eb YW |
1814 | if (r < 0) { |
1815 | log_error_errno(r, "Failed to reload DBus configuration: %m"); | |
1816 | bus_enter_closing(sd_bus_message_get_bus(m)); | |
1817 | return 1; | |
1818 | } | |
1819 | ||
200bc75d ZJS |
1820 | data->n_ref ++; |
1821 | assert_se(sd_bus_slot_set_destroy_callback(slot, request_name_destroy_callback) >= 0); | |
1822 | ||
1823 | r = sd_bus_slot_set_floating(slot, true); | |
1824 | if (r < 0) | |
1825 | return r; | |
1826 | ||
906cb2eb YW |
1827 | return 1; |
1828 | } | |
1829 | ||
1830 | r = sd_bus_message_read(m, "u", &ret); | |
1831 | if (r < 0) | |
1832 | return r; | |
1833 | ||
1834 | switch (ret) { | |
1835 | ||
1836 | case BUS_NAME_ALREADY_OWNER: | |
1837 | log_debug("Already owner of requested service name, ignoring."); | |
1838 | return 1; | |
1839 | ||
1840 | case BUS_NAME_IN_QUEUE: | |
1841 | log_debug("In queue for requested service name."); | |
1842 | return 1; | |
1843 | ||
1844 | case BUS_NAME_PRIMARY_OWNER: | |
1845 | log_debug("Successfully acquired requested service name."); | |
1846 | return 1; | |
1847 | ||
1848 | case BUS_NAME_EXISTS: | |
1849 | log_debug("Requested service name already owned, failing connection."); | |
1850 | bus_enter_closing(sd_bus_message_get_bus(m)); | |
1851 | return 1; | |
1852 | } | |
1853 | ||
1854 | log_debug("Unexpected response from RequestName(), failing connection."); | |
1855 | bus_enter_closing(sd_bus_message_get_bus(m)); | |
1856 | return 1; | |
1857 | } | |
1858 | ||
1859 | int bus_request_name_async_may_reload_dbus(sd_bus *bus, sd_bus_slot **ret_slot, const char *name, uint64_t flags, void *userdata) { | |
200bc75d ZJS |
1860 | _cleanup_free_ struct request_name_data *data = NULL; |
1861 | _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL; | |
1862 | int r; | |
906cb2eb | 1863 | |
83b6c1a6 | 1864 | data = new(struct request_name_data, 1); |
906cb2eb YW |
1865 | if (!data) |
1866 | return -ENOMEM; | |
1867 | ||
83b6c1a6 | 1868 | *data = (struct request_name_data) { |
200bc75d | 1869 | .n_ref = 1, |
83b6c1a6 ZJS |
1870 | .name = name, |
1871 | .flags = flags, | |
1872 | .userdata = userdata, | |
1873 | }; | |
906cb2eb | 1874 | |
200bc75d ZJS |
1875 | r = sd_bus_request_name_async(bus, &slot, name, flags, request_name_handler_may_reload_dbus, data); |
1876 | if (r < 0) | |
1877 | return r; | |
1878 | ||
1879 | assert_se(sd_bus_slot_set_destroy_callback(slot, request_name_destroy_callback) >= 0); | |
1880 | TAKE_PTR(data); | |
1881 | ||
1882 | if (ret_slot) | |
1883 | *ret_slot = TAKE_PTR(slot); | |
1884 | else { | |
1885 | r = sd_bus_slot_set_floating(slot, true); | |
1886 | if (r < 0) | |
1887 | return r; | |
1888 | } | |
1889 | ||
1890 | return 0; | |
906cb2eb | 1891 | } |
19017acb LP |
1892 | |
1893 | int bus_reply_pair_array(sd_bus_message *m, char **l) { | |
1894 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
1895 | char **k, **v; | |
1896 | int r; | |
1897 | ||
1898 | assert(m); | |
1899 | ||
1900 | /* Reply to the specified message with a message containing a dictionary put together from the specified | |
1901 | * strv */ | |
1902 | ||
1903 | r = sd_bus_message_new_method_return(m, &reply); | |
1904 | if (r < 0) | |
1905 | return r; | |
1906 | ||
1907 | r = sd_bus_message_open_container(reply, 'a', "{ss}"); | |
1908 | if (r < 0) | |
1909 | return r; | |
1910 | ||
1911 | STRV_FOREACH_PAIR(k, v, l) { | |
1912 | r = sd_bus_message_append(reply, "{ss}", *k, *v); | |
1913 | if (r < 0) | |
1914 | return r; | |
1915 | } | |
1916 | ||
1917 | r = sd_bus_message_close_container(reply); | |
1918 | if (r < 0) | |
1919 | return r; | |
1920 | ||
1921 | return sd_bus_send(NULL, reply, NULL); | |
1922 | } |