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