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