]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-util.c
6441c5b162da6279d1c22aeef6c8f34529987722
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-util.c
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
22 #include <sys/socket.h>
23 #include <sys/capability.h>
24
25 #include "util.h"
26 #include "strv.h"
27 #include "macro.h"
28 #include "def.h"
29 #include "path-util.h"
30 #include "missing.h"
31
32 #include "sd-event.h"
33 #include "sd-bus.h"
34 #include "bus-error.h"
35 #include "bus-message.h"
36 #include "bus-util.h"
37 #include "bus-internal.h"
38
39 static int name_owner_change_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
40 sd_event *e = userdata;
41
42 assert(bus);
43 assert(m);
44 assert(e);
45
46 sd_bus_close(bus);
47 sd_event_exit(e, 0);
48
49 return 1;
50 }
51
52 int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) {
53 _cleanup_free_ char *match = NULL;
54 const char *unique;
55 int r;
56
57 assert(e);
58 assert(bus);
59 assert(name);
60
61 /* We unregister the name here and then wait for the
62 * NameOwnerChanged signal for this event to arrive before we
63 * quit. We do this in order to make sure that any queued
64 * requests are still processed before we really exit. */
65
66 r = sd_bus_get_unique_name(bus, &unique);
67 if (r < 0)
68 return r;
69
70 r = asprintf(&match,
71 "sender='org.freedesktop.DBus',"
72 "type='signal',"
73 "interface='org.freedesktop.DBus',"
74 "member='NameOwnerChanged',"
75 "path='/org/freedesktop/DBus',"
76 "arg0='%s',"
77 "arg1='%s',"
78 "arg2=''", name, unique);
79 if (r < 0)
80 return -ENOMEM;
81
82 r = sd_bus_add_match(bus, NULL, match, name_owner_change_callback, e);
83 if (r < 0)
84 return r;
85
86 r = sd_bus_release_name(bus, name);
87 if (r < 0)
88 return r;
89
90 return 0;
91 }
92
93 int bus_event_loop_with_idle(
94 sd_event *e,
95 sd_bus *bus,
96 const char *name,
97 usec_t timeout,
98 check_idle_t check_idle,
99 void *userdata) {
100 bool exiting = false;
101 int r, code;
102
103 assert(e);
104 assert(bus);
105 assert(name);
106
107 for (;;) {
108 bool idle;
109
110 r = sd_event_get_state(e);
111 if (r < 0)
112 return r;
113 if (r == SD_EVENT_FINISHED)
114 break;
115
116 if (check_idle)
117 idle = check_idle(userdata);
118 else
119 idle = true;
120
121 r = sd_event_run(e, exiting || !idle ? (uint64_t) -1 : timeout);
122 if (r < 0)
123 return r;
124
125 if (r == 0 && !exiting) {
126
127 r = sd_bus_try_close(bus);
128 if (r == -EBUSY)
129 continue;
130
131 if (r == -ENOTSUP) {
132 /* Fallback for dbus1 connections: we
133 * unregister the name and wait for
134 * the response to come through for
135 * it */
136
137 r = bus_async_unregister_and_exit(e, bus, name);
138 if (r < 0)
139 return r;
140
141 exiting = true;
142 continue;
143 }
144
145 if (r < 0)
146 return r;
147
148 sd_event_exit(e, 0);
149 break;
150 }
151 }
152
153 r = sd_event_get_exit_code(e, &code);
154 if (r < 0)
155 return r;
156
157 return code;
158 }
159
160 int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
161 _cleanup_bus_message_unref_ sd_bus_message *rep = NULL;
162 int r, has_owner = 0;
163
164 assert(c);
165 assert(name);
166
167 r = sd_bus_call_method(c,
168 "org.freedesktop.DBus",
169 "/org/freedesktop/dbus",
170 "org.freedesktop.DBus",
171 "NameHasOwner",
172 error,
173 &rep,
174 "s",
175 name);
176 if (r < 0)
177 return r;
178
179 r = sd_bus_message_read_basic(rep, 'b', &has_owner);
180 if (r < 0)
181 return sd_bus_error_set_errno(error, r);
182
183 return has_owner;
184 }
185
186 int bus_verify_polkit(
187 sd_bus *bus,
188 sd_bus_message *m,
189 const char *action,
190 bool interactive,
191 bool *_challenge,
192 sd_bus_error *e) {
193
194 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
195 uid_t uid;
196 int r;
197
198 assert(bus);
199 assert(m);
200 assert(action);
201
202 r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID, &creds);
203 if (r < 0)
204 return r;
205
206 r = sd_bus_creds_get_uid(creds, &uid);
207 if (r < 0)
208 return r;
209
210 if (uid == 0)
211 return 1;
212
213 #ifdef ENABLE_POLKIT
214 else {
215 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
216 int authorized = false, challenge = false;
217 const char *sender;
218
219 sender = sd_bus_message_get_sender(m);
220 if (!sender)
221 return -EBADMSG;
222
223 r = sd_bus_call_method(
224 bus,
225 "org.freedesktop.PolicyKit1",
226 "/org/freedesktop/PolicyKit1/Authority",
227 "org.freedesktop.PolicyKit1.Authority",
228 "CheckAuthorization",
229 e,
230 &reply,
231 "(sa{sv})sa{ss}us",
232 "system-bus-name", 1, "name", "s", sender,
233 action,
234 0,
235 interactive ? 1 : 0,
236 "");
237
238 if (r < 0) {
239 /* Treat no PK available as access denied */
240 if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
241 sd_bus_error_free(e);
242 return -EACCES;
243 }
244
245 return r;
246 }
247
248 r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
249 if (r < 0)
250 return r;
251
252 r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
253 if (r < 0)
254 return r;
255
256 if (authorized)
257 return 1;
258
259 if (_challenge) {
260 *_challenge = challenge;
261 return 0;
262 }
263 }
264 #endif
265
266 return -EACCES;
267 }
268
269 #ifdef ENABLE_POLKIT
270
271 typedef struct AsyncPolkitQuery {
272 sd_bus_message *request, *reply;
273 sd_bus_message_handler_t callback;
274 void *userdata;
275 sd_bus_slot *slot;
276 Hashmap *registry;
277 } AsyncPolkitQuery;
278
279 static void async_polkit_query_free(AsyncPolkitQuery *q) {
280
281 if (!q)
282 return;
283
284 sd_bus_slot_unref(q->slot);
285
286 if (q->registry && q->request)
287 hashmap_remove(q->registry, q->request);
288
289 sd_bus_message_unref(q->request);
290 sd_bus_message_unref(q->reply);
291
292 free(q);
293 }
294
295 static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
296 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
297 AsyncPolkitQuery *q = userdata;
298 int r;
299
300 assert(bus);
301 assert(reply);
302 assert(q);
303
304 q->slot = sd_bus_slot_unref(q->slot);
305 q->reply = sd_bus_message_ref(reply);
306
307 r = sd_bus_message_rewind(q->request, true);
308 if (r < 0) {
309 r = sd_bus_reply_method_errno(q->request, r, NULL);
310 goto finish;
311 }
312
313 r = q->callback(bus, q->request, q->userdata, &error_buffer);
314 r = bus_maybe_reply_error(q->request, r, &error_buffer);
315
316 finish:
317 async_polkit_query_free(q);
318
319 return r;
320 }
321
322 #endif
323
324 int bus_verify_polkit_async(
325 sd_bus *bus,
326 Hashmap **registry,
327 sd_bus_message *m,
328 const char *action,
329 bool interactive,
330 sd_bus_error *error,
331 sd_bus_message_handler_t callback,
332 void *userdata) {
333
334 #ifdef ENABLE_POLKIT
335 _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
336 AsyncPolkitQuery *q;
337 const char *sender;
338 #endif
339 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
340 uid_t uid;
341 int r;
342
343 assert(bus);
344 assert(registry);
345 assert(m);
346 assert(action);
347
348 #ifdef ENABLE_POLKIT
349 q = hashmap_get(*registry, m);
350 if (q) {
351 int authorized, challenge;
352
353 /* This is the second invocation of this function, and
354 * there's already a response from polkit, let's
355 * process it */
356 assert(q->reply);
357
358 if (sd_bus_message_is_method_error(q->reply, NULL)) {
359 const sd_bus_error *e;
360
361 /* Copy error from polkit reply */
362 e = sd_bus_message_get_error(q->reply);
363 sd_bus_error_copy(error, e);
364
365 /* Treat no PK available as access denied */
366 if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN))
367 return -EACCES;
368
369 return -sd_bus_error_get_errno(e);
370 }
371
372 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
373 if (r >= 0)
374 r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
375
376 if (r < 0)
377 return r;
378
379 if (authorized)
380 return 1;
381
382 return -EACCES;
383 }
384 #endif
385
386 r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID, &creds);
387 if (r < 0)
388 return r;
389
390 r = sd_bus_creds_get_uid(creds, &uid);
391 if (r < 0)
392 return r;
393
394 if (uid == 0)
395 return 1;
396
397 #ifdef ENABLE_POLKIT
398 sender = sd_bus_message_get_sender(m);
399 if (!sender)
400 return -EBADMSG;
401
402 r = hashmap_ensure_allocated(registry, trivial_hash_func, trivial_compare_func);
403 if (r < 0)
404 return r;
405
406 r = sd_bus_message_new_method_call(
407 bus,
408 &pk,
409 "org.freedesktop.PolicyKit1",
410 "/org/freedesktop/PolicyKit1/Authority",
411 "org.freedesktop.PolicyKit1.Authority",
412 "CheckAuthorization");
413 if (r < 0)
414 return r;
415
416 r = sd_bus_message_append(
417 pk,
418 "(sa{sv})sa{ss}us",
419 "system-bus-name", 1, "name", "s", sender,
420 action,
421 0,
422 interactive ? 1 : 0,
423 NULL);
424 if (r < 0)
425 return r;
426
427 q = new0(AsyncPolkitQuery, 1);
428 if (!q)
429 return -ENOMEM;
430
431 q->request = sd_bus_message_ref(m);
432 q->callback = callback;
433 q->userdata = userdata;
434
435 r = hashmap_put(*registry, m, q);
436 if (r < 0) {
437 async_polkit_query_free(q);
438 return r;
439 }
440
441 q->registry = *registry;
442
443 r = sd_bus_call_async(bus, &q->slot, pk, async_polkit_callback, q, 0);
444 if (r < 0) {
445 async_polkit_query_free(q);
446 return r;
447 }
448
449 return 0;
450 #endif
451
452 return -EACCES;
453 }
454
455 void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry) {
456 #ifdef ENABLE_POLKIT
457 AsyncPolkitQuery *q;
458
459 while ((q = hashmap_steal_first(registry)))
460 async_polkit_query_free(q);
461
462 hashmap_free(registry);
463 #endif
464 }
465
466 int bus_check_peercred(sd_bus *c) {
467 struct ucred ucred;
468 socklen_t l;
469 int fd;
470
471 assert(c);
472
473 fd = sd_bus_get_fd(c);
474 if (fd < 0)
475 return fd;
476
477 l = sizeof(struct ucred);
478 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
479 return -errno;
480
481 if (l != sizeof(struct ucred))
482 return -E2BIG;
483
484 if (ucred.uid != 0 && ucred.uid != geteuid())
485 return -EPERM;
486
487 return 1;
488 }
489
490 int bus_open_system_systemd(sd_bus **_bus) {
491 _cleanup_bus_unref_ sd_bus *bus = NULL;
492 int r;
493
494 assert(_bus);
495
496 if (geteuid() != 0)
497 return sd_bus_open_system(_bus);
498
499 /* If we are root and kdbus is not available, then let's talk
500 * directly to the system instance, instead of going via the
501 * bus */
502
503 #ifdef ENABLE_KDBUS
504 r = sd_bus_new(&bus);
505 if (r < 0)
506 return r;
507
508 r = sd_bus_set_address(bus, KERNEL_SYSTEM_BUS_PATH);
509 if (r < 0)
510 return r;
511
512 bus->bus_client = true;
513
514 r = sd_bus_start(bus);
515 if (r >= 0) {
516 *_bus = bus;
517 bus = NULL;
518 return 0;
519 }
520
521 bus = sd_bus_unref(bus);
522 #endif
523
524 r = sd_bus_new(&bus);
525 if (r < 0)
526 return r;
527
528 r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
529 if (r < 0)
530 return r;
531
532 r = sd_bus_start(bus);
533 if (r < 0)
534 return sd_bus_open_system(_bus);
535
536 r = bus_check_peercred(bus);
537 if (r < 0)
538 return r;
539
540 *_bus = bus;
541 bus = NULL;
542
543 return 0;
544 }
545
546 int bus_open_user_systemd(sd_bus **_bus) {
547 _cleanup_bus_unref_ sd_bus *bus = NULL;
548 _cleanup_free_ char *ee = NULL;
549 const char *e;
550 int r;
551
552 /* Try via kdbus first, and then directly */
553
554 assert(_bus);
555
556 #ifdef ENABLE_KDBUS
557 r = sd_bus_new(&bus);
558 if (r < 0)
559 return r;
560
561 if (asprintf(&bus->address, KERNEL_USER_BUS_FMT, getuid()) < 0)
562 return -ENOMEM;
563
564 bus->bus_client = true;
565
566 r = sd_bus_start(bus);
567 if (r >= 0) {
568 *_bus = bus;
569 bus = NULL;
570 return 0;
571 }
572
573 bus = sd_bus_unref(bus);
574 #endif
575
576 e = secure_getenv("XDG_RUNTIME_DIR");
577 if (!e)
578 return sd_bus_open_user(_bus);
579
580 ee = bus_address_escape(e);
581 if (!ee)
582 return -ENOMEM;
583
584 r = sd_bus_new(&bus);
585 if (r < 0)
586 return r;
587
588 bus->address = strjoin("unix:path=", ee, "/systemd/private", NULL);
589 if (!bus->address)
590 return -ENOMEM;
591
592 r = sd_bus_start(bus);
593 if (r < 0)
594 return sd_bus_open_user(_bus);
595
596 r = bus_check_peercred(bus);
597 if (r < 0)
598 return r;
599
600 *_bus = bus;
601 bus = NULL;
602
603 return 0;
604 }
605
606 int bus_print_property(const char *name, sd_bus_message *property, bool all) {
607 char type;
608 const char *contents;
609 int r;
610
611 assert(name);
612 assert(property);
613
614 r = sd_bus_message_peek_type(property, &type, &contents);
615 if (r < 0)
616 return r;
617
618 switch (type) {
619
620 case SD_BUS_TYPE_STRING: {
621 const char *s;
622
623 r = sd_bus_message_read_basic(property, type, &s);
624 if (r < 0)
625 return r;
626
627 if (all || !isempty(s))
628 printf("%s=%s\n", name, s);
629
630 return 1;
631 }
632
633 case SD_BUS_TYPE_BOOLEAN: {
634 bool b;
635
636 r = sd_bus_message_read_basic(property, type, &b);
637 if (r < 0)
638 return r;
639
640 printf("%s=%s\n", name, yes_no(b));
641
642 return 1;
643 }
644
645 case SD_BUS_TYPE_UINT64: {
646 uint64_t u;
647
648 r = sd_bus_message_read_basic(property, type, &u);
649 if (r < 0)
650 return r;
651
652 /* Yes, heuristics! But we can change this check
653 * should it turn out to not be sufficient */
654
655 if (endswith(name, "Timestamp")) {
656 char timestamp[FORMAT_TIMESTAMP_MAX], *t;
657
658 t = format_timestamp(timestamp, sizeof(timestamp), u);
659 if (t || all)
660 printf("%s=%s\n", name, strempty(t));
661
662 } else if (strstr(name, "USec")) {
663 char timespan[FORMAT_TIMESPAN_MAX];
664
665 printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
666 } else
667 printf("%s=%llu\n", name, (unsigned long long) u);
668
669 return 1;
670 }
671
672 case SD_BUS_TYPE_UINT32: {
673 uint32_t u;
674
675 r = sd_bus_message_read_basic(property, type, &u);
676 if (r < 0)
677 return r;
678
679 if (strstr(name, "UMask") || strstr(name, "Mode"))
680 printf("%s=%04o\n", name, u);
681 else
682 printf("%s=%u\n", name, (unsigned) u);
683
684 return 1;
685 }
686
687 case SD_BUS_TYPE_INT32: {
688 int32_t i;
689
690 r = sd_bus_message_read_basic(property, type, &i);
691 if (r < 0)
692 return r;
693
694 printf("%s=%i\n", name, (int) i);
695 return 1;
696 }
697
698 case SD_BUS_TYPE_DOUBLE: {
699 double d;
700
701 r = sd_bus_message_read_basic(property, type, &d);
702 if (r < 0)
703 return r;
704
705 printf("%s=%g\n", name, d);
706 return 1;
707 }
708
709 case SD_BUS_TYPE_ARRAY:
710 if (streq(contents, "s")) {
711 bool first = true;
712 const char *str;
713
714 r = sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
715 if (r < 0)
716 return r;
717
718 while((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) {
719 if (first)
720 printf("%s=", name);
721
722 printf("%s%s", first ? "" : " ", str);
723
724 first = false;
725 }
726 if (r < 0)
727 return r;
728
729 if (first && all)
730 printf("%s=", name);
731 if (!first || all)
732 puts("");
733
734 r = sd_bus_message_exit_container(property);
735 if (r < 0)
736 return r;
737
738 return 1;
739
740 } else if (streq(contents, "y")) {
741 const uint8_t *u;
742 size_t n;
743
744 r = sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
745 if (r < 0)
746 return r;
747
748 if (all || n > 0) {
749 unsigned int i;
750
751 printf("%s=", name);
752
753 for (i = 0; i < n; i++)
754 printf("%02x", u[i]);
755
756 puts("");
757 }
758
759 return 1;
760
761 } else if (streq(contents, "u")) {
762 uint32_t *u;
763 size_t n;
764
765 r = sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
766 if (r < 0)
767 return r;
768
769 if (all || n > 0) {
770 unsigned int i;
771
772 printf("%s=", name);
773
774 for (i = 0; i < n; i++)
775 printf("%08x", u[i]);
776
777 puts("");
778 }
779
780 return 1;
781 }
782
783 break;
784 }
785
786 return 0;
787 }
788
789 int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all) {
790 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
791 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
792 int r;
793
794 assert(bus);
795 assert(path);
796
797 r = sd_bus_call_method(bus,
798 dest,
799 path,
800 "org.freedesktop.DBus.Properties",
801 "GetAll",
802 &error,
803 &reply,
804 "s", "");
805 if (r < 0)
806 return r;
807
808 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
809 if (r < 0)
810 return r;
811
812 while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
813 const char *name;
814 const char *contents;
815
816 r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name);
817 if (r < 0)
818 return r;
819
820 if (!filter || strv_find(filter, name)) {
821 r = sd_bus_message_peek_type(reply, NULL, &contents);
822 if (r < 0)
823 return r;
824
825 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
826 if (r < 0)
827 return r;
828
829 r = bus_print_property(name, reply, all);
830 if (r < 0)
831 return r;
832 if (r == 0) {
833 if (all)
834 printf("%s=[unprintable]\n", name);
835 /* skip what we didn't read */
836 r = sd_bus_message_skip(reply, contents);
837 if (r < 0)
838 return r;
839 }
840
841 r = sd_bus_message_exit_container(reply);
842 if (r < 0)
843 return r;
844 } else {
845 r = sd_bus_message_skip(reply, "v");
846 if (r < 0)
847 return r;
848 }
849
850 r = sd_bus_message_exit_container(reply);
851 if (r < 0)
852 return r;
853 }
854 if (r < 0)
855 return r;
856
857 r = sd_bus_message_exit_container(reply);
858 if (r < 0)
859 return r;
860
861 return 0;
862 }
863
864 int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
865 sd_id128_t *p = userdata;
866 const void *v;
867 size_t n;
868 int r;
869
870 r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n);
871 if (r < 0)
872 return r;
873
874 if (n == 0)
875 *p = SD_ID128_NULL;
876 else if (n == 16)
877 memcpy((*p).bytes, v, n);
878 else
879 return -EINVAL;
880
881 return 0;
882 }
883
884 static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
885 char type;
886 int r;
887
888 r = sd_bus_message_peek_type(m, &type, NULL);
889 if (r < 0)
890 return r;
891
892 switch (type) {
893 case SD_BUS_TYPE_STRING: {
894 const char *s;
895 char *str;
896 char **p = userdata;
897
898 r = sd_bus_message_read_basic(m, type, &s);
899 if (r < 0)
900 break;
901
902 if (isempty(s))
903 break;
904
905 str = strdup(s);
906 if (!str) {
907 r = -ENOMEM;
908 break;
909 }
910 free(*p);
911 *p = str;
912
913 break;
914 }
915
916 case SD_BUS_TYPE_ARRAY: {
917 _cleanup_strv_free_ char **l = NULL;
918 char ***p = userdata;
919
920 r = bus_message_read_strv_extend(m, &l);
921 if (r < 0)
922 break;
923
924 strv_free(*p);
925 *p = l;
926 l = NULL;
927
928 break;
929 }
930
931 case SD_BUS_TYPE_BOOLEAN: {
932 unsigned b;
933 bool *p = userdata;
934
935 r = sd_bus_message_read_basic(m, type, &b);
936 if (r < 0)
937 break;
938
939 *p = b;
940
941 break;
942 }
943
944 case SD_BUS_TYPE_UINT32: {
945 uint64_t u;
946 uint32_t *p = userdata;
947
948 r = sd_bus_message_read_basic(m, type, &u);
949 if (r < 0)
950 break;
951
952 *p = u;
953
954 break;
955 }
956
957 case SD_BUS_TYPE_UINT64: {
958 uint64_t t;
959 uint64_t *p = userdata;
960
961 r = sd_bus_message_read_basic(m, type, &t);
962 if (r < 0)
963 break;
964
965 *p = t;
966
967 break;
968 }
969
970 default:
971 break;
972 }
973
974 return r;
975 }
976
977 int bus_map_all_properties(sd_bus *bus,
978 const char *destination,
979 const char *path,
980 const struct bus_properties_map *map,
981 void *userdata) {
982 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
983 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
984 int r;
985
986 assert(bus);
987 assert(destination);
988 assert(path);
989 assert(map);
990
991 r = sd_bus_call_method(
992 bus,
993 destination,
994 path,
995 "org.freedesktop.DBus.Properties",
996 "GetAll",
997 &error,
998 &m,
999 "s", "");
1000 if (r < 0)
1001 return r;
1002
1003 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
1004 if (r < 0)
1005 return r;
1006
1007 while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
1008 const struct bus_properties_map *prop;
1009 const char *member;
1010 const char *contents;
1011 void *v;
1012 unsigned i;
1013
1014 r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
1015 if (r < 0)
1016 return r;
1017
1018 for (i = 0, prop = NULL; map[i].member; i++)
1019 if (streq(map[i].member, member)) {
1020 prop = &map[i];
1021 break;
1022 }
1023
1024 if (prop) {
1025 r = sd_bus_message_peek_type(m, NULL, &contents);
1026 if (r < 0)
1027 return r;
1028
1029 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
1030 if (r < 0)
1031 return r;
1032
1033 v = (uint8_t *)userdata + prop->offset;
1034 if (map[i].set)
1035 r = prop->set(bus, member, m, &error, v);
1036 else
1037 r = map_basic(bus, member, m, &error, v);
1038 if (r < 0)
1039 return r;
1040
1041 r = sd_bus_message_exit_container(m);
1042 if (r < 0)
1043 return r;
1044 } else {
1045 r = sd_bus_message_skip(m, "v");
1046 if (r < 0)
1047 return r;
1048 }
1049
1050 r = sd_bus_message_exit_container(m);
1051 if (r < 0)
1052 return r;
1053 }
1054
1055 return r;
1056 }
1057
1058 int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
1059 int r;
1060
1061 assert(transport >= 0);
1062 assert(transport < _BUS_TRANSPORT_MAX);
1063 assert(bus);
1064
1065 assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
1066 assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
1067
1068 switch (transport) {
1069
1070 case BUS_TRANSPORT_LOCAL:
1071 if (user)
1072 r = sd_bus_default_user(bus);
1073 else
1074 r = sd_bus_default_system(bus);
1075
1076 break;
1077
1078 case BUS_TRANSPORT_REMOTE:
1079 r = sd_bus_open_system_remote(bus, host);
1080 break;
1081
1082 case BUS_TRANSPORT_CONTAINER:
1083 r = sd_bus_open_system_container(bus, host);
1084 break;
1085
1086 default:
1087 assert_not_reached("Hmm, unknown transport type.");
1088 }
1089
1090 return r;
1091 }
1092
1093 int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
1094 int r;
1095
1096 assert(transport >= 0);
1097 assert(transport < _BUS_TRANSPORT_MAX);
1098 assert(bus);
1099
1100 assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
1101 assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
1102
1103 switch (transport) {
1104
1105 case BUS_TRANSPORT_LOCAL:
1106 if (user)
1107 r = bus_open_user_systemd(bus);
1108 else
1109 r = bus_open_system_systemd(bus);
1110
1111 break;
1112
1113 case BUS_TRANSPORT_REMOTE:
1114 r = sd_bus_open_system_remote(bus, host);
1115 break;
1116
1117 case BUS_TRANSPORT_CONTAINER:
1118 r = sd_bus_open_system_container(bus, host);
1119 break;
1120
1121 default:
1122 assert_not_reached("Hmm, unknown transport type.");
1123 }
1124
1125 return r;
1126 }
1127
1128 int bus_property_get_bool(
1129 sd_bus *bus,
1130 const char *path,
1131 const char *interface,
1132 const char *property,
1133 sd_bus_message *reply,
1134 void *userdata,
1135 sd_bus_error *error) {
1136
1137 int b = *(bool*) userdata;
1138
1139 return sd_bus_message_append_basic(reply, 'b', &b);
1140 }
1141
1142 #if __SIZEOF_SIZE_T__ != 8
1143 int bus_property_get_size(
1144 sd_bus *bus,
1145 const char *path,
1146 const char *interface,
1147 const char *property,
1148 sd_bus_message *reply,
1149 void *userdata,
1150 sd_bus_error *error) {
1151
1152 uint64_t sz = *(size_t*) userdata;
1153
1154 return sd_bus_message_append_basic(reply, 't', &sz);
1155 }
1156 #endif
1157
1158 #if __SIZEOF_LONG__ != 8
1159 int bus_property_get_long(
1160 sd_bus *bus,
1161 const char *path,
1162 const char *interface,
1163 const char *property,
1164 sd_bus_message *reply,
1165 void *userdata,
1166 sd_bus_error *error) {
1167
1168 int64_t l = *(long*) userdata;
1169
1170 return sd_bus_message_append_basic(reply, 'x', &l);
1171 }
1172
1173 int bus_property_get_ulong(
1174 sd_bus *bus,
1175 const char *path,
1176 const char *interface,
1177 const char *property,
1178 sd_bus_message *reply,
1179 void *userdata,
1180 sd_bus_error *error) {
1181
1182 uint64_t ul = *(unsigned long*) userdata;
1183
1184 return sd_bus_message_append_basic(reply, 't', &ul);
1185 }
1186 #endif
1187
1188 int bus_log_parse_error(int r) {
1189 log_error("Failed to parse message: %s", strerror(-r));
1190 return r;
1191 }
1192
1193 int bus_log_create_error(int r) {
1194 log_error("Failed to create message: %s", strerror(-r));
1195 return r;
1196 }
1197
1198 int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
1199 assert(message);
1200 assert(u);
1201
1202 u->machine = NULL;
1203
1204 return sd_bus_message_read(
1205 message,
1206 "(ssssssouso)",
1207 &u->id,
1208 &u->description,
1209 &u->load_state,
1210 &u->active_state,
1211 &u->sub_state,
1212 &u->following,
1213 &u->unit_path,
1214 &u->job_id,
1215 &u->job_type,
1216 &u->job_path);
1217 }
1218
1219 int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) {
1220 assert(m);
1221
1222 if (r < 0) {
1223 if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
1224 sd_bus_reply_method_errno(m, r, error);
1225
1226 } else if (sd_bus_error_is_set(error)) {
1227 if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
1228 sd_bus_reply_method_error(m, error);
1229 } else
1230 return r;
1231
1232 log_debug("Failed to process message [type=%s sender=%s path=%s interface=%s member=%s signature=%s]: %s",
1233 bus_message_type_to_string(m->header->type),
1234 strna(m->sender),
1235 strna(m->path),
1236 strna(m->interface),
1237 strna(m->member),
1238 strna(m->root_container.signature),
1239 bus_error_message(error, r));
1240
1241 return 1;
1242 }
1243
1244 int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
1245 const char *eq, *field;
1246 int r;
1247
1248 assert(m);
1249 assert(assignment);
1250
1251 eq = strchr(assignment, '=');
1252 if (!eq) {
1253 log_error("Not an assignment: %s", assignment);
1254 return -EINVAL;
1255 }
1256
1257 field = strndupa(assignment, eq - assignment);
1258 eq ++;
1259
1260 if (streq(field, "CPUQuota")) {
1261
1262 if (isempty(eq)) {
1263
1264 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec");
1265 if (r < 0)
1266 return bus_log_create_error(r);
1267
1268 r = sd_bus_message_append(m, "v", "t", (usec_t) -1);
1269
1270 } else if (endswith(eq, "%")) {
1271 double percent;
1272
1273 if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) {
1274 log_error("CPU quota '%s' invalid.", eq);
1275 return -EINVAL;
1276 }
1277
1278 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec");
1279 if (r < 0)
1280 return bus_log_create_error(r);
1281
1282 r = sd_bus_message_append(m, "v", "t", (usec_t) percent * USEC_PER_SEC / 100);
1283 } else {
1284 log_error("CPU quota needs to be in percent.");
1285 return -EINVAL;
1286 }
1287
1288 if (r < 0)
1289 return bus_log_create_error(r);
1290
1291 return 0;
1292 }
1293
1294 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1295 if (r < 0)
1296 return bus_log_create_error(r);
1297
1298 if (STR_IN_SET(field,
1299 "CPUAccounting", "MemoryAccounting", "BlockIOAccounting",
1300 "SendSIGHUP", "SendSIGKILL")) {
1301
1302 r = parse_boolean(eq);
1303 if (r < 0) {
1304 log_error("Failed to parse boolean assignment %s.", assignment);
1305 return -EINVAL;
1306 }
1307
1308 r = sd_bus_message_append(m, "v", "b", r);
1309
1310 } else if (streq(field, "MemoryLimit")) {
1311 off_t bytes;
1312
1313 r = parse_size(eq, 1024, &bytes);
1314 if (r < 0) {
1315 log_error("Failed to parse bytes specification %s", assignment);
1316 return -EINVAL;
1317 }
1318
1319 r = sd_bus_message_append(m, "v", "t", (uint64_t) bytes);
1320
1321 } else if (STR_IN_SET(field, "CPUShares", "BlockIOWeight")) {
1322 uint64_t u;
1323
1324 r = safe_atou64(eq, &u);
1325 if (r < 0) {
1326 log_error("Failed to parse %s value %s.", field, eq);
1327 return -EINVAL;
1328 }
1329
1330 r = sd_bus_message_append(m, "v", "t", u);
1331
1332 } else if (STR_IN_SET(field, "User", "Group", "DevicePolicy", "KillMode"))
1333 r = sd_bus_message_append(m, "v", "s", eq);
1334
1335 else if (streq(field, "DeviceAllow")) {
1336
1337 if (isempty(eq))
1338 r = sd_bus_message_append(m, "v", "a(ss)", 0);
1339 else {
1340 const char *path, *rwm, *e;
1341
1342 e = strchr(eq, ' ');
1343 if (e) {
1344 path = strndupa(eq, e - eq);
1345 rwm = e+1;
1346 } else {
1347 path = eq;
1348 rwm = "";
1349 }
1350
1351 if (!path_startswith(path, "/dev")) {
1352 log_error("%s is not a device file in /dev.", path);
1353 return -EINVAL;
1354 }
1355
1356 r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm);
1357 }
1358
1359 } else if (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
1360
1361 if (isempty(eq))
1362 r = sd_bus_message_append(m, "v", "a(st)", 0);
1363 else {
1364 const char *path, *bandwidth, *e;
1365 off_t bytes;
1366
1367 e = strchr(eq, ' ');
1368 if (e) {
1369 path = strndupa(eq, e - eq);
1370 bandwidth = e+1;
1371 } else {
1372 log_error("Failed to parse %s value %s.", field, eq);
1373 return -EINVAL;
1374 }
1375
1376 if (!path_startswith(path, "/dev")) {
1377 log_error("%s is not a device file in /dev.", path);
1378 return -EINVAL;
1379 }
1380
1381 r = parse_size(bandwidth, 1000, &bytes);
1382 if (r < 0) {
1383 log_error("Failed to parse byte value %s.", bandwidth);
1384 return -EINVAL;
1385 }
1386
1387 r = sd_bus_message_append(m, "v", "a(st)", 1, path, (uint64_t) bytes);
1388 }
1389
1390 } else if (streq(field, "BlockIODeviceWeight")) {
1391
1392 if (isempty(eq))
1393 r = sd_bus_message_append(m, "v", "a(st)", 0);
1394 else {
1395 const char *path, *weight, *e;
1396 uint64_t u;
1397
1398 e = strchr(eq, ' ');
1399 if (e) {
1400 path = strndupa(eq, e - eq);
1401 weight = e+1;
1402 } else {
1403 log_error("Failed to parse %s value %s.", field, eq);
1404 return -EINVAL;
1405 }
1406
1407 if (!path_startswith(path, "/dev")) {
1408 log_error("%s is not a device file in /dev.", path);
1409 return -EINVAL;
1410 }
1411
1412 r = safe_atou64(weight, &u);
1413 if (r < 0) {
1414 log_error("Failed to parse %s value %s.", field, weight);
1415 return -EINVAL;
1416 }
1417 r = sd_bus_message_append(m, "v", "a(st)", path, u);
1418 }
1419
1420 } else if (rlimit_from_string(field) >= 0) {
1421 uint64_t rl;
1422
1423 if (streq(eq, "infinity"))
1424 rl = (uint64_t) -1;
1425 else {
1426 r = safe_atou64(eq, &rl);
1427 if (r < 0) {
1428 log_error("Invalid resource limit: %s", eq);
1429 return -EINVAL;
1430 }
1431 }
1432
1433 r = sd_bus_message_append(m, "v", "t", rl);
1434
1435 } else if (streq(field, "Nice")) {
1436 int32_t i;
1437
1438 r = safe_atoi32(eq, &i);
1439 if (r < 0) {
1440 log_error("Failed to parse %s value %s.", field, eq);
1441 return -EINVAL;
1442 }
1443
1444 r = sd_bus_message_append(m, "v", "i", i);
1445
1446 } else if (streq(field, "Environment")) {
1447
1448 r = sd_bus_message_append(m, "v", "as", 1, eq);
1449
1450 } else if (streq(field, "KillSignal")) {
1451 int sig;
1452
1453 sig = signal_from_string_try_harder(eq);
1454 if (sig < 0) {
1455 log_error("Failed to parse %s value %s.", field, eq);
1456 return -EINVAL;
1457 }
1458
1459 r = sd_bus_message_append(m, "v", "i", sig);
1460
1461 } else {
1462 log_error("Unknown assignment %s.", assignment);
1463 return -EINVAL;
1464 }
1465
1466 if (r < 0)
1467 return bus_log_create_error(r);
1468
1469 return 0;
1470 }