1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "bus-internal.h"
7 #include "bus-message.h"
8 #include "bus-signature.h"
11 #include "string-util.h"
13 _public_
int sd_bus_emit_signalv(
16 const char *interface
,
18 const char *types
, va_list ap
) {
20 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
23 assert_return(bus
, -EINVAL
);
24 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
25 assert_return(!bus_pid_changed(bus
), -ECHILD
);
27 if (!BUS_IS_OPEN(bus
->state
))
30 r
= sd_bus_message_new_signal(bus
, &m
, path
, interface
, member
);
34 if (!isempty(types
)) {
35 r
= sd_bus_message_appendv(m
, types
, ap
);
40 return sd_bus_send(bus
, m
, NULL
);
43 _public_
int sd_bus_emit_signal(
46 const char *interface
,
48 const char *types
, ...) {
54 r
= sd_bus_emit_signalv(bus
, path
, interface
, member
, types
, ap
);
60 _public_
int sd_bus_call_method_asyncv(
63 const char *destination
,
65 const char *interface
,
67 sd_bus_message_handler_t callback
,
69 const char *types
, va_list ap
) {
71 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
74 assert_return(bus
, -EINVAL
);
75 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
76 assert_return(!bus_pid_changed(bus
), -ECHILD
);
78 if (!BUS_IS_OPEN(bus
->state
))
81 r
= sd_bus_message_new_method_call(bus
, &m
, destination
, path
, interface
, member
);
85 if (!isempty(types
)) {
86 r
= sd_bus_message_appendv(m
, types
, ap
);
91 return sd_bus_call_async(bus
, slot
, m
, callback
, userdata
, 0);
94 _public_
int sd_bus_call_method_async(
97 const char *destination
,
99 const char *interface
,
101 sd_bus_message_handler_t callback
,
103 const char *types
, ...) {
109 r
= sd_bus_call_method_asyncv(bus
, slot
, destination
, path
, interface
, member
, callback
, userdata
, types
, ap
);
115 _public_
int sd_bus_call_methodv(
117 const char *destination
,
119 const char *interface
,
122 sd_bus_message
**reply
,
123 const char *types
, va_list ap
) {
125 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
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
);
132 if (!BUS_IS_OPEN(bus
->state
)) {
137 r
= sd_bus_message_new_method_call(bus
, &m
, destination
, path
, interface
, member
);
141 if (!isempty(types
)) {
142 r
= sd_bus_message_appendv(m
, types
, ap
);
147 return sd_bus_call(bus
, m
, 0, error
, reply
);
150 return sd_bus_error_set_errno(error
, r
);
153 _public_
int sd_bus_call_method(
155 const char *destination
,
157 const char *interface
,
160 sd_bus_message
**reply
,
161 const char *types
, ...) {
167 r
= sd_bus_call_methodv(bus
, destination
, path
, interface
, member
, error
, reply
, types
, ap
);
173 _public_
int sd_bus_reply_method_returnv(
174 sd_bus_message
*call
,
175 const char *types
, va_list ap
) {
177 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
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
);
186 if (!BUS_IS_OPEN(call
->bus
->state
))
189 if (call
->header
->flags
& BUS_MESSAGE_NO_REPLY_EXPECTED
)
192 r
= sd_bus_message_new_method_return(call
, &m
);
196 if (!isempty(types
)) {
197 r
= sd_bus_message_appendv(m
, types
, ap
);
202 return sd_bus_send(call
->bus
, m
, NULL
);
205 _public_
int sd_bus_reply_method_return(
206 sd_bus_message
*call
,
207 const char *types
, ...) {
213 r
= sd_bus_reply_method_returnv(call
, types
, ap
);
219 _public_
int sd_bus_reply_method_error(
220 sd_bus_message
*call
,
221 const sd_bus_error
*e
) {
223 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
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
);
233 if (!BUS_IS_OPEN(call
->bus
->state
))
236 if (call
->header
->flags
& BUS_MESSAGE_NO_REPLY_EXPECTED
)
239 r
= sd_bus_message_new_method_error(call
, &m
, e
);
243 return sd_bus_send(call
->bus
, m
, NULL
);
246 _public_
int sd_bus_reply_method_errorfv(
247 sd_bus_message
*call
,
252 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
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
);
260 if (!BUS_IS_OPEN(call
->bus
->state
))
263 if (call
->header
->flags
& BUS_MESSAGE_NO_REPLY_EXPECTED
)
266 bus_error_setfv(&error
, name
, format
, ap
);
268 return sd_bus_reply_method_error(call
, &error
);
271 _public_
int sd_bus_reply_method_errorf(
272 sd_bus_message
*call
,
280 va_start(ap
, format
);
281 r
= sd_bus_reply_method_errorfv(call
, name
, format
, ap
);
287 _public_
int sd_bus_reply_method_errno(
288 sd_bus_message
*call
,
290 const sd_bus_error
*p
) {
292 _cleanup_(sd_bus_error_free
) sd_bus_error berror
= SD_BUS_ERROR_NULL
;
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
);
300 if (!BUS_IS_OPEN(call
->bus
->state
))
303 if (call
->header
->flags
& BUS_MESSAGE_NO_REPLY_EXPECTED
)
306 if (sd_bus_error_is_set(p
))
307 return sd_bus_reply_method_error(call
, p
);
309 sd_bus_error_set_errno(&berror
, error
);
311 return sd_bus_reply_method_error(call
, &berror
);
314 _public_
int sd_bus_reply_method_errnofv(
315 sd_bus_message
*call
,
320 _cleanup_(sd_bus_error_free
) sd_bus_error berror
= SD_BUS_ERROR_NULL
;
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
);
328 if (!BUS_IS_OPEN(call
->bus
->state
))
331 if (call
->header
->flags
& BUS_MESSAGE_NO_REPLY_EXPECTED
)
334 sd_bus_error_set_errnofv(&berror
, error
, format
, ap
);
336 return sd_bus_reply_method_error(call
, &berror
);
339 _public_
int sd_bus_reply_method_errnof(
340 sd_bus_message
*call
,
348 va_start(ap
, format
);
349 r
= sd_bus_reply_method_errnofv(call
, error
, format
, ap
);
355 _public_
int sd_bus_get_property(
357 const char *destination
,
359 const char *interface
,
362 sd_bus_message
**reply
,
365 sd_bus_message
*rep
= NULL
;
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
);
376 if (!BUS_IS_OPEN(bus
->state
)) {
381 r
= sd_bus_call_method(bus
, destination
, path
,
382 "org.freedesktop.DBus.Properties", "Get",
384 "ss", strempty(interface
), member
);
388 r
= sd_bus_message_enter_container(rep
, 'v', type
);
390 sd_bus_message_unref(rep
);
398 return sd_bus_error_set_errno(error
, r
);
401 _public_
int sd_bus_get_property_trivial(
403 const char *destination
,
405 const char *interface
,
408 char type
, void *ptr
) {
410 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
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
);
421 if (!BUS_IS_OPEN(bus
->state
)) {
426 r
= sd_bus_call_method(bus
, destination
, path
, "org.freedesktop.DBus.Properties", "Get", error
, &reply
, "ss", strempty(interface
), member
);
430 r
= sd_bus_message_enter_container(reply
, 'v', CHAR_TO_STR(type
));
434 r
= sd_bus_message_read_basic(reply
, type
, ptr
);
441 return sd_bus_error_set_errno(error
, r
);
444 _public_
int sd_bus_get_property_string(
446 const char *destination
,
448 const char *interface
,
453 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
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
);
465 if (!BUS_IS_OPEN(bus
->state
)) {
470 r
= sd_bus_call_method(bus
, destination
, path
, "org.freedesktop.DBus.Properties", "Get", error
, &reply
, "ss", strempty(interface
), member
);
474 r
= sd_bus_message_enter_container(reply
, 'v', "s");
478 r
= sd_bus_message_read_basic(reply
, 's', &s
);
492 return sd_bus_error_set_errno(error
, r
);
495 _public_
int sd_bus_get_property_strv(
497 const char *destination
,
499 const char *interface
,
504 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
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
);
514 if (!BUS_IS_OPEN(bus
->state
)) {
519 r
= sd_bus_call_method(bus
, destination
, path
, "org.freedesktop.DBus.Properties", "Get", error
, &reply
, "ss", strempty(interface
), member
);
523 r
= sd_bus_message_enter_container(reply
, 'v', NULL
);
527 r
= sd_bus_message_read_strv(reply
, ret
);
534 return sd_bus_error_set_errno(error
, r
);
537 _public_
int sd_bus_set_propertyv(
539 const char *destination
,
541 const char *interface
,
544 const char *type
, va_list ap
) {
546 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
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
);
556 if (!BUS_IS_OPEN(bus
->state
)) {
561 r
= sd_bus_message_new_method_call(bus
, &m
, destination
, path
, "org.freedesktop.DBus.Properties", "Set");
565 r
= sd_bus_message_append(m
, "ss", strempty(interface
), member
);
569 r
= sd_bus_message_open_container(m
, 'v', type
);
573 r
= sd_bus_message_appendv(m
, type
, ap
);
577 r
= sd_bus_message_close_container(m
);
581 return sd_bus_call(bus
, m
, 0, error
, NULL
);
584 return sd_bus_error_set_errno(error
, r
);
587 _public_
int sd_bus_set_property(
589 const char *destination
,
591 const char *interface
,
594 const char *type
, ...) {
600 r
= sd_bus_set_propertyv(bus
, destination
, path
, interface
, member
, error
, type
, ap
);
606 _public_
int sd_bus_query_sender_creds(sd_bus_message
*call
, uint64_t mask
, sd_bus_creds
**creds
) {
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
);
614 if (!BUS_IS_OPEN(call
->bus
->state
))
617 c
= sd_bus_message_get_creds(call
);
619 /* All data we need? */
620 if (c
&& (mask
& ~c
->mask
) == 0) {
621 *creds
= sd_bus_creds_ref(c
);
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. */
631 /* There's a sender, but the creds are missing. */
632 return sd_bus_get_name_creds(call
->bus
, call
->sender
, mask
, creds
);
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
);
640 return bus_creds_extend_by_pid(c
, mask
, creds
);
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
;
646 bool know_caps
= false;
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
);
654 if (!BUS_IS_OPEN(call
->bus
->state
))
657 if (capability
>= 0) {
659 r
= sd_bus_query_sender_creds(call
, SD_BUS_CREDS_UID
|SD_BUS_CREDS_EUID
|SD_BUS_CREDS_EFFECTIVE_CAPS
, &creds
);
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
668 assert_return((sd_bus_creds_get_augmented_mask(creds
) & SD_BUS_CREDS_EFFECTIVE_CAPS
) == 0, -EPERM
);
670 r
= sd_bus_creds_has_effective_cap(creds
, capability
);
676 r
= sd_bus_query_sender_creds(call
, SD_BUS_CREDS_UID
|SD_BUS_CREDS_EUID
, &creds
);
681 /* Now, check the UID, but only if the capability check wasn't
684 if (our_uid
!= 0 || !know_caps
|| capability
< 0) {
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
692 assert_return((sd_bus_creds_get_augmented_mask(creds
) & (SD_BUS_CREDS_UID
|SD_BUS_CREDS_EUID
)) == 0, -EPERM
);
694 /* Try to use the EUID, if we have it. */
695 r
= sd_bus_creds_get_euid(creds
, &sender_uid
);
697 r
= sd_bus_creds_get_uid(creds
, &sender_uid
);
700 /* Sender has same UID as us, then let's grant access */
701 if (sender_uid
== our_uid
)
704 /* Sender is root, we are not root. */
705 if (our_uid
!= 0 && sender_uid
== 0)
713 #define make_expression(sender, path, interface, member) \
716 sender ? ",sender='" : "", \
719 path ? ",path='" : "", \
722 interface ? ",interface='" : "", \
724 interface ? "'" : "", \
725 member ? ",member='" : "", \
730 _public_
int sd_bus_match_signal(
735 const char *interface
,
737 sd_bus_message_handler_t callback
,
740 const char *expression
;
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
);
750 expression
= make_expression(sender
, path
, interface
, member
);
752 return sd_bus_add_match(bus
, ret
, expression
, callback
, userdata
);
755 _public_
int sd_bus_match_signal_async(
760 const char *interface
,
762 sd_bus_message_handler_t callback
,
763 sd_bus_message_handler_t install_callback
,
766 const char *expression
;
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
);
776 expression
= make_expression(sender
, path
, interface
, member
);
778 return sd_bus_add_match_async(bus
, ret
, expression
, callback
, install_callback
, userdata
);