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