]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/bus-util.c
sd-bus: introduce sd_bus_slot objects encapsulating callbacks or vtables attached...
[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
ab9001a1 540 if (asprintf(&bus->address, KERNEL_USER_BUS_FMT, (unsigned long) 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 1106
ebcf1f97
LP
1107int bus_property_get_tristate(
1108 sd_bus *bus,
1109 const char *path,
1110 const char *interface,
1111 const char *property,
1112 sd_bus_message *reply,
1113 void *userdata,
1114 sd_bus_error *error) {
1115
1116 int *tristate = userdata;
1117
1118 return sd_bus_message_append(reply, "b", *tristate > 0);
1119}
1120
e6504030
LP
1121int bus_property_get_bool(
1122 sd_bus *bus,
1123 const char *path,
1124 const char *interface,
1125 const char *property,
1126 sd_bus_message *reply,
ebcf1f97
LP
1127 void *userdata,
1128 sd_bus_error *error) {
e6504030
LP
1129
1130 int b = *(bool*) userdata;
1131
1132 return sd_bus_message_append_basic(reply, 'b', &b);
1133}
1134
718db961
LP
1135#if __SIZEOF_SIZE_T__ != 8
1136int bus_property_get_size(
e6504030
LP
1137 sd_bus *bus,
1138 const char *path,
1139 const char *interface,
1140 const char *property,
1141 sd_bus_message *reply,
ebcf1f97
LP
1142 void *userdata,
1143 sd_bus_error *error) {
e6504030 1144
718db961 1145 uint64_t sz = *(size_t*) userdata;
e6504030 1146
718db961 1147 return sd_bus_message_append_basic(reply, 't', &sz);
e6504030 1148}
718db961
LP
1149#endif
1150
1151#if __SIZEOF_LONG__ != 8
1152int bus_property_get_long(
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 int64_t l = *(long*) userdata;
1162
1163 return sd_bus_message_append_basic(reply, 'x', &l);
1164}
1165
1166int bus_property_get_ulong(
1167 sd_bus *bus,
1168 const char *path,
1169 const char *interface,
1170 const char *property,
1171 sd_bus_message *reply,
ebcf1f97
LP
1172 void *userdata,
1173 sd_bus_error *error) {
718db961
LP
1174
1175 uint64_t ul = *(unsigned long*) userdata;
1176
1177 return sd_bus_message_append_basic(reply, 't', &ul);
1178}
1179#endif
5b30bef8
LP
1180
1181int bus_log_parse_error(int r) {
1182 log_error("Failed to parse message: %s", strerror(-r));
1183 return r;
1184}
f459b602
MAP
1185
1186int bus_log_create_error(int r) {
1187 log_error("Failed to create message: %s", strerror(-r));
1188 return r;
1189}
1190
1191int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
1192 assert(message);
1193 assert(u);
1194
1238ee09
LP
1195 u->machine = NULL;
1196
f459b602
MAP
1197 return sd_bus_message_read(
1198 message,
1199 "(ssssssouso)",
1200 &u->id,
1201 &u->description,
1202 &u->load_state,
1203 &u->active_state,
1204 &u->sub_state,
1205 &u->following,
1206 &u->unit_path,
1207 &u->job_id,
1208 &u->job_type,
1209 &u->job_path);
1210}
ebcf1f97
LP
1211
1212int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) {
1213 assert(m);
1214
1215 if (r < 0) {
1216 if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
1217 sd_bus_reply_method_errno(m, r, error);
1218
1219 } else if (sd_bus_error_is_set(error)) {
1220 if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
1221 sd_bus_reply_method_error(m, error);
ebcf1f97
LP
1222 } else
1223 return r;
1224
1225 log_debug("Failed to process message [type=%s sender=%s path=%s interface=%s member=%s signature=%s]: %s",
1226 bus_message_type_to_string(m->header->type),
1227 strna(m->sender),
1228 strna(m->path),
1229 strna(m->interface),
1230 strna(m->member),
1231 strna(m->root_container.signature),
1232 bus_error_message(error, r));
1233
1234 return 1;
1235}
df31a6c0
LP
1236
1237int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
1238 const char *eq, *field;
1239 int r;
1240
1241 assert(m);
1242 assert(assignment);
1243
1244 eq = strchr(assignment, '=');
1245 if (!eq) {
1246 log_error("Not an assignment: %s", assignment);
1247 return -EINVAL;
1248 }
1249
1250 field = strndupa(assignment, eq - assignment);
1251 eq ++;
1252
b2f8b02e
LP
1253 if (streq(field, "CPUQuota")) {
1254
1255 if (isempty(eq)) {
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) -1);
1262
1263 } else if (endswith(eq, "%")) {
1264 double percent;
1265
1266 if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) {
1267 log_error("CPU quota '%s' invalid.", eq);
1268 return -EINVAL;
1269 }
1270
1271 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec");
1272 if (r < 0)
1273 return bus_log_create_error(r);
1274
1275 r = sd_bus_message_append(m, "v", "t", (usec_t) percent * USEC_PER_SEC / 100);
1276 } else {
1277 usec_t us;
1278
1279 r = parse_sec(eq, &us);
1280 if (r < 0) {
1281 log_error("CPU quota '%s' invalid.", eq);
1282 return -EINVAL;
1283 }
1284
1285 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaUSec");
1286 if (r < 0)
1287 return bus_log_create_error(r);
1288
1289 r = sd_bus_message_append(m, "v", "t", us);
1290 }
1291
1292 if (r < 0)
1293 return bus_log_create_error(r);
1294
1295 return 0;
1296
1297 } else if (streq(field, "CPUQuotaPeriodSec")) {
1298 usec_t us;
1299
1300 r = parse_sec(eq, &us);
1301 if (r < 0) {
1302 log_error("CPU period '%s' invalid.", eq);
1303 return -EINVAL;
1304 }
1305
1306 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPeriodUSec");
1307 if (r < 0)
1308 return bus_log_create_error(r);
1309
1310 r = sd_bus_message_append(m, "v", "t", us);
1311 if (r < 0)
1312 return bus_log_create_error(r);
1313
1314 return 0;
1315 }
1316
df31a6c0
LP
1317 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1318 if (r < 0)
1319 return bus_log_create_error(r);
1320
e567439e
LP
1321 if (STR_IN_SET(field,
1322 "CPUAccounting", "MemoryAccounting", "BlockIOAccounting",
1323 "SendSIGHUP", "SendSIGKILL")) {
df31a6c0
LP
1324
1325 r = parse_boolean(eq);
1326 if (r < 0) {
1327 log_error("Failed to parse boolean assignment %s.", assignment);
1328 return -EINVAL;
1329 }
1330
1331 r = sd_bus_message_append(m, "v", "b", r);
1332
1333 } else if (streq(field, "MemoryLimit")) {
1334 off_t bytes;
1335
1336 r = parse_size(eq, 1024, &bytes);
1337 if (r < 0) {
1338 log_error("Failed to parse bytes specification %s", assignment);
1339 return -EINVAL;
1340 }
1341
1342 r = sd_bus_message_append(m, "v", "t", (uint64_t) bytes);
1343
1344 } else if (STR_IN_SET(field, "CPUShares", "BlockIOWeight")) {
1345 uint64_t u;
1346
1347 r = safe_atou64(eq, &u);
1348 if (r < 0) {
1349 log_error("Failed to parse %s value %s.", field, eq);
1350 return -EINVAL;
1351 }
1352
1353 r = sd_bus_message_append(m, "v", "t", u);
1354
e567439e 1355 } else if (STR_IN_SET(field, "User", "Group", "DevicePolicy", "KillMode"))
df31a6c0
LP
1356 r = sd_bus_message_append(m, "v", "s", eq);
1357
1358 else if (streq(field, "DeviceAllow")) {
1359
1360 if (isempty(eq))
1361 r = sd_bus_message_append(m, "v", "a(ss)", 0);
1362 else {
1363 const char *path, *rwm, *e;
1364
1365 e = strchr(eq, ' ');
1366 if (e) {
1367 path = strndupa(eq, e - eq);
1368 rwm = e+1;
1369 } else {
1370 path = eq;
1371 rwm = "";
1372 }
1373
1374 if (!path_startswith(path, "/dev")) {
1375 log_error("%s is not a device file in /dev.", path);
1376 return -EINVAL;
1377 }
1378
1379 r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm);
1380 }
1381
1382 } else if (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
1383
1384 if (isempty(eq))
1385 r = sd_bus_message_append(m, "v", "a(st)", 0);
1386 else {
1387 const char *path, *bandwidth, *e;
1388 off_t bytes;
1389
1390 e = strchr(eq, ' ');
1391 if (e) {
1392 path = strndupa(eq, e - eq);
1393 bandwidth = e+1;
1394 } else {
1395 log_error("Failed to parse %s value %s.", field, eq);
1396 return -EINVAL;
1397 }
1398
1399 if (!path_startswith(path, "/dev")) {
1400 log_error("%s is not a device file in /dev.", path);
1401 return -EINVAL;
1402 }
1403
1404 r = parse_size(bandwidth, 1000, &bytes);
1405 if (r < 0) {
1406 log_error("Failed to parse byte value %s.", bandwidth);
1407 return -EINVAL;
1408 }
1409
1410 r = sd_bus_message_append(m, "v", "a(st)", 1, path, (uint64_t) bytes);
1411 }
1412
1413 } else if (streq(field, "BlockIODeviceWeight")) {
1414
1415 if (isempty(eq))
1416 r = sd_bus_message_append(m, "v", "a(st)", 0);
1417 else {
1418 const char *path, *weight, *e;
1419 uint64_t u;
1420
1421 e = strchr(eq, ' ');
1422 if (e) {
1423 path = strndupa(eq, e - eq);
1424 weight = e+1;
1425 } else {
1426 log_error("Failed to parse %s value %s.", field, eq);
1427 return -EINVAL;
1428 }
1429
1430 if (!path_startswith(path, "/dev")) {
1431 log_error("%s is not a device file in /dev.", path);
1432 return -EINVAL;
1433 }
1434
1435 r = safe_atou64(weight, &u);
1436 if (r < 0) {
1437 log_error("Failed to parse %s value %s.", field, weight);
1438 return -EINVAL;
1439 }
1440 r = sd_bus_message_append(m, "v", "a(st)", path, u);
1441 }
1442
d584f638
LP
1443 } else if (rlimit_from_string(field) >= 0) {
1444 uint64_t rl;
1445
1446 if (streq(eq, "infinity"))
1447 rl = (uint64_t) -1;
1448 else {
1449 r = safe_atou64(eq, &rl);
1450 if (r < 0) {
1451 log_error("Invalid resource limit: %s", eq);
1452 return -EINVAL;
1453 }
1454 }
1455
1456 r = sd_bus_message_append(m, "v", "t", rl);
1457
e567439e
LP
1458 } else if (streq(field, "Nice")) {
1459 int32_t i;
1460
1461 r = safe_atoi32(eq, &i);
1462 if (r < 0) {
1463 log_error("Failed to parse %s value %s.", field, eq);
1464 return -EINVAL;
1465 }
1466
1467 r = sd_bus_message_append(m, "v", "i", i);
1468
1469 } else if (streq(field, "Environment")) {
1470
1471 r = sd_bus_message_append(m, "v", "as", 1, eq);
1472
1473 } else if (streq(field, "KillSignal")) {
1474 int sig;
1475
1476 sig = signal_from_string_try_harder(eq);
1477 if (sig < 0) {
1478 log_error("Failed to parse %s value %s.", field, eq);
1479 return -EINVAL;
1480 }
1481
1482 r = sd_bus_message_append(m, "v", "i", sig);
1483
df31a6c0
LP
1484 } else {
1485 log_error("Unknown assignment %s.", assignment);
1486 return -EINVAL;
1487 }
1488
1489 if (r < 0)
1490 return bus_log_create_error(r);
1491
1492 return 0;
1493}