]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-convenience.c
47da334b93318f16bda46d458f81f5dfcdc3927f
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-convenience.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 Copyright 2013 Lennart Poettering
4 ***/
5
6 #include "bus-internal.h"
7 #include "bus-message.h"
8 #include "bus-signature.h"
9 #include "bus-type.h"
10 #include "bus-util.h"
11 #include "string-util.h"
12
13 _public_ int sd_bus_emit_signal(
14 sd_bus *bus,
15 const char *path,
16 const char *interface,
17 const char *member,
18 const char *types, ...) {
19
20 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
21 int r;
22
23 assert_return(bus, -EINVAL);
24 assert_return(bus = bus_resolve(bus), -ENOPKG);
25 assert_return(!bus_pid_changed(bus), -ECHILD);
26
27 if (!BUS_IS_OPEN(bus->state))
28 return -ENOTCONN;
29
30 r = sd_bus_message_new_signal(bus, &m, path, interface, member);
31 if (r < 0)
32 return r;
33
34 if (!isempty(types)) {
35 va_list ap;
36
37 va_start(ap, types);
38 r = sd_bus_message_appendv(m, types, ap);
39 va_end(ap);
40 if (r < 0)
41 return r;
42 }
43
44 return sd_bus_send(bus, m, NULL);
45 }
46
47 _public_ int sd_bus_call_method_async(
48 sd_bus *bus,
49 sd_bus_slot **slot,
50 const char *destination,
51 const char *path,
52 const char *interface,
53 const char *member,
54 sd_bus_message_handler_t callback,
55 void *userdata,
56 const char *types, ...) {
57
58 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
59 int r;
60
61 assert_return(bus, -EINVAL);
62 assert_return(bus = bus_resolve(bus), -ENOPKG);
63 assert_return(!bus_pid_changed(bus), -ECHILD);
64
65 if (!BUS_IS_OPEN(bus->state))
66 return -ENOTCONN;
67
68 r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member);
69 if (r < 0)
70 return r;
71
72 if (!isempty(types)) {
73 va_list ap;
74
75 va_start(ap, types);
76 r = sd_bus_message_appendv(m, types, ap);
77 va_end(ap);
78 if (r < 0)
79 return r;
80 }
81
82 return sd_bus_call_async(bus, slot, m, callback, userdata, 0);
83 }
84
85 _public_ int sd_bus_call_method(
86 sd_bus *bus,
87 const char *destination,
88 const char *path,
89 const char *interface,
90 const char *member,
91 sd_bus_error *error,
92 sd_bus_message **reply,
93 const char *types, ...) {
94
95 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
96 int r;
97
98 bus_assert_return(bus, -EINVAL, error);
99 bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
100
101 if (!BUS_IS_OPEN(bus->state)) {
102 r = -ENOTCONN;
103 goto fail;
104 }
105
106 r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member);
107 if (r < 0)
108 goto fail;
109
110 if (!isempty(types)) {
111 va_list ap;
112
113 va_start(ap, types);
114 r = sd_bus_message_appendv(m, types, ap);
115 va_end(ap);
116 if (r < 0)
117 goto fail;
118 }
119
120 return sd_bus_call(bus, m, 0, error, reply);
121
122 fail:
123 return sd_bus_error_set_errno(error, r);
124 }
125
126 _public_ int sd_bus_reply_method_return(
127 sd_bus_message *call,
128 const char *types, ...) {
129
130 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
131 int r;
132
133 assert_return(call, -EINVAL);
134 assert_return(call->sealed, -EPERM);
135 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
136 assert_return(call->bus, -EINVAL);
137 assert_return(!bus_pid_changed(call->bus), -ECHILD);
138
139 if (!BUS_IS_OPEN(call->bus->state))
140 return -ENOTCONN;
141
142 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
143 return 0;
144
145 r = sd_bus_message_new_method_return(call, &m);
146 if (r < 0)
147 return r;
148
149 if (!isempty(types)) {
150 va_list ap;
151
152 va_start(ap, types);
153 r = sd_bus_message_appendv(m, types, ap);
154 va_end(ap);
155 if (r < 0)
156 return r;
157 }
158
159 return sd_bus_send(call->bus, m, NULL);
160 }
161
162 _public_ int sd_bus_reply_method_error(
163 sd_bus_message *call,
164 const sd_bus_error *e) {
165
166 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
167 int r;
168
169 assert_return(call, -EINVAL);
170 assert_return(call->sealed, -EPERM);
171 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
172 assert_return(sd_bus_error_is_set(e), -EINVAL);
173 assert_return(call->bus, -EINVAL);
174 assert_return(!bus_pid_changed(call->bus), -ECHILD);
175
176 if (!BUS_IS_OPEN(call->bus->state))
177 return -ENOTCONN;
178
179 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
180 return 0;
181
182 r = sd_bus_message_new_method_error(call, &m, e);
183 if (r < 0)
184 return r;
185
186 return sd_bus_send(call->bus, m, NULL);
187 }
188
189 _public_ int sd_bus_reply_method_errorf(
190 sd_bus_message *call,
191 const char *name,
192 const char *format,
193 ...) {
194
195 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
196 va_list ap;
197
198 assert_return(call, -EINVAL);
199 assert_return(call->sealed, -EPERM);
200 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
201 assert_return(call->bus, -EINVAL);
202 assert_return(!bus_pid_changed(call->bus), -ECHILD);
203
204 if (!BUS_IS_OPEN(call->bus->state))
205 return -ENOTCONN;
206
207 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
208 return 0;
209
210 va_start(ap, format);
211 bus_error_setfv(&error, name, format, ap);
212 va_end(ap);
213
214 return sd_bus_reply_method_error(call, &error);
215 }
216
217 _public_ int sd_bus_reply_method_errno(
218 sd_bus_message *call,
219 int error,
220 const sd_bus_error *p) {
221
222 _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL;
223
224 assert_return(call, -EINVAL);
225 assert_return(call->sealed, -EPERM);
226 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
227 assert_return(call->bus, -EINVAL);
228 assert_return(!bus_pid_changed(call->bus), -ECHILD);
229
230 if (!BUS_IS_OPEN(call->bus->state))
231 return -ENOTCONN;
232
233 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
234 return 0;
235
236 if (sd_bus_error_is_set(p))
237 return sd_bus_reply_method_error(call, p);
238
239 sd_bus_error_set_errno(&berror, error);
240
241 return sd_bus_reply_method_error(call, &berror);
242 }
243
244 _public_ int sd_bus_reply_method_errnof(
245 sd_bus_message *call,
246 int error,
247 const char *format,
248 ...) {
249
250 _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL;
251 va_list ap;
252
253 assert_return(call, -EINVAL);
254 assert_return(call->sealed, -EPERM);
255 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
256 assert_return(call->bus, -EINVAL);
257 assert_return(!bus_pid_changed(call->bus), -ECHILD);
258
259 if (!BUS_IS_OPEN(call->bus->state))
260 return -ENOTCONN;
261
262 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
263 return 0;
264
265 va_start(ap, format);
266 sd_bus_error_set_errnofv(&berror, error, format, ap);
267 va_end(ap);
268
269 return sd_bus_reply_method_error(call, &berror);
270 }
271
272 _public_ int sd_bus_get_property(
273 sd_bus *bus,
274 const char *destination,
275 const char *path,
276 const char *interface,
277 const char *member,
278 sd_bus_error *error,
279 sd_bus_message **reply,
280 const char *type) {
281
282 sd_bus_message *rep = NULL;
283 int r;
284
285 bus_assert_return(bus, -EINVAL, error);
286 bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
287 bus_assert_return(member_name_is_valid(member), -EINVAL, error);
288 bus_assert_return(reply, -EINVAL, error);
289 bus_assert_return(signature_is_single(type, false), -EINVAL, error);
290 bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
291
292 if (!BUS_IS_OPEN(bus->state)) {
293 r = -ENOTCONN;
294 goto fail;
295 }
296
297 r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &rep, "ss", strempty(interface), member);
298 if (r < 0)
299 return r;
300
301 r = sd_bus_message_enter_container(rep, 'v', type);
302 if (r < 0) {
303 sd_bus_message_unref(rep);
304 goto fail;
305 }
306
307 *reply = rep;
308 return 0;
309
310 fail:
311 return sd_bus_error_set_errno(error, r);
312 }
313
314 _public_ int sd_bus_get_property_trivial(
315 sd_bus *bus,
316 const char *destination,
317 const char *path,
318 const char *interface,
319 const char *member,
320 sd_bus_error *error,
321 char type, void *ptr) {
322
323 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
324 int r;
325
326 bus_assert_return(bus, -EINVAL, error);
327 bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
328 bus_assert_return(member_name_is_valid(member), -EINVAL, error);
329 bus_assert_return(bus_type_is_trivial(type), -EINVAL, error);
330 bus_assert_return(ptr, -EINVAL, error);
331 bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
332
333 if (!BUS_IS_OPEN(bus->state)) {
334 r = -ENOTCONN;
335 goto fail;
336 }
337
338 r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
339 if (r < 0)
340 return r;
341
342 r = sd_bus_message_enter_container(reply, 'v', CHAR_TO_STR(type));
343 if (r < 0)
344 goto fail;
345
346 r = sd_bus_message_read_basic(reply, type, ptr);
347 if (r < 0)
348 goto fail;
349
350 return 0;
351
352 fail:
353 return sd_bus_error_set_errno(error, r);
354 }
355
356 _public_ int sd_bus_get_property_string(
357 sd_bus *bus,
358 const char *destination,
359 const char *path,
360 const char *interface,
361 const char *member,
362 sd_bus_error *error,
363 char **ret) {
364
365 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
366 const char *s;
367 char *n;
368 int r;
369
370 bus_assert_return(bus, -EINVAL, error);
371 bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
372 bus_assert_return(member_name_is_valid(member), -EINVAL, error);
373 bus_assert_return(ret, -EINVAL, error);
374 bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
375
376 if (!BUS_IS_OPEN(bus->state)) {
377 r = -ENOTCONN;
378 goto fail;
379 }
380
381 r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
382 if (r < 0)
383 return r;
384
385 r = sd_bus_message_enter_container(reply, 'v', "s");
386 if (r < 0)
387 goto fail;
388
389 r = sd_bus_message_read_basic(reply, 's', &s);
390 if (r < 0)
391 goto fail;
392
393 n = strdup(s);
394 if (!n) {
395 r = -ENOMEM;
396 goto fail;
397 }
398
399 *ret = n;
400 return 0;
401
402 fail:
403 return sd_bus_error_set_errno(error, r);
404 }
405
406 _public_ int sd_bus_get_property_strv(
407 sd_bus *bus,
408 const char *destination,
409 const char *path,
410 const char *interface,
411 const char *member,
412 sd_bus_error *error,
413 char ***ret) {
414
415 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
416 int r;
417
418 bus_assert_return(bus, -EINVAL, error);
419 bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
420 bus_assert_return(member_name_is_valid(member), -EINVAL, error);
421 bus_assert_return(ret, -EINVAL, error);
422 bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
423
424 if (!BUS_IS_OPEN(bus->state)) {
425 r = -ENOTCONN;
426 goto fail;
427 }
428
429 r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
430 if (r < 0)
431 return r;
432
433 r = sd_bus_message_enter_container(reply, 'v', NULL);
434 if (r < 0)
435 goto fail;
436
437 r = sd_bus_message_read_strv(reply, ret);
438 if (r < 0)
439 goto fail;
440
441 return 0;
442
443 fail:
444 return sd_bus_error_set_errno(error, r);
445 }
446
447 _public_ int sd_bus_set_property(
448 sd_bus *bus,
449 const char *destination,
450 const char *path,
451 const char *interface,
452 const char *member,
453 sd_bus_error *error,
454 const char *type, ...) {
455
456 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
457 va_list ap;
458 int r;
459
460 bus_assert_return(bus, -EINVAL, error);
461 bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
462 bus_assert_return(member_name_is_valid(member), -EINVAL, error);
463 bus_assert_return(signature_is_single(type, false), -EINVAL, error);
464 bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
465
466 if (!BUS_IS_OPEN(bus->state)) {
467 r = -ENOTCONN;
468 goto fail;
469 }
470
471 r = sd_bus_message_new_method_call(bus, &m, destination, path, "org.freedesktop.DBus.Properties", "Set");
472 if (r < 0)
473 goto fail;
474
475 r = sd_bus_message_append(m, "ss", strempty(interface), member);
476 if (r < 0)
477 goto fail;
478
479 r = sd_bus_message_open_container(m, 'v', type);
480 if (r < 0)
481 goto fail;
482
483 va_start(ap, type);
484 r = sd_bus_message_appendv(m, type, ap);
485 va_end(ap);
486 if (r < 0)
487 goto fail;
488
489 r = sd_bus_message_close_container(m);
490 if (r < 0)
491 goto fail;
492
493 return sd_bus_call(bus, m, 0, error, NULL);
494
495 fail:
496 return sd_bus_error_set_errno(error, r);
497 }
498
499 _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds) {
500 sd_bus_creds *c;
501
502 assert_return(call, -EINVAL);
503 assert_return(call->sealed, -EPERM);
504 assert_return(call->bus, -EINVAL);
505 assert_return(!bus_pid_changed(call->bus), -ECHILD);
506
507 if (!BUS_IS_OPEN(call->bus->state))
508 return -ENOTCONN;
509
510 c = sd_bus_message_get_creds(call);
511
512 /* All data we need? */
513 if (c && (mask & ~c->mask) == 0) {
514 *creds = sd_bus_creds_ref(c);
515 return 0;
516 }
517
518 /* No data passed? Or not enough data passed to retrieve the missing bits? */
519 if (!c || !(c->mask & SD_BUS_CREDS_PID)) {
520 /* We couldn't read anything from the call, let's try
521 * to get it from the sender or peer. */
522
523 if (call->sender)
524 /* There's a sender, but the creds are missing. */
525 return sd_bus_get_name_creds(call->bus, call->sender, mask, creds);
526 else
527 /* There's no sender. For direct connections
528 * the credentials of the AF_UNIX peer matter,
529 * which may be queried via sd_bus_get_owner_creds(). */
530 return sd_bus_get_owner_creds(call->bus, mask, creds);
531 }
532
533 return bus_creds_extend_by_pid(c, mask, creds);
534 }
535
536 _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) {
537 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
538 uid_t our_uid;
539 bool know_caps = false;
540 int r;
541
542 assert_return(call, -EINVAL);
543 assert_return(call->sealed, -EPERM);
544 assert_return(call->bus, -EINVAL);
545 assert_return(!bus_pid_changed(call->bus), -ECHILD);
546
547 if (!BUS_IS_OPEN(call->bus->state))
548 return -ENOTCONN;
549
550 if (capability >= 0) {
551
552 r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds);
553 if (r < 0)
554 return r;
555
556 /* We cannot use augmented caps for authorization,
557 * since then data is acquired raceful from
558 * /proc. This can never actually happen, but let's
559 * better be safe than sorry, and do an extra check
560 * here. */
561 assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EFFECTIVE_CAPS) == 0, -EPERM);
562
563 r = sd_bus_creds_has_effective_cap(creds, capability);
564 if (r > 0)
565 return 1;
566 if (r == 0)
567 know_caps = true;
568 } else {
569 r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID, &creds);
570 if (r < 0)
571 return r;
572 }
573
574 /* Now, check the UID, but only if the capability check wasn't
575 * sufficient */
576 our_uid = getuid();
577 if (our_uid != 0 || !know_caps || capability < 0) {
578 uid_t sender_uid;
579
580 /* We cannot use augmented uid/euid for authorization,
581 * since then data is acquired raceful from
582 * /proc. This can never actually happen, but let's
583 * better be safe than sorry, and do an extra check
584 * here. */
585 assert_return((sd_bus_creds_get_augmented_mask(creds) & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID)) == 0, -EPERM);
586
587 /* Try to use the EUID, if we have it. */
588 r = sd_bus_creds_get_euid(creds, &sender_uid);
589 if (r < 0)
590 r = sd_bus_creds_get_uid(creds, &sender_uid);
591
592 if (r >= 0) {
593 /* Sender has same UID as us, then let's grant access */
594 if (sender_uid == our_uid)
595 return 1;
596
597 /* Sender is root, we are not root. */
598 if (our_uid != 0 && sender_uid == 0)
599 return 1;
600 }
601 }
602
603 return 0;
604 }
605
606 #define make_expression(sender, path, interface, member) \
607 strjoina( \
608 "type='signal'", \
609 sender ? ",sender='" : "", \
610 sender ?: "", \
611 sender ? "'" : "", \
612 path ? ",path='" : "", \
613 path ?: "", \
614 path ? "'" : "", \
615 interface ? ",interface='" : "", \
616 interface ?: "", \
617 interface ? "'" : "", \
618 member ? ",member='" : "", \
619 member ?: "", \
620 member ? "'" : "" \
621 )
622
623 _public_ int sd_bus_match_signal(
624 sd_bus *bus,
625 sd_bus_slot **ret,
626 const char *sender,
627 const char *path,
628 const char *interface,
629 const char *member,
630 sd_bus_message_handler_t callback,
631 void *userdata) {
632
633 const char *expression;
634
635 assert_return(bus, -EINVAL);
636 assert_return(bus = bus_resolve(bus), -ENOPKG);
637 assert_return(!bus_pid_changed(bus), -ECHILD);
638 assert_return(!sender || service_name_is_valid(sender), -EINVAL);
639 assert_return(!path || object_path_is_valid(path), -EINVAL);
640 assert_return(!interface || interface_name_is_valid(interface), -EINVAL);
641 assert_return(!member || member_name_is_valid(member), -EINVAL);
642
643 expression = make_expression(sender, path, interface, member);
644
645 return sd_bus_add_match(bus, ret, expression, callback, userdata);
646 }
647
648 _public_ int sd_bus_match_signal_async(
649 sd_bus *bus,
650 sd_bus_slot **ret,
651 const char *sender,
652 const char *path,
653 const char *interface,
654 const char *member,
655 sd_bus_message_handler_t callback,
656 sd_bus_message_handler_t install_callback,
657 void *userdata) {
658
659 const char *expression;
660
661 assert_return(bus, -EINVAL);
662 assert_return(bus = bus_resolve(bus), -ENOPKG);
663 assert_return(!bus_pid_changed(bus), -ECHILD);
664 assert_return(!sender || service_name_is_valid(sender), -EINVAL);
665 assert_return(!path || object_path_is_valid(path), -EINVAL);
666 assert_return(!interface || interface_name_is_valid(interface), -EINVAL);
667 assert_return(!member || member_name_is_valid(member), -EINVAL);
668
669 expression = make_expression(sender, path, interface, member);
670
671 return sd_bus_add_match_async(bus, ret, expression, callback, install_callback, userdata);
672 }