chiark / gitweb /
47da334b93318f16bda46d458f81f5dfcdc3927f
[elogind.git] / src / libelogind / 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 }