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