]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/bus-util.c
sd-bus: add support for new allow-interactive-authorization message header flag
[thirdparty/systemd.git] / src / libsystemd / sd-bus / 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 22#include <sys/socket.h>
5b12334d 23#include <sys/capability.h>
0c842e0a 24
430e21c2
LP
25#include "systemd/sd-daemon.h"
26
40ca29a1 27#include "util.h"
ffc06c35 28#include "strv.h"
40ca29a1
LP
29#include "macro.h"
30#include "def.h"
df31a6c0 31#include "path-util.h"
2bba9a57 32#include "missing.h"
40ca29a1 33
ffc06c35
KS
34#include "sd-event.h"
35#include "sd-bus.h"
36#include "bus-error.h"
37#include "bus-message.h"
40ca29a1 38#include "bus-util.h"
ebcf1f97 39#include "bus-internal.h"
40ca29a1 40
6203e07a 41static int name_owner_change_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
40ca29a1
LP
42 sd_event *e = userdata;
43
44 assert(bus);
45 assert(m);
46 assert(e);
47
b27adf35 48 sd_bus_close(bus);
6203e07a 49 sd_event_exit(e, 0);
b27adf35 50
40ca29a1
LP
51 return 1;
52}
53
6203e07a 54int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) {
40ca29a1 55 _cleanup_free_ char *match = NULL;
11846aa7 56 const char *unique;
40ca29a1
LP
57 int r;
58
59 assert(e);
60 assert(bus);
61 assert(name);
62
6203e07a
LP
63 /* We unregister the name here and then wait for the
64 * NameOwnerChanged signal for this event to arrive before we
65 * quit. We do this in order to make sure that any queued
66 * requests are still processed before we really exit. */
67
11846aa7 68 r = sd_bus_get_unique_name(bus, &unique);
40ca29a1
LP
69 if (r < 0)
70 return r;
71
11846aa7
LP
72 r = asprintf(&match,
73 "sender='org.freedesktop.DBus',"
74 "type='signal',"
75 "interface='org.freedesktop.DBus',"
76 "member='NameOwnerChanged',"
77 "path='/org/freedesktop/DBus',"
78 "arg0='%s',"
79 "arg1='%s',"
80 "arg2=''", name, unique);
81 if (r < 0)
82 return -ENOMEM;
83
19befb2d 84 r = sd_bus_add_match(bus, NULL, match, name_owner_change_callback, e);
40ca29a1
LP
85 if (r < 0)
86 return r;
87
88 r = sd_bus_release_name(bus, name);
89 if (r < 0)
90 return r;
91
40ca29a1
LP
92 return 0;
93}
94
37224a5f
LP
95int bus_event_loop_with_idle(
96 sd_event *e,
97 sd_bus *bus,
98 const char *name,
99 usec_t timeout,
100 check_idle_t check_idle,
101 void *userdata) {
40ca29a1 102 bool exiting = false;
6203e07a 103 int r, code;
40ca29a1
LP
104
105 assert(e);
106 assert(bus);
107 assert(name);
108
109 for (;;) {
37224a5f
LP
110 bool idle;
111
40ca29a1
LP
112 r = sd_event_get_state(e);
113 if (r < 0)
114 return r;
40ca29a1
LP
115 if (r == SD_EVENT_FINISHED)
116 break;
117
37224a5f
LP
118 if (check_idle)
119 idle = check_idle(userdata);
120 else
121 idle = true;
122
123 r = sd_event_run(e, exiting || !idle ? (uint64_t) -1 : timeout);
40ca29a1
LP
124 if (r < 0)
125 return r;
126
127 if (r == 0 && !exiting) {
b27adf35
LP
128
129 r = sd_bus_try_close(bus);
130 if (r == -EBUSY)
131 continue;
132
430e21c2
LP
133 /* Fallback for dbus1 connections: we
134 * unregister the name and wait for the
135 * response to come through for it */
b27adf35 136 if (r == -ENOTSUP) {
430e21c2
LP
137
138 /* Inform the service manager that we
139 * are going down, so that it will
140 * queue all further start requests,
141 * instead of assuming we are already
142 * running. */
143 sd_notify(false, "STOPPING=1");
b27adf35
LP
144
145 r = bus_async_unregister_and_exit(e, bus, name);
146 if (r < 0)
147 return r;
148
149 exiting = true;
150 continue;
151 }
152
40ca29a1
LP
153 if (r < 0)
154 return r;
155
b27adf35
LP
156 sd_event_exit(e, 0);
157 break;
40ca29a1
LP
158 }
159 }
160
6203e07a
LP
161 r = sd_event_get_exit_code(e, &code);
162 if (r < 0)
163 return r;
164
165 return code;
40ca29a1
LP
166}
167
5fd38859
DH
168int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
169 _cleanup_bus_message_unref_ sd_bus_message *rep = NULL;
170 int r, has_owner = 0;
171
172 assert(c);
173 assert(name);
174
175 r = sd_bus_call_method(c,
176 "org.freedesktop.DBus",
177 "/org/freedesktop/dbus",
178 "org.freedesktop.DBus",
179 "NameHasOwner",
180 error,
181 &rep,
182 "s",
183 name);
184 if (r < 0)
185 return r;
186
187 r = sd_bus_message_read_basic(rep, 'b', &has_owner);
188 if (r < 0)
189 return sd_bus_error_set_errno(error, r);
190
191 return has_owner;
192}
193
40ca29a1 194int bus_verify_polkit(
f3885791 195 sd_bus_message *call,
def9a7aa 196 int capability,
40ca29a1
LP
197 const char *action,
198 bool interactive,
199 bool *_challenge,
200 sd_bus_error *e) {
201
40ca29a1
LP
202 int r;
203
f3885791 204 assert(call);
40ca29a1
LP
205 assert(action);
206
f3885791 207 r = sd_bus_query_sender_privilege(call, capability);
5b12334d
LP
208 if (r < 0)
209 return r;
f3885791 210 else if (r > 0)
40ca29a1 211 return 1;
40ca29a1
LP
212#ifdef ENABLE_POLKIT
213 else {
214 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
102d8f81 215 int authorized = false, challenge = false;
5b12334d
LP
216 const char *sender;
217
f3885791 218 sender = sd_bus_message_get_sender(call);
5b12334d
LP
219 if (!sender)
220 return -EBADMSG;
40ca29a1
LP
221
222 r = sd_bus_call_method(
f3885791 223 call->bus,
40ca29a1
LP
224 "org.freedesktop.PolicyKit1",
225 "/org/freedesktop/PolicyKit1/Authority",
226 "org.freedesktop.PolicyKit1.Authority",
227 "CheckAuthorization",
228 e,
229 &reply,
230 "(sa{sv})sa{ss}us",
231 "system-bus-name", 1, "name", "s", sender,
232 action,
233 0,
234 interactive ? 1 : 0,
235 "");
236
237 if (r < 0) {
238 /* Treat no PK available as access denied */
239 if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
240 sd_bus_error_free(e);
241 return -EACCES;
242 }
243
244 return r;
245 }
246
313333b4 247 r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
2b49a470
TA
248 if (r < 0)
249 return r;
250
251 r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
252 if (r < 0)
253 return r;
40ca29a1
LP
254
255 if (authorized)
256 return 1;
257
258 if (_challenge) {
259 *_challenge = challenge;
260 return 0;
261 }
262 }
263#endif
264
265 return -EACCES;
266}
267
268#ifdef ENABLE_POLKIT
269
270typedef struct AsyncPolkitQuery {
271 sd_bus_message *request, *reply;
272 sd_bus_message_handler_t callback;
273 void *userdata;
19befb2d 274 sd_bus_slot *slot;
ebcf1f97 275 Hashmap *registry;
40ca29a1
LP
276} AsyncPolkitQuery;
277
19befb2d 278static void async_polkit_query_free(AsyncPolkitQuery *q) {
ebcf1f97
LP
279
280 if (!q)
281 return;
282
19befb2d 283 sd_bus_slot_unref(q->slot);
ebcf1f97
LP
284
285 if (q->registry && q->request)
286 hashmap_remove(q->registry, q->request);
287
288 sd_bus_message_unref(q->request);
289 sd_bus_message_unref(q->reply);
290
291 free(q);
292}
293
294static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
295 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
ebcf1f97 296 AsyncPolkitQuery *q = userdata;
40ca29a1
LP
297 int r;
298
299 assert(bus);
300 assert(reply);
301 assert(q);
302
19befb2d 303 q->slot = sd_bus_slot_unref(q->slot);
40ca29a1 304 q->reply = sd_bus_message_ref(reply);
40ca29a1 305
ebcf1f97
LP
306 r = sd_bus_message_rewind(q->request, true);
307 if (r < 0) {
308 r = sd_bus_reply_method_errno(q->request, r, NULL);
309 goto finish;
310 }
40ca29a1 311
ebcf1f97
LP
312 r = q->callback(bus, q->request, q->userdata, &error_buffer);
313 r = bus_maybe_reply_error(q->request, r, &error_buffer);
40ca29a1 314
ebcf1f97 315finish:
19befb2d
LP
316 async_polkit_query_free(q);
317
ebcf1f97 318 return r;
40ca29a1
LP
319}
320
321#endif
322
323int bus_verify_polkit_async(
f3885791 324 sd_bus_message *call,
def9a7aa 325 int capability,
40ca29a1
LP
326 const char *action,
327 bool interactive,
f3885791
LP
328 Hashmap **registry,
329 sd_bus_error *error) {
40ca29a1
LP
330
331#ifdef ENABLE_POLKIT
332 _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
333 AsyncPolkitQuery *q;
40ca29a1 334 const char *sender;
f3885791
LP
335 sd_bus_message_handler_t callback;
336 void *userdata;
5b12334d 337#endif
40ca29a1
LP
338 int r;
339
f3885791 340 assert(call);
40ca29a1 341 assert(action);
f3885791 342 assert(registry);
40ca29a1
LP
343
344#ifdef ENABLE_POLKIT
f3885791 345 q = hashmap_get(*registry, call);
40ca29a1 346 if (q) {
102d8f81 347 int authorized, challenge;
40ca29a1
LP
348
349 /* This is the second invocation of this function, and
350 * there's already a response from polkit, let's
351 * process it */
352 assert(q->reply);
353
354 if (sd_bus_message_is_method_error(q->reply, NULL)) {
355 const sd_bus_error *e;
356
ebcf1f97 357 /* Copy error from polkit reply */
40ca29a1
LP
358 e = sd_bus_message_get_error(q->reply);
359 sd_bus_error_copy(error, e);
40ca29a1 360
ebcf1f97
LP
361 /* Treat no PK available as access denied */
362 if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN))
363 return -EACCES;
364
5958d089 365 return -sd_bus_error_get_errno(e);
40ca29a1
LP
366 }
367
368 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
369 if (r >= 0)
370 r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
371
40ca29a1
LP
372 if (r < 0)
373 return r;
374
375 if (authorized)
376 return 1;
377
378 return -EACCES;
379 }
380#endif
381
f3885791 382 r = sd_bus_query_sender_privilege(call, capability);
5b12334d
LP
383 if (r < 0)
384 return r;
f3885791 385 else if (r > 0)
40ca29a1 386 return 1;
5b12334d 387
40ca29a1 388#ifdef ENABLE_POLKIT
f3885791
LP
389 if (sd_bus_get_current_message(call->bus) != call)
390 return -EINVAL;
391
392 callback = sd_bus_get_current_handler(call->bus);
393 if (!callback)
394 return -EINVAL;
395
396 userdata = sd_bus_get_current_userdata(call->bus);
397
398 sender = sd_bus_message_get_sender(call);
5b12334d
LP
399 if (!sender)
400 return -EBADMSG;
40ca29a1 401
d5099efc 402 r = hashmap_ensure_allocated(registry, NULL);
40ca29a1
LP
403 if (r < 0)
404 return r;
405
406 r = sd_bus_message_new_method_call(
f3885791 407 call->bus,
151b9b96 408 &pk,
40ca29a1
LP
409 "org.freedesktop.PolicyKit1",
410 "/org/freedesktop/PolicyKit1/Authority",
411 "org.freedesktop.PolicyKit1.Authority",
151b9b96 412 "CheckAuthorization");
40ca29a1
LP
413 if (r < 0)
414 return r;
415
416 r = sd_bus_message_append(
417 pk,
418 "(sa{sv})sa{ss}us",
419 "system-bus-name", 1, "name", "s", sender,
420 action,
421 0,
422 interactive ? 1 : 0,
ebcf1f97 423 NULL);
40ca29a1
LP
424 if (r < 0)
425 return r;
426
427 q = new0(AsyncPolkitQuery, 1);
428 if (!q)
429 return -ENOMEM;
430
f3885791 431 q->request = sd_bus_message_ref(call);
40ca29a1
LP
432 q->callback = callback;
433 q->userdata = userdata;
434
f3885791 435 r = hashmap_put(*registry, call, q);
40ca29a1 436 if (r < 0) {
19befb2d 437 async_polkit_query_free(q);
40ca29a1
LP
438 return r;
439 }
440
ebcf1f97
LP
441 q->registry = *registry;
442
f3885791 443 r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0);
ebcf1f97 444 if (r < 0) {
19befb2d 445 async_polkit_query_free(q);
40ca29a1 446 return r;
ebcf1f97 447 }
40ca29a1
LP
448
449 return 0;
450#endif
451
452 return -EACCES;
453}
454
36e34057 455void bus_verify_polkit_async_registry_free(Hashmap *registry) {
40ca29a1
LP
456#ifdef ENABLE_POLKIT
457 AsyncPolkitQuery *q;
458
459 while ((q = hashmap_steal_first(registry)))
19befb2d 460 async_polkit_query_free(q);
40ca29a1
LP
461
462 hashmap_free(registry);
463#endif
464}
0c842e0a 465
718db961 466int bus_check_peercred(sd_bus *c) {
0c842e0a
TG
467 struct ucred ucred;
468 socklen_t l;
0f8bd8de 469 int fd;
0c842e0a
TG
470
471 assert(c);
472
473 fd = sd_bus_get_fd(c);
0f8bd8de
LP
474 if (fd < 0)
475 return fd;
0c842e0a
TG
476
477 l = sizeof(struct ucred);
0f8bd8de 478 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
0c842e0a 479 return -errno;
0c842e0a 480
0f8bd8de 481 if (l != sizeof(struct ucred))
0c842e0a 482 return -E2BIG;
0c842e0a
TG
483
484 if (ucred.uid != 0 && ucred.uid != geteuid())
485 return -EPERM;
486
487 return 1;
488}
489
0f8bd8de
LP
490int bus_open_system_systemd(sd_bus **_bus) {
491 _cleanup_bus_unref_ sd_bus *bus = NULL;
0c842e0a 492 int r;
0c842e0a
TG
493
494 assert(_bus);
495
0f8bd8de
LP
496 if (geteuid() != 0)
497 return sd_bus_open_system(_bus);
a1da8583 498
a6aa8912
LP
499 /* If we are root and kdbus is not available, then let's talk
500 * directly to the system instance, instead of going via the
501 * bus */
a1da8583 502
a6aa8912 503#ifdef ENABLE_KDBUS
0f8bd8de
LP
504 r = sd_bus_new(&bus);
505 if (r < 0)
506 return r;
a1da8583 507
ab9001a1 508 r = sd_bus_set_address(bus, KERNEL_SYSTEM_BUS_PATH);
0f8bd8de
LP
509 if (r < 0)
510 return r;
a1da8583 511
a6aa8912
LP
512 bus->bus_client = true;
513
0f8bd8de 514 r = sd_bus_start(bus);
a6aa8912
LP
515 if (r >= 0) {
516 *_bus = bus;
517 bus = NULL;
518 return 0;
519 }
520
521 bus = sd_bus_unref(bus);
522#endif
523
524 r = sd_bus_new(&bus);
0f8bd8de
LP
525 if (r < 0)
526 return r;
a1da8583 527
a6aa8912
LP
528 r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
529 if (r < 0)
530 return r;
531
532 r = sd_bus_start(bus);
533 if (r < 0)
534 return sd_bus_open_system(_bus);
535
0f8bd8de 536 r = bus_check_peercred(bus);
a1da8583
TG
537 if (r < 0)
538 return r;
539
540 *_bus = bus;
0f8bd8de
LP
541 bus = NULL;
542
a1da8583
TG
543 return 0;
544}
545
41dd15e4
LP
546int bus_open_user_systemd(sd_bus **_bus) {
547 _cleanup_bus_unref_ sd_bus *bus = NULL;
a6aa8912 548 _cleanup_free_ char *ee = NULL;
41dd15e4
LP
549 const char *e;
550 int r;
551
a6aa8912 552 /* Try via kdbus first, and then directly */
41dd15e4
LP
553
554 assert(_bus);
555
a6aa8912
LP
556#ifdef ENABLE_KDBUS
557 r = sd_bus_new(&bus);
558 if (r < 0)
559 return r;
560
de0671ee 561 if (asprintf(&bus->address, KERNEL_USER_BUS_FMT, getuid()) < 0)
a6aa8912
LP
562 return -ENOMEM;
563
564 bus->bus_client = true;
565
566 r = sd_bus_start(bus);
567 if (r >= 0) {
568 *_bus = bus;
569 bus = NULL;
570 return 0;
571 }
572
573 bus = sd_bus_unref(bus);
574#endif
575
41dd15e4 576 e = secure_getenv("XDG_RUNTIME_DIR");
537220d9 577 if (!e)
3f78871b 578 return sd_bus_open_user(_bus);
537220d9 579
a6aa8912
LP
580 ee = bus_address_escape(e);
581 if (!ee)
537220d9 582 return -ENOMEM;
41dd15e4
LP
583
584 r = sd_bus_new(&bus);
585 if (r < 0)
586 return r;
587
a6aa8912
LP
588 bus->address = strjoin("unix:path=", ee, "/systemd/private", NULL);
589 if (!bus->address)
590 return -ENOMEM;
41dd15e4
LP
591
592 r = sd_bus_start(bus);
593 if (r < 0)
3f78871b 594 return sd_bus_open_user(_bus);
41dd15e4
LP
595
596 r = bus_check_peercred(bus);
597 if (r < 0)
598 return r;
599
600 *_bus = bus;
601 bus = NULL;
602
603 return 0;
604}
605
9f6eb1cd 606int bus_print_property(const char *name, sd_bus_message *property, bool all) {
a1da8583
TG
607 char type;
608 const char *contents;
9f6eb1cd 609 int r;
a1da8583
TG
610
611 assert(name);
612 assert(property);
613
9f6eb1cd
KS
614 r = sd_bus_message_peek_type(property, &type, &contents);
615 if (r < 0)
616 return r;
a1da8583
TG
617
618 switch (type) {
619
620 case SD_BUS_TYPE_STRING: {
621 const char *s;
9f6eb1cd
KS
622
623 r = sd_bus_message_read_basic(property, type, &s);
624 if (r < 0)
625 return r;
a1da8583
TG
626
627 if (all || !isempty(s))
628 printf("%s=%s\n", name, s);
629
630 return 1;
631 }
632
633 case SD_BUS_TYPE_BOOLEAN: {
c2fa048c 634 int b;
a1da8583 635
9f6eb1cd
KS
636 r = sd_bus_message_read_basic(property, type, &b);
637 if (r < 0)
638 return r;
639
a1da8583
TG
640 printf("%s=%s\n", name, yes_no(b));
641
642 return 1;
643 }
644
645 case SD_BUS_TYPE_UINT64: {
646 uint64_t u;
647
9f6eb1cd
KS
648 r = sd_bus_message_read_basic(property, type, &u);
649 if (r < 0)
650 return r;
a1da8583
TG
651
652 /* Yes, heuristics! But we can change this check
653 * should it turn out to not be sufficient */
654
655 if (endswith(name, "Timestamp")) {
656 char timestamp[FORMAT_TIMESTAMP_MAX], *t;
657
658 t = format_timestamp(timestamp, sizeof(timestamp), u);
659 if (t || all)
660 printf("%s=%s\n", name, strempty(t));
661
662 } else if (strstr(name, "USec")) {
663 char timespan[FORMAT_TIMESPAN_MAX];
664
665 printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
666 } else
667 printf("%s=%llu\n", name, (unsigned long long) u);
668
669 return 1;
670 }
671
672 case SD_BUS_TYPE_UINT32: {
673 uint32_t u;
674
9f6eb1cd
KS
675 r = sd_bus_message_read_basic(property, type, &u);
676 if (r < 0)
677 return r;
a1da8583
TG
678
679 if (strstr(name, "UMask") || strstr(name, "Mode"))
680 printf("%s=%04o\n", name, u);
681 else
682 printf("%s=%u\n", name, (unsigned) u);
683
684 return 1;
685 }
686
687 case SD_BUS_TYPE_INT32: {
688 int32_t i;
689
9f6eb1cd
KS
690 r = sd_bus_message_read_basic(property, type, &i);
691 if (r < 0)
692 return r;
a1da8583
TG
693
694 printf("%s=%i\n", name, (int) i);
695 return 1;
696 }
697
698 case SD_BUS_TYPE_DOUBLE: {
699 double d;
700
9f6eb1cd
KS
701 r = sd_bus_message_read_basic(property, type, &d);
702 if (r < 0)
703 return r;
a1da8583
TG
704
705 printf("%s=%g\n", name, d);
706 return 1;
707 }
708
709 case SD_BUS_TYPE_ARRAY:
a1da8583 710 if (streq(contents, "s")) {
261afec5
MAP
711 bool first = true;
712 const char *str;
a1da8583 713
9f6eb1cd
KS
714 r = sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
715 if (r < 0)
716 return r;
717
261afec5
MAP
718 while((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) {
719 if (first)
720 printf("%s=", name);
721
722 printf("%s%s", first ? "" : " ", str);
723
724 first = false;
725 }
9f6eb1cd
KS
726 if (r < 0)
727 return r;
a1da8583 728
261afec5 729 if (first && all)
a1da8583 730 printf("%s=", name);
261afec5 731 if (!first || all)
a1da8583 732 puts("");
a1da8583 733
9f6eb1cd
KS
734 r = sd_bus_message_exit_container(property);
735 if (r < 0)
736 return r;
a1da8583
TG
737
738 return 1;
739
740 } else if (streq(contents, "y")) {
741 const uint8_t *u;
742 size_t n;
743
9f6eb1cd
KS
744 r = sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
745 if (r < 0)
746 return r;
747
a1da8583
TG
748 if (all || n > 0) {
749 unsigned int i;
750
751 printf("%s=", name);
752
753 for (i = 0; i < n; i++)
754 printf("%02x", u[i]);
755
756 puts("");
757 }
758
759 return 1;
760
761 } else if (streq(contents, "u")) {
762 uint32_t *u;
763 size_t n;
764
9f6eb1cd
KS
765 r = sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
766 if (r < 0)
767 return r;
768
a1da8583
TG
769 if (all || n > 0) {
770 unsigned int i;
771
772 printf("%s=", name);
773
774 for (i = 0; i < n; i++)
775 printf("%08x", u[i]);
776
777 puts("");
778 }
779
780 return 1;
781 }
782
783 break;
784 }
785
786 return 0;
787}
d21ed1ea 788
27e72d6b 789int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all) {
9f6eb1cd 790 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
ffc06c35
KS
791 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
792 int r;
793
9f6eb1cd
KS
794 assert(bus);
795 assert(path);
796
797 r = sd_bus_call_method(bus,
27e72d6b 798 dest,
ffc06c35
KS
799 path,
800 "org.freedesktop.DBus.Properties",
801 "GetAll",
802 &error,
9f6eb1cd 803 &reply,
ffc06c35 804 "s", "");
9f6eb1cd 805 if (r < 0)
ffc06c35 806 return r;
ffc06c35 807
9f6eb1cd 808 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
ffc06c35
KS
809 if (r < 0)
810 return r;
811
9f6eb1cd 812 while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
ffc06c35 813 const char *name;
ffc06c35 814 const char *contents;
ffc06c35 815
9f6eb1cd 816 r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name);
ffc06c35
KS
817 if (r < 0)
818 return r;
819
9f6eb1cd
KS
820 if (!filter || strv_find(filter, name)) {
821 r = sd_bus_message_peek_type(reply, NULL, &contents);
822 if (r < 0)
823 return r;
824
825 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
826 if (r < 0)
827 return r;
ffc06c35 828
9f6eb1cd
KS
829 r = bus_print_property(name, reply, all);
830 if (r < 0)
831 return r;
27e72d6b
SP
832 if (r == 0) {
833 if (all)
834 printf("%s=[unprintable]\n", name);
835 /* skip what we didn't read */
836 r = sd_bus_message_skip(reply, contents);
837 if (r < 0)
838 return r;
839 }
9f6eb1cd
KS
840
841 r = sd_bus_message_exit_container(reply);
842 if (r < 0)
843 return r;
844 } else {
845 r = sd_bus_message_skip(reply, "v");
846 if (r < 0)
847 return r;
848 }
849
850 r = sd_bus_message_exit_container(reply);
ffc06c35
KS
851 if (r < 0)
852 return r;
9f6eb1cd
KS
853 }
854 if (r < 0)
855 return r;
ffc06c35 856
9f6eb1cd
KS
857 r = sd_bus_message_exit_container(reply);
858 if (r < 0)
859 return r;
ffc06c35 860
9f6eb1cd
KS
861 return 0;
862}
ffc06c35 863
9f6eb1cd
KS
864int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
865 sd_id128_t *p = userdata;
866 const void *v;
867 size_t n;
868 int r;
ffc06c35 869
9f6eb1cd
KS
870 r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n);
871 if (r < 0)
872 return r;
ffc06c35 873
9f6eb1cd
KS
874 if (n == 0)
875 *p = SD_ID128_NULL;
876 else if (n == 16)
877 memcpy((*p).bytes, v, n);
878 else
879 return -EINVAL;
ffc06c35 880
9f6eb1cd
KS
881 return 0;
882}
ffc06c35 883
9f6eb1cd
KS
884static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
885 char type;
886 int r;
ffc06c35 887
9f6eb1cd
KS
888 r = sd_bus_message_peek_type(m, &type, NULL);
889 if (r < 0)
890 return r;
ffc06c35 891
9f6eb1cd
KS
892 switch (type) {
893 case SD_BUS_TYPE_STRING: {
894 const char *s;
895 char *str;
896 char **p = userdata;
ffc06c35 897
9f6eb1cd
KS
898 r = sd_bus_message_read_basic(m, type, &s);
899 if (r < 0)
900 break;
ffc06c35 901
9f6eb1cd
KS
902 if (isempty(s))
903 break;
ffc06c35 904
9f6eb1cd
KS
905 str = strdup(s);
906 if (!str) {
907 r = -ENOMEM;
ffc06c35
KS
908 break;
909 }
9f6eb1cd
KS
910 free(*p);
911 *p = str;
ffc06c35 912
9f6eb1cd
KS
913 break;
914 }
ffc06c35 915
9f6eb1cd
KS
916 case SD_BUS_TYPE_ARRAY: {
917 _cleanup_strv_free_ char **l = NULL;
918 char ***p = userdata;
ffc06c35 919
9f6eb1cd
KS
920 r = bus_message_read_strv_extend(m, &l);
921 if (r < 0)
922 break;
ffc06c35 923
9f6eb1cd
KS
924 strv_free(*p);
925 *p = l;
926 l = NULL;
ffc06c35 927
9f6eb1cd
KS
928 break;
929 }
ffc06c35 930
9f6eb1cd
KS
931 case SD_BUS_TYPE_BOOLEAN: {
932 unsigned b;
933 bool *p = userdata;
ffc06c35 934
9f6eb1cd
KS
935 r = sd_bus_message_read_basic(m, type, &b);
936 if (r < 0)
937 break;
ffc06c35 938
9f6eb1cd 939 *p = b;
ffc06c35 940
9f6eb1cd
KS
941 break;
942 }
ffc06c35 943
9f6eb1cd
KS
944 case SD_BUS_TYPE_UINT32: {
945 uint64_t u;
946 uint32_t *p = userdata;
947
948 r = sd_bus_message_read_basic(m, type, &u);
949 if (r < 0)
ffc06c35 950 break;
ffc06c35 951
9f6eb1cd
KS
952 *p = u;
953
954 break;
955 }
956
957 case SD_BUS_TYPE_UINT64: {
958 uint64_t t;
959 uint64_t *p = userdata;
960
961 r = sd_bus_message_read_basic(m, type, &t);
962 if (r < 0)
ffc06c35 963 break;
ffc06c35 964
9f6eb1cd
KS
965 *p = t;
966
967 break;
968 }
969
970 default:
971 break;
972 }
973
974 return r;
975}
976
aae2b488
DH
977int bus_message_map_all_properties(sd_bus *bus,
978 sd_bus_message *m,
979 const struct bus_properties_map *map,
980 void *userdata) {
9f6eb1cd
KS
981 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
982 int r;
983
984 assert(bus);
aae2b488 985 assert(m);
9f6eb1cd
KS
986 assert(map);
987
9f6eb1cd
KS
988 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
989 if (r < 0)
990 return r;
991
992 while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
993 const struct bus_properties_map *prop;
994 const char *member;
995 const char *contents;
996 void *v;
997 unsigned i;
998
999 r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
ffc06c35
KS
1000 if (r < 0)
1001 return r;
1002
9f6eb1cd
KS
1003 for (i = 0, prop = NULL; map[i].member; i++)
1004 if (streq(map[i].member, member)) {
1005 prop = &map[i];
1006 break;
1007 }
1008
1009 if (prop) {
1010 r = sd_bus_message_peek_type(m, NULL, &contents);
1011 if (r < 0)
1012 return r;
1013
1014 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
1015 if (r < 0)
1016 return r;
1017
1018 v = (uint8_t *)userdata + prop->offset;
27e72d6b 1019 if (map[i].set)
9f6eb1cd
KS
1020 r = prop->set(bus, member, m, &error, v);
1021 else
1022 r = map_basic(bus, member, m, &error, v);
2b49a470
TA
1023 if (r < 0)
1024 return r;
9f6eb1cd
KS
1025
1026 r = sd_bus_message_exit_container(m);
1027 if (r < 0)
1028 return r;
1029 } else {
1030 r = sd_bus_message_skip(m, "v");
1031 if (r < 0)
6c1508b8 1032 return r;
9f6eb1cd
KS
1033 }
1034
ffc06c35
KS
1035 r = sd_bus_message_exit_container(m);
1036 if (r < 0)
1037 return r;
1038 }
1039
aae2b488
DH
1040 return sd_bus_message_exit_container(m);
1041}
1042
1043int bus_message_map_properties_changed(sd_bus *bus,
1044 sd_bus_message *m,
1045 const struct bus_properties_map *map,
1046 void *userdata) {
1047 const char *member;
1048 int r, invalidated, i;
1049
1050 assert(bus);
1051 assert(m);
1052 assert(map);
1053
aae2b488
DH
1054 r = bus_message_map_all_properties(bus, m, map, userdata);
1055 if (r < 0)
1056 return r;
1057
1058 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s");
1059 if (r < 0)
1060 return r;
1061
1062 invalidated = 0;
1063 while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member)) > 0)
1064 for (i = 0; map[i].member; i++)
1065 if (streq(map[i].member, member)) {
1066 ++invalidated;
1067 break;
1068 }
1069
1070 r = sd_bus_message_exit_container(m);
1071 if (r < 0)
1072 return r;
1073
1074 return invalidated;
1075}
1076
1077int bus_map_all_properties(sd_bus *bus,
1078 const char *destination,
1079 const char *path,
1080 const struct bus_properties_map *map,
1081 void *userdata) {
1082 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1083 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1084 int r;
1085
1086 assert(bus);
1087 assert(destination);
1088 assert(path);
1089 assert(map);
1090
1091 r = sd_bus_call_method(
1092 bus,
1093 destination,
1094 path,
1095 "org.freedesktop.DBus.Properties",
1096 "GetAll",
1097 &error,
1098 &m,
1099 "s", "");
1100 if (r < 0)
1101 return r;
1102
1103 return bus_message_map_all_properties(bus, m, map, userdata);
ffc06c35
KS
1104}
1105
d21ed1ea
LP
1106int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
1107 int r;
1108
1109 assert(transport >= 0);
1110 assert(transport < _BUS_TRANSPORT_MAX);
1111 assert(bus);
1112
1113 assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
1114 assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
1115
1116 switch (transport) {
1117
1118 case BUS_TRANSPORT_LOCAL:
1119 if (user)
76b54375 1120 r = sd_bus_default_user(bus);
d21ed1ea 1121 else
76b54375 1122 r = sd_bus_default_system(bus);
d21ed1ea
LP
1123
1124 break;
1125
1126 case BUS_TRANSPORT_REMOTE:
3db729cb 1127 r = sd_bus_open_system_remote(bus, host);
41dd15e4
LP
1128 break;
1129
1130 case BUS_TRANSPORT_CONTAINER:
3db729cb 1131 r = sd_bus_open_system_container(bus, host);
41dd15e4
LP
1132 break;
1133
1134 default:
1135 assert_not_reached("Hmm, unknown transport type.");
1136 }
1137
1138 return r;
1139}
1140
1141int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
1142 int r;
1143
1144 assert(transport >= 0);
1145 assert(transport < _BUS_TRANSPORT_MAX);
1146 assert(bus);
1147
1148 assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
1149 assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
1150
1151 switch (transport) {
1152
1153 case BUS_TRANSPORT_LOCAL:
1154 if (user)
1155 r = bus_open_user_systemd(bus);
1156 else
1157 r = bus_open_system_systemd(bus);
1158
1159 break;
1160
1161 case BUS_TRANSPORT_REMOTE:
3db729cb 1162 r = sd_bus_open_system_remote(bus, host);
d21ed1ea
LP
1163 break;
1164
1165 case BUS_TRANSPORT_CONTAINER:
3db729cb 1166 r = sd_bus_open_system_container(bus, host);
d21ed1ea
LP
1167 break;
1168
1169 default:
1170 assert_not_reached("Hmm, unknown transport type.");
1171 }
1172
1173 return r;
1174}
e6504030
LP
1175
1176int bus_property_get_bool(
1177 sd_bus *bus,
1178 const char *path,
1179 const char *interface,
1180 const char *property,
1181 sd_bus_message *reply,
ebcf1f97
LP
1182 void *userdata,
1183 sd_bus_error *error) {
e6504030
LP
1184
1185 int b = *(bool*) userdata;
1186
1187 return sd_bus_message_append_basic(reply, 'b', &b);
1188}
1189
718db961
LP
1190#if __SIZEOF_SIZE_T__ != 8
1191int bus_property_get_size(
e6504030
LP
1192 sd_bus *bus,
1193 const char *path,
1194 const char *interface,
1195 const char *property,
1196 sd_bus_message *reply,
ebcf1f97
LP
1197 void *userdata,
1198 sd_bus_error *error) {
e6504030 1199
718db961 1200 uint64_t sz = *(size_t*) userdata;
e6504030 1201
718db961 1202 return sd_bus_message_append_basic(reply, 't', &sz);
e6504030 1203}
718db961
LP
1204#endif
1205
1206#if __SIZEOF_LONG__ != 8
1207int bus_property_get_long(
1208 sd_bus *bus,
1209 const char *path,
1210 const char *interface,
1211 const char *property,
1212 sd_bus_message *reply,
ebcf1f97
LP
1213 void *userdata,
1214 sd_bus_error *error) {
718db961
LP
1215
1216 int64_t l = *(long*) userdata;
1217
1218 return sd_bus_message_append_basic(reply, 'x', &l);
1219}
1220
1221int bus_property_get_ulong(
1222 sd_bus *bus,
1223 const char *path,
1224 const char *interface,
1225 const char *property,
1226 sd_bus_message *reply,
ebcf1f97
LP
1227 void *userdata,
1228 sd_bus_error *error) {
718db961
LP
1229
1230 uint64_t ul = *(unsigned long*) userdata;
1231
1232 return sd_bus_message_append_basic(reply, 't', &ul);
1233}
1234#endif
5b30bef8
LP
1235
1236int bus_log_parse_error(int r) {
ee8c4568 1237 log_error("Failed to parse bus message: %s", strerror(-r));
5b30bef8
LP
1238 return r;
1239}
f459b602
MAP
1240
1241int bus_log_create_error(int r) {
ee8c4568 1242 log_error("Failed to create bus message: %s", strerror(-r));
f459b602
MAP
1243 return r;
1244}
1245
1246int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
1247 assert(message);
1248 assert(u);
1249
1238ee09
LP
1250 u->machine = NULL;
1251
f459b602
MAP
1252 return sd_bus_message_read(
1253 message,
1254 "(ssssssouso)",
1255 &u->id,
1256 &u->description,
1257 &u->load_state,
1258 &u->active_state,
1259 &u->sub_state,
1260 &u->following,
1261 &u->unit_path,
1262 &u->job_id,
1263 &u->job_type,
1264 &u->job_path);
1265}
ebcf1f97
LP
1266
1267int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) {
1268 assert(m);
1269
1270 if (r < 0) {
1271 if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
1272 sd_bus_reply_method_errno(m, r, error);
1273
1274 } else if (sd_bus_error_is_set(error)) {
1275 if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
1276 sd_bus_reply_method_error(m, error);
ebcf1f97
LP
1277 } else
1278 return r;
1279
1280 log_debug("Failed to process message [type=%s sender=%s path=%s interface=%s member=%s signature=%s]: %s",
1281 bus_message_type_to_string(m->header->type),
1282 strna(m->sender),
1283 strna(m->path),
1284 strna(m->interface),
1285 strna(m->member),
1286 strna(m->root_container.signature),
1287 bus_error_message(error, r));
1288
1289 return 1;
1290}
df31a6c0
LP
1291
1292int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
1293 const char *eq, *field;
1294 int r;
1295
1296 assert(m);
1297 assert(assignment);
1298
1299 eq = strchr(assignment, '=');
1300 if (!eq) {
1301 log_error("Not an assignment: %s", assignment);
1302 return -EINVAL;
1303 }
1304
1305 field = strndupa(assignment, eq - assignment);
1306 eq ++;
1307
b2f8b02e
LP
1308 if (streq(field, "CPUQuota")) {
1309
1310 if (isempty(eq)) {
1311
1312 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec");
1313 if (r < 0)
1314 return bus_log_create_error(r);
1315
3a43da28 1316 r = sd_bus_message_append(m, "v", "t", USEC_INFINITY);
b2f8b02e
LP
1317
1318 } else if (endswith(eq, "%")) {
1319 double percent;
1320
1321 if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) {
1322 log_error("CPU quota '%s' invalid.", eq);
1323 return -EINVAL;
1324 }
1325
1326 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec");
1327 if (r < 0)
1328 return bus_log_create_error(r);
1329
1330 r = sd_bus_message_append(m, "v", "t", (usec_t) percent * USEC_PER_SEC / 100);
1331 } else {
9a054909 1332 log_error("CPU quota needs to be in percent.");
b2f8b02e
LP
1333 return -EINVAL;
1334 }
1335
b2f8b02e
LP
1336 if (r < 0)
1337 return bus_log_create_error(r);
1338
1339 return 0;
1340 }
1341
df31a6c0
LP
1342 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1343 if (r < 0)
1344 return bus_log_create_error(r);
1345
e567439e
LP
1346 if (STR_IN_SET(field,
1347 "CPUAccounting", "MemoryAccounting", "BlockIOAccounting",
1348 "SendSIGHUP", "SendSIGKILL")) {
df31a6c0
LP
1349
1350 r = parse_boolean(eq);
1351 if (r < 0) {
1352 log_error("Failed to parse boolean assignment %s.", assignment);
1353 return -EINVAL;
1354 }
1355
1356 r = sd_bus_message_append(m, "v", "b", r);
1357
1358 } else if (streq(field, "MemoryLimit")) {
1359 off_t bytes;
1360
1361 r = parse_size(eq, 1024, &bytes);
1362 if (r < 0) {
1363 log_error("Failed to parse bytes specification %s", assignment);
1364 return -EINVAL;
1365 }
1366
1367 r = sd_bus_message_append(m, "v", "t", (uint64_t) bytes);
1368
1369 } else if (STR_IN_SET(field, "CPUShares", "BlockIOWeight")) {
1370 uint64_t u;
1371
1372 r = safe_atou64(eq, &u);
1373 if (r < 0) {
1374 log_error("Failed to parse %s value %s.", field, eq);
1375 return -EINVAL;
1376 }
1377
1378 r = sd_bus_message_append(m, "v", "t", u);
1379
e567439e 1380 } else if (STR_IN_SET(field, "User", "Group", "DevicePolicy", "KillMode"))
df31a6c0
LP
1381 r = sd_bus_message_append(m, "v", "s", eq);
1382
1383 else if (streq(field, "DeviceAllow")) {
1384
1385 if (isempty(eq))
1386 r = sd_bus_message_append(m, "v", "a(ss)", 0);
1387 else {
1388 const char *path, *rwm, *e;
1389
1390 e = strchr(eq, ' ');
1391 if (e) {
1392 path = strndupa(eq, e - eq);
1393 rwm = e+1;
1394 } else {
1395 path = eq;
1396 rwm = "";
1397 }
1398
1399 if (!path_startswith(path, "/dev")) {
1400 log_error("%s is not a device file in /dev.", path);
1401 return -EINVAL;
1402 }
1403
1404 r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm);
1405 }
1406
1407 } else if (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
1408
1409 if (isempty(eq))
1410 r = sd_bus_message_append(m, "v", "a(st)", 0);
1411 else {
1412 const char *path, *bandwidth, *e;
1413 off_t bytes;
1414
1415 e = strchr(eq, ' ');
1416 if (e) {
1417 path = strndupa(eq, e - eq);
1418 bandwidth = e+1;
1419 } else {
1420 log_error("Failed to parse %s value %s.", field, eq);
1421 return -EINVAL;
1422 }
1423
1424 if (!path_startswith(path, "/dev")) {
1425 log_error("%s is not a device file in /dev.", path);
1426 return -EINVAL;
1427 }
1428
1429 r = parse_size(bandwidth, 1000, &bytes);
1430 if (r < 0) {
1431 log_error("Failed to parse byte value %s.", bandwidth);
1432 return -EINVAL;
1433 }
1434
1435 r = sd_bus_message_append(m, "v", "a(st)", 1, path, (uint64_t) bytes);
1436 }
1437
1438 } else if (streq(field, "BlockIODeviceWeight")) {
1439
1440 if (isempty(eq))
1441 r = sd_bus_message_append(m, "v", "a(st)", 0);
1442 else {
1443 const char *path, *weight, *e;
1444 uint64_t u;
1445
1446 e = strchr(eq, ' ');
1447 if (e) {
1448 path = strndupa(eq, e - eq);
1449 weight = e+1;
1450 } else {
1451 log_error("Failed to parse %s value %s.", field, eq);
1452 return -EINVAL;
1453 }
1454
1455 if (!path_startswith(path, "/dev")) {
1456 log_error("%s is not a device file in /dev.", path);
1457 return -EINVAL;
1458 }
1459
1460 r = safe_atou64(weight, &u);
1461 if (r < 0) {
1462 log_error("Failed to parse %s value %s.", field, weight);
1463 return -EINVAL;
1464 }
1465 r = sd_bus_message_append(m, "v", "a(st)", path, u);
1466 }
1467
d584f638
LP
1468 } else if (rlimit_from_string(field) >= 0) {
1469 uint64_t rl;
1470
1471 if (streq(eq, "infinity"))
1472 rl = (uint64_t) -1;
1473 else {
1474 r = safe_atou64(eq, &rl);
1475 if (r < 0) {
1476 log_error("Invalid resource limit: %s", eq);
1477 return -EINVAL;
1478 }
1479 }
1480
1481 r = sd_bus_message_append(m, "v", "t", rl);
1482
e567439e
LP
1483 } else if (streq(field, "Nice")) {
1484 int32_t i;
1485
1486 r = safe_atoi32(eq, &i);
1487 if (r < 0) {
1488 log_error("Failed to parse %s value %s.", field, eq);
1489 return -EINVAL;
1490 }
1491
1492 r = sd_bus_message_append(m, "v", "i", i);
1493
1494 } else if (streq(field, "Environment")) {
1495
1496 r = sd_bus_message_append(m, "v", "as", 1, eq);
1497
1498 } else if (streq(field, "KillSignal")) {
1499 int sig;
1500
1501 sig = signal_from_string_try_harder(eq);
1502 if (sig < 0) {
1503 log_error("Failed to parse %s value %s.", field, eq);
1504 return -EINVAL;
1505 }
1506
1507 r = sd_bus_message_append(m, "v", "i", sig);
1508
df31a6c0
LP
1509 } else {
1510 log_error("Unknown assignment %s.", assignment);
1511 return -EINVAL;
1512 }
1513
1514 if (r < 0)
1515 return bus_log_create_error(r);
1516
1517 return 0;
1518}