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