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