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