]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-bus/bus-util.c
bus: add sd_bus_message_skip() to skip over multiple fields
[thirdparty/systemd.git] / src / libsystemd-bus / bus-util.c
CommitLineData
40ca29a1
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
0c842e0a
TG
22#include <sys/socket.h>
23
40ca29a1 24#include "util.h"
ffc06c35 25#include "strv.h"
40ca29a1
LP
26#include "macro.h"
27#include "def.h"
28
ffc06c35
KS
29#include "sd-event.h"
30#include "sd-bus.h"
31#include "bus-error.h"
32#include "bus-message.h"
33
40ca29a1
LP
34#include "bus-util.h"
35
36static int quit_callback(sd_bus *bus, sd_bus_message *m, void *userdata) {
37 sd_event *e = userdata;
38
39 assert(bus);
40 assert(m);
41 assert(e);
42
43 sd_event_request_quit(e);
44 return 1;
45}
46
47int bus_async_unregister_and_quit(sd_event *e, sd_bus *bus, const char *name) {
48 _cleanup_free_ char *match = NULL;
49 int r;
50
51 assert(e);
52 assert(bus);
53 assert(name);
54
55 r = asprintf(&match, "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameLost',arg0='%s'", name);
56 if (r < 0)
57 return r;
58
59 r = sd_bus_add_match(bus, match, quit_callback, e);
60 if (r < 0)
61 return r;
62
63 r = sd_bus_release_name(bus, name);
64 if (r < 0)
65 return r;
66
67 if (r != SD_BUS_NAME_RELEASED)
68 return -EIO;
69
70 return 0;
71}
72
73int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout) {
74 bool exiting = false;
75 int r;
76
77 assert(e);
78 assert(bus);
79 assert(name);
80
81 for (;;) {
82 r = sd_event_get_state(e);
83 if (r < 0)
84 return r;
85
86 if (r == SD_EVENT_FINISHED)
87 break;
88
abc5fe72 89 r = sd_event_run(e, exiting ? (uint64_t) -1 : timeout);
40ca29a1
LP
90 if (r < 0)
91 return r;
92
93 if (r == 0 && !exiting) {
94 r = bus_async_unregister_and_quit(e, bus, name);
95 if (r < 0)
96 return r;
97
98 exiting = true;
99 }
100 }
101
102 return 0;
103}
104
105int bus_property_get_tristate(
106 sd_bus *bus,
107 const char *path,
108 const char *interface,
109 const char *property,
110 sd_bus_message *reply,
111 sd_bus_error *error,
112 void *userdata) {
113
114 int *tristate = userdata;
115 int r;
116
117 r = sd_bus_message_append(reply, "b", *tristate > 0);
118 if (r < 0)
119 return r;
120
121 return 1;
122}
123
124int bus_verify_polkit(
125 sd_bus *bus,
126 sd_bus_message *m,
127 const char *action,
128 bool interactive,
129 bool *_challenge,
130 sd_bus_error *e) {
131
132 const char *sender;
133 uid_t uid;
134 int r;
135
136 assert(bus);
137 assert(m);
138 assert(action);
139
140 sender = sd_bus_message_get_sender(m);
141 if (!sender)
142 return -EBADMSG;
143
144 r = sd_bus_get_owner_uid(bus, sender, &uid);
145 if (r < 0)
146 return r;
147
148 if (uid == 0)
149 return 1;
150
151#ifdef ENABLE_POLKIT
152 else {
153 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
9bcbce42 154 unsigned authorized = false, challenge = false;
40ca29a1
LP
155
156 r = sd_bus_call_method(
157 bus,
158 "org.freedesktop.PolicyKit1",
159 "/org/freedesktop/PolicyKit1/Authority",
160 "org.freedesktop.PolicyKit1.Authority",
161 "CheckAuthorization",
162 e,
163 &reply,
164 "(sa{sv})sa{ss}us",
165 "system-bus-name", 1, "name", "s", sender,
166 action,
167 0,
168 interactive ? 1 : 0,
169 "");
170
171 if (r < 0) {
172 /* Treat no PK available as access denied */
173 if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
174 sd_bus_error_free(e);
175 return -EACCES;
176 }
177
178 return r;
179 }
180
181 r = sd_bus_message_read(reply, "(bb)", &authorized, &challenge);
182 if (r < 0)
183 return r;
184
185 if (authorized)
186 return 1;
187
188 if (_challenge) {
189 *_challenge = challenge;
190 return 0;
191 }
192 }
193#endif
194
195 return -EACCES;
196}
197
198#ifdef ENABLE_POLKIT
199
200typedef struct AsyncPolkitQuery {
201 sd_bus_message *request, *reply;
202 sd_bus_message_handler_t callback;
203 void *userdata;
204 uint64_t serial;
205} AsyncPolkitQuery;
206
207static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata) {
208 AsyncPolkitQuery *q = userdata;
209 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
210 int r;
211
212 assert(bus);
213 assert(reply);
214 assert(q);
215
216 q->reply = sd_bus_message_ref(reply);
217 q->serial = 0;
218
219 m = sd_bus_message_ref(q->request);
220
221 r = sd_bus_message_rewind(m, true);
222 if (r < 0)
223 return r;
224
225 r = q->callback(bus, m, q->userdata);
226 if (r < 0)
227 return r;
228
229 return 1;
230}
231
232static void async_polkit_query_free(sd_bus *b, AsyncPolkitQuery *q) {
233
234 if (!q)
235 return;
236
237 if (q->serial > 0 && b)
238 sd_bus_send_with_reply_cancel(b, q->serial);
239
240 sd_bus_message_unref(q->request);
241 sd_bus_message_unref(q->reply);
242 free(q);
243}
244
245#endif
246
247int bus_verify_polkit_async(
248 sd_bus *bus,
249 Hashmap **registry,
250 sd_bus_message *m,
251 const char *action,
252 bool interactive,
253 sd_bus_error *error,
254 sd_bus_message_handler_t callback,
255 void *userdata) {
256
257#ifdef ENABLE_POLKIT
258 _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
259 AsyncPolkitQuery *q;
260#endif
261 const char *sender;
262 uid_t uid;
263 int r;
264
265 assert(bus);
266 assert(registry);
267 assert(m);
268 assert(action);
269
270#ifdef ENABLE_POLKIT
271 q = hashmap_remove(*registry, m);
272 if (q) {
9bcbce42 273 unsigned authorized, challenge;
40ca29a1
LP
274
275 /* This is the second invocation of this function, and
276 * there's already a response from polkit, let's
277 * process it */
278 assert(q->reply);
279
280 if (sd_bus_message_is_method_error(q->reply, NULL)) {
281 const sd_bus_error *e;
282
283 /* Treat no PK available as access denied */
284 if (sd_bus_message_is_method_error(q->reply, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
285 async_polkit_query_free(bus, q);
286 return -EACCES;
287 }
288
289 e = sd_bus_message_get_error(q->reply);
290 sd_bus_error_copy(error, e);
291 r = sd_bus_error_get_errno(e);
292
293 async_polkit_query_free(bus, q);
294 return r;
295 }
296
297 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
298 if (r >= 0)
299 r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
300
301 async_polkit_query_free(bus, q);
302
303 if (r < 0)
304 return r;
305
306 if (authorized)
307 return 1;
308
309 return -EACCES;
310 }
311#endif
312
313 sender = sd_bus_message_get_sender(m);
314 if (!sender)
315 return -EBADMSG;
316
317 r = sd_bus_get_owner_uid(bus, sender, &uid);
318 if (r < 0)
319 return r;
320
321 if (uid == 0)
322 return 1;
323#ifdef ENABLE_POLKIT
324
325 r = hashmap_ensure_allocated(registry, trivial_hash_func, trivial_compare_func);
326 if (r < 0)
327 return r;
328
329 r = sd_bus_message_new_method_call(
330 bus,
331 "org.freedesktop.PolicyKit1",
332 "/org/freedesktop/PolicyKit1/Authority",
333 "org.freedesktop.PolicyKit1.Authority",
334 "CheckAuthorization",
335 &pk);
336 if (r < 0)
337 return r;
338
339 r = sd_bus_message_append(
340 pk,
341 "(sa{sv})sa{ss}us",
342 "system-bus-name", 1, "name", "s", sender,
343 action,
344 0,
345 interactive ? 1 : 0,
346 "");
347 if (r < 0)
348 return r;
349
350 q = new0(AsyncPolkitQuery, 1);
351 if (!q)
352 return -ENOMEM;
353
354 q->request = sd_bus_message_ref(m);
355 q->callback = callback;
356 q->userdata = userdata;
357
358 r = hashmap_put(*registry, m, q);
359 if (r < 0) {
360 async_polkit_query_free(bus, q);
361 return r;
362 }
363
364 r = sd_bus_send_with_reply(bus, pk, async_polkit_callback, q, 0, &q->serial);
365 if (r < 0)
366 return r;
367
368 return 0;
369#endif
370
371 return -EACCES;
372}
373
374void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry) {
375#ifdef ENABLE_POLKIT
376 AsyncPolkitQuery *q;
377
378 while ((q = hashmap_steal_first(registry)))
379 async_polkit_query_free(bus, q);
380
381 hashmap_free(registry);
382#endif
383}
0c842e0a
TG
384
385static int bus_check_peercred(sd_bus *c) {
0c842e0a
TG
386 struct ucred ucred;
387 socklen_t l;
0f8bd8de 388 int fd;
0c842e0a
TG
389
390 assert(c);
391
392 fd = sd_bus_get_fd(c);
0f8bd8de
LP
393 if (fd < 0)
394 return fd;
0c842e0a
TG
395
396 l = sizeof(struct ucred);
0f8bd8de 397 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
0c842e0a 398 return -errno;
0c842e0a 399
0f8bd8de 400 if (l != sizeof(struct ucred))
0c842e0a 401 return -E2BIG;
0c842e0a
TG
402
403 if (ucred.uid != 0 && ucred.uid != geteuid())
404 return -EPERM;
405
406 return 1;
407}
408
0f8bd8de
LP
409int bus_open_system_systemd(sd_bus **_bus) {
410 _cleanup_bus_unref_ sd_bus *bus = NULL;
0c842e0a 411 int r;
0c842e0a
TG
412
413 assert(_bus);
414
0f8bd8de
LP
415 if (geteuid() != 0)
416 return sd_bus_open_system(_bus);
a1da8583 417
0f8bd8de
LP
418 /* If we are root, then let's talk directly to the system
419 * instance, instead of going via the bus */
a1da8583 420
0f8bd8de
LP
421 r = sd_bus_new(&bus);
422 if (r < 0)
423 return r;
a1da8583 424
0f8bd8de
LP
425 r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
426 if (r < 0)
427 return r;
a1da8583 428
0f8bd8de
LP
429 r = sd_bus_start(bus);
430 if (r < 0)
431 return r;
a1da8583 432
0f8bd8de 433 r = bus_check_peercred(bus);
a1da8583
TG
434 if (r < 0)
435 return r;
436
437 *_bus = bus;
0f8bd8de
LP
438 bus = NULL;
439
a1da8583
TG
440 return 0;
441}
442
443int bus_generic_print_property(const char *name, sd_bus_message *property, bool all) {
444 char type;
445 const char *contents;
446
447 assert(name);
448 assert(property);
449
450 sd_bus_message_peek_type(property, &type, &contents);
451
452 switch (type) {
453
454 case SD_BUS_TYPE_STRING: {
455 const char *s;
456 sd_bus_message_read_basic(property, type, &s);
457
458 if (all || !isempty(s))
459 printf("%s=%s\n", name, s);
460
461 return 1;
462 }
463
464 case SD_BUS_TYPE_BOOLEAN: {
465 bool b;
466
467 sd_bus_message_read_basic(property, type, &b);
468 printf("%s=%s\n", name, yes_no(b));
469
470 return 1;
471 }
472
473 case SD_BUS_TYPE_UINT64: {
474 uint64_t u;
475
476 sd_bus_message_read_basic(property, type, &u);
477
478 /* Yes, heuristics! But we can change this check
479 * should it turn out to not be sufficient */
480
481 if (endswith(name, "Timestamp")) {
482 char timestamp[FORMAT_TIMESTAMP_MAX], *t;
483
484 t = format_timestamp(timestamp, sizeof(timestamp), u);
485 if (t || all)
486 printf("%s=%s\n", name, strempty(t));
487
488 } else if (strstr(name, "USec")) {
489 char timespan[FORMAT_TIMESPAN_MAX];
490
491 printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
492 } else
493 printf("%s=%llu\n", name, (unsigned long long) u);
494
495 return 1;
496 }
497
498 case SD_BUS_TYPE_UINT32: {
499 uint32_t u;
500
501 sd_bus_message_read_basic(property, type, &u);
502
503 if (strstr(name, "UMask") || strstr(name, "Mode"))
504 printf("%s=%04o\n", name, u);
505 else
506 printf("%s=%u\n", name, (unsigned) u);
507
508 return 1;
509 }
510
511 case SD_BUS_TYPE_INT32: {
512 int32_t i;
513
514 sd_bus_message_read_basic(property, type, &i);
515
516 printf("%s=%i\n", name, (int) i);
517 return 1;
518 }
519
520 case SD_BUS_TYPE_DOUBLE: {
521 double d;
522
523 sd_bus_message_read_basic(property, type, &d);
524
525 printf("%s=%g\n", name, d);
526 return 1;
527 }
528
529 case SD_BUS_TYPE_ARRAY:
530
531 if (streq(contents, "s")) {
532 bool space = false;
533 char tp;
534 const char *cnt;
535
536 sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
537
538 sd_bus_message_peek_type(property, &tp, &cnt);
539 if (all || cnt) {
4d7859d1 540 const char *str;
a1da8583
TG
541
542 printf("%s=", name);
543
544
07d978b0 545 while(sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str) > 0) {
a1da8583
TG
546 printf("%s%s", space ? " " : "", str);
547
548 space = true;
549 }
550
551 puts("");
552 }
553
554 sd_bus_message_exit_container(property);
555
556 return 1;
557
558 } else if (streq(contents, "y")) {
559 const uint8_t *u;
560 size_t n;
561
562 sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
563 if (all || n > 0) {
564 unsigned int i;
565
566 printf("%s=", name);
567
568 for (i = 0; i < n; i++)
569 printf("%02x", u[i]);
570
571 puts("");
572 }
573
574 return 1;
575
576 } else if (streq(contents, "u")) {
577 uint32_t *u;
578 size_t n;
579
580 sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
581 if (all || n > 0) {
582 unsigned int i;
583
584 printf("%s=", name);
585
586 for (i = 0; i < n; i++)
587 printf("%08x", u[i]);
588
589 puts("");
590 }
591
592 return 1;
593 }
594
595 break;
596 }
597
598 return 0;
599}
d21ed1ea 600
ffc06c35
KS
601int bus_map_all_properties(sd_bus *bus,
602 const char *destination,
603 const char *path,
604 const struct bus_properties_map *map) {
605 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
606 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
607 int r;
608
609 r = sd_bus_call_method( bus,
610 destination,
611 path,
612 "org.freedesktop.DBus.Properties",
613 "GetAll",
614 &error,
615 &m,
616 "s", "");
617 if (r < 0) {
618 log_error("Could not get properties: %s", bus_error_message(&error, -r));
619 return r;
620 }
621
622 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
623 if (r < 0)
624 return r;
625
626 while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
627 const char *name;
628 char type;
629 const char *contents;
630 unsigned i;
631
632 r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name);
633 if (r < 0)
634 return r;
635
636 r = sd_bus_message_peek_type(m, NULL, &contents);
637 if (r < 0)
638 return r;
639
640 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
641 if (r < 0)
642 return r;
643
644 r = sd_bus_message_peek_type(m, &type, &contents);
645 if (r < 0) {
646 log_error("Could not determine type of message: %s", strerror(-r));
647 return r;
648 }
649
650 switch (type) {
651 case SD_BUS_TYPE_STRING: {
652 const char *s;
653
654 sd_bus_message_read_basic(m, type, &s);
655 if (isempty(s))
656 break;
657
658 for (i = 0; map[i].type; i++) {
164ddcfa 659 char **p;
ffc06c35
KS
660
661 if (!streq(map[i].type, "s"))
662 continue;
663 if (!streq(map[i].name, name))
664 continue;
665
666 p = map[i].ptr;
164ddcfa 667 free(*p);
ffc06c35
KS
668 *p = strdup(s);
669 if (!*p) {
670 r = -ENOMEM;
671 goto fail;
672 }
673 }
674 break;
675 }
676
677 case SD_BUS_TYPE_ARRAY: {
678 _cleanup_strv_free_ char **l = NULL;
679
680 if (!streq(contents, "s"))
681 break;
682
683 for (i = 0; map[i].type; i++) {
684 char ***p;
685
686 if (!streq(map[i].type, "as"))
687 continue;
688 if (!streq(map[i].name, name))
689 continue;
690
691 r = bus_message_read_strv_extend(m, &l);
692 if (r < 0)
693 break;
694
695 p = map[i].ptr;
696 strv_free(*p);
697 *p = l;
698 l = NULL;
699 }
700 break;
701 }
702
703 case SD_BUS_TYPE_BOOLEAN: {
704 unsigned b;
705
706 sd_bus_message_read_basic(m, type, &b);
707
708 for (i = 0; map[i].type; i++) {
709 bool *p;
710
711 if (!streq(map[i].type, "b"))
712 continue;
713 if (!streq(map[i].name, name))
714 continue;
715
716 p = map[i].ptr;
717 *p = b;
718 }
719 break;
720 }
721
722 case SD_BUS_TYPE_UINT64: {
723 uint64_t t;
724
725 sd_bus_message_read_basic(m, type, &t);
726
727 for (i = 0; map[i].type; i++) {
728 uint64_t *p;
729
730 if (!streq(map[i].type, "t"))
731 continue;
732 if (!streq(map[i].name, name))
733 continue;
734
735 p = map[i].ptr;
736 *p = t;
737 }
738 break;
739 }
740
741 default:
742 break;
743 }
744
745 r = sd_bus_message_exit_container(m);
746 if (r < 0)
747 return r;
748
749 r = sd_bus_message_exit_container(m);
750 if (r < 0)
751 return r;
752 }
753
754fail:
755 return r;
756}
757
d21ed1ea
LP
758int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
759 int r;
760
761 assert(transport >= 0);
762 assert(transport < _BUS_TRANSPORT_MAX);
763 assert(bus);
764
765 assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
766 assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
767
768 switch (transport) {
769
770 case BUS_TRANSPORT_LOCAL:
771 if (user)
772 r = sd_bus_open_user(bus);
773 else
774 r = sd_bus_open_system(bus);
775
776 break;
777
778 case BUS_TRANSPORT_REMOTE:
779 r = sd_bus_open_system_remote(host, bus);
780 break;
781
782 case BUS_TRANSPORT_CONTAINER:
783 r = sd_bus_open_system_container(host, bus);
784 break;
785
786 default:
787 assert_not_reached("Hmm, unknown transport type.");
788 }
789
790 return r;
791}
e6504030
LP
792
793int bus_property_get_bool(
794 sd_bus *bus,
795 const char *path,
796 const char *interface,
797 const char *property,
798 sd_bus_message *reply,
799 sd_bus_error *error,
800 void *userdata) {
801
802 int b = *(bool*) userdata;
803
804 return sd_bus_message_append_basic(reply, 'b', &b);
805}
806
807int bus_property_get_uid(
808 sd_bus *bus,
809 const char *path,
810 const char *interface,
811 const char *property,
812 sd_bus_message *reply,
813 sd_bus_error *error,
814 void *userdata) {
815
816 assert_cc(sizeof(uint32_t) == sizeof(uid_t));
817 assert_cc(sizeof(uint32_t) == sizeof(gid_t));
818 assert_cc(sizeof(uint32_t) == sizeof(pid_t));
819
820 return sd_bus_message_append_basic(reply, 'u', userdata);
821}