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