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