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