]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bus-unit-util.c
service: add new RootImageOptions feature
[thirdparty/systemd.git] / src / shared / bus-unit-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "alloc-util.h"
4 #include "bus-error.h"
5 #include "bus-unit-util.h"
6 #include "bus-util.h"
7 #include "cap-list.h"
8 #include "cgroup-setup.h"
9 #include "cgroup-util.h"
10 #include "condition.h"
11 #include "coredump-util.h"
12 #include "cpu-set-util.h"
13 #include "escape.h"
14 #include "exec-util.h"
15 #include "exit-status.h"
16 #include "fileio.h"
17 #include "hexdecoct.h"
18 #include "hostname-util.h"
19 #include "in-addr-util.h"
20 #include "ip-protocol-list.h"
21 #include "locale-util.h"
22 #include "log.h"
23 #include "missing_fs.h"
24 #include "mountpoint-util.h"
25 #include "nsflags.h"
26 #include "numa-util.h"
27 #include "parse-util.h"
28 #include "path-util.h"
29 #include "process-util.h"
30 #include "rlimit-util.h"
31 #include "securebits-util.h"
32 #include "signal-util.h"
33 #include "socket-util.h"
34 #include "sort-util.h"
35 #include "stdio-util.h"
36 #include "string-util.h"
37 #include "syslog-util.h"
38 #include "terminal-util.h"
39 #include "unit-def.h"
40 #include "user-util.h"
41 #include "utf8.h"
42
43 int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
44 assert(message);
45 assert(u);
46
47 u->machine = NULL;
48
49 return sd_bus_message_read(
50 message,
51 "(ssssssouso)",
52 &u->id,
53 &u->description,
54 &u->load_state,
55 &u->active_state,
56 &u->sub_state,
57 &u->following,
58 &u->unit_path,
59 &u->job_id,
60 &u->job_type,
61 &u->job_path);
62 }
63
64 #define DEFINE_BUS_APPEND_PARSE_PTR(bus_type, cast_type, type, parse_func) \
65 static int bus_append_##parse_func( \
66 sd_bus_message *m, \
67 const char *field, \
68 const char *eq) { \
69 type val; \
70 int r; \
71 \
72 r = parse_func(eq, &val); \
73 if (r < 0) \
74 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); \
75 \
76 r = sd_bus_message_append(m, "(sv)", field, \
77 bus_type, (cast_type) val); \
78 if (r < 0) \
79 return bus_log_create_error(r); \
80 \
81 return 1; \
82 }
83
84 #define DEFINE_BUS_APPEND_PARSE(bus_type, parse_func) \
85 static int bus_append_##parse_func( \
86 sd_bus_message *m, \
87 const char *field, \
88 const char *eq) { \
89 int r; \
90 \
91 r = parse_func(eq); \
92 if (r < 0) \
93 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse %s: %s", field, eq); \
94 \
95 r = sd_bus_message_append(m, "(sv)", field, \
96 bus_type, (int32_t) r); \
97 if (r < 0) \
98 return bus_log_create_error(r); \
99 \
100 return 1; \
101 }
102
103 DEFINE_BUS_APPEND_PARSE("b", parse_boolean);
104 DEFINE_BUS_APPEND_PARSE("i", ioprio_class_from_string);
105 DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string);
106 DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string);
107 DEFINE_BUS_APPEND_PARSE("i", log_level_from_string);
108 DEFINE_BUS_APPEND_PARSE("i", parse_errno);
109 DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string);
110 DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string);
111 DEFINE_BUS_APPEND_PARSE("i", signal_from_string);
112 DEFINE_BUS_APPEND_PARSE("i", parse_ip_protocol);
113 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, ioprio_parse_priority);
114 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, parse_nice);
115 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, safe_atoi);
116 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, nsec_t, parse_nsec);
117 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_blkio_weight_parse);
118 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_shares_parse);
119 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_weight_parse);
120 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, unsigned long, mount_propagation_flags_from_string);
121 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, safe_atou64);
122 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t, parse_mode);
123 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou);
124 DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64);
125 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, coredump_filter_mask_from_string);
126
127 static int bus_append_string(sd_bus_message *m, const char *field, const char *eq) {
128 int r;
129
130 r = sd_bus_message_append(m, "(sv)", field, "s", eq);
131 if (r < 0)
132 return bus_log_create_error(r);
133
134 return 1;
135 }
136
137 static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, ExtractFlags flags) {
138 const char *p;
139 int r;
140
141 r = sd_bus_message_open_container(m, 'r', "sv");
142 if (r < 0)
143 return bus_log_create_error(r);
144
145 r = sd_bus_message_append_basic(m, 's', field);
146 if (r < 0)
147 return bus_log_create_error(r);
148
149 r = sd_bus_message_open_container(m, 'v', "as");
150 if (r < 0)
151 return bus_log_create_error(r);
152
153 r = sd_bus_message_open_container(m, 'a', "s");
154 if (r < 0)
155 return bus_log_create_error(r);
156
157 for (p = eq;;) {
158 _cleanup_free_ char *word = NULL;
159
160 r = extract_first_word(&p, &word, NULL, flags);
161 if (r == 0)
162 break;
163 if (r == -ENOMEM)
164 return log_oom();
165 if (r < 0)
166 return log_error_errno(r, "Invalid syntax: %s", eq);
167
168 r = sd_bus_message_append_basic(m, 's', word);
169 if (r < 0)
170 return bus_log_create_error(r);
171 }
172
173 r = sd_bus_message_close_container(m);
174 if (r < 0)
175 return bus_log_create_error(r);
176
177 r = sd_bus_message_close_container(m);
178 if (r < 0)
179 return bus_log_create_error(r);
180
181 r = sd_bus_message_close_container(m);
182 if (r < 0)
183 return bus_log_create_error(r);
184
185 return 1;
186 }
187
188 static int bus_append_byte_array(sd_bus_message *m, const char *field, const void *buf, size_t n) {
189 int r;
190
191 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
192 if (r < 0)
193 return bus_log_create_error(r);
194
195 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
196 if (r < 0)
197 return bus_log_create_error(r);
198
199 r = sd_bus_message_open_container(m, 'v', "ay");
200 if (r < 0)
201 return bus_log_create_error(r);
202
203 r = sd_bus_message_append_array(m, 'y', buf, n);
204 if (r < 0)
205 return bus_log_create_error(r);
206
207 r = sd_bus_message_close_container(m);
208 if (r < 0)
209 return bus_log_create_error(r);
210
211 r = sd_bus_message_close_container(m);
212 if (r < 0)
213 return bus_log_create_error(r);
214
215 return 1;
216 }
217
218 static int bus_append_parse_sec_rename(sd_bus_message *m, const char *field, const char *eq) {
219 char *n;
220 usec_t t;
221 size_t l;
222 int r;
223
224 r = parse_sec(eq, &t);
225 if (r < 0)
226 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
227
228 l = strlen(field);
229 n = newa(char, l + 2);
230 /* Change suffix Sec → USec */
231 strcpy(mempcpy(n, field, l - 3), "USec");
232
233 r = sd_bus_message_append(m, "(sv)", n, "t", t);
234 if (r < 0)
235 return bus_log_create_error(r);
236
237 return 1;
238 }
239
240 static int bus_append_parse_size(sd_bus_message *m, const char *field, const char *eq, uint64_t base) {
241 uint64_t v;
242 int r;
243
244 r = parse_size(eq, base, &v);
245 if (r < 0)
246 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
247
248 r = sd_bus_message_append(m, "(sv)", field, "t", v);
249 if (r < 0)
250 return bus_log_create_error(r);
251
252 return 1;
253 }
254
255 static int bus_append_exec_command(sd_bus_message *m, const char *field, const char *eq) {
256 bool explicit_path = false, done = false;
257 _cleanup_strv_free_ char **l = NULL, **ex_opts = NULL;
258 _cleanup_free_ char *path = NULL, *upgraded_name = NULL;
259 ExecCommandFlags flags = 0;
260 bool is_ex_prop = endswith(field, "Ex");
261 int r;
262
263 do {
264 switch (*eq) {
265
266 case '-':
267 if (FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE))
268 done = true;
269 else {
270 flags |= EXEC_COMMAND_IGNORE_FAILURE;
271 eq++;
272 }
273 break;
274
275 case '@':
276 if (explicit_path)
277 done = true;
278 else {
279 explicit_path = true;
280 eq++;
281 }
282 break;
283
284 case ':':
285 if (FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND))
286 done = true;
287 else {
288 flags |= EXEC_COMMAND_NO_ENV_EXPAND;
289 eq++;
290 }
291 break;
292
293 case '+':
294 if (flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC))
295 done = true;
296 else {
297 flags |= EXEC_COMMAND_FULLY_PRIVILEGED;
298 eq++;
299 }
300 break;
301
302 case '!':
303 if (flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_AMBIENT_MAGIC))
304 done = true;
305 else if (FLAGS_SET(flags, EXEC_COMMAND_NO_SETUID)) {
306 flags &= ~EXEC_COMMAND_NO_SETUID;
307 flags |= EXEC_COMMAND_AMBIENT_MAGIC;
308 eq++;
309 } else {
310 flags |= EXEC_COMMAND_NO_SETUID;
311 eq++;
312 }
313 break;
314
315 default:
316 done = true;
317 break;
318 }
319 } while (!done);
320
321 if (!is_ex_prop && (flags & (EXEC_COMMAND_NO_ENV_EXPAND|EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC))) {
322 /* Upgrade the ExecXYZ= property to ExecXYZEx= for convenience */
323 is_ex_prop = true;
324 upgraded_name = strjoin(field, "Ex");
325 if (!upgraded_name)
326 return log_oom();
327 }
328
329 if (is_ex_prop) {
330 r = exec_command_flags_to_strv(flags, &ex_opts);
331 if (r < 0)
332 return log_error_errno(r, "Failed to convert ExecCommandFlags to strv: %m");
333 }
334
335 if (explicit_path) {
336 r = extract_first_word(&eq, &path, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
337 if (r < 0)
338 return log_error_errno(r, "Failed to parse path: %m");
339 }
340
341 r = strv_split_extract(&l, eq, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
342 if (r < 0)
343 return log_error_errno(r, "Failed to parse command line: %m");
344
345 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
346 if (r < 0)
347 return bus_log_create_error(r);
348
349 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, upgraded_name ?: field);
350 if (r < 0)
351 return bus_log_create_error(r);
352
353 r = sd_bus_message_open_container(m, 'v', is_ex_prop ? "a(sasas)" : "a(sasb)");
354 if (r < 0)
355 return bus_log_create_error(r);
356
357 r = sd_bus_message_open_container(m, 'a', is_ex_prop ? "(sasas)" : "(sasb)");
358 if (r < 0)
359 return bus_log_create_error(r);
360
361 if (!strv_isempty(l)) {
362
363 r = sd_bus_message_open_container(m, 'r', is_ex_prop ? "sasas" : "sasb");
364 if (r < 0)
365 return bus_log_create_error(r);
366
367 r = sd_bus_message_append(m, "s", path ?: l[0]);
368 if (r < 0)
369 return bus_log_create_error(r);
370
371 r = sd_bus_message_append_strv(m, l);
372 if (r < 0)
373 return bus_log_create_error(r);
374
375 r = is_ex_prop ? sd_bus_message_append_strv(m, ex_opts) : sd_bus_message_append(m, "b", FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE));
376 if (r < 0)
377 return bus_log_create_error(r);
378
379 r = sd_bus_message_close_container(m);
380 if (r < 0)
381 return bus_log_create_error(r);
382 }
383
384 r = sd_bus_message_close_container(m);
385 if (r < 0)
386 return bus_log_create_error(r);
387
388 r = sd_bus_message_close_container(m);
389 if (r < 0)
390 return bus_log_create_error(r);
391
392 r = sd_bus_message_close_container(m);
393 if (r < 0)
394 return bus_log_create_error(r);
395
396 return 1;
397 }
398
399 static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) {
400 int r;
401
402 assert(m);
403 assert(prefix);
404
405 r = sd_bus_message_open_container(m, 'r', "iayu");
406 if (r < 0)
407 return r;
408
409 r = sd_bus_message_append(m, "i", family);
410 if (r < 0)
411 return r;
412
413 r = sd_bus_message_append_array(m, 'y', prefix, FAMILY_ADDRESS_SIZE(family));
414 if (r < 0)
415 return r;
416
417 r = sd_bus_message_append(m, "u", prefixlen);
418 if (r < 0)
419 return r;
420
421 return sd_bus_message_close_container(m);
422 }
423
424 static int bus_append_cgroup_property(sd_bus_message *m, const char *field, const char *eq) {
425 int r;
426
427 if (STR_IN_SET(field, "DevicePolicy", "Slice"))
428 return bus_append_string(m, field, eq);
429
430 if (STR_IN_SET(field, "CPUAccounting",
431 "MemoryAccounting",
432 "IOAccounting",
433 "BlockIOAccounting",
434 "TasksAccounting",
435 "IPAccounting"))
436 return bus_append_parse_boolean(m, field, eq);
437
438 if (STR_IN_SET(field, "CPUWeight",
439 "StartupCPUWeight",
440 "IOWeight",
441 "StartupIOWeight"))
442 return bus_append_cg_weight_parse(m, field, eq);
443
444 if (STR_IN_SET(field, "CPUShares",
445 "StartupCPUShares"))
446 return bus_append_cg_cpu_shares_parse(m, field, eq);
447
448 if (STR_IN_SET(field, "AllowedCPUs",
449 "AllowedMemoryNodes")) {
450 _cleanup_(cpu_set_reset) CPUSet cpuset = {};
451 _cleanup_free_ uint8_t *array = NULL;
452 size_t allocated;
453
454 r = parse_cpu_set(eq, &cpuset);
455 if (r < 0)
456 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
457
458 r = cpu_set_to_dbus(&cpuset, &array, &allocated);
459 if (r < 0)
460 return log_error_errno(r, "Failed to serialize CPUSet: %m");
461
462 return bus_append_byte_array(m, field, array, allocated);
463 }
464
465 if (STR_IN_SET(field, "BlockIOWeight",
466 "StartupBlockIOWeight"))
467 return bus_append_cg_blkio_weight_parse(m, field, eq);
468
469 if (streq(field, "DisableControllers"))
470 return bus_append_strv(m, "DisableControllers", eq, EXTRACT_UNQUOTE);
471
472 if (streq(field, "Delegate")) {
473 r = parse_boolean(eq);
474 if (r < 0)
475 return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_UNQUOTE);
476
477 r = sd_bus_message_append(m, "(sv)", "Delegate", "b", r);
478 if (r < 0)
479 return bus_log_create_error(r);
480
481 return 1;
482 }
483
484 if (STR_IN_SET(field, "MemoryMin",
485 "DefaultMemoryLow",
486 "DefaultMemoryMin",
487 "MemoryLow",
488 "MemoryHigh",
489 "MemoryMax",
490 "MemorySwapMax",
491 "MemoryLimit",
492 "TasksMax")) {
493
494 if (streq(eq, "infinity")) {
495 r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX);
496 if (r < 0)
497 return bus_log_create_error(r);
498 return 1;
499 } else if (isempty(eq)) {
500 uint64_t empty_value = STR_IN_SET(field,
501 "DefaultMemoryLow",
502 "DefaultMemoryMin",
503 "MemoryLow",
504 "MemoryMin") ?
505 CGROUP_LIMIT_MIN :
506 CGROUP_LIMIT_MAX;
507
508 r = sd_bus_message_append(m, "(sv)", field, "t", empty_value);
509 if (r < 0)
510 return bus_log_create_error(r);
511 return 1;
512 }
513
514 r = parse_permille(eq);
515 if (r >= 0) {
516 char *n;
517
518 /* When this is a percentage we'll convert this into a relative value in the range 0…UINT32_MAX
519 * and pass it in the MemoryLowScale property (and related ones). This way the physical memory
520 * size can be determined server-side. */
521
522 n = strjoina(field, "Scale");
523 r = sd_bus_message_append(m, "(sv)", n, "u", (uint32_t) (((uint64_t) r * UINT32_MAX) / 1000U));
524 if (r < 0)
525 return bus_log_create_error(r);
526
527 return 1;
528 }
529
530 if (streq(field, "TasksMax"))
531 return bus_append_safe_atou64(m, field, eq);
532
533 return bus_append_parse_size(m, field, eq, 1024);
534 }
535
536 if (streq(field, "CPUQuota")) {
537 if (isempty(eq))
538 r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
539 else {
540 r = parse_permille_unbounded(eq);
541 if (r == 0)
542 return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
543 "CPU quota too small.");
544 if (r < 0)
545 return log_error_errno(r, "CPU quota '%s' invalid.", eq);
546
547 r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (((uint64_t) r * USEC_PER_SEC) / 1000U));
548 }
549
550 if (r < 0)
551 return bus_log_create_error(r);
552
553 return 1;
554 }
555
556 if (streq(field, "CPUQuotaPeriodSec")) {
557 usec_t u = USEC_INFINITY;
558
559 r = parse_sec_def_infinity(eq, &u);
560 if (r < 0)
561 return log_error_errno(r, "CPU quota period '%s' invalid.", eq);
562
563 r = sd_bus_message_append(m, "(sv)", "CPUQuotaPeriodUSec", "t", u);
564 if (r < 0)
565 return bus_log_create_error(r);
566
567 return 1;
568 }
569
570 if (streq(field, "DeviceAllow")) {
571 if (isempty(eq))
572 r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
573 else {
574 const char *path = eq, *rwm = NULL, *e;
575
576 e = strchr(eq, ' ');
577 if (e) {
578 path = strndupa(eq, e - eq);
579 rwm = e+1;
580 }
581
582 r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, path, strempty(rwm));
583 }
584
585 if (r < 0)
586 return bus_log_create_error(r);
587
588 return 1;
589 }
590
591 if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
592 if (isempty(eq))
593 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
594 else {
595 const char *path, *bandwidth, *e;
596 uint64_t bytes;
597
598 e = strchr(eq, ' ');
599 if (!e)
600 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
601 "Failed to parse %s value %s.",
602 field, eq);
603
604 path = strndupa(eq, e - eq);
605 bandwidth = e+1;
606
607 if (streq(bandwidth, "infinity"))
608 bytes = CGROUP_LIMIT_MAX;
609 else {
610 r = parse_size(bandwidth, 1000, &bytes);
611 if (r < 0)
612 return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth);
613 }
614
615 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, bytes);
616 }
617
618 if (r < 0)
619 return bus_log_create_error(r);
620
621 return 1;
622 }
623
624 if (STR_IN_SET(field, "IODeviceWeight",
625 "BlockIODeviceWeight")) {
626 if (isempty(eq))
627 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
628 else {
629 const char *path, *weight, *e;
630 uint64_t u;
631
632 e = strchr(eq, ' ');
633 if (!e)
634 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
635 "Failed to parse %s value %s.",
636 field, eq);
637
638 path = strndupa(eq, e - eq);
639 weight = e+1;
640
641 r = safe_atou64(weight, &u);
642 if (r < 0)
643 return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight);
644
645 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, u);
646 }
647
648 if (r < 0)
649 return bus_log_create_error(r);
650
651 return 1;
652 }
653
654 if (streq(field, "IODeviceLatencyTargetSec")) {
655 const char *field_usec = "IODeviceLatencyTargetUSec";
656
657 if (isempty(eq))
658 r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", USEC_INFINITY);
659 else {
660 const char *path, *target, *e;
661 usec_t usec;
662
663 e = strchr(eq, ' ');
664 if (!e)
665 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
666 "Failed to parse %s value %s.",
667 field, eq);
668
669 path = strndupa(eq, e - eq);
670 target = e+1;
671
672 r = parse_sec(target, &usec);
673 if (r < 0)
674 return log_error_errno(r, "Failed to parse %s value %s: %m", field, target);
675
676 r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", 1, path, usec);
677 }
678
679 if (r < 0)
680 return bus_log_create_error(r);
681
682 return 1;
683 }
684
685 if (STR_IN_SET(field, "IPAddressAllow",
686 "IPAddressDeny")) {
687 unsigned char prefixlen;
688 union in_addr_union prefix = {};
689 int family;
690
691 if (isempty(eq)) {
692 r = sd_bus_message_append(m, "(sv)", field, "a(iayu)", 0);
693 if (r < 0)
694 return bus_log_create_error(r);
695
696 return 1;
697 }
698
699 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
700 if (r < 0)
701 return bus_log_create_error(r);
702
703 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
704 if (r < 0)
705 return bus_log_create_error(r);
706
707 r = sd_bus_message_open_container(m, 'v', "a(iayu)");
708 if (r < 0)
709 return bus_log_create_error(r);
710
711 r = sd_bus_message_open_container(m, 'a', "(iayu)");
712 if (r < 0)
713 return bus_log_create_error(r);
714
715 if (streq(eq, "any")) {
716 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
717
718 r = bus_append_ip_address_access(m, AF_INET, &prefix, 0);
719 if (r < 0)
720 return bus_log_create_error(r);
721
722 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0);
723 if (r < 0)
724 return bus_log_create_error(r);
725
726 } else if (is_localhost(eq)) {
727 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
728
729 prefix.in.s_addr = htobe32(0x7f000000);
730 r = bus_append_ip_address_access(m, AF_INET, &prefix, 8);
731 if (r < 0)
732 return bus_log_create_error(r);
733
734 prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
735 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128);
736 if (r < 0)
737 return r;
738
739 } else if (streq(eq, "link-local")) {
740 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
741
742 prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
743 r = bus_append_ip_address_access(m, AF_INET, &prefix, 16);
744 if (r < 0)
745 return bus_log_create_error(r);
746
747 prefix.in6 = (struct in6_addr) {
748 .s6_addr32[0] = htobe32(0xfe800000)
749 };
750 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64);
751 if (r < 0)
752 return bus_log_create_error(r);
753
754 } else if (streq(eq, "multicast")) {
755 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
756
757 prefix.in.s_addr = htobe32((UINT32_C(224) << 24));
758 r = bus_append_ip_address_access(m, AF_INET, &prefix, 4);
759 if (r < 0)
760 return bus_log_create_error(r);
761
762 prefix.in6 = (struct in6_addr) {
763 .s6_addr32[0] = htobe32(0xff000000)
764 };
765 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8);
766 if (r < 0)
767 return bus_log_create_error(r);
768
769 } else {
770 for (;;) {
771 _cleanup_free_ char *word = NULL;
772
773 r = extract_first_word(&eq, &word, NULL, 0);
774 if (r == 0)
775 break;
776 if (r == -ENOMEM)
777 return log_oom();
778 if (r < 0)
779 return log_error_errno(r, "Failed to parse %s: %s", field, eq);
780
781 r = in_addr_prefix_from_string_auto(word, &family, &prefix, &prefixlen);
782 if (r < 0)
783 return log_error_errno(r, "Failed to parse IP address prefix: %s", word);
784
785 r = bus_append_ip_address_access(m, family, &prefix, prefixlen);
786 if (r < 0)
787 return bus_log_create_error(r);
788 }
789 }
790
791 r = sd_bus_message_close_container(m);
792 if (r < 0)
793 return bus_log_create_error(r);
794
795 r = sd_bus_message_close_container(m);
796 if (r < 0)
797 return bus_log_create_error(r);
798
799 r = sd_bus_message_close_container(m);
800 if (r < 0)
801 return bus_log_create_error(r);
802
803 return 1;
804 }
805
806 if (STR_IN_SET(field, "IPIngressFilterPath",
807 "IPEgressFilterPath")) {
808 if (isempty(eq))
809 r = sd_bus_message_append(m, "(sv)", field, "as", 0);
810 else
811 r = sd_bus_message_append(m, "(sv)", field, "as", 1, eq);
812
813 if (r < 0)
814 return bus_log_create_error(r);
815
816 return 1;
817 }
818
819 return 0;
820 }
821
822 static int bus_append_automount_property(sd_bus_message *m, const char *field, const char *eq) {
823 if (streq(field, "Where"))
824 return bus_append_string(m, field, eq);
825
826 if (streq(field, "DirectoryMode"))
827 return bus_append_parse_mode(m, field, eq);
828
829 if (streq(field, "TimeoutIdleSec"))
830 return bus_append_parse_sec_rename(m, field, eq);
831
832 return 0;
833 }
834
835 static int bus_append_execute_property(sd_bus_message *m, const char *field, const char *eq) {
836 const char *suffix;
837 int r;
838
839 if (STR_IN_SET(field, "User",
840 "Group",
841 "UtmpIdentifier",
842 "UtmpMode",
843 "PAMName",
844 "TTYPath",
845 "WorkingDirectory",
846 "RootDirectory",
847 "SyslogIdentifier",
848 "ProtectSystem",
849 "ProtectHome",
850 "SELinuxContext",
851 "RootImage",
852 "RootVerity",
853 "RuntimeDirectoryPreserve",
854 "Personality",
855 "KeyringMode",
856 "NetworkNamespacePath",
857 "LogNamespace"))
858 return bus_append_string(m, field, eq);
859
860 if (STR_IN_SET(field, "IgnoreSIGPIPE",
861 "TTYVHangup",
862 "TTYReset",
863 "TTYVTDisallocate",
864 "PrivateTmp",
865 "PrivateDevices",
866 "PrivateNetwork",
867 "PrivateUsers",
868 "PrivateMounts",
869 "NoNewPrivileges",
870 "SyslogLevelPrefix",
871 "MemoryDenyWriteExecute",
872 "RestrictRealtime",
873 "DynamicUser",
874 "RemoveIPC",
875 "ProtectKernelTunables",
876 "ProtectKernelModules",
877 "ProtectKernelLogs",
878 "ProtectClock",
879 "ProtectControlGroups",
880 "MountAPIVFS",
881 "CPUSchedulingResetOnFork",
882 "LockPersonality",
883 "ProtectHostname",
884 "RestrictSUIDSGID"))
885 return bus_append_parse_boolean(m, field, eq);
886
887 if (STR_IN_SET(field, "ReadWriteDirectories",
888 "ReadOnlyDirectories",
889 "InaccessibleDirectories",
890 "ReadWritePaths",
891 "ReadOnlyPaths",
892 "InaccessiblePaths",
893 "RuntimeDirectory",
894 "StateDirectory",
895 "CacheDirectory",
896 "LogsDirectory",
897 "ConfigurationDirectory",
898 "SupplementaryGroups",
899 "SystemCallArchitectures"))
900 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
901
902 if (STR_IN_SET(field, "SyslogLevel",
903 "LogLevelMax"))
904 return bus_append_log_level_from_string(m, field, eq);
905
906 if (streq(field, "SyslogFacility"))
907 return bus_append_log_facility_unshifted_from_string(m, field, eq);
908
909 if (streq(field, "SecureBits"))
910 return bus_append_secure_bits_from_string(m, field, eq);
911
912 if (streq(field, "CPUSchedulingPolicy"))
913 return bus_append_sched_policy_from_string(m, field, eq);
914
915 if (STR_IN_SET(field, "CPUSchedulingPriority",
916 "OOMScoreAdjust"))
917 return bus_append_safe_atoi(m, field, eq);
918
919 if (streq(field, "CoredumpFilter"))
920 return bus_append_coredump_filter_mask_from_string(m, field, eq);
921
922 if (streq(field, "Nice"))
923 return bus_append_parse_nice(m, field, eq);
924
925 if (streq(field, "SystemCallErrorNumber"))
926 return bus_append_parse_errno(m, field, eq);
927
928 if (streq(field, "IOSchedulingClass"))
929 return bus_append_ioprio_class_from_string(m, field, eq);
930
931 if (streq(field, "IOSchedulingPriority"))
932 return bus_append_ioprio_parse_priority(m, field, eq);
933
934 if (STR_IN_SET(field, "RuntimeDirectoryMode",
935 "StateDirectoryMode",
936 "CacheDirectoryMode",
937 "LogsDirectoryMode",
938 "ConfigurationDirectoryMode",
939 "UMask"))
940 return bus_append_parse_mode(m, field, eq);
941
942 if (streq(field, "TimerSlackNSec"))
943 return bus_append_parse_nsec(m, field, eq);
944
945 if (streq(field, "LogRateLimitIntervalSec"))
946 return bus_append_parse_sec_rename(m, field, eq);
947
948 if (streq(field, "LogRateLimitBurst"))
949 return bus_append_safe_atou(m, field, eq);
950
951 if (streq(field, "MountFlags"))
952 return bus_append_mount_propagation_flags_from_string(m, field, eq);
953
954 if (STR_IN_SET(field, "Environment",
955 "UnsetEnvironment",
956 "PassEnvironment"))
957 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
958
959 if (streq(field, "EnvironmentFile")) {
960 if (isempty(eq))
961 r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0);
962 else
963 r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 1,
964 eq[0] == '-' ? eq + 1 : eq,
965 eq[0] == '-');
966 if (r < 0)
967 return bus_log_create_error(r);
968
969 return 1;
970 }
971
972 if (streq(field, "LogExtraFields")) {
973 r = sd_bus_message_open_container(m, 'r', "sv");
974 if (r < 0)
975 return bus_log_create_error(r);
976
977 r = sd_bus_message_append_basic(m, 's', "LogExtraFields");
978 if (r < 0)
979 return bus_log_create_error(r);
980
981 r = sd_bus_message_open_container(m, 'v', "aay");
982 if (r < 0)
983 return bus_log_create_error(r);
984
985 r = sd_bus_message_open_container(m, 'a', "ay");
986 if (r < 0)
987 return bus_log_create_error(r);
988
989 r = sd_bus_message_append_array(m, 'y', eq, strlen(eq));
990 if (r < 0)
991 return bus_log_create_error(r);
992
993 r = sd_bus_message_close_container(m);
994 if (r < 0)
995 return bus_log_create_error(r);
996
997 r = sd_bus_message_close_container(m);
998 if (r < 0)
999 return bus_log_create_error(r);
1000
1001 r = sd_bus_message_close_container(m);
1002 if (r < 0)
1003 return bus_log_create_error(r);
1004
1005 return 1;
1006 }
1007
1008 if (STR_IN_SET(field, "StandardInput",
1009 "StandardOutput",
1010 "StandardError")) {
1011 const char *n, *appended;
1012
1013 if ((n = startswith(eq, "fd:"))) {
1014 appended = strjoina(field, "FileDescriptorName");
1015 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
1016 } else if ((n = startswith(eq, "file:"))) {
1017 appended = strjoina(field, "File");
1018 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
1019 } else if ((n = startswith(eq, "append:"))) {
1020 appended = strjoina(field, "FileToAppend");
1021 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
1022 } else
1023 r = sd_bus_message_append(m, "(sv)", field, "s", eq);
1024 if (r < 0)
1025 return bus_log_create_error(r);
1026
1027 return 1;
1028 }
1029
1030 if (streq(field, "StandardInputText")) {
1031 _cleanup_free_ char *unescaped = NULL;
1032
1033 r = cunescape(eq, 0, &unescaped);
1034 if (r < 0)
1035 return log_error_errno(r, "Failed to unescape text '%s': %m", eq);
1036
1037 if (!strextend(&unescaped, "\n", NULL))
1038 return log_oom();
1039
1040 /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
1041 * interface anyway */
1042
1043 return bus_append_byte_array(m, field, unescaped, strlen(unescaped));
1044 }
1045
1046 if (streq(field, "StandardInputData")) {
1047 _cleanup_free_ void *decoded = NULL;
1048 size_t sz;
1049
1050 r = unbase64mem(eq, (size_t) -1, &decoded, &sz);
1051 if (r < 0)
1052 return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq);
1053
1054 return bus_append_byte_array(m, field, decoded, sz);
1055 }
1056
1057 if ((suffix = startswith(field, "Limit"))) {
1058 int rl;
1059
1060 rl = rlimit_from_string(suffix);
1061 if (rl >= 0) {
1062 const char *sn;
1063 struct rlimit l;
1064
1065 r = rlimit_parse(rl, eq, &l);
1066 if (r < 0)
1067 return log_error_errno(r, "Failed to parse resource limit: %s", eq);
1068
1069 r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max);
1070 if (r < 0)
1071 return bus_log_create_error(r);
1072
1073 sn = strjoina(field, "Soft");
1074 r = sd_bus_message_append(m, "(sv)", sn, "t", l.rlim_cur);
1075 if (r < 0)
1076 return bus_log_create_error(r);
1077
1078 return 1;
1079 }
1080 }
1081
1082 if (STR_IN_SET(field, "AppArmorProfile",
1083 "SmackProcessLabel")) {
1084 int ignore = 0;
1085 const char *s = eq;
1086
1087 if (eq[0] == '-') {
1088 ignore = 1;
1089 s = eq + 1;
1090 }
1091
1092 r = sd_bus_message_append(m, "(sv)", field, "(bs)", ignore, s);
1093 if (r < 0)
1094 return bus_log_create_error(r);
1095
1096 return 1;
1097 }
1098
1099 if (STR_IN_SET(field, "CapabilityBoundingSet",
1100 "AmbientCapabilities")) {
1101 uint64_t sum = 0;
1102 bool invert = false;
1103 const char *p = eq;
1104
1105 if (*p == '~') {
1106 invert = true;
1107 p++;
1108 }
1109
1110 r = capability_set_from_string(p, &sum);
1111 if (r < 0)
1112 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
1113
1114 sum = invert ? ~sum : sum;
1115
1116 r = sd_bus_message_append(m, "(sv)", field, "t", sum);
1117 if (r < 0)
1118 return bus_log_create_error(r);
1119
1120 return 1;
1121 }
1122
1123 if (streq(field, "CPUAffinity")) {
1124 _cleanup_(cpu_set_reset) CPUSet cpuset = {};
1125 _cleanup_free_ uint8_t *array = NULL;
1126 size_t allocated;
1127
1128 if (eq && streq(eq, "numa")) {
1129 r = sd_bus_message_append(m, "(sv)", "CPUAffinityFromNUMA", "b", true);
1130 if (r < 0)
1131 return bus_log_create_error(r);
1132 return r;
1133 }
1134
1135 r = parse_cpu_set(eq, &cpuset);
1136 if (r < 0)
1137 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
1138
1139 r = cpu_set_to_dbus(&cpuset, &array, &allocated);
1140 if (r < 0)
1141 return log_error_errno(r, "Failed to serialize CPUAffinity: %m");
1142
1143 return bus_append_byte_array(m, field, array, allocated);
1144 }
1145
1146 if (streq(field, "NUMAPolicy")) {
1147 r = mpol_from_string(eq);
1148 if (r < 0)
1149 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
1150
1151 r = sd_bus_message_append(m, "(sv)", field, "i", (int32_t) r);
1152 if (r < 0)
1153 return bus_log_create_error(r);
1154
1155 return 1;
1156 }
1157
1158 if (streq(field, "NUMAMask")) {
1159 _cleanup_(cpu_set_reset) CPUSet nodes = {};
1160 _cleanup_free_ uint8_t *array = NULL;
1161 size_t allocated;
1162
1163 r = parse_cpu_set(eq, &nodes);
1164 if (r < 0)
1165 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
1166
1167 r = cpu_set_to_dbus(&nodes, &array, &allocated);
1168 if (r < 0)
1169 return log_error_errno(r, "Failed to serialize NUMAMask: %m");
1170
1171 return bus_append_byte_array(m, field, array, allocated);
1172 }
1173
1174 if (STR_IN_SET(field, "RestrictAddressFamilies",
1175 "SystemCallFilter")) {
1176 int allow_list = 1;
1177 const char *p = eq;
1178
1179 if (*p == '~') {
1180 allow_list = 0;
1181 p++;
1182 }
1183
1184 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1185 if (r < 0)
1186 return bus_log_create_error(r);
1187
1188 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1189 if (r < 0)
1190 return bus_log_create_error(r);
1191
1192 r = sd_bus_message_open_container(m, 'v', "(bas)");
1193 if (r < 0)
1194 return bus_log_create_error(r);
1195
1196 r = sd_bus_message_open_container(m, 'r', "bas");
1197 if (r < 0)
1198 return bus_log_create_error(r);
1199
1200 r = sd_bus_message_append_basic(m, 'b', &allow_list);
1201 if (r < 0)
1202 return bus_log_create_error(r);
1203
1204 r = sd_bus_message_open_container(m, 'a', "s");
1205 if (r < 0)
1206 return bus_log_create_error(r);
1207
1208 for (;;) {
1209 _cleanup_free_ char *word = NULL;
1210
1211 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
1212 if (r == 0)
1213 break;
1214 if (r == -ENOMEM)
1215 return log_oom();
1216 if (r < 0)
1217 return log_error_errno(r, "Invalid syntax: %s", eq);
1218
1219 r = sd_bus_message_append_basic(m, 's', word);
1220 if (r < 0)
1221 return bus_log_create_error(r);
1222 }
1223
1224 r = sd_bus_message_close_container(m);
1225 if (r < 0)
1226 return bus_log_create_error(r);
1227
1228 r = sd_bus_message_close_container(m);
1229 if (r < 0)
1230 return bus_log_create_error(r);
1231
1232 r = sd_bus_message_close_container(m);
1233 if (r < 0)
1234 return bus_log_create_error(r);
1235
1236 r = sd_bus_message_close_container(m);
1237 if (r < 0)
1238 return bus_log_create_error(r);
1239
1240 return 1;
1241 }
1242
1243 if (streq(field, "RestrictNamespaces")) {
1244 bool invert = false;
1245 unsigned long flags;
1246
1247 r = parse_boolean(eq);
1248 if (r > 0)
1249 flags = 0;
1250 else if (r == 0)
1251 flags = NAMESPACE_FLAGS_ALL;
1252 else {
1253 if (eq[0] == '~') {
1254 invert = true;
1255 eq++;
1256 }
1257
1258 r = namespace_flags_from_string(eq, &flags);
1259 if (r < 0)
1260 return log_error_errno(r, "Failed to parse %s value %s.", field, eq);
1261 }
1262
1263 if (invert)
1264 flags = (~flags) & NAMESPACE_FLAGS_ALL;
1265
1266 r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) flags);
1267 if (r < 0)
1268 return bus_log_create_error(r);
1269
1270 return 1;
1271 }
1272
1273 if (STR_IN_SET(field, "BindPaths",
1274 "BindReadOnlyPaths")) {
1275 const char *p = eq;
1276
1277 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1278 if (r < 0)
1279 return bus_log_create_error(r);
1280
1281 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1282 if (r < 0)
1283 return bus_log_create_error(r);
1284
1285 r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
1286 if (r < 0)
1287 return bus_log_create_error(r);
1288
1289 r = sd_bus_message_open_container(m, 'a', "(ssbt)");
1290 if (r < 0)
1291 return bus_log_create_error(r);
1292
1293 for (;;) {
1294 _cleanup_free_ char *source = NULL, *destination = NULL;
1295 char *s = NULL, *d = NULL;
1296 bool ignore_enoent = false;
1297 uint64_t flags = MS_REC;
1298
1299 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
1300 if (r < 0)
1301 return log_error_errno(r, "Failed to parse argument: %m");
1302 if (r == 0)
1303 break;
1304
1305 s = source;
1306 if (s[0] == '-') {
1307 ignore_enoent = true;
1308 s++;
1309 }
1310
1311 if (p && p[-1] == ':') {
1312 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
1313 if (r < 0)
1314 return log_error_errno(r, "Failed to parse argument: %m");
1315 if (r == 0)
1316 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1317 "Missing argument after ':': %s",
1318 eq);
1319
1320 d = destination;
1321
1322 if (p && p[-1] == ':') {
1323 _cleanup_free_ char *options = NULL;
1324
1325 r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE);
1326 if (r < 0)
1327 return log_error_errno(r, "Failed to parse argument: %m");
1328
1329 if (isempty(options) || streq(options, "rbind"))
1330 flags = MS_REC;
1331 else if (streq(options, "norbind"))
1332 flags = 0;
1333 else
1334 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1335 "Unknown options: %s",
1336 eq);
1337 }
1338 } else
1339 d = s;
1340
1341 r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
1342 if (r < 0)
1343 return bus_log_create_error(r);
1344 }
1345
1346 r = sd_bus_message_close_container(m);
1347 if (r < 0)
1348 return bus_log_create_error(r);
1349
1350 r = sd_bus_message_close_container(m);
1351 if (r < 0)
1352 return bus_log_create_error(r);
1353
1354 r = sd_bus_message_close_container(m);
1355 if (r < 0)
1356 return bus_log_create_error(r);
1357
1358 return 1;
1359 }
1360
1361 if (streq(field, "TemporaryFileSystem")) {
1362 const char *p = eq;
1363
1364 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1365 if (r < 0)
1366 return bus_log_create_error(r);
1367
1368 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1369 if (r < 0)
1370 return bus_log_create_error(r);
1371
1372 r = sd_bus_message_open_container(m, 'v', "a(ss)");
1373 if (r < 0)
1374 return bus_log_create_error(r);
1375
1376 r = sd_bus_message_open_container(m, 'a', "(ss)");
1377 if (r < 0)
1378 return bus_log_create_error(r);
1379
1380 for (;;) {
1381 _cleanup_free_ char *word = NULL, *path = NULL;
1382 const char *w;
1383
1384 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
1385 if (r < 0)
1386 return log_error_errno(r, "Failed to parse argument: %m");
1387 if (r == 0)
1388 break;
1389
1390 w = word;
1391 r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1392 if (r < 0)
1393 return log_error_errno(r, "Failed to parse argument: %m");
1394 if (r == 0)
1395 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1396 "Failed to parse argument: %s",
1397 p);
1398
1399 r = sd_bus_message_append(m, "(ss)", path, w);
1400 if (r < 0)
1401 return bus_log_create_error(r);
1402 }
1403
1404 r = sd_bus_message_close_container(m);
1405 if (r < 0)
1406 return bus_log_create_error(r);
1407
1408 r = sd_bus_message_close_container(m);
1409 if (r < 0)
1410 return bus_log_create_error(r);
1411
1412 r = sd_bus_message_close_container(m);
1413 if (r < 0)
1414 return bus_log_create_error(r);
1415
1416 return 1;
1417 }
1418
1419 if (streq(field, "RootHash")) {
1420 _cleanup_free_ void *roothash_decoded = NULL;
1421 size_t roothash_decoded_size = 0;
1422
1423 /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
1424 if (path_is_absolute(eq))
1425 return bus_append_string(m, "RootHashPath", eq);
1426
1427 /* We have a roothash to decode, eg: RootHash=012345789abcdef */
1428 r = unhexmem(eq, strlen(eq), &roothash_decoded, &roothash_decoded_size);
1429 if (r < 0)
1430 return log_error_errno(r, "Failed to decode RootHash= '%s': %m", eq);
1431 if (roothash_decoded_size < sizeof(sd_id128_t))
1432 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "RootHash= '%s' is too short: %m", eq);
1433
1434 return bus_append_byte_array(m, field, roothash_decoded, roothash_decoded_size);
1435 }
1436
1437 if (streq(field, "RootHashSignature")) {
1438 _cleanup_free_ void *roothash_sig_decoded = NULL;
1439 char *value;
1440 size_t roothash_sig_decoded_size = 0;
1441
1442 /* We have the path to a roothash signature to load and decode, eg: RootHash=/foo/bar.roothash.p7s */
1443 if (path_is_absolute(eq))
1444 return bus_append_string(m, "RootHashSignaturePath", eq);
1445
1446 if (!(value = startswith(eq, "base64:")))
1447 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decode RootHashSignature= '%s', not a path but doesn't start with 'base64:': %m", eq);
1448
1449 /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
1450 r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size);
1451 if (r < 0)
1452 return log_error_errno(r, "Failed to decode RootHashSignature= '%s': %m", eq);
1453
1454 return bus_append_byte_array(m, field, roothash_sig_decoded, roothash_sig_decoded_size);
1455 }
1456
1457 if (streq(field, "RootImageOptions")) {
1458 const char *p = eq;
1459
1460 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1461 if (r < 0)
1462 return bus_log_create_error(r);
1463
1464 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1465 if (r < 0)
1466 return bus_log_create_error(r);
1467
1468 r = sd_bus_message_open_container(m, 'v', "a(us)");
1469 if (r < 0)
1470 return bus_log_create_error(r);
1471
1472 r = sd_bus_message_open_container(m, 'a', "(us)");
1473 if (r < 0)
1474 return bus_log_create_error(r);
1475
1476 for (;;) {
1477 _cleanup_free_ char *first = NULL, *tuple = NULL;
1478 const char *mount_options = NULL, *second = NULL;
1479 unsigned partition_number = 0;
1480
1481 r = extract_first_word(&p, &tuple, WHITESPACE, EXTRACT_UNQUOTE);
1482 if (r == 0)
1483 break;
1484 if (r < 0)
1485 return log_error_errno(r, "Failed to parse argument: %m");
1486
1487 second = tuple;
1488 r = extract_first_word(&second, &first, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
1489 if (r == 0)
1490 continue;
1491 if (r < 0)
1492 return log_error_errno(r, "Failed to parse argument: %m");
1493
1494 /* Format is either '0:foo' or 'foo' (0 is implied) */
1495 if (!isempty(second) && second[-1] == ':') {
1496 mount_options = second;
1497 r = safe_atou(first, &partition_number);
1498 if (r < 0) {
1499 log_error_errno(r, "Failed to parse partition number from %s: %m", first);
1500 continue;
1501 }
1502 } else
1503 mount_options = first;
1504
1505 r = sd_bus_message_append(m, "(us)", partition_number, mount_options);
1506 if (r < 0)
1507 return bus_log_create_error(r);
1508 }
1509
1510 r = sd_bus_message_close_container(m);
1511 if (r < 0)
1512 return bus_log_create_error(r);
1513
1514 r = sd_bus_message_close_container(m);
1515 if (r < 0)
1516 return bus_log_create_error(r);
1517
1518 r = sd_bus_message_close_container(m);
1519 if (r < 0)
1520 return bus_log_create_error(r);
1521
1522 return 1;
1523 }
1524
1525 return 0;
1526 }
1527
1528 static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
1529 if (streq(field, "KillMode"))
1530 return bus_append_string(m, field, eq);
1531
1532 if (STR_IN_SET(field, "SendSIGHUP",
1533 "SendSIGKILL"))
1534 return bus_append_parse_boolean(m, field, eq);
1535
1536 if (STR_IN_SET(field, "KillSignal",
1537 "RestartKillSignal",
1538 "FinalKillSignal",
1539 "WatchdogSignal"))
1540 return bus_append_signal_from_string(m, field, eq);
1541
1542 return 0;
1543 }
1544
1545 static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) {
1546
1547 if (STR_IN_SET(field, "What",
1548 "Where",
1549 "Options",
1550 "Type"))
1551 return bus_append_string(m, field, eq);
1552
1553 if (streq(field, "TimeoutSec"))
1554 return bus_append_parse_sec_rename(m, field, eq);
1555
1556 if (streq(field, "DirectoryMode"))
1557 return bus_append_parse_mode(m, field, eq);
1558
1559 if (STR_IN_SET(field, "SloppyOptions",
1560 "LazyUnmount",
1561 "ForceUnmount",
1562 "ReadwriteOnly"))
1563 return bus_append_parse_boolean(m, field, eq);
1564
1565 return 0;
1566 }
1567
1568 static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) {
1569 int r;
1570
1571 if (streq(field, "MakeDirectory"))
1572 return bus_append_parse_boolean(m, field, eq);
1573
1574 if (streq(field, "DirectoryMode"))
1575 return bus_append_parse_mode(m, field, eq);
1576
1577 if (STR_IN_SET(field, "PathExists",
1578 "PathExistsGlob",
1579 "PathChanged",
1580 "PathModified",
1581 "DirectoryNotEmpty")) {
1582 if (isempty(eq))
1583 r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0);
1584 else
1585 r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 1, field, eq);
1586 if (r < 0)
1587 return bus_log_create_error(r);
1588
1589 return 1;
1590 }
1591
1592 return 0;
1593 }
1594
1595 static int bus_append_scope_property(sd_bus_message *m, const char *field, const char *eq) {
1596 if (streq(field, "RuntimeMaxSec"))
1597 return bus_append_parse_sec_rename(m, field, eq);
1598
1599 if (streq(field, "TimeoutStopSec"))
1600 return bus_append_parse_sec_rename(m, field, eq);
1601
1602 return 0;
1603 }
1604
1605 static int bus_append_service_property(sd_bus_message *m, const char *field, const char *eq) {
1606 int r;
1607
1608 if (STR_IN_SET(field, "PIDFile",
1609 "Type",
1610 "Restart",
1611 "BusName",
1612 "NotifyAccess",
1613 "USBFunctionDescriptors",
1614 "USBFunctionStrings",
1615 "OOMPolicy",
1616 "TimeoutStartFailureMode",
1617 "TimeoutStopFailureMode"))
1618 return bus_append_string(m, field, eq);
1619
1620 if (STR_IN_SET(field, "PermissionsStartOnly",
1621 "RootDirectoryStartOnly",
1622 "RemainAfterExit",
1623 "GuessMainPID"))
1624 return bus_append_parse_boolean(m, field, eq);
1625
1626 if (STR_IN_SET(field, "RestartSec",
1627 "TimeoutStartSec",
1628 "TimeoutStopSec",
1629 "TimeoutAbortSec",
1630 "RuntimeMaxSec",
1631 "WatchdogSec"))
1632 return bus_append_parse_sec_rename(m, field, eq);
1633
1634 if (streq(field, "TimeoutSec")) {
1635 r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq);
1636 if (r < 0)
1637 return r;
1638
1639 return bus_append_parse_sec_rename(m, "TimeoutStopSec", eq);
1640 }
1641
1642 if (streq(field, "FileDescriptorStoreMax"))
1643 return bus_append_safe_atou(m, field, eq);
1644
1645 if (STR_IN_SET(field, "ExecCondition",
1646 "ExecStartPre",
1647 "ExecStart",
1648 "ExecStartPost",
1649 "ExecConditionEx",
1650 "ExecStartPreEx",
1651 "ExecStartEx",
1652 "ExecStartPostEx",
1653 "ExecReload",
1654 "ExecStop",
1655 "ExecStopPost",
1656 "ExecReloadEx",
1657 "ExecStopEx",
1658 "ExecStopPostEx"))
1659 return bus_append_exec_command(m, field, eq);
1660
1661 if (STR_IN_SET(field, "RestartPreventExitStatus",
1662 "RestartForceExitStatus",
1663 "SuccessExitStatus")) {
1664 _cleanup_free_ int *status = NULL, *signal = NULL;
1665 size_t n_status = 0, n_signal = 0;
1666 const char *p;
1667
1668 for (p = eq;;) {
1669 _cleanup_free_ char *word = NULL;
1670
1671 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
1672 if (r == 0)
1673 break;
1674 if (r == -ENOMEM)
1675 return log_oom();
1676 if (r < 0)
1677 return log_error_errno(r, "Invalid syntax in %s: %s", field, eq);
1678
1679 /* We need to call exit_status_from_string() first, because we want
1680 * to parse numbers as exit statuses, not signals. */
1681
1682 r = exit_status_from_string(word);
1683 if (r >= 0) {
1684 assert(r >= 0 && r < 256);
1685
1686 status = reallocarray(status, n_status + 1, sizeof(int));
1687 if (!status)
1688 return log_oom();
1689
1690 status[n_status++] = r;
1691
1692 } else if ((r = signal_from_string(word)) >= 0) {
1693 signal = reallocarray(signal, n_signal + 1, sizeof(int));
1694 if (!signal)
1695 return log_oom();
1696
1697 signal[n_signal++] = r;
1698
1699 } else
1700 /* original r from exit_status_to_string() */
1701 return log_error_errno(r, "Invalid status or signal %s in %s: %m",
1702 word, field);
1703 }
1704
1705 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1706 if (r < 0)
1707 return bus_log_create_error(r);
1708
1709 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1710 if (r < 0)
1711 return bus_log_create_error(r);
1712
1713 r = sd_bus_message_open_container(m, 'v', "(aiai)");
1714 if (r < 0)
1715 return bus_log_create_error(r);
1716
1717 r = sd_bus_message_open_container(m, 'r', "aiai");
1718 if (r < 0)
1719 return bus_log_create_error(r);
1720
1721 r = sd_bus_message_append_array(m, 'i', status, n_status * sizeof(int));
1722 if (r < 0)
1723 return bus_log_create_error(r);
1724
1725 r = sd_bus_message_append_array(m, 'i', signal, n_signal * sizeof(int));
1726 if (r < 0)
1727 return bus_log_create_error(r);
1728
1729 r = sd_bus_message_close_container(m);
1730 if (r < 0)
1731 return bus_log_create_error(r);
1732
1733 r = sd_bus_message_close_container(m);
1734 if (r < 0)
1735 return bus_log_create_error(r);
1736
1737 r = sd_bus_message_close_container(m);
1738 if (r < 0)
1739 return bus_log_create_error(r);
1740
1741 return 1;
1742 }
1743
1744 return 0;
1745 }
1746
1747 static int bus_append_socket_property(sd_bus_message *m, const char *field, const char *eq) {
1748 int r;
1749
1750 if (STR_IN_SET(field, "Accept",
1751 "Writable",
1752 "KeepAlive",
1753 "NoDelay",
1754 "FreeBind",
1755 "Transparent",
1756 "Broadcast",
1757 "PassCredentials",
1758 "PassSecurity",
1759 "PassPacketInfo",
1760 "ReusePort",
1761 "RemoveOnStop",
1762 "SELinuxContextFromNet"))
1763 return bus_append_parse_boolean(m, field, eq);
1764
1765 if (STR_IN_SET(field, "Priority",
1766 "IPTTL",
1767 "Mark"))
1768 return bus_append_safe_atoi(m, field, eq);
1769
1770 if (streq(field, "IPTOS"))
1771 return bus_append_ip_tos_from_string(m, field, eq);
1772
1773 if (STR_IN_SET(field, "Backlog",
1774 "MaxConnections",
1775 "MaxConnectionsPerSource",
1776 "KeepAliveProbes",
1777 "TriggerLimitBurst"))
1778 return bus_append_safe_atou(m, field, eq);
1779
1780 if (STR_IN_SET(field, "SocketMode",
1781 "DirectoryMode"))
1782 return bus_append_parse_mode(m, field, eq);
1783
1784 if (STR_IN_SET(field, "MessageQueueMaxMessages",
1785 "MessageQueueMessageSize"))
1786 return bus_append_safe_atoi64(m, field, eq);
1787
1788 if (STR_IN_SET(field, "TimeoutSec",
1789 "KeepAliveTimeSec",
1790 "KeepAliveIntervalSec",
1791 "DeferAcceptSec",
1792 "TriggerLimitIntervalSec"))
1793 return bus_append_parse_sec_rename(m, field, eq);
1794
1795 if (STR_IN_SET(field, "ReceiveBuffer",
1796 "SendBuffer",
1797 "PipeSize"))
1798 return bus_append_parse_size(m, field, eq, 1024);
1799
1800 if (STR_IN_SET(field, "ExecStartPre",
1801 "ExecStartPost",
1802 "ExecReload",
1803 "ExecStopPost"))
1804 return bus_append_exec_command(m, field, eq);
1805
1806 if (STR_IN_SET(field, "SmackLabel",
1807 "SmackLabelIPIn",
1808 "SmackLabelIPOut",
1809 "TCPCongestion",
1810 "BindToDevice",
1811 "BindIPv6Only",
1812 "FileDescriptorName",
1813 "SocketUser",
1814 "SocketGroup"))
1815 return bus_append_string(m, field, eq);
1816
1817 if (streq(field, "Symlinks"))
1818 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
1819
1820 if (streq(field, "SocketProtocol"))
1821 return bus_append_parse_ip_protocol(m, field, eq);
1822
1823 if (STR_IN_SET(field, "ListenStream",
1824 "ListenDatagram",
1825 "ListenSequentialPacket",
1826 "ListenNetlink",
1827 "ListenSpecial",
1828 "ListenMessageQueue",
1829 "ListenFIFO",
1830 "ListenUSBFunction")) {
1831 if (isempty(eq))
1832 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0);
1833 else
1834 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 1, field + STRLEN("Listen"), eq);
1835 if (r < 0)
1836 return bus_log_create_error(r);
1837
1838 return 1;
1839 }
1840
1841 return 0;
1842 }
1843 static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) {
1844 int r;
1845
1846 if (STR_IN_SET(field, "WakeSystem",
1847 "RemainAfterElapse",
1848 "Persistent",
1849 "OnTimezoneChange",
1850 "OnClockChange"))
1851 return bus_append_parse_boolean(m, field, eq);
1852
1853 if (STR_IN_SET(field, "AccuracySec",
1854 "RandomizedDelaySec"))
1855 return bus_append_parse_sec_rename(m, field, eq);
1856
1857 if (STR_IN_SET(field, "OnActiveSec",
1858 "OnBootSec",
1859 "OnStartupSec",
1860 "OnUnitActiveSec",
1861 "OnUnitInactiveSec")) {
1862 if (isempty(eq))
1863 r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0);
1864 else {
1865 usec_t t;
1866 r = parse_sec(eq, &t);
1867 if (r < 0)
1868 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
1869
1870 r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 1, field, t);
1871 }
1872 if (r < 0)
1873 return bus_log_create_error(r);
1874
1875 return 1;
1876 }
1877
1878 if (streq(field, "OnCalendar")) {
1879 if (isempty(eq))
1880 r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0);
1881 else
1882 r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 1, field, eq);
1883 if (r < 0)
1884 return bus_log_create_error(r);
1885
1886 return 1;
1887 }
1888
1889 return 0;
1890 }
1891
1892 static int bus_append_unit_property(sd_bus_message *m, const char *field, const char *eq) {
1893 ConditionType t = _CONDITION_TYPE_INVALID;
1894 bool is_condition = false;
1895 int r;
1896
1897 if (STR_IN_SET(field, "Description",
1898 "SourcePath",
1899 "OnFailureJobMode",
1900 "JobTimeoutAction",
1901 "JobTimeoutRebootArgument",
1902 "StartLimitAction",
1903 "FailureAction",
1904 "SuccessAction",
1905 "RebootArgument",
1906 "CollectMode"))
1907 return bus_append_string(m, field, eq);
1908
1909 if (STR_IN_SET(field, "StopWhenUnneeded",
1910 "RefuseManualStart",
1911 "RefuseManualStop",
1912 "AllowIsolate",
1913 "IgnoreOnIsolate",
1914 "DefaultDependencies"))
1915 return bus_append_parse_boolean(m, field, eq);
1916
1917 if (STR_IN_SET(field, "JobTimeoutSec",
1918 "JobRunningTimeoutSec",
1919 "StartLimitIntervalSec"))
1920 return bus_append_parse_sec_rename(m, field, eq);
1921
1922 if (streq(field, "StartLimitBurst"))
1923 return bus_append_safe_atou(m, field, eq);
1924
1925 if (STR_IN_SET(field, "SuccessActionExitStatus",
1926 "FailureActionExitStatus")) {
1927 if (isempty(eq))
1928 r = sd_bus_message_append(m, "(sv)", field, "i", -1);
1929 else {
1930 uint8_t u;
1931
1932 r = safe_atou8(eq, &u);
1933 if (r < 0)
1934 return log_error_errno(r, "Failed to parse %s=%s", field, eq);
1935
1936 r = sd_bus_message_append(m, "(sv)", field, "i", (int) u);
1937 }
1938 if (r < 0)
1939 return bus_log_create_error(r);
1940
1941 return 1;
1942 }
1943
1944 if (unit_dependency_from_string(field) >= 0 ||
1945 STR_IN_SET(field, "Documentation",
1946 "RequiresMountsFor"))
1947 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
1948
1949 t = condition_type_from_string(field);
1950 if (t >= 0)
1951 is_condition = true;
1952 else
1953 t = assert_type_from_string(field);
1954 if (t >= 0) {
1955 if (isempty(eq))
1956 r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 0);
1957 else {
1958 const char *p = eq;
1959 int trigger, negate;
1960
1961 trigger = *p == '|';
1962 if (trigger)
1963 p++;
1964
1965 negate = *p == '!';
1966 if (negate)
1967 p++;
1968
1969 r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 1,
1970 field, trigger, negate, p);
1971 }
1972 if (r < 0)
1973 return bus_log_create_error(r);
1974
1975 return 1;
1976 }
1977
1978 return 0;
1979 }
1980
1981 int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment) {
1982 const char *eq, *field;
1983 int r;
1984
1985 assert(m);
1986 assert(assignment);
1987
1988 eq = strchr(assignment, '=');
1989 if (!eq)
1990 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1991 "Not an assignment: %s", assignment);
1992
1993 field = strndupa(assignment, eq - assignment);
1994 eq++;
1995
1996 switch (t) {
1997 case UNIT_SERVICE:
1998 r = bus_append_cgroup_property(m, field, eq);
1999 if (r != 0)
2000 return r;
2001
2002 r = bus_append_execute_property(m, field, eq);
2003 if (r != 0)
2004 return r;
2005
2006 r = bus_append_kill_property(m, field, eq);
2007 if (r != 0)
2008 return r;
2009
2010 r = bus_append_service_property(m, field, eq);
2011 if (r != 0)
2012 return r;
2013 break;
2014
2015 case UNIT_SOCKET:
2016 r = bus_append_cgroup_property(m, field, eq);
2017 if (r != 0)
2018 return r;
2019
2020 r = bus_append_execute_property(m, field, eq);
2021 if (r != 0)
2022 return r;
2023
2024 r = bus_append_kill_property(m, field, eq);
2025 if (r != 0)
2026 return r;
2027
2028 r = bus_append_socket_property(m, field, eq);
2029 if (r != 0)
2030 return r;
2031 break;
2032
2033 case UNIT_TIMER:
2034 r = bus_append_timer_property(m, field, eq);
2035 if (r != 0)
2036 return r;
2037 break;
2038
2039 case UNIT_PATH:
2040 r = bus_append_path_property(m, field, eq);
2041 if (r != 0)
2042 return r;
2043 break;
2044
2045 case UNIT_SLICE:
2046 r = bus_append_cgroup_property(m, field, eq);
2047 if (r != 0)
2048 return r;
2049 break;
2050
2051 case UNIT_SCOPE:
2052 r = bus_append_cgroup_property(m, field, eq);
2053 if (r != 0)
2054 return r;
2055
2056 r = bus_append_kill_property(m, field, eq);
2057 if (r != 0)
2058 return r;
2059
2060 r = bus_append_scope_property(m, field, eq);
2061 if (r != 0)
2062 return r;
2063 break;
2064
2065 case UNIT_MOUNT:
2066 r = bus_append_cgroup_property(m, field, eq);
2067 if (r != 0)
2068 return r;
2069
2070 r = bus_append_execute_property(m, field, eq);
2071 if (r != 0)
2072 return r;
2073
2074 r = bus_append_kill_property(m, field, eq);
2075 if (r != 0)
2076 return r;
2077
2078 r = bus_append_mount_property(m, field, eq);
2079 if (r != 0)
2080 return r;
2081
2082 break;
2083
2084 case UNIT_AUTOMOUNT:
2085 r = bus_append_automount_property(m, field, eq);
2086 if (r != 0)
2087 return r;
2088
2089 break;
2090
2091 case UNIT_TARGET:
2092 case UNIT_DEVICE:
2093 case UNIT_SWAP:
2094 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2095 "Not supported unit type");
2096
2097 default:
2098 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2099 "Invalid unit type");
2100 }
2101
2102 r = bus_append_unit_property(m, field, eq);
2103 if (r != 0)
2104 return r;
2105
2106 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2107 "Unknown assignment: %s", assignment);
2108 }
2109
2110 int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) {
2111 char **i;
2112 int r;
2113
2114 assert(m);
2115
2116 STRV_FOREACH(i, l) {
2117 r = bus_append_unit_property_assignment(m, t, *i);
2118 if (r < 0)
2119 return r;
2120 }
2121
2122 return 0;
2123 }
2124
2125 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, size_t *n_changes) {
2126 const char *type, *path, *source;
2127 int r;
2128
2129 /* changes is dereferenced when calling unit_file_dump_changes() later,
2130 * so we have to make sure this is not NULL. */
2131 assert(changes);
2132 assert(n_changes);
2133
2134 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
2135 if (r < 0)
2136 return bus_log_parse_error(r);
2137
2138 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
2139 /* We expect only "success" changes to be sent over the bus.
2140 Hence, reject anything negative. */
2141 UnitFileChangeType ch = unit_file_change_type_from_string(type);
2142
2143 if (ch < 0) {
2144 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path);
2145 continue;
2146 }
2147
2148 r = unit_file_changes_add(changes, n_changes, ch, path, source);
2149 if (r < 0)
2150 return r;
2151 }
2152 if (r < 0)
2153 return bus_log_parse_error(r);
2154
2155 r = sd_bus_message_exit_container(m);
2156 if (r < 0)
2157 return bus_log_parse_error(r);
2158
2159 unit_file_dump_changes(0, NULL, *changes, *n_changes, quiet);
2160 return 0;
2161 }
2162
2163 int unit_load_state(sd_bus *bus, const char *name, char **load_state) {
2164 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2165 _cleanup_free_ char *path = NULL;
2166 int r;
2167
2168 path = unit_dbus_path_from_name(name);
2169 if (!path)
2170 return log_oom();
2171
2172 /* This function warns on it's own, because otherwise it'd be awkward to pass
2173 * the dbus error message around. */
2174
2175 r = sd_bus_get_property_string(
2176 bus,
2177 "org.freedesktop.systemd1",
2178 path,
2179 "org.freedesktop.systemd1.Unit",
2180 "LoadState",
2181 &error,
2182 load_state);
2183 if (r < 0)
2184 return log_error_errno(r, "Failed to get load state of %s: %s", name, bus_error_message(&error, r));
2185
2186 return 0;
2187 }