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