]> git.proxmox.com Git - systemd.git/blame - src/shared/bus-util.c
New upstream version 236
[systemd.git] / src / shared / bus-util.c
CommitLineData
52ad194e 1/* SPDX-License-Identifier: LGPL-2.1+ */
60f067b4
JS
2/***
3 This file is part of systemd.
4
5 Copyright 2013 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
4c89c718
MP
21#include <errno.h>
22#include <fcntl.h>
23#include <inttypes.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <sys/ioctl.h>
28#include <sys/resource.h>
60f067b4 29#include <sys/socket.h>
4c89c718 30#include <unistd.h>
5eef597e 31
4c89c718 32#include "sd-bus-protocol.h"
db2df898 33#include "sd-bus.h"
e735f4d4
MP
34#include "sd-daemon.h"
35#include "sd-event.h"
4c89c718 36#include "sd-id128.h"
6300502b 37
db2df898 38#include "alloc-util.h"
6300502b
MP
39#include "bus-internal.h"
40#include "bus-label.h"
41#include "bus-message.h"
db2df898 42#include "bus-util.h"
f5e65279
MB
43#include "cap-list.h"
44#include "cgroup-util.h"
60f067b4 45#include "def.h"
db2df898
MP
46#include "escape.h"
47#include "fd-util.h"
60f067b4 48#include "missing.h"
f5e65279 49#include "mount-util.h"
2897b343 50#include "nsflags.h"
db2df898 51#include "parse-util.h"
db2df898 52#include "proc-cmdline.h"
db2df898 53#include "rlimit-util.h"
db2df898 54#include "stdio-util.h"
6300502b 55#include "strv.h"
db2df898 56#include "user-util.h"
60f067b4 57
e3bff60a 58static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
60f067b4
JS
59 sd_event *e = userdata;
60
60f067b4
JS
61 assert(m);
62 assert(e);
63
e3bff60a 64 sd_bus_close(sd_bus_message_get_bus(m));
60f067b4
JS
65 sd_event_exit(e, 0);
66
67 return 1;
68}
69
70int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) {
71 _cleanup_free_ char *match = NULL;
72 const char *unique;
73 int r;
74
75 assert(e);
76 assert(bus);
77 assert(name);
78
79 /* We unregister the name here and then wait for the
80 * NameOwnerChanged signal for this event to arrive before we
81 * quit. We do this in order to make sure that any queued
82 * requests are still processed before we really exit. */
83
84 r = sd_bus_get_unique_name(bus, &unique);
85 if (r < 0)
86 return r;
87
88 r = asprintf(&match,
89 "sender='org.freedesktop.DBus',"
90 "type='signal',"
91 "interface='org.freedesktop.DBus',"
92 "member='NameOwnerChanged',"
93 "path='/org/freedesktop/DBus',"
94 "arg0='%s',"
95 "arg1='%s',"
96 "arg2=''", name, unique);
97 if (r < 0)
98 return -ENOMEM;
99
100 r = sd_bus_add_match(bus, NULL, match, name_owner_change_callback, e);
101 if (r < 0)
102 return r;
103
104 r = sd_bus_release_name(bus, name);
105 if (r < 0)
106 return r;
107
108 return 0;
109}
110
111int bus_event_loop_with_idle(
112 sd_event *e,
113 sd_bus *bus,
114 const char *name,
115 usec_t timeout,
116 check_idle_t check_idle,
117 void *userdata) {
118 bool exiting = false;
119 int r, code;
120
121 assert(e);
122 assert(bus);
123 assert(name);
124
125 for (;;) {
126 bool idle;
127
128 r = sd_event_get_state(e);
129 if (r < 0)
130 return r;
131 if (r == SD_EVENT_FINISHED)
132 break;
133
134 if (check_idle)
135 idle = check_idle(userdata);
136 else
137 idle = true;
138
139 r = sd_event_run(e, exiting || !idle ? (uint64_t) -1 : timeout);
140 if (r < 0)
141 return r;
142
e735f4d4 143 if (r == 0 && !exiting && idle) {
60f067b4
JS
144
145 r = sd_bus_try_close(bus);
146 if (r == -EBUSY)
147 continue;
148
5eef597e
MP
149 /* Fallback for dbus1 connections: we
150 * unregister the name and wait for the
151 * response to come through for it */
e3bff60a 152 if (r == -EOPNOTSUPP) {
5eef597e
MP
153
154 /* Inform the service manager that we
155 * are going down, so that it will
156 * queue all further start requests,
157 * instead of assuming we are already
158 * running. */
159 sd_notify(false, "STOPPING=1");
60f067b4
JS
160
161 r = bus_async_unregister_and_exit(e, bus, name);
162 if (r < 0)
163 return r;
164
165 exiting = true;
166 continue;
167 }
168
169 if (r < 0)
170 return r;
171
172 sd_event_exit(e, 0);
173 break;
174 }
175 }
176
177 r = sd_event_get_exit_code(e, &code);
178 if (r < 0)
179 return r;
180
181 return code;
182}
183
184int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
4c89c718 185 _cleanup_(sd_bus_message_unrefp) sd_bus_message *rep = NULL;
60f067b4
JS
186 int r, has_owner = 0;
187
188 assert(c);
189 assert(name);
190
191 r = sd_bus_call_method(c,
192 "org.freedesktop.DBus",
193 "/org/freedesktop/dbus",
194 "org.freedesktop.DBus",
195 "NameHasOwner",
196 error,
197 &rep,
198 "s",
199 name);
200 if (r < 0)
201 return r;
202
203 r = sd_bus_message_read_basic(rep, 'b', &has_owner);
204 if (r < 0)
205 return sd_bus_error_set_errno(error, r);
206
207 return has_owner;
208}
209
e3bff60a 210static int check_good_user(sd_bus_message *m, uid_t good_user) {
4c89c718 211 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
e3bff60a
MP
212 uid_t sender_uid;
213 int r;
214
215 assert(m);
216
217 if (good_user == UID_INVALID)
218 return 0;
219
220 r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
221 if (r < 0)
222 return r;
223
224 /* Don't trust augmented credentials for authorization */
225 assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM);
226
227 r = sd_bus_creds_get_euid(creds, &sender_uid);
228 if (r < 0)
229 return r;
230
231 return sender_uid == good_user;
232}
233
234int bus_test_polkit(
5eef597e
MP
235 sd_bus_message *call,
236 int capability,
60f067b4 237 const char *action,
d9dfd233 238 const char **details,
e3bff60a 239 uid_t good_user,
60f067b4
JS
240 bool *_challenge,
241 sd_bus_error *e) {
242
60f067b4
JS
243 int r;
244
5eef597e 245 assert(call);
60f067b4
JS
246 assert(action);
247
e3bff60a
MP
248 /* Tests non-interactively! */
249
250 r = check_good_user(call, good_user);
251 if (r != 0)
252 return r;
253
5eef597e 254 r = sd_bus_query_sender_privilege(call, capability);
60f067b4
JS
255 if (r < 0)
256 return r;
5eef597e 257 else if (r > 0)
60f067b4 258 return 1;
f5e65279 259#if ENABLE_POLKIT
60f067b4 260 else {
4c89c718
MP
261 _cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL;
262 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
e3bff60a 263 int authorized = false, challenge = false;
d9dfd233 264 const char *sender, **k, **v;
60f067b4 265
5eef597e 266 sender = sd_bus_message_get_sender(call);
60f067b4
JS
267 if (!sender)
268 return -EBADMSG;
269
d9dfd233 270 r = sd_bus_message_new_method_call(
5eef597e 271 call->bus,
d9dfd233 272 &request,
60f067b4
JS
273 "org.freedesktop.PolicyKit1",
274 "/org/freedesktop/PolicyKit1/Authority",
275 "org.freedesktop.PolicyKit1.Authority",
d9dfd233
MP
276 "CheckAuthorization");
277 if (r < 0)
278 return r;
279
280 r = sd_bus_message_append(
281 request,
282 "(sa{sv})s",
60f067b4 283 "system-bus-name", 1, "name", "s", sender,
d9dfd233
MP
284 action);
285 if (r < 0)
286 return r;
287
288 r = sd_bus_message_open_container(request, 'a', "{ss}");
289 if (r < 0)
290 return r;
60f067b4 291
d9dfd233
MP
292 STRV_FOREACH_PAIR(k, v, details) {
293 r = sd_bus_message_append(request, "{ss}", *k, *v);
294 if (r < 0)
295 return r;
296 }
297
298 r = sd_bus_message_close_container(request);
299 if (r < 0)
300 return r;
301
302 r = sd_bus_message_append(request, "us", 0, NULL);
303 if (r < 0)
304 return r;
305
306 r = sd_bus_call(call->bus, request, 0, e, &reply);
60f067b4
JS
307 if (r < 0) {
308 /* Treat no PK available as access denied */
309 if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
310 sd_bus_error_free(e);
311 return -EACCES;
312 }
313
314 return r;
315 }
316
317 r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
318 if (r < 0)
319 return r;
320
321 r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
322 if (r < 0)
323 return r;
324
325 if (authorized)
326 return 1;
327
328 if (_challenge) {
329 *_challenge = challenge;
330 return 0;
331 }
332 }
333#endif
334
335 return -EACCES;
336}
337
f5e65279 338#if ENABLE_POLKIT
60f067b4
JS
339
340typedef struct AsyncPolkitQuery {
341 sd_bus_message *request, *reply;
342 sd_bus_message_handler_t callback;
343 void *userdata;
344 sd_bus_slot *slot;
345 Hashmap *registry;
346} AsyncPolkitQuery;
347
348static void async_polkit_query_free(AsyncPolkitQuery *q) {
349
350 if (!q)
351 return;
352
353 sd_bus_slot_unref(q->slot);
354
355 if (q->registry && q->request)
356 hashmap_remove(q->registry, q->request);
357
358 sd_bus_message_unref(q->request);
359 sd_bus_message_unref(q->reply);
360
361 free(q);
362}
363
e3bff60a 364static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
4c89c718 365 _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
60f067b4
JS
366 AsyncPolkitQuery *q = userdata;
367 int r;
368
60f067b4
JS
369 assert(reply);
370 assert(q);
371
372 q->slot = sd_bus_slot_unref(q->slot);
373 q->reply = sd_bus_message_ref(reply);
374
375 r = sd_bus_message_rewind(q->request, true);
376 if (r < 0) {
377 r = sd_bus_reply_method_errno(q->request, r, NULL);
378 goto finish;
379 }
380
e3bff60a 381 r = q->callback(q->request, q->userdata, &error_buffer);
60f067b4
JS
382 r = bus_maybe_reply_error(q->request, r, &error_buffer);
383
384finish:
385 async_polkit_query_free(q);
386
387 return r;
388}
389
390#endif
391
392int bus_verify_polkit_async(
5eef597e
MP
393 sd_bus_message *call,
394 int capability,
60f067b4 395 const char *action,
d9dfd233 396 const char **details,
60f067b4 397 bool interactive,
e3bff60a 398 uid_t good_user,
5eef597e
MP
399 Hashmap **registry,
400 sd_bus_error *error) {
60f067b4 401
f5e65279 402#if ENABLE_POLKIT
4c89c718 403 _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
60f067b4 404 AsyncPolkitQuery *q;
d9dfd233 405 const char *sender, **k, **v;
5eef597e
MP
406 sd_bus_message_handler_t callback;
407 void *userdata;
408 int c;
60f067b4 409#endif
60f067b4
JS
410 int r;
411
5eef597e 412 assert(call);
60f067b4 413 assert(action);
5eef597e 414 assert(registry);
60f067b4 415
e3bff60a
MP
416 r = check_good_user(call, good_user);
417 if (r != 0)
418 return r;
419
f5e65279 420#if ENABLE_POLKIT
5eef597e 421 q = hashmap_get(*registry, call);
60f067b4
JS
422 if (q) {
423 int authorized, challenge;
424
425 /* This is the second invocation of this function, and
426 * there's already a response from polkit, let's
427 * process it */
428 assert(q->reply);
429
430 if (sd_bus_message_is_method_error(q->reply, NULL)) {
431 const sd_bus_error *e;
432
433 /* Copy error from polkit reply */
434 e = sd_bus_message_get_error(q->reply);
435 sd_bus_error_copy(error, e);
436
437 /* Treat no PK available as access denied */
438 if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN))
439 return -EACCES;
440
441 return -sd_bus_error_get_errno(e);
442 }
443
444 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
445 if (r >= 0)
446 r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
447
448 if (r < 0)
449 return r;
450
451 if (authorized)
452 return 1;
453
5eef597e
MP
454 if (challenge)
455 return sd_bus_error_set(error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required.");
456
60f067b4
JS
457 return -EACCES;
458 }
459#endif
460
5eef597e 461 r = sd_bus_query_sender_privilege(call, capability);
60f067b4
JS
462 if (r < 0)
463 return r;
5eef597e 464 else if (r > 0)
60f067b4
JS
465 return 1;
466
f5e65279 467#if ENABLE_POLKIT
5eef597e
MP
468 if (sd_bus_get_current_message(call->bus) != call)
469 return -EINVAL;
470
471 callback = sd_bus_get_current_handler(call->bus);
472 if (!callback)
473 return -EINVAL;
474
475 userdata = sd_bus_get_current_userdata(call->bus);
476
477 sender = sd_bus_message_get_sender(call);
60f067b4
JS
478 if (!sender)
479 return -EBADMSG;
480
5eef597e
MP
481 c = sd_bus_message_get_allow_interactive_authorization(call);
482 if (c < 0)
483 return c;
484 if (c > 0)
485 interactive = true;
486
487 r = hashmap_ensure_allocated(registry, NULL);
60f067b4
JS
488 if (r < 0)
489 return r;
490
491 r = sd_bus_message_new_method_call(
5eef597e 492 call->bus,
60f067b4
JS
493 &pk,
494 "org.freedesktop.PolicyKit1",
495 "/org/freedesktop/PolicyKit1/Authority",
496 "org.freedesktop.PolicyKit1.Authority",
497 "CheckAuthorization");
498 if (r < 0)
499 return r;
500
501 r = sd_bus_message_append(
502 pk,
d9dfd233 503 "(sa{sv})s",
60f067b4 504 "system-bus-name", 1, "name", "s", sender,
d9dfd233
MP
505 action);
506 if (r < 0)
507 return r;
508
509 r = sd_bus_message_open_container(pk, 'a', "{ss}");
510 if (r < 0)
511 return r;
512
513 STRV_FOREACH_PAIR(k, v, details) {
514 r = sd_bus_message_append(pk, "{ss}", *k, *v);
515 if (r < 0)
516 return r;
517 }
518
519 r = sd_bus_message_close_container(pk);
520 if (r < 0)
521 return r;
522
523 r = sd_bus_message_append(pk, "us", !!interactive, NULL);
60f067b4
JS
524 if (r < 0)
525 return r;
526
527 q = new0(AsyncPolkitQuery, 1);
528 if (!q)
529 return -ENOMEM;
530
5eef597e 531 q->request = sd_bus_message_ref(call);
60f067b4
JS
532 q->callback = callback;
533 q->userdata = userdata;
534
5eef597e 535 r = hashmap_put(*registry, call, q);
60f067b4
JS
536 if (r < 0) {
537 async_polkit_query_free(q);
538 return r;
539 }
540
541 q->registry = *registry;
542
5eef597e 543 r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0);
60f067b4
JS
544 if (r < 0) {
545 async_polkit_query_free(q);
546 return r;
547 }
548
549 return 0;
550#endif
551
552 return -EACCES;
553}
554
5eef597e 555void bus_verify_polkit_async_registry_free(Hashmap *registry) {
f5e65279 556#if ENABLE_POLKIT
52ad194e 557 hashmap_free_with_destructor(registry, async_polkit_query_free);
60f067b4
JS
558#endif
559}
560
561int bus_check_peercred(sd_bus *c) {
562 struct ucred ucred;
563 socklen_t l;
564 int fd;
565
566 assert(c);
567
568 fd = sd_bus_get_fd(c);
569 if (fd < 0)
570 return fd;
571
572 l = sizeof(struct ucred);
573 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
574 return -errno;
575
576 if (l != sizeof(struct ucred))
577 return -E2BIG;
578
579 if (ucred.uid != 0 && ucred.uid != geteuid())
580 return -EPERM;
581
582 return 1;
583}
584
6300502b 585int bus_connect_system_systemd(sd_bus **_bus) {
4c89c718 586 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
60f067b4
JS
587 int r;
588
589 assert(_bus);
590
591 if (geteuid() != 0)
6300502b 592 return sd_bus_default_system(_bus);
60f067b4 593
f5e65279
MB
594 /* If we are root then let's talk directly to the system
595 * instance, instead of going via the bus */
60f067b4
JS
596
597 r = sd_bus_new(&bus);
598 if (r < 0)
599 return r;
600
601 r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
602 if (r < 0)
603 return r;
604
605 r = sd_bus_start(bus);
606 if (r < 0)
6300502b 607 return sd_bus_default_system(_bus);
60f067b4
JS
608
609 r = bus_check_peercred(bus);
610 if (r < 0)
611 return r;
612
613 *_bus = bus;
614 bus = NULL;
615
616 return 0;
617}
618
6300502b 619int bus_connect_user_systemd(sd_bus **_bus) {
4c89c718 620 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
60f067b4
JS
621 _cleanup_free_ char *ee = NULL;
622 const char *e;
623 int r;
624
60f067b4
JS
625 assert(_bus);
626
60f067b4
JS
627 e = secure_getenv("XDG_RUNTIME_DIR");
628 if (!e)
6300502b 629 return sd_bus_default_user(_bus);
60f067b4
JS
630
631 ee = bus_address_escape(e);
632 if (!ee)
633 return -ENOMEM;
634
635 r = sd_bus_new(&bus);
636 if (r < 0)
637 return r;
638
2897b343 639 bus->address = strjoin("unix:path=", ee, "/systemd/private");
60f067b4
JS
640 if (!bus->address)
641 return -ENOMEM;
642
643 r = sd_bus_start(bus);
644 if (r < 0)
6300502b 645 return sd_bus_default_user(_bus);
60f067b4
JS
646
647 r = bus_check_peercred(bus);
648 if (r < 0)
649 return r;
650
651 *_bus = bus;
652 bus = NULL;
653
654 return 0;
655}
656
aa27b158
MP
657#define print_property(name, fmt, ...) \
658 do { \
659 if (value) \
660 printf(fmt "\n", __VA_ARGS__); \
661 else \
662 printf("%s=" fmt "\n", name, __VA_ARGS__); \
52ad194e 663 } while (0)
aa27b158
MP
664
665int bus_print_property(const char *name, sd_bus_message *property, bool value, bool all) {
60f067b4
JS
666 char type;
667 const char *contents;
668 int r;
669
670 assert(name);
671 assert(property);
672
673 r = sd_bus_message_peek_type(property, &type, &contents);
674 if (r < 0)
675 return r;
676
677 switch (type) {
678
679 case SD_BUS_TYPE_STRING: {
680 const char *s;
681
682 r = sd_bus_message_read_basic(property, type, &s);
683 if (r < 0)
684 return r;
685
f47781d8 686 if (all || !isempty(s)) {
81c58355 687 bool good;
f47781d8 688
81c58355
MB
689 /* This property has a single value, so we need to take
690 * care not to print a new line, everything else is OK. */
691 good = !strchr(s, '\n');
692 print_property(name, "%s", good ? s : "[unprintable]");
f47781d8 693 }
60f067b4
JS
694
695 return 1;
696 }
697
698 case SD_BUS_TYPE_BOOLEAN: {
5eef597e 699 int b;
60f067b4
JS
700
701 r = sd_bus_message_read_basic(property, type, &b);
702 if (r < 0)
703 return r;
704
aa27b158 705 print_property(name, "%s", yes_no(b));
60f067b4
JS
706
707 return 1;
708 }
709
710 case SD_BUS_TYPE_UINT64: {
711 uint64_t u;
712
713 r = sd_bus_message_read_basic(property, type, &u);
714 if (r < 0)
715 return r;
716
717 /* Yes, heuristics! But we can change this check
718 * should it turn out to not be sufficient */
719
720 if (endswith(name, "Timestamp")) {
721 char timestamp[FORMAT_TIMESTAMP_MAX], *t;
722
723 t = format_timestamp(timestamp, sizeof(timestamp), u);
724 if (t || all)
aa27b158 725 print_property(name, "%s", strempty(t));
60f067b4
JS
726
727 } else if (strstr(name, "USec")) {
728 char timespan[FORMAT_TIMESPAN_MAX];
729
aa27b158 730 print_property(name, "%s", format_timespan(timespan, sizeof(timespan), u, 0));
2897b343
MP
731 } else if (streq(name, "RestrictNamespaces")) {
732 _cleanup_free_ char *s = NULL;
f5e65279 733 const char *result;
2897b343
MP
734
735 if ((u & NAMESPACE_FLAGS_ALL) == 0)
736 result = "yes";
737 else if ((u & NAMESPACE_FLAGS_ALL) == NAMESPACE_FLAGS_ALL)
738 result = "no";
739 else {
740 r = namespace_flag_to_string_many(u, &s);
741 if (r < 0)
742 return r;
743
744 result = s;
745 }
746
747 print_property(name, "%s", result);
f5e65279
MB
748
749 } else if (streq(name, "MountFlags")) {
750 const char *result;
751
752 result = mount_propagation_flags_to_string(u);
753 if (!result)
754 return -EINVAL;
755
756 print_property(name, "%s", result);
757
758 } else if (STR_IN_SET(name, "CapabilityBoundingSet", "AmbientCapabilities")) {
759 _cleanup_free_ char *s = NULL;
760
761 r = capability_set_to_string_alloc(u, &s);
762 if (r < 0)
763 return r;
764
765 print_property(name, "%s", s);
766
767 } else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) ||
768 (STR_IN_SET(name, "CPUShares", "StartupCPUShares") && u == CGROUP_CPU_SHARES_INVALID) ||
769 (STR_IN_SET(name, "BlockIOWeight", "StartupBlockIOWeight") && u == CGROUP_BLKIO_WEIGHT_INVALID) ||
770 (STR_IN_SET(name, "MemoryCurrent", "TasksCurrent") && u == (uint64_t) -1) ||
771 (endswith(name, "NSec") && u == (uint64_t) -1))
772
773 print_property(name, "%s", "[not set]");
774
775 else if ((STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) ||
776 (STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == (uint64_t) -1) ||
777 (startswith(name, "Limit") && u == (uint64_t) -1) ||
778 (startswith(name, "DefaultLimit") && u == (uint64_t) -1))
779
780 print_property(name, "%s", "infinity");
781 else
aa27b158 782 print_property(name, "%"PRIu64, u);
60f067b4
JS
783
784 return 1;
785 }
786
e3bff60a
MP
787 case SD_BUS_TYPE_INT64: {
788 int64_t i;
789
790 r = sd_bus_message_read_basic(property, type, &i);
791 if (r < 0)
792 return r;
793
aa27b158 794 print_property(name, "%"PRIi64, i);
e3bff60a
MP
795
796 return 1;
797 }
798
60f067b4
JS
799 case SD_BUS_TYPE_UINT32: {
800 uint32_t u;
801
802 r = sd_bus_message_read_basic(property, type, &u);
803 if (r < 0)
804 return r;
805
806 if (strstr(name, "UMask") || strstr(name, "Mode"))
aa27b158 807 print_property(name, "%04o", u);
f5e65279
MB
808 else if (streq(name, "UID")) {
809 if (u == UID_INVALID)
810 print_property(name, "%s", "[not set]");
811 else
812 print_property(name, "%"PRIu32, u);
813 } else if (streq(name, "GID")) {
814 if (u == GID_INVALID)
815 print_property(name, "%s", "[not set]");
816 else
817 print_property(name, "%"PRIu32, u);
818 } else
aa27b158 819 print_property(name, "%"PRIu32, u);
60f067b4
JS
820
821 return 1;
822 }
823
824 case SD_BUS_TYPE_INT32: {
825 int32_t i;
826
827 r = sd_bus_message_read_basic(property, type, &i);
828 if (r < 0)
829 return r;
830
aa27b158 831 print_property(name, "%"PRIi32, i);
60f067b4
JS
832 return 1;
833 }
834
835 case SD_BUS_TYPE_DOUBLE: {
836 double d;
837
838 r = sd_bus_message_read_basic(property, type, &d);
839 if (r < 0)
840 return r;
841
aa27b158 842 print_property(name, "%g", d);
60f067b4
JS
843 return 1;
844 }
845
846 case SD_BUS_TYPE_ARRAY:
847 if (streq(contents, "s")) {
848 bool first = true;
849 const char *str;
850
851 r = sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
852 if (r < 0)
853 return r;
854
aa27b158 855 while ((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) {
81c58355 856 bool good;
f47781d8 857
aa27b158 858 if (first && !value)
60f067b4
JS
859 printf("%s=", name);
860
f5e65279 861 /* This property has multiple space-separated values, so
81c58355
MB
862 * neither spaces not newlines can be allowed in a value. */
863 good = str[strcspn(str, " \n")] == '\0';
f47781d8 864
81c58355 865 printf("%s%s", first ? "" : " ", good ? str : "[unprintable]");
60f067b4
JS
866
867 first = false;
868 }
869 if (r < 0)
870 return r;
871
aa27b158 872 if (first && all && !value)
60f067b4
JS
873 printf("%s=", name);
874 if (!first || all)
875 puts("");
876
877 r = sd_bus_message_exit_container(property);
878 if (r < 0)
879 return r;
880
881 return 1;
882
883 } else if (streq(contents, "y")) {
884 const uint8_t *u;
885 size_t n;
886
887 r = sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
888 if (r < 0)
889 return r;
890
891 if (all || n > 0) {
892 unsigned int i;
893
aa27b158
MP
894 if (!value)
895 printf("%s=", name);
60f067b4
JS
896
897 for (i = 0; i < n; i++)
898 printf("%02x", u[i]);
899
900 puts("");
901 }
902
903 return 1;
904
905 } else if (streq(contents, "u")) {
906 uint32_t *u;
907 size_t n;
908
909 r = sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
910 if (r < 0)
911 return r;
912
913 if (all || n > 0) {
914 unsigned int i;
915
aa27b158
MP
916 if (!value)
917 printf("%s=", name);
60f067b4
JS
918
919 for (i = 0; i < n; i++)
920 printf("%08x", u[i]);
921
922 puts("");
923 }
924
925 return 1;
926 }
927
928 break;
929 }
930
931 return 0;
932}
933
aa27b158 934int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool value, bool all) {
4c89c718
MP
935 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
936 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
60f067b4
JS
937 int r;
938
939 assert(bus);
940 assert(path);
941
942 r = sd_bus_call_method(bus,
943 dest,
944 path,
945 "org.freedesktop.DBus.Properties",
946 "GetAll",
947 &error,
948 &reply,
949 "s", "");
950 if (r < 0)
951 return r;
952
953 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
954 if (r < 0)
955 return r;
956
957 while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
958 const char *name;
959 const char *contents;
960
961 r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name);
962 if (r < 0)
963 return r;
964
965 if (!filter || strv_find(filter, name)) {
966 r = sd_bus_message_peek_type(reply, NULL, &contents);
967 if (r < 0)
968 return r;
969
970 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
971 if (r < 0)
972 return r;
973
aa27b158 974 r = bus_print_property(name, reply, value, all);
60f067b4
JS
975 if (r < 0)
976 return r;
977 if (r == 0) {
978 if (all)
979 printf("%s=[unprintable]\n", name);
980 /* skip what we didn't read */
981 r = sd_bus_message_skip(reply, contents);
982 if (r < 0)
983 return r;
984 }
985
986 r = sd_bus_message_exit_container(reply);
987 if (r < 0)
988 return r;
989 } else {
990 r = sd_bus_message_skip(reply, "v");
991 if (r < 0)
992 return r;
993 }
994
995 r = sd_bus_message_exit_container(reply);
996 if (r < 0)
997 return r;
998 }
999 if (r < 0)
1000 return r;
1001
1002 r = sd_bus_message_exit_container(reply);
1003 if (r < 0)
1004 return r;
1005
1006 return 0;
1007}
1008
1009int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
1010 sd_id128_t *p = userdata;
1011 const void *v;
1012 size_t n;
1013 int r;
1014
1015 r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n);
1016 if (r < 0)
1017 return r;
1018
1019 if (n == 0)
1020 *p = SD_ID128_NULL;
1021 else if (n == 16)
1022 memcpy((*p).bytes, v, n);
1023 else
1024 return -EINVAL;
1025
1026 return 0;
1027}
1028
1029static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
1030 char type;
1031 int r;
1032
1033 r = sd_bus_message_peek_type(m, &type, NULL);
1034 if (r < 0)
1035 return r;
1036
1037 switch (type) {
8a584da2 1038
60f067b4 1039 case SD_BUS_TYPE_STRING: {
60f067b4 1040 char **p = userdata;
8a584da2 1041 const char *s;
60f067b4
JS
1042
1043 r = sd_bus_message_read_basic(m, type, &s);
1044 if (r < 0)
8a584da2 1045 return r;
60f067b4
JS
1046
1047 if (isempty(s))
8a584da2 1048 s = NULL;
60f067b4 1049
8a584da2 1050 return free_and_strdup(p, s);
60f067b4
JS
1051 }
1052
1053 case SD_BUS_TYPE_ARRAY: {
13d276d0
MP
1054 _cleanup_strv_free_ char **l = NULL;
1055 char ***p = userdata;
60f067b4
JS
1056
1057 r = bus_message_read_strv_extend(m, &l);
1058 if (r < 0)
8a584da2 1059 return r;
60f067b4
JS
1060
1061 strv_free(*p);
1062 *p = l;
1063 l = NULL;
8a584da2 1064 return 0;
60f067b4
JS
1065 }
1066
1067 case SD_BUS_TYPE_BOOLEAN: {
1068 unsigned b;
5a920b42 1069 int *p = userdata;
60f067b4
JS
1070
1071 r = sd_bus_message_read_basic(m, type, &b);
1072 if (r < 0)
8a584da2 1073 return r;
60f067b4
JS
1074
1075 *p = b;
8a584da2 1076 return 0;
60f067b4
JS
1077 }
1078
8a584da2 1079 case SD_BUS_TYPE_INT32:
60f067b4 1080 case SD_BUS_TYPE_UINT32: {
8a584da2 1081 uint32_t u, *p = userdata;
60f067b4
JS
1082
1083 r = sd_bus_message_read_basic(m, type, &u);
1084 if (r < 0)
8a584da2 1085 return r;
60f067b4
JS
1086
1087 *p = u;
8a584da2 1088 return 0;
60f067b4
JS
1089 }
1090
8a584da2 1091 case SD_BUS_TYPE_INT64:
60f067b4 1092 case SD_BUS_TYPE_UINT64: {
8a584da2 1093 uint64_t t, *p = userdata;
60f067b4
JS
1094
1095 r = sd_bus_message_read_basic(m, type, &t);
1096 if (r < 0)
8a584da2 1097 return r;
60f067b4
JS
1098
1099 *p = t;
8a584da2 1100 return 0;
60f067b4
JS
1101 }
1102
5a920b42 1103 case SD_BUS_TYPE_DOUBLE: {
8a584da2 1104 double d, *p = userdata;
5a920b42
MP
1105
1106 r = sd_bus_message_read_basic(m, type, &d);
1107 if (r < 0)
8a584da2 1108 return r;
5a920b42
MP
1109
1110 *p = d;
8a584da2
MP
1111 return 0;
1112 }}
5a920b42 1113
8a584da2 1114 return -EOPNOTSUPP;
60f067b4
JS
1115}
1116
e3bff60a
MP
1117int bus_message_map_all_properties(
1118 sd_bus_message *m,
1119 const struct bus_properties_map *map,
2897b343 1120 sd_bus_error *error,
e3bff60a
MP
1121 void *userdata) {
1122
60f067b4
JS
1123 int r;
1124
5eef597e 1125 assert(m);
60f067b4
JS
1126 assert(map);
1127
60f067b4
JS
1128 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
1129 if (r < 0)
1130 return r;
1131
1132 while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
1133 const struct bus_properties_map *prop;
1134 const char *member;
1135 const char *contents;
1136 void *v;
1137 unsigned i;
1138
1139 r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
1140 if (r < 0)
1141 return r;
1142
1143 for (i = 0, prop = NULL; map[i].member; i++)
1144 if (streq(map[i].member, member)) {
1145 prop = &map[i];
1146 break;
1147 }
1148
1149 if (prop) {
1150 r = sd_bus_message_peek_type(m, NULL, &contents);
1151 if (r < 0)
1152 return r;
1153
1154 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
1155 if (r < 0)
1156 return r;
1157
1158 v = (uint8_t *)userdata + prop->offset;
1159 if (map[i].set)
2897b343 1160 r = prop->set(sd_bus_message_get_bus(m), member, m, error, v);
60f067b4 1161 else
2897b343 1162 r = map_basic(sd_bus_message_get_bus(m), member, m, error, v);
60f067b4
JS
1163 if (r < 0)
1164 return r;
1165
1166 r = sd_bus_message_exit_container(m);
1167 if (r < 0)
1168 return r;
1169 } else {
1170 r = sd_bus_message_skip(m, "v");
1171 if (r < 0)
1172 return r;
1173 }
1174
1175 r = sd_bus_message_exit_container(m);
1176 if (r < 0)
1177 return r;
1178 }
e3bff60a
MP
1179 if (r < 0)
1180 return r;
60f067b4 1181
5eef597e
MP
1182 return sd_bus_message_exit_container(m);
1183}
1184
e3bff60a
MP
1185int bus_message_map_properties_changed(
1186 sd_bus_message *m,
1187 const struct bus_properties_map *map,
2897b343 1188 sd_bus_error *error,
e3bff60a
MP
1189 void *userdata) {
1190
5eef597e
MP
1191 const char *member;
1192 int r, invalidated, i;
1193
5eef597e
MP
1194 assert(m);
1195 assert(map);
1196
2897b343 1197 r = bus_message_map_all_properties(m, map, error, userdata);
5eef597e
MP
1198 if (r < 0)
1199 return r;
1200
1201 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s");
1202 if (r < 0)
1203 return r;
1204
1205 invalidated = 0;
1206 while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member)) > 0)
1207 for (i = 0; map[i].member; i++)
1208 if (streq(map[i].member, member)) {
1209 ++invalidated;
1210 break;
1211 }
e3bff60a
MP
1212 if (r < 0)
1213 return r;
5eef597e
MP
1214
1215 r = sd_bus_message_exit_container(m);
1216 if (r < 0)
1217 return r;
1218
1219 return invalidated;
1220}
1221
e3bff60a
MP
1222int bus_map_all_properties(
1223 sd_bus *bus,
1224 const char *destination,
1225 const char *path,
1226 const struct bus_properties_map *map,
2897b343 1227 sd_bus_error *error,
e3bff60a
MP
1228 void *userdata) {
1229
4c89c718 1230 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
5eef597e
MP
1231 int r;
1232
1233 assert(bus);
1234 assert(destination);
1235 assert(path);
1236 assert(map);
1237
1238 r = sd_bus_call_method(
1239 bus,
1240 destination,
1241 path,
1242 "org.freedesktop.DBus.Properties",
1243 "GetAll",
2897b343 1244 error,
5eef597e
MP
1245 &m,
1246 "s", "");
1247 if (r < 0)
1248 return r;
1249
2897b343 1250 return bus_message_map_all_properties(m, map, error, userdata);
60f067b4
JS
1251}
1252
8a584da2
MP
1253int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **ret) {
1254 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
60f067b4
JS
1255 int r;
1256
1257 assert(transport >= 0);
1258 assert(transport < _BUS_TRANSPORT_MAX);
8a584da2 1259 assert(ret);
60f067b4
JS
1260
1261 assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
e3bff60a 1262 assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
60f067b4
JS
1263
1264 switch (transport) {
1265
1266 case BUS_TRANSPORT_LOCAL:
1267 if (user)
8a584da2 1268 r = sd_bus_default_user(&bus);
60f067b4 1269 else
8a584da2 1270 r = sd_bus_default_system(&bus);
60f067b4
JS
1271
1272 break;
1273
1274 case BUS_TRANSPORT_REMOTE:
8a584da2 1275 r = sd_bus_open_system_remote(&bus, host);
60f067b4
JS
1276 break;
1277
e735f4d4 1278 case BUS_TRANSPORT_MACHINE:
8a584da2 1279 r = sd_bus_open_system_machine(&bus, host);
60f067b4
JS
1280 break;
1281
1282 default:
1283 assert_not_reached("Hmm, unknown transport type.");
1284 }
8a584da2
MP
1285 if (r < 0)
1286 return r;
60f067b4 1287
8a584da2
MP
1288 r = sd_bus_set_exit_on_disconnect(bus, true);
1289 if (r < 0)
1290 return r;
1291
1292 *ret = bus;
1293 bus = NULL;
1294
1295 return 0;
60f067b4
JS
1296}
1297
6300502b 1298int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
60f067b4
JS
1299 int r;
1300
1301 assert(transport >= 0);
1302 assert(transport < _BUS_TRANSPORT_MAX);
1303 assert(bus);
1304
1305 assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
e3bff60a 1306 assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
60f067b4
JS
1307
1308 switch (transport) {
1309
1310 case BUS_TRANSPORT_LOCAL:
1311 if (user)
6300502b 1312 r = bus_connect_user_systemd(bus);
60f067b4 1313 else
6300502b 1314 r = bus_connect_system_systemd(bus);
60f067b4
JS
1315
1316 break;
1317
1318 case BUS_TRANSPORT_REMOTE:
1319 r = sd_bus_open_system_remote(bus, host);
1320 break;
1321
e735f4d4
MP
1322 case BUS_TRANSPORT_MACHINE:
1323 r = sd_bus_open_system_machine(bus, host);
60f067b4
JS
1324 break;
1325
1326 default:
1327 assert_not_reached("Hmm, unknown transport type.");
1328 }
1329
1330 return r;
1331}
1332
1333int bus_property_get_bool(
1334 sd_bus *bus,
1335 const char *path,
1336 const char *interface,
1337 const char *property,
1338 sd_bus_message *reply,
1339 void *userdata,
1340 sd_bus_error *error) {
1341
1342 int b = *(bool*) userdata;
1343
1344 return sd_bus_message_append_basic(reply, 'b', &b);
1345}
1346
8a584da2
MP
1347int bus_property_get_id128(
1348 sd_bus *bus,
1349 const char *path,
1350 const char *interface,
1351 const char *property,
1352 sd_bus_message *reply,
1353 void *userdata,
1354 sd_bus_error *error) {
1355
1356 sd_id128_t *id = userdata;
1357
1358 if (sd_id128_is_null(*id)) /* Add an empty array if the ID is zero */
1359 return sd_bus_message_append(reply, "ay", 0);
1360 else
1361 return sd_bus_message_append_array(reply, 'y', id->bytes, 16);
1362}
1363
60f067b4
JS
1364#if __SIZEOF_SIZE_T__ != 8
1365int bus_property_get_size(
1366 sd_bus *bus,
1367 const char *path,
1368 const char *interface,
1369 const char *property,
1370 sd_bus_message *reply,
1371 void *userdata,
1372 sd_bus_error *error) {
1373
1374 uint64_t sz = *(size_t*) userdata;
1375
1376 return sd_bus_message_append_basic(reply, 't', &sz);
1377}
1378#endif
1379
1380#if __SIZEOF_LONG__ != 8
1381int bus_property_get_long(
1382 sd_bus *bus,
1383 const char *path,
1384 const char *interface,
1385 const char *property,
1386 sd_bus_message *reply,
1387 void *userdata,
1388 sd_bus_error *error) {
1389
1390 int64_t l = *(long*) userdata;
1391
1392 return sd_bus_message_append_basic(reply, 'x', &l);
1393}
1394
1395int bus_property_get_ulong(
1396 sd_bus *bus,
1397 const char *path,
1398 const char *interface,
1399 const char *property,
1400 sd_bus_message *reply,
1401 void *userdata,
1402 sd_bus_error *error) {
1403
1404 uint64_t ul = *(unsigned long*) userdata;
1405
1406 return sd_bus_message_append_basic(reply, 't', &ul);
1407}
1408#endif
1409
1410int bus_log_parse_error(int r) {
f47781d8 1411 return log_error_errno(r, "Failed to parse bus message: %m");
60f067b4
JS
1412}
1413
1414int bus_log_create_error(int r) {
f47781d8 1415 return log_error_errno(r, "Failed to create bus message: %m");
60f067b4
JS
1416}
1417
e3bff60a
MP
1418/**
1419 * bus_path_encode_unique() - encode unique object path
1420 * @b: bus connection or NULL
1421 * @prefix: object path prefix
1422 * @sender_id: unique-name of client, or NULL
1423 * @external_id: external ID to be chosen by client, or NULL
1424 * @ret_path: storage for encoded object path pointer
1425 *
1426 * Whenever we provide a bus API that allows clients to create and manage
1427 * server-side objects, we need to provide a unique name for these objects. If
1428 * we let the server choose the name, we suffer from a race condition: If a
1429 * client creates an object asynchronously, it cannot destroy that object until
1430 * it received the method reply. It cannot know the name of the new object,
1431 * thus, it cannot destroy it. Furthermore, it enforces a round-trip.
1432 *
1433 * Therefore, many APIs allow the client to choose the unique name for newly
1434 * created objects. There're two problems to solve, though:
1435 * 1) Object names are usually defined via dbus object paths, which are
1436 * usually globally namespaced. Therefore, multiple clients must be able
1437 * to choose unique object names without interference.
1438 * 2) If multiple libraries share the same bus connection, they must be
1439 * able to choose unique object names without interference.
1440 * The first problem is solved easily by prefixing a name with the
1441 * unique-bus-name of a connection. The server side must enforce this and
1442 * reject any other name. The second problem is solved by providing unique
1443 * suffixes from within sd-bus.
1444 *
1445 * This helper allows clients to create unique object-paths. It uses the
1446 * template '/prefix/sender_id/external_id' and returns the new path in
1447 * @ret_path (must be freed by the caller).
1448 * If @sender_id is NULL, the unique-name of @b is used. If @external_id is
1449 * NULL, this function allocates a unique suffix via @b (by requesting a new
1450 * cookie). If both @sender_id and @external_id are given, @b can be passed as
1451 * NULL.
1452 *
1453 * Returns: 0 on success, negative error code on failure.
1454 */
1455int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path) {
1456 _cleanup_free_ char *sender_label = NULL, *external_label = NULL;
1457 char external_buf[DECIMAL_STR_MAX(uint64_t)], *p;
1458 int r;
1459
1460 assert_return(b || (sender_id && external_id), -EINVAL);
1461 assert_return(object_path_is_valid(prefix), -EINVAL);
1462 assert_return(ret_path, -EINVAL);
1463
1464 if (!sender_id) {
1465 r = sd_bus_get_unique_name(b, &sender_id);
1466 if (r < 0)
1467 return r;
1468 }
1469
1470 if (!external_id) {
1471 xsprintf(external_buf, "%"PRIu64, ++b->cookie);
1472 external_id = external_buf;
1473 }
1474
1475 sender_label = bus_label_escape(sender_id);
1476 if (!sender_label)
1477 return -ENOMEM;
1478
1479 external_label = bus_label_escape(external_id);
1480 if (!external_label)
1481 return -ENOMEM;
1482
2897b343 1483 p = strjoin(prefix, "/", sender_label, "/", external_label);
e3bff60a
MP
1484 if (!p)
1485 return -ENOMEM;
1486
1487 *ret_path = p;
1488 return 0;
1489}
1490
1491/**
1492 * bus_path_decode_unique() - decode unique object path
1493 * @path: object path to decode
1494 * @prefix: object path prefix
1495 * @ret_sender: output parameter for sender-id label
1496 * @ret_external: output parameter for external-id label
1497 *
1498 * This does the reverse of bus_path_encode_unique() (see its description for
1499 * details). Both trailing labels, sender-id and external-id, are unescaped and
1500 * returned in the given output parameters (the caller must free them).
1501 *
1502 * Note that this function returns 0 if the path does not match the template
1503 * (see bus_path_encode_unique()), 1 if it matched.
1504 *
1505 * Returns: Negative error code on failure, 0 if the given object path does not
1506 * match the template (return parameters are set to NULL), 1 if it was
1507 * parsed successfully (return parameters contain allocated labels).
1508 */
1509int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external) {
1510 const char *p, *q;
1511 char *sender, *external;
1512
1513 assert(object_path_is_valid(path));
1514 assert(object_path_is_valid(prefix));
1515 assert(ret_sender);
1516 assert(ret_external);
1517
1518 p = object_path_startswith(path, prefix);
1519 if (!p) {
1520 *ret_sender = NULL;
1521 *ret_external = NULL;
1522 return 0;
1523 }
1524
1525 q = strchr(p, '/');
1526 if (!q) {
1527 *ret_sender = NULL;
1528 *ret_external = NULL;
1529 return 0;
1530 }
1531
1532 sender = bus_label_unescape_n(p, q - p);
1533 external = bus_label_unescape(q + 1);
1534 if (!sender || !external) {
1535 free(sender);
1536 free(external);
1537 return -ENOMEM;
1538 }
1539
1540 *ret_sender = sender;
1541 *ret_external = external;
1542 return 1;
1543}
1544
db2df898
MP
1545int bus_property_get_rlimit(
1546 sd_bus *bus,
1547 const char *path,
1548 const char *interface,
1549 const char *property,
1550 sd_bus_message *reply,
1551 void *userdata,
1552 sd_bus_error *error) {
1553
1554 struct rlimit *rl;
1555 uint64_t u;
1556 rlim_t x;
4c89c718 1557 const char *is_soft;
db2df898
MP
1558
1559 assert(bus);
1560 assert(reply);
1561 assert(userdata);
1562
4c89c718 1563 is_soft = endswith(property, "Soft");
db2df898
MP
1564 rl = *(struct rlimit**) userdata;
1565 if (rl)
4c89c718 1566 x = is_soft ? rl->rlim_cur : rl->rlim_max;
db2df898
MP
1567 else {
1568 struct rlimit buf = {};
1569 int z;
4c89c718
MP
1570 const char *s;
1571
1572 s = is_soft ? strndupa(property, is_soft - property) : property;
db2df898 1573
4c89c718 1574 z = rlimit_from_string(strstr(s, "Limit"));
db2df898
MP
1575 assert(z >= 0);
1576
1577 getrlimit(z, &buf);
4c89c718 1578 x = is_soft ? buf.rlim_cur : buf.rlim_max;
db2df898
MP
1579 }
1580
1581 /* rlim_t might have different sizes, let's map
1582 * RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on
1583 * all archs */
1584 u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x;
1585
1586 return sd_bus_message_append(reply, "t", u);
1587}
2897b343
MP
1588
1589int bus_track_add_name_many(sd_bus_track *t, char **l) {
1590 int r = 0;
1591 char **i;
1592
1593 assert(t);
1594
1595 /* Continues adding after failure, and returns the first failure. */
1596
1597 STRV_FOREACH(i, l) {
1598 int k;
1599
1600 k = sd_bus_track_add_name(t, *i);
1601 if (k < 0 && r >= 0)
1602 r = k;
1603 }
1604
1605 return r;
1606}