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