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