chiark / gitweb /
Prep v229: Remove remaining emacs settings [3/6] src/libelogind
[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 = bus_message_append_ap(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 = bus_message_append_ap(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 = bus_message_append_ap(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 = bus_message_append_ap(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 #if 0 /// UNNEEDED by elogind
331 _public_ int sd_bus_get_property_trivial(
332                 sd_bus *bus,
333                 const char *destination,
334                 const char *path,
335                 const char *interface,
336                 const char *member,
337                 sd_bus_error *error,
338                 char type, void *ptr) {
339
340         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
341         int r;
342
343         bus_assert_return(bus, -EINVAL, error);
344         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
345         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
346         bus_assert_return(bus_type_is_trivial(type), -EINVAL, error);
347         bus_assert_return(ptr, -EINVAL, error);
348         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
349
350         if (!BUS_IS_OPEN(bus->state)) {
351                 r = -ENOTCONN;
352                 goto fail;
353         }
354
355         r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
356         if (r < 0)
357                 return r;
358
359         r = sd_bus_message_enter_container(reply, 'v', CHAR_TO_STR(type));
360         if (r < 0)
361                 goto fail;
362
363         r = sd_bus_message_read_basic(reply, type, ptr);
364         if (r < 0)
365                 goto fail;
366
367         return 0;
368
369 fail:
370         return sd_bus_error_set_errno(error, r);
371 }
372 #endif // 0
373
374 _public_ int sd_bus_get_property_string(
375                 sd_bus *bus,
376                 const char *destination,
377                 const char *path,
378                 const char *interface,
379                 const char *member,
380                 sd_bus_error *error,
381                 char **ret) {
382
383         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
384         const char *s;
385         char *n;
386         int r;
387
388         bus_assert_return(bus, -EINVAL, error);
389         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
390         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
391         bus_assert_return(ret, -EINVAL, error);
392         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
393
394         if (!BUS_IS_OPEN(bus->state)) {
395                 r = -ENOTCONN;
396                 goto fail;
397         }
398
399         r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
400         if (r < 0)
401                 return r;
402
403         r = sd_bus_message_enter_container(reply, 'v', "s");
404         if (r < 0)
405                 goto fail;
406
407         r = sd_bus_message_read_basic(reply, 's', &s);
408         if (r < 0)
409                 goto fail;
410
411         n = strdup(s);
412         if (!n) {
413                 r = -ENOMEM;
414                 goto fail;
415         }
416
417         *ret = n;
418         return 0;
419
420 fail:
421         return sd_bus_error_set_errno(error, r);
422 }
423
424 #if 0 /// UNNEEDED by elogind
425 _public_ int sd_bus_get_property_strv(
426                 sd_bus *bus,
427                 const char *destination,
428                 const char *path,
429                 const char *interface,
430                 const char *member,
431                 sd_bus_error *error,
432                 char ***ret) {
433
434         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
435         int r;
436
437         bus_assert_return(bus, -EINVAL, error);
438         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
439         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
440         bus_assert_return(ret, -EINVAL, error);
441         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
442
443         if (!BUS_IS_OPEN(bus->state)) {
444                 r = -ENOTCONN;
445                 goto fail;
446         }
447
448         r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
449         if (r < 0)
450                 return r;
451
452         r = sd_bus_message_enter_container(reply, 'v', NULL);
453         if (r < 0)
454                 goto fail;
455
456         r = sd_bus_message_read_strv(reply, ret);
457         if (r < 0)
458                 goto fail;
459
460         return 0;
461
462 fail:
463         return sd_bus_error_set_errno(error, r);
464 }
465
466 _public_ int sd_bus_set_property(
467                 sd_bus *bus,
468                 const char *destination,
469                 const char *path,
470                 const char *interface,
471                 const char *member,
472                 sd_bus_error *error,
473                 const char *type, ...) {
474
475         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
476         va_list ap;
477         int r;
478
479         bus_assert_return(bus, -EINVAL, error);
480         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
481         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
482         bus_assert_return(signature_is_single(type, false), -EINVAL, error);
483         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
484
485         if (!BUS_IS_OPEN(bus->state)) {
486                 r = -ENOTCONN;
487                 goto fail;
488         }
489
490         r = sd_bus_message_new_method_call(bus, &m, destination, path, "org.freedesktop.DBus.Properties", "Set");
491         if (r < 0)
492                 goto fail;
493
494         r = sd_bus_message_append(m, "ss", strempty(interface), member);
495         if (r < 0)
496                 goto fail;
497
498         r = sd_bus_message_open_container(m, 'v', type);
499         if (r < 0)
500                 goto fail;
501
502         va_start(ap, type);
503         r = bus_message_append_ap(m, type, ap);
504         va_end(ap);
505         if (r < 0)
506                 goto fail;
507
508         r = sd_bus_message_close_container(m);
509         if (r < 0)
510                 goto fail;
511
512         return sd_bus_call(bus, m, 0, error, NULL);
513
514 fail:
515         return sd_bus_error_set_errno(error, r);
516 }
517 #endif // 0
518
519 _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds) {
520         sd_bus_creds *c;
521
522         assert_return(call, -EINVAL);
523         assert_return(call->sealed, -EPERM);
524         assert_return(call->bus, -EINVAL);
525         assert_return(!bus_pid_changed(call->bus), -ECHILD);
526
527         if (!BUS_IS_OPEN(call->bus->state))
528                 return -ENOTCONN;
529
530         c = sd_bus_message_get_creds(call);
531
532         /* All data we need? */
533         if (c && (mask & ~c->mask) == 0) {
534                 *creds = sd_bus_creds_ref(c);
535                 return 0;
536         }
537
538         /* No data passed? Or not enough data passed to retrieve the missing bits? */
539         if (!c || !(c->mask & SD_BUS_CREDS_PID)) {
540                 /* We couldn't read anything from the call, let's try
541                  * to get it from the sender or peer. */
542
543                 if (call->sender)
544                         /* There's a sender, but the creds are
545                          * missing. This means we are talking via
546                          * dbus1, or are getting a message that was
547                          * sent to us via kdbus, but was converted
548                          * from a dbus1 message by the bus-proxy and
549                          * thus also lacks the creds. */
550                         return sd_bus_get_name_creds(call->bus, call->sender, mask, creds);
551                 else
552                         /* There's no sender, hence we are on a dbus1
553                          * direct connection. For direct connections
554                          * the credentials of the AF_UNIX peer matter,
555                          * which may be queried via
556                          * sd_bus_get_owner_creds(). */
557                         return sd_bus_get_owner_creds(call->bus, mask, creds);
558         }
559
560         return bus_creds_extend_by_pid(c, mask, creds);
561 }
562
563 _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) {
564         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
565         uid_t our_uid;
566         bool know_caps = false;
567         int r;
568
569         assert_return(call, -EINVAL);
570         assert_return(call->sealed, -EPERM);
571         assert_return(call->bus, -EINVAL);
572         assert_return(!bus_pid_changed(call->bus), -ECHILD);
573
574         if (!BUS_IS_OPEN(call->bus->state))
575                 return -ENOTCONN;
576
577         if (capability >= 0) {
578
579                 r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds);
580                 if (r < 0)
581                         return r;
582
583                 /* We cannot use augmented caps for authorization,
584                  * since then data is acquired raceful from
585                  * /proc. This can never actually happen, but let's
586                  * better be safe than sorry, and do an extra check
587                  * here. */
588                 assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EFFECTIVE_CAPS) == 0, -EPERM);
589
590                 /* Note that not even on kdbus we might have the caps
591                  * field, due to faked identities, or namespace
592                  * translation issues. */
593                 r = sd_bus_creds_has_effective_cap(creds, capability);
594                 if (r > 0)
595                         return 1;
596                 if (r == 0)
597                         know_caps = true;
598         } else {
599                 r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID, &creds);
600                 if (r < 0)
601                         return r;
602         }
603
604         /* Now, check the UID, but only if the capability check wasn't
605          * sufficient */
606         our_uid = getuid();
607         if (our_uid != 0 || !know_caps || capability < 0) {
608                 uid_t sender_uid;
609
610                 /* We cannot use augmented uid/euid for authorization,
611                  * since then data is acquired raceful from
612                  * /proc. This can never actually happen, but let's
613                  * better be safe than sorry, and do an extra check
614                  * here. */
615                 assert_return((sd_bus_creds_get_augmented_mask(creds) & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID)) == 0, -EPERM);
616
617                 /* Try to use the EUID, if we have it. */
618                 r = sd_bus_creds_get_euid(creds, &sender_uid);
619                 if (r < 0)
620                         r = sd_bus_creds_get_uid(creds, &sender_uid);
621
622                 if (r >= 0) {
623                         /* Sender has same UID as us, then let's grant access */
624                         if (sender_uid == our_uid)
625                                 return 1;
626
627                         /* Sender is root, we are not root. */
628                         if (our_uid != 0 && sender_uid == 0)
629                                 return 1;
630                 }
631         }
632
633         return 0;
634 }