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