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