]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/bus-util.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / shared / 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
TG
22#include <sys/socket.h>
23
4f5dd394 24#include "sd-bus.h"
ebd011d9
LP
25#include "sd-daemon.h"
26#include "sd-event.h"
d53d9474
LP
27
28#include "bus-error.h"
29#include "bus-internal.h"
30#include "bus-label.h"
31#include "bus-message.h"
32#include "cgroup-util.h"
40ca29a1 33#include "def.h"
e9876fc9 34#include "env-util.h"
4f5dd394 35#include "escape.h"
d53d9474 36#include "macro.h"
2bba9a57 37#include "missing.h"
d53d9474 38#include "path-util.h"
ebd011d9 39#include "set.h"
24882e06 40#include "signal-util.h"
07630cea 41#include "string-util.h"
d53d9474 42#include "strv.h"
d5cad221 43#include "unit-name.h"
08596068 44#include "utf8.h"
d53d9474 45#include "util.h"
40ca29a1
LP
46#include "bus-util.h"
47
19070062 48static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
40ca29a1
LP
49 sd_event *e = userdata;
50
40ca29a1
LP
51 assert(m);
52 assert(e);
53
19070062 54 sd_bus_close(sd_bus_message_get_bus(m));
6203e07a 55 sd_event_exit(e, 0);
b27adf35 56
40ca29a1
LP
57 return 1;
58}
59
6203e07a 60int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) {
40ca29a1 61 _cleanup_free_ char *match = NULL;
11846aa7 62 const char *unique;
40ca29a1
LP
63 int r;
64
65 assert(e);
66 assert(bus);
67 assert(name);
68
6203e07a
LP
69 /* We unregister the name here and then wait for the
70 * NameOwnerChanged signal for this event to arrive before we
71 * quit. We do this in order to make sure that any queued
72 * requests are still processed before we really exit. */
73
11846aa7 74 r = sd_bus_get_unique_name(bus, &unique);
40ca29a1
LP
75 if (r < 0)
76 return r;
77
11846aa7
LP
78 r = asprintf(&match,
79 "sender='org.freedesktop.DBus',"
80 "type='signal',"
81 "interface='org.freedesktop.DBus',"
82 "member='NameOwnerChanged',"
83 "path='/org/freedesktop/DBus',"
84 "arg0='%s',"
85 "arg1='%s',"
86 "arg2=''", name, unique);
87 if (r < 0)
88 return -ENOMEM;
89
19befb2d 90 r = sd_bus_add_match(bus, NULL, match, name_owner_change_callback, e);
40ca29a1
LP
91 if (r < 0)
92 return r;
93
94 r = sd_bus_release_name(bus, name);
95 if (r < 0)
96 return r;
97
40ca29a1
LP
98 return 0;
99}
100
37224a5f
LP
101int bus_event_loop_with_idle(
102 sd_event *e,
103 sd_bus *bus,
104 const char *name,
105 usec_t timeout,
106 check_idle_t check_idle,
107 void *userdata) {
40ca29a1 108 bool exiting = false;
6203e07a 109 int r, code;
40ca29a1
LP
110
111 assert(e);
112 assert(bus);
113 assert(name);
114
115 for (;;) {
37224a5f
LP
116 bool idle;
117
40ca29a1
LP
118 r = sd_event_get_state(e);
119 if (r < 0)
120 return r;
40ca29a1
LP
121 if (r == SD_EVENT_FINISHED)
122 break;
123
37224a5f
LP
124 if (check_idle)
125 idle = check_idle(userdata);
126 else
127 idle = true;
128
129 r = sd_event_run(e, exiting || !idle ? (uint64_t) -1 : timeout);
40ca29a1
LP
130 if (r < 0)
131 return r;
132
a8ba6cd1 133 if (r == 0 && !exiting && idle) {
b27adf35
LP
134
135 r = sd_bus_try_close(bus);
136 if (r == -EBUSY)
137 continue;
138
430e21c2
LP
139 /* Fallback for dbus1 connections: we
140 * unregister the name and wait for the
141 * response to come through for it */
15411c0c 142 if (r == -EOPNOTSUPP) {
430e21c2
LP
143
144 /* Inform the service manager that we
145 * are going down, so that it will
146 * queue all further start requests,
147 * instead of assuming we are already
148 * running. */
149 sd_notify(false, "STOPPING=1");
b27adf35
LP
150
151 r = bus_async_unregister_and_exit(e, bus, name);
152 if (r < 0)
153 return r;
154
155 exiting = true;
156 continue;
157 }
158
40ca29a1
LP
159 if (r < 0)
160 return r;
161
b27adf35
LP
162 sd_event_exit(e, 0);
163 break;
40ca29a1
LP
164 }
165 }
166
6203e07a
LP
167 r = sd_event_get_exit_code(e, &code);
168 if (r < 0)
169 return r;
170
171 return code;
40ca29a1
LP
172}
173
5fd38859
DH
174int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
175 _cleanup_bus_message_unref_ sd_bus_message *rep = NULL;
176 int r, has_owner = 0;
177
178 assert(c);
179 assert(name);
180
181 r = sd_bus_call_method(c,
182 "org.freedesktop.DBus",
183 "/org/freedesktop/dbus",
184 "org.freedesktop.DBus",
185 "NameHasOwner",
186 error,
187 &rep,
188 "s",
189 name);
190 if (r < 0)
191 return r;
192
193 r = sd_bus_message_read_basic(rep, 'b', &has_owner);
194 if (r < 0)
195 return sd_bus_error_set_errno(error, r);
196
197 return has_owner;
198}
199
c529695e
LP
200static int check_good_user(sd_bus_message *m, uid_t good_user) {
201 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
202 uid_t sender_uid;
203 int r;
204
205 assert(m);
206
207 if (good_user == UID_INVALID)
208 return 0;
209
210 r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
211 if (r < 0)
212 return r;
213
0f514420
LP
214 /* Don't trust augmented credentials for authorization */
215 assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM);
216
c529695e
LP
217 r = sd_bus_creds_get_euid(creds, &sender_uid);
218 if (r < 0)
219 return r;
220
221 return sender_uid == good_user;
222}
223
ceb24229 224int bus_test_polkit(
f3885791 225 sd_bus_message *call,
def9a7aa 226 int capability,
40ca29a1 227 const char *action,
403ed0e5 228 const char **details,
c529695e 229 uid_t good_user,
40ca29a1
LP
230 bool *_challenge,
231 sd_bus_error *e) {
232
40ca29a1
LP
233 int r;
234
f3885791 235 assert(call);
40ca29a1
LP
236 assert(action);
237
ceb24229
LP
238 /* Tests non-interactively! */
239
c529695e
LP
240 r = check_good_user(call, good_user);
241 if (r != 0)
242 return r;
243
f3885791 244 r = sd_bus_query_sender_privilege(call, capability);
5b12334d
LP
245 if (r < 0)
246 return r;
f3885791 247 else if (r > 0)
40ca29a1 248 return 1;
40ca29a1
LP
249#ifdef ENABLE_POLKIT
250 else {
403ed0e5 251 _cleanup_bus_message_unref_ sd_bus_message *request = NULL;
40ca29a1 252 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
ceb24229 253 int authorized = false, challenge = false;
403ed0e5 254 const char *sender, **k, **v;
5b12334d 255
f3885791 256 sender = sd_bus_message_get_sender(call);
5b12334d
LP
257 if (!sender)
258 return -EBADMSG;
40ca29a1 259
403ed0e5 260 r = sd_bus_message_new_method_call(
f3885791 261 call->bus,
403ed0e5 262 &request,
40ca29a1
LP
263 "org.freedesktop.PolicyKit1",
264 "/org/freedesktop/PolicyKit1/Authority",
265 "org.freedesktop.PolicyKit1.Authority",
403ed0e5
MC
266 "CheckAuthorization");
267 if (r < 0)
268 return r;
269
270 r = sd_bus_message_append(
271 request,
272 "(sa{sv})s",
40ca29a1 273 "system-bus-name", 1, "name", "s", sender,
403ed0e5
MC
274 action);
275 if (r < 0)
276 return r;
277
278 r = sd_bus_message_open_container(request, 'a', "{ss}");
279 if (r < 0)
280 return r;
40ca29a1 281
403ed0e5
MC
282 STRV_FOREACH_PAIR(k, v, details) {
283 r = sd_bus_message_append(request, "{ss}", *k, *v);
284 if (r < 0)
285 return r;
286 }
287
288 r = sd_bus_message_close_container(request);
289 if (r < 0)
290 return r;
291
292 r = sd_bus_message_append(request, "us", 0, NULL);
293 if (r < 0)
294 return r;
295
296 r = sd_bus_call(call->bus, request, 0, e, &reply);
40ca29a1
LP
297 if (r < 0) {
298 /* Treat no PK available as access denied */
299 if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
300 sd_bus_error_free(e);
301 return -EACCES;
302 }
303
304 return r;
305 }
306
313333b4 307 r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
2b49a470
TA
308 if (r < 0)
309 return r;
310
311 r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
312 if (r < 0)
313 return r;
40ca29a1
LP
314
315 if (authorized)
316 return 1;
317
318 if (_challenge) {
319 *_challenge = challenge;
320 return 0;
321 }
322 }
323#endif
324
325 return -EACCES;
326}
327
328#ifdef ENABLE_POLKIT
329
330typedef struct AsyncPolkitQuery {
331 sd_bus_message *request, *reply;
332 sd_bus_message_handler_t callback;
333 void *userdata;
19befb2d 334 sd_bus_slot *slot;
ebcf1f97 335 Hashmap *registry;
40ca29a1
LP
336} AsyncPolkitQuery;
337
19befb2d 338static void async_polkit_query_free(AsyncPolkitQuery *q) {
ebcf1f97
LP
339
340 if (!q)
341 return;
342
19befb2d 343 sd_bus_slot_unref(q->slot);
ebcf1f97
LP
344
345 if (q->registry && q->request)
346 hashmap_remove(q->registry, q->request);
347
348 sd_bus_message_unref(q->request);
349 sd_bus_message_unref(q->reply);
350
351 free(q);
352}
353
19070062 354static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
ebcf1f97 355 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
ebcf1f97 356 AsyncPolkitQuery *q = userdata;
40ca29a1
LP
357 int r;
358
40ca29a1
LP
359 assert(reply);
360 assert(q);
361
19befb2d 362 q->slot = sd_bus_slot_unref(q->slot);
40ca29a1 363 q->reply = sd_bus_message_ref(reply);
40ca29a1 364
ebcf1f97
LP
365 r = sd_bus_message_rewind(q->request, true);
366 if (r < 0) {
367 r = sd_bus_reply_method_errno(q->request, r, NULL);
368 goto finish;
369 }
40ca29a1 370
19070062 371 r = q->callback(q->request, q->userdata, &error_buffer);
ebcf1f97 372 r = bus_maybe_reply_error(q->request, r, &error_buffer);
40ca29a1 373
ebcf1f97 374finish:
19befb2d
LP
375 async_polkit_query_free(q);
376
ebcf1f97 377 return r;
40ca29a1
LP
378}
379
380#endif
381
382int bus_verify_polkit_async(
f3885791 383 sd_bus_message *call,
def9a7aa 384 int capability,
40ca29a1 385 const char *action,
403ed0e5 386 const char **details,
40ca29a1 387 bool interactive,
c529695e 388 uid_t good_user,
f3885791
LP
389 Hashmap **registry,
390 sd_bus_error *error) {
40ca29a1
LP
391
392#ifdef ENABLE_POLKIT
393 _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
394 AsyncPolkitQuery *q;
403ed0e5 395 const char *sender, **k, **v;
f3885791
LP
396 sd_bus_message_handler_t callback;
397 void *userdata;
b911eb15 398 int c;
5b12334d 399#endif
40ca29a1
LP
400 int r;
401
f3885791 402 assert(call);
40ca29a1 403 assert(action);
f3885791 404 assert(registry);
40ca29a1 405
c529695e
LP
406 r = check_good_user(call, good_user);
407 if (r != 0)
408 return r;
409
40ca29a1 410#ifdef ENABLE_POLKIT
f3885791 411 q = hashmap_get(*registry, call);
40ca29a1 412 if (q) {
102d8f81 413 int authorized, challenge;
40ca29a1
LP
414
415 /* This is the second invocation of this function, and
416 * there's already a response from polkit, let's
417 * process it */
418 assert(q->reply);
419
420 if (sd_bus_message_is_method_error(q->reply, NULL)) {
421 const sd_bus_error *e;
422
ebcf1f97 423 /* Copy error from polkit reply */
40ca29a1
LP
424 e = sd_bus_message_get_error(q->reply);
425 sd_bus_error_copy(error, e);
40ca29a1 426
ebcf1f97
LP
427 /* Treat no PK available as access denied */
428 if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN))
429 return -EACCES;
430
5958d089 431 return -sd_bus_error_get_errno(e);
40ca29a1
LP
432 }
433
434 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
435 if (r >= 0)
436 r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
437
40ca29a1
LP
438 if (r < 0)
439 return r;
440
441 if (authorized)
442 return 1;
443
f2288cc6
LP
444 if (challenge)
445 return sd_bus_error_set(error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required.");
446
40ca29a1
LP
447 return -EACCES;
448 }
449#endif
450
f3885791 451 r = sd_bus_query_sender_privilege(call, capability);
5b12334d
LP
452 if (r < 0)
453 return r;
f3885791 454 else if (r > 0)
40ca29a1 455 return 1;
5b12334d 456
40ca29a1 457#ifdef ENABLE_POLKIT
f3885791
LP
458 if (sd_bus_get_current_message(call->bus) != call)
459 return -EINVAL;
460
461 callback = sd_bus_get_current_handler(call->bus);
462 if (!callback)
463 return -EINVAL;
464
465 userdata = sd_bus_get_current_userdata(call->bus);
466
467 sender = sd_bus_message_get_sender(call);
5b12334d
LP
468 if (!sender)
469 return -EBADMSG;
40ca29a1 470
b911eb15
LP
471 c = sd_bus_message_get_allow_interactive_authorization(call);
472 if (c < 0)
473 return c;
474 if (c > 0)
475 interactive = true;
476
d5099efc 477 r = hashmap_ensure_allocated(registry, NULL);
40ca29a1
LP
478 if (r < 0)
479 return r;
480
481 r = sd_bus_message_new_method_call(
f3885791 482 call->bus,
151b9b96 483 &pk,
40ca29a1
LP
484 "org.freedesktop.PolicyKit1",
485 "/org/freedesktop/PolicyKit1/Authority",
486 "org.freedesktop.PolicyKit1.Authority",
151b9b96 487 "CheckAuthorization");
40ca29a1
LP
488 if (r < 0)
489 return r;
490
491 r = sd_bus_message_append(
492 pk,
403ed0e5 493 "(sa{sv})s",
40ca29a1 494 "system-bus-name", 1, "name", "s", sender,
403ed0e5
MC
495 action);
496 if (r < 0)
497 return r;
498
499 r = sd_bus_message_open_container(pk, 'a', "{ss}");
500 if (r < 0)
501 return r;
502
503 STRV_FOREACH_PAIR(k, v, details) {
504 r = sd_bus_message_append(pk, "{ss}", *k, *v);
505 if (r < 0)
506 return r;
507 }
508
509 r = sd_bus_message_close_container(pk);
510 if (r < 0)
511 return r;
512
513 r = sd_bus_message_append(pk, "us", !!interactive, NULL);
40ca29a1
LP
514 if (r < 0)
515 return r;
516
517 q = new0(AsyncPolkitQuery, 1);
518 if (!q)
519 return -ENOMEM;
520
f3885791 521 q->request = sd_bus_message_ref(call);
40ca29a1
LP
522 q->callback = callback;
523 q->userdata = userdata;
524
f3885791 525 r = hashmap_put(*registry, call, q);
40ca29a1 526 if (r < 0) {
19befb2d 527 async_polkit_query_free(q);
40ca29a1
LP
528 return r;
529 }
530
ebcf1f97
LP
531 q->registry = *registry;
532
f3885791 533 r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0);
ebcf1f97 534 if (r < 0) {
19befb2d 535 async_polkit_query_free(q);
40ca29a1 536 return r;
ebcf1f97 537 }
40ca29a1
LP
538
539 return 0;
540#endif
541
542 return -EACCES;
543}
544
36e34057 545void bus_verify_polkit_async_registry_free(Hashmap *registry) {
40ca29a1
LP
546#ifdef ENABLE_POLKIT
547 AsyncPolkitQuery *q;
548
549 while ((q = hashmap_steal_first(registry)))
19befb2d 550 async_polkit_query_free(q);
40ca29a1
LP
551
552 hashmap_free(registry);
553#endif
554}
0c842e0a 555
718db961 556int bus_check_peercred(sd_bus *c) {
0c842e0a
TG
557 struct ucred ucred;
558 socklen_t l;
0f8bd8de 559 int fd;
0c842e0a
TG
560
561 assert(c);
562
563 fd = sd_bus_get_fd(c);
0f8bd8de
LP
564 if (fd < 0)
565 return fd;
0c842e0a
TG
566
567 l = sizeof(struct ucred);
0f8bd8de 568 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
0c842e0a 569 return -errno;
0c842e0a 570
0f8bd8de 571 if (l != sizeof(struct ucred))
0c842e0a 572 return -E2BIG;
0c842e0a
TG
573
574 if (ucred.uid != 0 && ucred.uid != geteuid())
575 return -EPERM;
576
577 return 1;
578}
579
266f3e26 580int bus_connect_system_systemd(sd_bus **_bus) {
0f8bd8de 581 _cleanup_bus_unref_ sd_bus *bus = NULL;
0c842e0a 582 int r;
0c842e0a
TG
583
584 assert(_bus);
585
0f8bd8de 586 if (geteuid() != 0)
266f3e26 587 return sd_bus_default_system(_bus);
a1da8583 588
a6aa8912
LP
589 /* If we are root and kdbus is not available, then let's talk
590 * directly to the system instance, instead of going via the
591 * bus */
a1da8583 592
0f8bd8de
LP
593 r = sd_bus_new(&bus);
594 if (r < 0)
595 return r;
a1da8583 596
e3afaf6b 597 r = sd_bus_set_address(bus, KERNEL_SYSTEM_BUS_ADDRESS);
0f8bd8de
LP
598 if (r < 0)
599 return r;
a1da8583 600
a6aa8912
LP
601 bus->bus_client = true;
602
0f8bd8de 603 r = sd_bus_start(bus);
a6aa8912
LP
604 if (r >= 0) {
605 *_bus = bus;
606 bus = NULL;
607 return 0;
608 }
609
610 bus = sd_bus_unref(bus);
a6aa8912
LP
611
612 r = sd_bus_new(&bus);
0f8bd8de
LP
613 if (r < 0)
614 return r;
a1da8583 615
a6aa8912
LP
616 r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
617 if (r < 0)
618 return r;
619
620 r = sd_bus_start(bus);
621 if (r < 0)
266f3e26 622 return sd_bus_default_system(_bus);
a6aa8912 623
0f8bd8de 624 r = bus_check_peercred(bus);
a1da8583
TG
625 if (r < 0)
626 return r;
627
628 *_bus = bus;
0f8bd8de
LP
629 bus = NULL;
630
a1da8583
TG
631 return 0;
632}
633
266f3e26 634int bus_connect_user_systemd(sd_bus **_bus) {
41dd15e4 635 _cleanup_bus_unref_ sd_bus *bus = NULL;
a6aa8912 636 _cleanup_free_ char *ee = NULL;
41dd15e4
LP
637 const char *e;
638 int r;
639
a6aa8912 640 /* Try via kdbus first, and then directly */
41dd15e4
LP
641
642 assert(_bus);
643
a6aa8912
LP
644 r = sd_bus_new(&bus);
645 if (r < 0)
646 return r;
647
e3afaf6b 648 if (asprintf(&bus->address, KERNEL_USER_BUS_ADDRESS_FMT, getuid()) < 0)
a6aa8912
LP
649 return -ENOMEM;
650
651 bus->bus_client = true;
652
653 r = sd_bus_start(bus);
654 if (r >= 0) {
655 *_bus = bus;
656 bus = NULL;
657 return 0;
658 }
659
660 bus = sd_bus_unref(bus);
a6aa8912 661
41dd15e4 662 e = secure_getenv("XDG_RUNTIME_DIR");
537220d9 663 if (!e)
266f3e26 664 return sd_bus_default_user(_bus);
537220d9 665
a6aa8912
LP
666 ee = bus_address_escape(e);
667 if (!ee)
537220d9 668 return -ENOMEM;
41dd15e4
LP
669
670 r = sd_bus_new(&bus);
671 if (r < 0)
672 return r;
673
a6aa8912
LP
674 bus->address = strjoin("unix:path=", ee, "/systemd/private", NULL);
675 if (!bus->address)
676 return -ENOMEM;
41dd15e4
LP
677
678 r = sd_bus_start(bus);
679 if (r < 0)
266f3e26 680 return sd_bus_default_user(_bus);
41dd15e4
LP
681
682 r = bus_check_peercred(bus);
683 if (r < 0)
684 return r;
685
686 *_bus = bus;
687 bus = NULL;
688
689 return 0;
690}
691
9f6eb1cd 692int bus_print_property(const char *name, sd_bus_message *property, bool all) {
a1da8583
TG
693 char type;
694 const char *contents;
9f6eb1cd 695 int r;
a1da8583
TG
696
697 assert(name);
698 assert(property);
699
9f6eb1cd
KS
700 r = sd_bus_message_peek_type(property, &type, &contents);
701 if (r < 0)
702 return r;
a1da8583
TG
703
704 switch (type) {
705
706 case SD_BUS_TYPE_STRING: {
707 const char *s;
9f6eb1cd
KS
708
709 r = sd_bus_message_read_basic(property, type, &s);
710 if (r < 0)
711 return r;
a1da8583 712
27e9c5af
LP
713 if (all || !isempty(s)) {
714 _cleanup_free_ char *escaped = NULL;
715
716 escaped = xescape(s, "\n");
717 if (!escaped)
718 return -ENOMEM;
719
720 printf("%s=%s\n", name, escaped);
721 }
a1da8583
TG
722
723 return 1;
724 }
725
726 case SD_BUS_TYPE_BOOLEAN: {
c2fa048c 727 int b;
a1da8583 728
9f6eb1cd
KS
729 r = sd_bus_message_read_basic(property, type, &b);
730 if (r < 0)
731 return r;
732
a1da8583
TG
733 printf("%s=%s\n", name, yes_no(b));
734
735 return 1;
736 }
737
738 case SD_BUS_TYPE_UINT64: {
739 uint64_t u;
740
9f6eb1cd
KS
741 r = sd_bus_message_read_basic(property, type, &u);
742 if (r < 0)
743 return r;
a1da8583
TG
744
745 /* Yes, heuristics! But we can change this check
746 * should it turn out to not be sufficient */
747
748 if (endswith(name, "Timestamp")) {
749 char timestamp[FORMAT_TIMESTAMP_MAX], *t;
750
751 t = format_timestamp(timestamp, sizeof(timestamp), u);
752 if (t || all)
753 printf("%s=%s\n", name, strempty(t));
754
755 } else if (strstr(name, "USec")) {
756 char timespan[FORMAT_TIMESPAN_MAX];
757
758 printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
759 } else
760 printf("%s=%llu\n", name, (unsigned long long) u);
761
762 return 1;
763 }
764
d7161865
DM
765 case SD_BUS_TYPE_INT64: {
766 int64_t i;
767
768 r = sd_bus_message_read_basic(property, type, &i);
769 if (r < 0)
770 return r;
771
772 printf("%s=%lld\n", name, (long long) i);
773
774 return 1;
775 }
776
a1da8583
TG
777 case SD_BUS_TYPE_UINT32: {
778 uint32_t u;
779
9f6eb1cd
KS
780 r = sd_bus_message_read_basic(property, type, &u);
781 if (r < 0)
782 return r;
a1da8583
TG
783
784 if (strstr(name, "UMask") || strstr(name, "Mode"))
785 printf("%s=%04o\n", name, u);
786 else
787 printf("%s=%u\n", name, (unsigned) u);
788
789 return 1;
790 }
791
792 case SD_BUS_TYPE_INT32: {
793 int32_t i;
794
9f6eb1cd
KS
795 r = sd_bus_message_read_basic(property, type, &i);
796 if (r < 0)
797 return r;
a1da8583
TG
798
799 printf("%s=%i\n", name, (int) i);
800 return 1;
801 }
802
803 case SD_BUS_TYPE_DOUBLE: {
804 double d;
805
9f6eb1cd
KS
806 r = sd_bus_message_read_basic(property, type, &d);
807 if (r < 0)
808 return r;
a1da8583
TG
809
810 printf("%s=%g\n", name, d);
811 return 1;
812 }
813
814 case SD_BUS_TYPE_ARRAY:
a1da8583 815 if (streq(contents, "s")) {
261afec5
MAP
816 bool first = true;
817 const char *str;
a1da8583 818
9f6eb1cd
KS
819 r = sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
820 if (r < 0)
821 return r;
822
261afec5 823 while((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) {
27e9c5af
LP
824 _cleanup_free_ char *escaped = NULL;
825
261afec5
MAP
826 if (first)
827 printf("%s=", name);
828
27e9c5af
LP
829 escaped = xescape(str, "\n ");
830 if (!escaped)
831 return -ENOMEM;
832
833 printf("%s%s", first ? "" : " ", escaped);
261afec5
MAP
834
835 first = false;
836 }
9f6eb1cd
KS
837 if (r < 0)
838 return r;
a1da8583 839
261afec5 840 if (first && all)
a1da8583 841 printf("%s=", name);
261afec5 842 if (!first || all)
a1da8583 843 puts("");
a1da8583 844
9f6eb1cd
KS
845 r = sd_bus_message_exit_container(property);
846 if (r < 0)
847 return r;
a1da8583
TG
848
849 return 1;
850
851 } else if (streq(contents, "y")) {
852 const uint8_t *u;
853 size_t n;
854
9f6eb1cd
KS
855 r = sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
856 if (r < 0)
857 return r;
858
a1da8583
TG
859 if (all || n > 0) {
860 unsigned int i;
861
862 printf("%s=", name);
863
864 for (i = 0; i < n; i++)
865 printf("%02x", u[i]);
866
867 puts("");
868 }
869
870 return 1;
871
872 } else if (streq(contents, "u")) {
873 uint32_t *u;
874 size_t n;
875
9f6eb1cd
KS
876 r = sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
877 if (r < 0)
878 return r;
879
a1da8583
TG
880 if (all || n > 0) {
881 unsigned int i;
882
883 printf("%s=", name);
884
885 for (i = 0; i < n; i++)
886 printf("%08x", u[i]);
887
888 puts("");
889 }
890
891 return 1;
892 }
893
894 break;
895 }
896
897 return 0;
898}
d21ed1ea 899
27e72d6b 900int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all) {
9f6eb1cd 901 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
ffc06c35
KS
902 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
903 int r;
904
9f6eb1cd
KS
905 assert(bus);
906 assert(path);
907
908 r = sd_bus_call_method(bus,
27e72d6b 909 dest,
ffc06c35
KS
910 path,
911 "org.freedesktop.DBus.Properties",
912 "GetAll",
913 &error,
9f6eb1cd 914 &reply,
ffc06c35 915 "s", "");
9f6eb1cd 916 if (r < 0)
ffc06c35 917 return r;
ffc06c35 918
9f6eb1cd 919 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
ffc06c35
KS
920 if (r < 0)
921 return r;
922
9f6eb1cd 923 while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
ffc06c35 924 const char *name;
ffc06c35 925 const char *contents;
ffc06c35 926
9f6eb1cd 927 r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name);
ffc06c35
KS
928 if (r < 0)
929 return r;
930
9f6eb1cd
KS
931 if (!filter || strv_find(filter, name)) {
932 r = sd_bus_message_peek_type(reply, NULL, &contents);
933 if (r < 0)
934 return r;
935
936 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
937 if (r < 0)
938 return r;
ffc06c35 939
9f6eb1cd
KS
940 r = bus_print_property(name, reply, all);
941 if (r < 0)
942 return r;
27e72d6b
SP
943 if (r == 0) {
944 if (all)
945 printf("%s=[unprintable]\n", name);
946 /* skip what we didn't read */
947 r = sd_bus_message_skip(reply, contents);
948 if (r < 0)
949 return r;
950 }
9f6eb1cd
KS
951
952 r = sd_bus_message_exit_container(reply);
953 if (r < 0)
954 return r;
955 } else {
956 r = sd_bus_message_skip(reply, "v");
957 if (r < 0)
958 return r;
959 }
960
961 r = sd_bus_message_exit_container(reply);
ffc06c35
KS
962 if (r < 0)
963 return r;
9f6eb1cd
KS
964 }
965 if (r < 0)
966 return r;
ffc06c35 967
9f6eb1cd
KS
968 r = sd_bus_message_exit_container(reply);
969 if (r < 0)
970 return r;
ffc06c35 971
9f6eb1cd
KS
972 return 0;
973}
ffc06c35 974
9f6eb1cd
KS
975int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
976 sd_id128_t *p = userdata;
977 const void *v;
978 size_t n;
979 int r;
ffc06c35 980
9f6eb1cd
KS
981 r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n);
982 if (r < 0)
983 return r;
ffc06c35 984
9f6eb1cd
KS
985 if (n == 0)
986 *p = SD_ID128_NULL;
987 else if (n == 16)
988 memcpy((*p).bytes, v, n);
989 else
990 return -EINVAL;
ffc06c35 991
9f6eb1cd
KS
992 return 0;
993}
ffc06c35 994
9f6eb1cd
KS
995static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
996 char type;
997 int r;
ffc06c35 998
9f6eb1cd
KS
999 r = sd_bus_message_peek_type(m, &type, NULL);
1000 if (r < 0)
1001 return r;
ffc06c35 1002
9f6eb1cd
KS
1003 switch (type) {
1004 case SD_BUS_TYPE_STRING: {
1005 const char *s;
9f6eb1cd 1006 char **p = userdata;
ffc06c35 1007
9f6eb1cd
KS
1008 r = sd_bus_message_read_basic(m, type, &s);
1009 if (r < 0)
1010 break;
ffc06c35 1011
9f6eb1cd
KS
1012 if (isempty(s))
1013 break;
ffc06c35 1014
0704ea57 1015 r = free_and_strdup(p, s);
9f6eb1cd
KS
1016 break;
1017 }
ffc06c35 1018
9f6eb1cd 1019 case SD_BUS_TYPE_ARRAY: {
7d6884b6
TA
1020 _cleanup_strv_free_ char **l = NULL;
1021 char ***p = userdata;
ffc06c35 1022
9f6eb1cd
KS
1023 r = bus_message_read_strv_extend(m, &l);
1024 if (r < 0)
1025 break;
ffc06c35 1026
9f6eb1cd
KS
1027 strv_free(*p);
1028 *p = l;
1029 l = NULL;
ffc06c35 1030
9f6eb1cd
KS
1031 break;
1032 }
ffc06c35 1033
9f6eb1cd
KS
1034 case SD_BUS_TYPE_BOOLEAN: {
1035 unsigned b;
1036 bool *p = userdata;
ffc06c35 1037
9f6eb1cd
KS
1038 r = sd_bus_message_read_basic(m, type, &b);
1039 if (r < 0)
1040 break;
ffc06c35 1041
9f6eb1cd 1042 *p = b;
ffc06c35 1043
9f6eb1cd
KS
1044 break;
1045 }
ffc06c35 1046
9f6eb1cd
KS
1047 case SD_BUS_TYPE_UINT32: {
1048 uint64_t u;
1049 uint32_t *p = userdata;
1050
1051 r = sd_bus_message_read_basic(m, type, &u);
1052 if (r < 0)
ffc06c35 1053 break;
ffc06c35 1054
9f6eb1cd
KS
1055 *p = u;
1056
1057 break;
1058 }
1059
1060 case SD_BUS_TYPE_UINT64: {
1061 uint64_t t;
1062 uint64_t *p = userdata;
1063
1064 r = sd_bus_message_read_basic(m, type, &t);
1065 if (r < 0)
ffc06c35 1066 break;
ffc06c35 1067
9f6eb1cd
KS
1068 *p = t;
1069
1070 break;
1071 }
1072
1073 default:
1074 break;
1075 }
1076
1077 return r;
1078}
1079
fe506d56
LP
1080int bus_message_map_all_properties(
1081 sd_bus_message *m,
1082 const struct bus_properties_map *map,
1083 void *userdata) {
1084
9f6eb1cd
KS
1085 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1086 int r;
1087
aae2b488 1088 assert(m);
9f6eb1cd
KS
1089 assert(map);
1090
9f6eb1cd
KS
1091 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
1092 if (r < 0)
1093 return r;
1094
1095 while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
1096 const struct bus_properties_map *prop;
1097 const char *member;
1098 const char *contents;
1099 void *v;
1100 unsigned i;
1101
1102 r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
ffc06c35
KS
1103 if (r < 0)
1104 return r;
1105
9f6eb1cd
KS
1106 for (i = 0, prop = NULL; map[i].member; i++)
1107 if (streq(map[i].member, member)) {
1108 prop = &map[i];
1109 break;
1110 }
1111
1112 if (prop) {
1113 r = sd_bus_message_peek_type(m, NULL, &contents);
1114 if (r < 0)
1115 return r;
1116
1117 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
1118 if (r < 0)
1119 return r;
1120
1121 v = (uint8_t *)userdata + prop->offset;
27e72d6b 1122 if (map[i].set)
fe506d56 1123 r = prop->set(sd_bus_message_get_bus(m), member, m, &error, v);
9f6eb1cd 1124 else
fe506d56 1125 r = map_basic(sd_bus_message_get_bus(m), member, m, &error, v);
2b49a470
TA
1126 if (r < 0)
1127 return r;
9f6eb1cd
KS
1128
1129 r = sd_bus_message_exit_container(m);
1130 if (r < 0)
1131 return r;
1132 } else {
1133 r = sd_bus_message_skip(m, "v");
1134 if (r < 0)
6c1508b8 1135 return r;
9f6eb1cd
KS
1136 }
1137
ffc06c35
KS
1138 r = sd_bus_message_exit_container(m);
1139 if (r < 0)
1140 return r;
1141 }
fe506d56
LP
1142 if (r < 0)
1143 return r;
ffc06c35 1144
aae2b488
DH
1145 return sd_bus_message_exit_container(m);
1146}
1147
fe506d56
LP
1148int bus_message_map_properties_changed(
1149 sd_bus_message *m,
1150 const struct bus_properties_map *map,
1151 void *userdata) {
1152
aae2b488
DH
1153 const char *member;
1154 int r, invalidated, i;
1155
aae2b488
DH
1156 assert(m);
1157 assert(map);
1158
fe506d56 1159 r = bus_message_map_all_properties(m, map, userdata);
aae2b488
DH
1160 if (r < 0)
1161 return r;
1162
1163 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s");
1164 if (r < 0)
1165 return r;
1166
1167 invalidated = 0;
1168 while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member)) > 0)
1169 for (i = 0; map[i].member; i++)
1170 if (streq(map[i].member, member)) {
1171 ++invalidated;
1172 break;
1173 }
fe506d56
LP
1174 if (r < 0)
1175 return r;
aae2b488
DH
1176
1177 r = sd_bus_message_exit_container(m);
1178 if (r < 0)
1179 return r;
1180
1181 return invalidated;
1182}
1183
fe506d56
LP
1184int bus_map_all_properties(
1185 sd_bus *bus,
1186 const char *destination,
1187 const char *path,
1188 const struct bus_properties_map *map,
1189 void *userdata) {
1190
aae2b488
DH
1191 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1192 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1193 int r;
1194
1195 assert(bus);
1196 assert(destination);
1197 assert(path);
1198 assert(map);
1199
1200 r = sd_bus_call_method(
1201 bus,
1202 destination,
1203 path,
1204 "org.freedesktop.DBus.Properties",
1205 "GetAll",
1206 &error,
1207 &m,
1208 "s", "");
1209 if (r < 0)
1210 return r;
1211
fe506d56 1212 return bus_message_map_all_properties(m, map, userdata);
ffc06c35
KS
1213}
1214
266f3e26 1215int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
d21ed1ea
LP
1216 int r;
1217
1218 assert(transport >= 0);
1219 assert(transport < _BUS_TRANSPORT_MAX);
1220 assert(bus);
1221
1222 assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
15411c0c 1223 assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
d21ed1ea
LP
1224
1225 switch (transport) {
1226
1227 case BUS_TRANSPORT_LOCAL:
1228 if (user)
76b54375 1229 r = sd_bus_default_user(bus);
d21ed1ea 1230 else
76b54375 1231 r = sd_bus_default_system(bus);
d21ed1ea
LP
1232
1233 break;
1234
1235 case BUS_TRANSPORT_REMOTE:
3db729cb 1236 r = sd_bus_open_system_remote(bus, host);
41dd15e4
LP
1237 break;
1238
de33fc62
LP
1239 case BUS_TRANSPORT_MACHINE:
1240 r = sd_bus_open_system_machine(bus, host);
41dd15e4
LP
1241 break;
1242
1243 default:
1244 assert_not_reached("Hmm, unknown transport type.");
1245 }
1246
1247 return r;
1248}
1249
266f3e26 1250int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
41dd15e4
LP
1251 int r;
1252
1253 assert(transport >= 0);
1254 assert(transport < _BUS_TRANSPORT_MAX);
1255 assert(bus);
1256
1257 assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
15411c0c 1258 assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
41dd15e4
LP
1259
1260 switch (transport) {
1261
1262 case BUS_TRANSPORT_LOCAL:
1263 if (user)
266f3e26 1264 r = bus_connect_user_systemd(bus);
41dd15e4 1265 else
266f3e26 1266 r = bus_connect_system_systemd(bus);
41dd15e4
LP
1267
1268 break;
1269
1270 case BUS_TRANSPORT_REMOTE:
3db729cb 1271 r = sd_bus_open_system_remote(bus, host);
d21ed1ea
LP
1272 break;
1273
de33fc62
LP
1274 case BUS_TRANSPORT_MACHINE:
1275 r = sd_bus_open_system_machine(bus, host);
d21ed1ea
LP
1276 break;
1277
1278 default:
1279 assert_not_reached("Hmm, unknown transport type.");
1280 }
1281
1282 return r;
1283}
e6504030
LP
1284
1285int bus_property_get_bool(
1286 sd_bus *bus,
1287 const char *path,
1288 const char *interface,
1289 const char *property,
1290 sd_bus_message *reply,
ebcf1f97
LP
1291 void *userdata,
1292 sd_bus_error *error) {
e6504030
LP
1293
1294 int b = *(bool*) userdata;
1295
1296 return sd_bus_message_append_basic(reply, 'b', &b);
1297}
1298
718db961
LP
1299#if __SIZEOF_SIZE_T__ != 8
1300int bus_property_get_size(
e6504030
LP
1301 sd_bus *bus,
1302 const char *path,
1303 const char *interface,
1304 const char *property,
1305 sd_bus_message *reply,
ebcf1f97
LP
1306 void *userdata,
1307 sd_bus_error *error) {
e6504030 1308
718db961 1309 uint64_t sz = *(size_t*) userdata;
e6504030 1310
718db961 1311 return sd_bus_message_append_basic(reply, 't', &sz);
e6504030 1312}
718db961
LP
1313#endif
1314
1315#if __SIZEOF_LONG__ != 8
1316int bus_property_get_long(
1317 sd_bus *bus,
1318 const char *path,
1319 const char *interface,
1320 const char *property,
1321 sd_bus_message *reply,
ebcf1f97
LP
1322 void *userdata,
1323 sd_bus_error *error) {
718db961
LP
1324
1325 int64_t l = *(long*) userdata;
1326
1327 return sd_bus_message_append_basic(reply, 'x', &l);
1328}
1329
1330int bus_property_get_ulong(
1331 sd_bus *bus,
1332 const char *path,
1333 const char *interface,
1334 const char *property,
1335 sd_bus_message *reply,
ebcf1f97
LP
1336 void *userdata,
1337 sd_bus_error *error) {
718db961
LP
1338
1339 uint64_t ul = *(unsigned long*) userdata;
1340
1341 return sd_bus_message_append_basic(reply, 't', &ul);
1342}
1343#endif
5b30bef8
LP
1344
1345int bus_log_parse_error(int r) {
23bbb0de 1346 return log_error_errno(r, "Failed to parse bus message: %m");
5b30bef8 1347}
f459b602
MAP
1348
1349int bus_log_create_error(int r) {
23bbb0de 1350 return log_error_errno(r, "Failed to create bus message: %m");
f459b602
MAP
1351}
1352
1353int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
1354 assert(message);
1355 assert(u);
1356
1238ee09
LP
1357 u->machine = NULL;
1358
f459b602
MAP
1359 return sd_bus_message_read(
1360 message,
1361 "(ssssssouso)",
1362 &u->id,
1363 &u->description,
1364 &u->load_state,
1365 &u->active_state,
1366 &u->sub_state,
1367 &u->following,
1368 &u->unit_path,
1369 &u->job_id,
1370 &u->job_type,
1371 &u->job_path);
1372}
ebcf1f97 1373
df31a6c0
LP
1374int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
1375 const char *eq, *field;
1376 int r;
1377
1378 assert(m);
1379 assert(assignment);
1380
1381 eq = strchr(assignment, '=');
1382 if (!eq) {
1383 log_error("Not an assignment: %s", assignment);
1384 return -EINVAL;
1385 }
1386
1387 field = strndupa(assignment, eq - assignment);
1388 eq ++;
1389
b2f8b02e
LP
1390 if (streq(field, "CPUQuota")) {
1391
1392 if (isempty(eq)) {
1393
1394 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec");
1395 if (r < 0)
1396 return bus_log_create_error(r);
1397
3a43da28 1398 r = sd_bus_message_append(m, "v", "t", USEC_INFINITY);
b2f8b02e
LP
1399
1400 } else if (endswith(eq, "%")) {
1401 double percent;
1402
1403 if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) {
1404 log_error("CPU quota '%s' invalid.", eq);
1405 return -EINVAL;
1406 }
1407
1408 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec");
1409 if (r < 0)
1410 return bus_log_create_error(r);
1411
1412 r = sd_bus_message_append(m, "v", "t", (usec_t) percent * USEC_PER_SEC / 100);
1413 } else {
9a054909 1414 log_error("CPU quota needs to be in percent.");
b2f8b02e
LP
1415 return -EINVAL;
1416 }
1417
b2f8b02e
LP
1418 if (r < 0)
1419 return bus_log_create_error(r);
1420
1421 return 0;
ceb728cf
NC
1422 } else if (streq(field, "EnvironmentFile")) {
1423 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "EnvironmentFiles");
1424 if (r < 0)
1425 return r;
1426
1427 r = sd_bus_message_append(m, "v", "a(sb)", 1,
1428 eq[0] == '-' ? eq + 1 : eq,
1429 eq[0] == '-');
1430 if (r < 0)
1431 return r;
1432 return 0;
b2f8b02e
LP
1433 }
1434
df31a6c0
LP
1435 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1436 if (r < 0)
1437 return bus_log_create_error(r);
1438
e567439e 1439 if (STR_IN_SET(field,
03a7b521 1440 "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting",
8bed4cbc 1441 "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
b9c50073 1442 "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
047d9933
EV
1443 "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges",
1444 "SyslogLevelPrefix")) {
df31a6c0
LP
1445
1446 r = parse_boolean(eq);
1447 if (r < 0) {
1448 log_error("Failed to parse boolean assignment %s.", assignment);
1449 return -EINVAL;
1450 }
1451
1452 r = sd_bus_message_append(m, "v", "b", r);
1453
1454 } else if (streq(field, "MemoryLimit")) {
59f448cf 1455 uint64_t bytes;
df31a6c0 1456
03a7b521
LP
1457 if (isempty(eq) || streq(eq, "infinity"))
1458 bytes = (uint64_t) -1;
1459 else {
1460 r = parse_size(eq, 1024, &bytes);
1461 if (r < 0) {
1462 log_error("Failed to parse bytes specification %s", assignment);
1463 return -EINVAL;
1464 }
df31a6c0
LP
1465 }
1466
59f448cf 1467 r = sd_bus_message_append(m, "v", "t", bytes);
df31a6c0 1468
03a7b521
LP
1469 } else if (streq(field, "TasksMax")) {
1470 uint64_t n;
1471
1472 if (isempty(eq) || streq(eq, "infinity"))
1473 n = (uint64_t) -1;
1474 else {
1475 r = safe_atou64(eq, &n);
1476 if (r < 0) {
1477 log_error("Failed to parse maximum tasks specification %s", assignment);
1478 return -EINVAL;
1479 }
1480 }
1481
1482 r = sd_bus_message_append(m, "v", "t", n);
1483
d53d9474
LP
1484 } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) {
1485 uint64_t u;
1486
1487 r = cg_cpu_shares_parse(eq, &u);
1488 if (r < 0) {
1489 log_error("Failed to parse %s value %s.", field, eq);
1490 return -EINVAL;
1491 }
1492
1493 r = sd_bus_message_append(m, "v", "t", u);
1494
1495 } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) {
df31a6c0
LP
1496 uint64_t u;
1497
d53d9474 1498 r = cg_cpu_shares_parse(eq, &u);
df31a6c0
LP
1499 if (r < 0) {
1500 log_error("Failed to parse %s value %s.", field, eq);
1501 return -EINVAL;
1502 }
1503
1504 r = sd_bus_message_append(m, "v", "t", u);
1505
8bed4cbc
LP
1506 } else if (STR_IN_SET(field,
1507 "User", "Group", "DevicePolicy", "KillMode",
1508 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
1509 "StandardInput", "StandardOutput", "StandardError",
602b8355 1510 "Description", "Slice", "Type", "WorkingDirectory",
de53c417 1511 "RootDirectory", "SyslogIdentifier"))
df31a6c0
LP
1512 r = sd_bus_message_append(m, "v", "s", eq);
1513
a8a13575
EV
1514 else if (streq(field, "SyslogLevel")) {
1515 int level;
1516
1517 level = log_level_from_string(eq);
1518 if (level < 0) {
1519 log_error("Failed to parse %s value %s.", field, eq);
1520 return -EINVAL;
1521 }
1522
1523 r = sd_bus_message_append(m, "v", "i", level);
1524
460ed929
EV
1525 } else if (streq(field, "SyslogFacility")) {
1526 int facility;
1527
1528 facility = log_facility_unshifted_from_string(eq);
1529 if (facility < 0) {
1530 log_error("Failed to parse %s value %s.", field, eq);
1531 return -EINVAL;
1532 }
1533
1534 r = sd_bus_message_append(m, "v", "i", facility);
1535
a8a13575 1536 } else if (streq(field, "DeviceAllow")) {
df31a6c0
LP
1537
1538 if (isempty(eq))
1539 r = sd_bus_message_append(m, "v", "a(ss)", 0);
1540 else {
1541 const char *path, *rwm, *e;
1542
1543 e = strchr(eq, ' ');
1544 if (e) {
1545 path = strndupa(eq, e - eq);
1546 rwm = e+1;
1547 } else {
1548 path = eq;
1549 rwm = "";
1550 }
1551
1552 if (!path_startswith(path, "/dev")) {
1553 log_error("%s is not a device file in /dev.", path);
1554 return -EINVAL;
1555 }
1556
1557 r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm);
1558 }
1559
1560 } else if (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
1561
1562 if (isempty(eq))
1563 r = sd_bus_message_append(m, "v", "a(st)", 0);
1564 else {
1565 const char *path, *bandwidth, *e;
59f448cf 1566 uint64_t bytes;
df31a6c0
LP
1567
1568 e = strchr(eq, ' ');
1569 if (e) {
1570 path = strndupa(eq, e - eq);
1571 bandwidth = e+1;
1572 } else {
1573 log_error("Failed to parse %s value %s.", field, eq);
1574 return -EINVAL;
1575 }
1576
1577 if (!path_startswith(path, "/dev")) {
1578 log_error("%s is not a device file in /dev.", path);
1579 return -EINVAL;
1580 }
1581
1582 r = parse_size(bandwidth, 1000, &bytes);
1583 if (r < 0) {
1584 log_error("Failed to parse byte value %s.", bandwidth);
1585 return -EINVAL;
1586 }
1587
59f448cf 1588 r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes);
df31a6c0
LP
1589 }
1590
1591 } else if (streq(field, "BlockIODeviceWeight")) {
1592
1593 if (isempty(eq))
1594 r = sd_bus_message_append(m, "v", "a(st)", 0);
1595 else {
1596 const char *path, *weight, *e;
1597 uint64_t u;
1598
1599 e = strchr(eq, ' ');
1600 if (e) {
1601 path = strndupa(eq, e - eq);
1602 weight = e+1;
1603 } else {
1604 log_error("Failed to parse %s value %s.", field, eq);
1605 return -EINVAL;
1606 }
1607
1608 if (!path_startswith(path, "/dev")) {
1609 log_error("%s is not a device file in /dev.", path);
1610 return -EINVAL;
1611 }
1612
1613 r = safe_atou64(weight, &u);
1614 if (r < 0) {
1615 log_error("Failed to parse %s value %s.", field, weight);
1616 return -EINVAL;
1617 }
1618 r = sd_bus_message_append(m, "v", "a(st)", path, u);
1619 }
1620
d584f638
LP
1621 } else if (rlimit_from_string(field) >= 0) {
1622 uint64_t rl;
1623
1624 if (streq(eq, "infinity"))
1625 rl = (uint64_t) -1;
1626 else {
1627 r = safe_atou64(eq, &rl);
1628 if (r < 0) {
1629 log_error("Invalid resource limit: %s", eq);
1630 return -EINVAL;
1631 }
1632 }
1633
1634 r = sd_bus_message_append(m, "v", "t", rl);
1635
e567439e
LP
1636 } else if (streq(field, "Nice")) {
1637 int32_t i;
1638
1639 r = safe_atoi32(eq, &i);
1640 if (r < 0) {
1641 log_error("Failed to parse %s value %s.", field, eq);
1642 return -EINVAL;
1643 }
1644
1645 r = sd_bus_message_append(m, "v", "i", i);
1646
1647 } else if (streq(field, "Environment")) {
e9876fc9 1648 const char *p;
e567439e 1649
e9876fc9
EV
1650 r = sd_bus_message_open_container(m, 'v', "as");
1651 if (r < 0)
1652 return bus_log_create_error(r);
1653
1654 r = sd_bus_message_open_container(m, 'a', "s");
1655 if (r < 0)
1656 return bus_log_create_error(r);
1657
1658 p = eq;
1659
1660 for (;;) {
1661 _cleanup_free_ char *word = NULL;
1662
1663 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
1664 if (r < 0) {
1665 log_error("Failed to parse Environment value %s", eq);
1666 return -EINVAL;
1667 }
1668 if (r == 0)
1669 break;
1670
1671 if (!env_assignment_is_valid(word)) {
1672 log_error("Invalid environment assignment: %s", eq);
1673 return -EINVAL;
1674 }
1675
1676 r = sd_bus_message_append_basic(m, 's', word);
1677 if (r < 0)
1678 return bus_log_create_error(r);
1679 }
1680
1681 r = sd_bus_message_close_container(m);
1682 if (r < 0)
1683 return bus_log_create_error(r);
1684
1685 r = sd_bus_message_close_container(m);
e567439e
LP
1686
1687 } else if (streq(field, "KillSignal")) {
1688 int sig;
1689
1690 sig = signal_from_string_try_harder(eq);
1691 if (sig < 0) {
1692 log_error("Failed to parse %s value %s.", field, eq);
1693 return -EINVAL;
1694 }
1695
1696 r = sd_bus_message_append(m, "v", "i", sig);
1697
4c213d6c
WC
1698 } else if (streq(field, "AccuracySec")) {
1699 usec_t u;
1700
1701 r = parse_sec(eq, &u);
1702 if (r < 0) {
1703 log_error("Failed to parse %s value %s", field, eq);
1704 return -EINVAL;
1705 }
1706
1707 r = sd_bus_message_append(m, "v", "t", u);
f1db3327
EV
1708 } else if (streq(field, "TimerSlackNSec")) {
1709 nsec_t n;
4c213d6c 1710
f1db3327
EV
1711 r = parse_nsec(eq, &n);
1712 if (r < 0) {
1713 log_error("Failed to parse %s value %s", field, eq);
1714 return -EINVAL;
1715 }
1716
1717 r = sd_bus_message_append(m, "v", "t", n);
6b862936
EV
1718 } else if (streq(field, "OOMScoreAdjust")) {
1719 int oa;
1720
1721 r = safe_atoi(eq, &oa);
1722 if (r < 0) {
1723 log_error("Failed to parse %s value %s", field, eq);
1724 return -EINVAL;
1725 }
1726
1727 if (!oom_score_adjust_is_valid(oa)) {
1728 log_error("OOM score adjust value out of range");
1729 return -EINVAL;
1730 }
1731
1732 r = sd_bus_message_append(m, "v", "i", oa);
08596068
EV
1733 } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) {
1734 const char *p;
1735
1736 r = sd_bus_message_open_container(m, 'v', "as");
1737 if (r < 0)
1738 return bus_log_create_error(r);
1739
1740 r = sd_bus_message_open_container(m, 'a', "s");
1741 if (r < 0)
1742 return bus_log_create_error(r);
1743
1744 p = eq;
1745
1746 for (;;) {
1747 _cleanup_free_ char *word = NULL;
1748 int offset;
1749
1750 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
1751 if (r < 0) {
1752 log_error("Failed to parse %s value %s", field, eq);
1753 return -EINVAL;
1754 }
1755 if (r == 0)
1756 break;
1757
1758 if (!utf8_is_valid(word)) {
1759 log_error("Failed to parse %s value %s", field, eq);
1760 return -EINVAL;
1761 }
1762
1763 offset = word[0] == '-';
1764 if (!path_is_absolute(word + offset)) {
1765 log_error("Failed to parse %s value %s", field, eq);
1766 return -EINVAL;
1767 }
1768
1769 path_kill_slashes(word + offset);
1770
1771 r = sd_bus_message_append_basic(m, 's', word);
1772 if (r < 0)
1773 return bus_log_create_error(r);
1774 }
1775
1776 r = sd_bus_message_close_container(m);
1777 if (r < 0)
1778 return bus_log_create_error(r);
1779
1780 r = sd_bus_message_close_container(m);
1781
df31a6c0
LP
1782 } else {
1783 log_error("Unknown assignment %s.", assignment);
1784 return -EINVAL;
1785 }
1786
1787 if (r < 0)
1788 return bus_log_create_error(r);
1789
1790 return 0;
1791}
ebd011d9
LP
1792
1793typedef struct BusWaitForJobs {
1794 sd_bus *bus;
1795 Set *jobs;
1796
1797 char *name;
1798 char *result;
1799
1800 sd_bus_slot *slot_job_removed;
1801 sd_bus_slot *slot_disconnected;
1802} BusWaitForJobs;
1803
19070062 1804static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
ebd011d9
LP
1805 assert(m);
1806
1807 log_error("Warning! D-Bus connection terminated.");
19070062 1808 sd_bus_close(sd_bus_message_get_bus(m));
ebd011d9
LP
1809
1810 return 0;
1811}
1812
19070062 1813static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
ebd011d9
LP
1814 const char *path, *unit, *result;
1815 BusWaitForJobs *d = userdata;
1816 uint32_t id;
1817 char *found;
1818 int r;
1819
ebd011d9
LP
1820 assert(m);
1821 assert(d);
1822
1823 r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
1824 if (r < 0) {
1825 bus_log_parse_error(r);
1826 return 0;
1827 }
1828
1829 found = set_remove(d->jobs, (char*) path);
1830 if (!found)
1831 return 0;
1832
1833 free(found);
1834
1835 if (!isempty(result))
1836 d->result = strdup(result);
1837
1838 if (!isempty(unit))
1839 d->name = strdup(unit);
1840
1841 return 0;
1842}
1843
1844void bus_wait_for_jobs_free(BusWaitForJobs *d) {
1845 if (!d)
1846 return;
1847
1848 set_free_free(d->jobs);
1849
1850 sd_bus_slot_unref(d->slot_disconnected);
1851 sd_bus_slot_unref(d->slot_job_removed);
1852
1853 sd_bus_unref(d->bus);
1854
1855 free(d->name);
1856 free(d->result);
1857
1858 free(d);
1859}
1860
1861int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
1862 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
1863 int r;
1864
1865 assert(bus);
1866 assert(ret);
1867
1868 d = new0(BusWaitForJobs, 1);
1869 if (!d)
1870 return -ENOMEM;
1871
1872 d->bus = sd_bus_ref(bus);
1873
b798e7ba
LP
1874 /* When we are a bus client we match by sender. Direct
1875 * connections OTOH have no initialized sender field, and
1876 * hence we ignore the sender then */
ebd011d9
LP
1877 r = sd_bus_add_match(
1878 bus,
1879 &d->slot_job_removed,
b798e7ba 1880 bus->bus_client ?
ebd011d9
LP
1881 "type='signal',"
1882 "sender='org.freedesktop.systemd1',"
1883 "interface='org.freedesktop.systemd1.Manager',"
1884 "member='JobRemoved',"
b798e7ba
LP
1885 "path='/org/freedesktop/systemd1'" :
1886 "type='signal',"
1887 "interface='org.freedesktop.systemd1.Manager',"
1888 "member='JobRemoved',"
ebd011d9
LP
1889 "path='/org/freedesktop/systemd1'",
1890 match_job_removed, d);
1891 if (r < 0)
1892 return r;
1893
1894 r = sd_bus_add_match(
1895 bus,
1896 &d->slot_disconnected,
1897 "type='signal',"
1898 "sender='org.freedesktop.DBus.Local',"
1899 "interface='org.freedesktop.DBus.Local',"
1900 "member='Disconnected'",
1901 match_disconnected, d);
1902 if (r < 0)
1903 return r;
1904
1905 *ret = d;
1906 d = NULL;
1907
1908 return 0;
1909}
1910
1911static int bus_process_wait(sd_bus *bus) {
1912 int r;
1913
1914 for (;;) {
1915 r = sd_bus_process(bus, NULL);
1916 if (r < 0)
1917 return r;
1918 if (r > 0)
1919 return 0;
1920
1921 r = sd_bus_wait(bus, (uint64_t) -1);
1922 if (r < 0)
1923 return r;
1924 }
1925}
1926
d5cad221
MS
1927static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
1928 _cleanup_free_ char *dbus_path = NULL;
1929
1930 assert(d);
1931 assert(d->name);
1932 assert(result);
1933
1934 dbus_path = unit_dbus_path_from_name(d->name);
1935 if (!dbus_path)
1936 return -ENOMEM;
1937
1938 return sd_bus_get_property_string(d->bus,
1939 "org.freedesktop.systemd1",
1940 dbus_path,
1941 "org.freedesktop.systemd1.Service",
1942 "Result",
1943 NULL,
1944 result);
1945}
1946
1947static const struct {
1948 const char *result, *explanation;
1949} explanations [] = {
a61cc460
ZJS
1950 { "resources", "a configured resource limit was exceeded" },
1951 { "timeout", "a timeout was exceeded" },
1952 { "exit-code", "the control process exited with error code" },
1953 { "signal", "a fatal signal was delivered to the control process" },
1954 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1955 { "watchdog", "the service failed to send watchdog ping" },
1956 { "start-limit", "start of the service was attempted too often" }
d5cad221
MS
1957};
1958
1959static void log_job_error_with_service_result(const char* service, const char *result) {
d5cad221
MS
1960 _cleanup_free_ char *service_shell_quoted = NULL;
1961
1962 assert(service);
d5cad221
MS
1963
1964 service_shell_quoted = shell_maybe_quote(service);
1965
373a99e4
LP
1966 if (!isempty(result)) {
1967 unsigned i;
d5cad221 1968
373a99e4
LP
1969 for (i = 0; i < ELEMENTSOF(explanations); ++i)
1970 if (streq(result, explanations[i].result))
1971 break;
1972
1973 if (i < ELEMENTSOF(explanations)) {
1974 log_error("Job for %s failed because %s. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
1975 service,
1976 explanations[i].explanation,
1977 strna(service_shell_quoted));
d5cad221 1978
373a99e4
LP
1979 goto finish;
1980 }
1981 }
1982
1983 log_error("Job for %s failed. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
1984 service,
1985 strna(service_shell_quoted));
1986
1987finish:
d5cad221
MS
1988 /* For some results maybe additional explanation is required */
1989 if (streq_ptr(result, "start-limit"))
a61cc460 1990 log_info("To force a start use \"systemctl reset-failed %1$s\" followed by \"systemctl start %1$s\" again.",
d5cad221
MS
1991 strna(service_shell_quoted));
1992}
1993
ebd011d9
LP
1994static int check_wait_response(BusWaitForJobs *d, bool quiet) {
1995 int r = 0;
1996
1997 assert(d->result);
1998
1999 if (!quiet) {
2000 if (streq(d->result, "canceled"))
2001 log_error("Job for %s canceled.", strna(d->name));
2002 else if (streq(d->result, "timeout"))
2003 log_error("Job for %s timed out.", strna(d->name));
2004 else if (streq(d->result, "dependency"))
2005 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
2006 else if (streq(d->result, "invalid"))
2007 log_error("Job for %s invalid.", strna(d->name));
2008 else if (streq(d->result, "assert"))
2009 log_error("Assertion failed on job for %s.", strna(d->name));
2010 else if (streq(d->result, "unsupported"))
2011 log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
2012 else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
d5cad221
MS
2013 if (d->name) {
2014 int q;
2015 _cleanup_free_ char *result = NULL;
ebd011d9 2016
d5cad221
MS
2017 q = bus_job_get_service_result(d, &result);
2018 if (q < 0)
2019 log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name);
ebd011d9 2020
d5cad221
MS
2021 log_job_error_with_service_result(d->name, result);
2022 } else
2023 log_error("Job failed. See \"journalctl -xe\" for details.");
ebd011d9
LP
2024 }
2025 }
2026
2027 if (streq(d->result, "canceled"))
2028 r = -ECANCELED;
2029 else if (streq(d->result, "timeout"))
2030 r = -ETIME;
2031 else if (streq(d->result, "dependency"))
2032 r = -EIO;
2033 else if (streq(d->result, "invalid"))
2034 r = -ENOEXEC;
2035 else if (streq(d->result, "assert"))
2036 r = -EPROTO;
2037 else if (streq(d->result, "unsupported"))
15411c0c 2038 r = -EOPNOTSUPP;
ebd011d9
LP
2039 else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
2040 r = -EIO;
2041
2042 return r;
2043}
2044
2045int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet) {
2046 int r = 0;
2047
2048 assert(d);
2049
2050 while (!set_isempty(d->jobs)) {
2051 int q;
2052
2053 q = bus_process_wait(d->bus);
2054 if (q < 0)
2055 return log_error_errno(q, "Failed to wait for response: %m");
2056
2057 if (d->result) {
2058 q = check_wait_response(d, quiet);
2059 /* Return the first error as it is most likely to be
2060 * meaningful. */
2061 if (q < 0 && r == 0)
2062 r = q;
2063
2064 log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name));
2065 }
2066
a1e58e8e
LP
2067 d->name = mfree(d->name);
2068 d->result = mfree(d->result);
ebd011d9
LP
2069 }
2070
2071 return r;
2072}
2073
2074int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
2075 int r;
2076
2077 assert(d);
2078
2079 r = set_ensure_allocated(&d->jobs, &string_hash_ops);
2080 if (r < 0)
2081 return r;
2082
2083 return set_put_strdup(d->jobs, path);
2084}
d8f52ed2 2085
de158ed2
LP
2086int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
2087 int r;
2088
2089 r = bus_wait_for_jobs_add(d, path);
2090 if (r < 0)
2091 return log_oom();
2092
2093 return bus_wait_for_jobs(d, quiet);
2094}
2095
57ab2eab 2096int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
d8f52ed2
LP
2097 const char *type, *path, *source;
2098 int r;
2099
2100 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
2101 if (r < 0)
2102 return bus_log_parse_error(r);
2103
2104 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
2105 if (!quiet) {
2106 if (streq(type, "symlink"))
2107 log_info("Created symlink from %s to %s.", path, source);
2108 else
2109 log_info("Removed symlink %s.", path);
2110 }
57ab2eab
JS
2111
2112 r = unit_file_changes_add(changes, n_changes, streq(type, "symlink") ? UNIT_FILE_SYMLINK : UNIT_FILE_UNLINK, path, source);
2113 if (r < 0)
2114 return r;
d8f52ed2
LP
2115 }
2116 if (r < 0)
2117 return bus_log_parse_error(r);
2118
2119 r = sd_bus_message_exit_container(m);
2120 if (r < 0)
2121 return bus_log_parse_error(r);
2122
2123 return 0;
2124}
98a4c30b
DH
2125
2126/**
2127 * bus_path_encode_unique() - encode unique object path
2128 * @b: bus connection or NULL
2129 * @prefix: object path prefix
2130 * @sender_id: unique-name of client, or NULL
2131 * @external_id: external ID to be chosen by client, or NULL
2132 * @ret_path: storage for encoded object path pointer
2133 *
2134 * Whenever we provide a bus API that allows clients to create and manage
2135 * server-side objects, we need to provide a unique name for these objects. If
2136 * we let the server choose the name, we suffer from a race condition: If a
2137 * client creates an object asynchronously, it cannot destroy that object until
2138 * it received the method reply. It cannot know the name of the new object,
2139 * thus, it cannot destroy it. Furthermore, it enforces a round-trip.
2140 *
2141 * Therefore, many APIs allow the client to choose the unique name for newly
2142 * created objects. There're two problems to solve, though:
2143 * 1) Object names are usually defined via dbus object paths, which are
2144 * usually globally namespaced. Therefore, multiple clients must be able
2145 * to choose unique object names without interference.
2146 * 2) If multiple libraries share the same bus connection, they must be
2147 * able to choose unique object names without interference.
2148 * The first problem is solved easily by prefixing a name with the
2149 * unique-bus-name of a connection. The server side must enforce this and
2150 * reject any other name. The second problem is solved by providing unique
2151 * suffixes from within sd-bus.
2152 *
2153 * This helper allows clients to create unique object-paths. It uses the
2154 * template '/prefix/sender_id/external_id' and returns the new path in
2155 * @ret_path (must be freed by the caller).
2156 * If @sender_id is NULL, the unique-name of @b is used. If @external_id is
2157 * NULL, this function allocates a unique suffix via @b (by requesting a new
2158 * cookie). If both @sender_id and @external_id are given, @b can be passed as
2159 * NULL.
2160 *
2161 * Returns: 0 on success, negative error code on failure.
2162 */
2163int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path) {
2164 _cleanup_free_ char *sender_label = NULL, *external_label = NULL;
2165 char external_buf[DECIMAL_STR_MAX(uint64_t)], *p;
2166 int r;
2167
2168 assert_return(b || (sender_id && external_id), -EINVAL);
2169 assert_return(object_path_is_valid(prefix), -EINVAL);
2170 assert_return(ret_path, -EINVAL);
2171
2172 if (!sender_id) {
2173 r = sd_bus_get_unique_name(b, &sender_id);
2174 if (r < 0)
2175 return r;
2176 }
2177
2178 if (!external_id) {
2179 xsprintf(external_buf, "%"PRIu64, ++b->cookie);
2180 external_id = external_buf;
2181 }
2182
2183 sender_label = bus_label_escape(sender_id);
2184 if (!sender_label)
2185 return -ENOMEM;
2186
2187 external_label = bus_label_escape(external_id);
2188 if (!external_label)
2189 return -ENOMEM;
2190
2191 p = strjoin(prefix, "/", sender_label, "/", external_label, NULL);
2192 if (!p)
2193 return -ENOMEM;
2194
2195 *ret_path = p;
2196 return 0;
2197}
2198
2199/**
2200 * bus_path_decode_unique() - decode unique object path
2201 * @path: object path to decode
2202 * @prefix: object path prefix
2203 * @ret_sender: output parameter for sender-id label
2204 * @ret_external: output parameter for external-id label
2205 *
2206 * This does the reverse of bus_path_encode_unique() (see its description for
2207 * details). Both trailing labels, sender-id and external-id, are unescaped and
2208 * returned in the given output parameters (the caller must free them).
2209 *
2210 * Note that this function returns 0 if the path does not match the template
2211 * (see bus_path_encode_unique()), 1 if it matched.
2212 *
2213 * Returns: Negative error code on failure, 0 if the given object path does not
2214 * match the template (return parameters are set to NULL), 1 if it was
2215 * parsed successfully (return parameters contain allocated labels).
2216 */
2217int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external) {
2218 const char *p, *q;
2219 char *sender, *external;
2220
2221 assert(object_path_is_valid(path));
2222 assert(object_path_is_valid(prefix));
2223 assert(ret_sender);
2224 assert(ret_external);
2225
2226 p = object_path_startswith(path, prefix);
2227 if (!p) {
2228 *ret_sender = NULL;
2229 *ret_external = NULL;
2230 return 0;
2231 }
2232
2233 q = strchr(p, '/');
2234 if (!q) {
2235 *ret_sender = NULL;
2236 *ret_external = NULL;
2237 return 0;
2238 }
2239
2240 sender = bus_label_unescape_n(p, q - p);
2241 external = bus_label_unescape(q + 1);
2242 if (!sender || !external) {
2243 free(sender);
2244 free(external);
2245 return -ENOMEM;
2246 }
2247
2248 *ret_sender = sender;
2249 *ret_external = external;
2250 return 1;
2251}
057171ef
DH
2252
2253bool is_kdbus_wanted(void) {
2254 _cleanup_free_ char *value = NULL;
2255#ifdef ENABLE_KDBUS
2256 const bool configured = true;
2257#else
2258 const bool configured = false;
2259#endif
2260
2261 int r;
2262
2263 if (get_proc_cmdline_key("kdbus", NULL) > 0)
2264 return true;
2265
2266 r = get_proc_cmdline_key("kdbus=", &value);
2267 if (r <= 0)
2268 return configured;
2269
2270 return parse_boolean(value) == 1;
2271}
2272
2273bool is_kdbus_available(void) {
2274 _cleanup_close_ int fd = -1;
2275 struct kdbus_cmd cmd = { .size = sizeof(cmd), .flags = KDBUS_FLAG_NEGOTIATE };
2276
2277 if (!is_kdbus_wanted())
2278 return false;
2279
2280 fd = open("/sys/fs/kdbus/control", O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY);
2281 if (fd < 0)
2282 return false;
2283
2284 return ioctl(fd, KDBUS_CMD_BUS_MAKE, &cmd) >= 0;
2285}
c9d031c3
EV
2286
2287int bus_property_get_rlimit(
2288 sd_bus *bus,
2289 const char *path,
2290 const char *interface,
2291 const char *property,
2292 sd_bus_message *reply,
2293 void *userdata,
2294 sd_bus_error *error) {
2295
2296 struct rlimit *rl;
2297 uint64_t u;
2298 rlim_t x;
2299
2300 assert(bus);
2301 assert(reply);
2302 assert(userdata);
2303
2304 rl = *(struct rlimit**) userdata;
2305 if (rl)
2306 x = rl->rlim_max;
2307 else {
2308 struct rlimit buf = {};
2309 int z;
2310
f6bcaf4c 2311 z = rlimit_from_string(strstr(property, "Limit"));
c9d031c3
EV
2312 assert(z >= 0);
2313
2314 getrlimit(z, &buf);
2315 x = buf.rlim_max;
2316 }
2317
2318 /* rlim_t might have different sizes, let's map
2319 * RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on
2320 * all archs */
2321 u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x;
2322
2323 return sd_bus_message_append(reply, "t", u);
2324}