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