]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bus-unit-util.c
doc: replace wrong idiom in homed comment
[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 return bus_append_string(m, field, eq);
964
965 if (STR_IN_SET(field, "IgnoreSIGPIPE",
966 "TTYVHangup",
967 "TTYReset",
968 "TTYVTDisallocate",
969 "PrivateTmp",
970 "PrivateDevices",
971 "PrivateNetwork",
972 "PrivateUsers",
973 "PrivateMounts",
974 "PrivateIPC",
975 "NoNewPrivileges",
976 "SyslogLevelPrefix",
977 "MemoryDenyWriteExecute",
978 "RestrictRealtime",
979 "DynamicUser",
980 "RemoveIPC",
981 "ProtectKernelTunables",
982 "ProtectKernelModules",
983 "ProtectKernelLogs",
984 "ProtectClock",
985 "ProtectControlGroups",
986 "MountAPIVFS",
987 "CPUSchedulingResetOnFork",
988 "LockPersonality",
989 "ProtectHostname",
990 "RestrictSUIDSGID"))
991 return bus_append_parse_boolean(m, field, eq);
992
993 if (STR_IN_SET(field, "ReadWriteDirectories",
994 "ReadOnlyDirectories",
995 "InaccessibleDirectories",
996 "ReadWritePaths",
997 "ReadOnlyPaths",
998 "InaccessiblePaths",
999 "ExecPaths",
1000 "NoExecPaths",
1001 "ExecSearchPath",
1002 "ExtensionDirectories",
1003 "ConfigurationDirectory",
1004 "SupplementaryGroups",
1005 "SystemCallArchitectures"))
1006 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
1007
1008 if (STR_IN_SET(field, "SyslogLevel",
1009 "LogLevelMax"))
1010 return bus_append_log_level_from_string(m, field, eq);
1011
1012 if (streq(field, "SyslogFacility"))
1013 return bus_append_log_facility_unshifted_from_string(m, field, eq);
1014
1015 if (streq(field, "SecureBits"))
1016 return bus_append_secure_bits_from_string(m, field, eq);
1017
1018 if (streq(field, "CPUSchedulingPolicy"))
1019 return bus_append_sched_policy_from_string(m, field, eq);
1020
1021 if (STR_IN_SET(field, "CPUSchedulingPriority",
1022 "OOMScoreAdjust"))
1023 return bus_append_safe_atoi(m, field, eq);
1024
1025 if (streq(field, "CoredumpFilter"))
1026 return bus_append_coredump_filter_mask_from_string(m, field, eq);
1027
1028 if (streq(field, "Nice"))
1029 return bus_append_parse_nice(m, field, eq);
1030
1031 if (streq(field, "SystemCallErrorNumber"))
1032 return bus_append_seccomp_parse_errno_or_action(m, field, eq);
1033
1034 if (streq(field, "IOSchedulingClass"))
1035 return bus_append_ioprio_class_from_string(m, field, eq);
1036
1037 if (streq(field, "IOSchedulingPriority"))
1038 return bus_append_ioprio_parse_priority(m, field, eq);
1039
1040 if (STR_IN_SET(field, "RuntimeDirectoryMode",
1041 "StateDirectoryMode",
1042 "CacheDirectoryMode",
1043 "LogsDirectoryMode",
1044 "ConfigurationDirectoryMode",
1045 "UMask"))
1046 return bus_append_parse_mode(m, field, eq);
1047
1048 if (streq(field, "TimerSlackNSec"))
1049 return bus_append_parse_nsec(m, field, eq);
1050
1051 if (streq(field, "LogRateLimitIntervalSec"))
1052 return bus_append_parse_sec_rename(m, field, eq);
1053
1054 if (STR_IN_SET(field, "LogRateLimitBurst",
1055 "TTYRows",
1056 "TTYColumns"))
1057 return bus_append_safe_atou(m, field, eq);
1058
1059 if (streq(field, "MountFlags"))
1060 return bus_append_mount_propagation_flag_from_string(m, field, eq);
1061
1062 if (STR_IN_SET(field, "Environment",
1063 "UnsetEnvironment",
1064 "PassEnvironment"))
1065 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
1066
1067 if (streq(field, "EnvironmentFile")) {
1068 if (isempty(eq))
1069 r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0);
1070 else
1071 r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 1,
1072 eq[0] == '-' ? eq + 1 : eq,
1073 eq[0] == '-');
1074 if (r < 0)
1075 return bus_log_create_error(r);
1076
1077 return 1;
1078 }
1079
1080 if (STR_IN_SET(field, "SetCredential", "SetCredentialEncrypted")) {
1081 r = sd_bus_message_open_container(m, 'r', "sv");
1082 if (r < 0)
1083 return bus_log_create_error(r);
1084
1085 r = sd_bus_message_append_basic(m, 's', field);
1086 if (r < 0)
1087 return bus_log_create_error(r);
1088
1089 r = sd_bus_message_open_container(m, 'v', "a(say)");
1090 if (r < 0)
1091 return bus_log_create_error(r);
1092
1093 if (isempty(eq))
1094 r = sd_bus_message_append(m, "a(say)", 0);
1095 else {
1096 _cleanup_free_ char *word = NULL;
1097 const char *p = eq;
1098
1099 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1100 if (r == -ENOMEM)
1101 return log_oom();
1102 if (r < 0)
1103 return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
1104 if (r == 0 || !p)
1105 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to %s=.", field);
1106
1107 r = sd_bus_message_open_container(m, 'a', "(say)");
1108 if (r < 0)
1109 return bus_log_create_error(r);
1110
1111 r = sd_bus_message_open_container(m, 'r', "say");
1112 if (r < 0)
1113 return bus_log_create_error(r);
1114
1115 r = sd_bus_message_append(m, "s", word);
1116 if (r < 0)
1117 return bus_log_create_error(r);
1118
1119 if (streq(field, "SetCredentialEncrypted")) {
1120 _cleanup_free_ void *decoded = NULL;
1121 size_t decoded_size;
1122
1123 r = unbase64mem(p, SIZE_MAX, &decoded, &decoded_size);
1124 if (r < 0)
1125 return log_error_errno(r, "Failed to base64 decode encrypted credential: %m");
1126
1127 r = sd_bus_message_append_array(m, 'y', decoded, decoded_size);
1128 } else {
1129 _cleanup_free_ char *unescaped = NULL;
1130 ssize_t l;
1131
1132 l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped);
1133 if (l < 0)
1134 return log_error_errno(l, "Failed to unescape %s= value: %s", field, p);
1135
1136 r = sd_bus_message_append_array(m, 'y', unescaped, l);
1137 }
1138 if (r < 0)
1139 return bus_log_create_error(r);
1140
1141 r = sd_bus_message_close_container(m);
1142 if (r < 0)
1143 return bus_log_create_error(r);
1144
1145 r = sd_bus_message_close_container(m);
1146 }
1147 if (r < 0)
1148 return bus_log_create_error(r);
1149
1150 r = sd_bus_message_close_container(m);
1151 if (r < 0)
1152 return bus_log_create_error(r);
1153
1154 r = sd_bus_message_close_container(m);
1155 if (r < 0)
1156 return bus_log_create_error(r);
1157
1158 return 1;
1159 }
1160
1161 if (STR_IN_SET(field, "LoadCredential", "LoadCredentialEncrypted")) {
1162 r = sd_bus_message_open_container(m, 'r', "sv");
1163 if (r < 0)
1164 return bus_log_create_error(r);
1165
1166 r = sd_bus_message_append_basic(m, 's', field);
1167 if (r < 0)
1168 return bus_log_create_error(r);
1169
1170 r = sd_bus_message_open_container(m, 'v', "a(ss)");
1171 if (r < 0)
1172 return bus_log_create_error(r);
1173
1174 if (isempty(eq))
1175 r = sd_bus_message_append(m, "a(ss)", 0);
1176 else {
1177 _cleanup_free_ char *word = NULL;
1178 const char *p = eq;
1179
1180 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1181 if (r == -ENOMEM)
1182 return log_oom();
1183 if (r < 0)
1184 return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
1185 if (r == 0)
1186 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to %s=.", field);
1187
1188 if (isempty(p)) /* If only one field is specified, then this means "inherit from above" */
1189 p = eq;
1190
1191 r = sd_bus_message_append(m, "a(ss)", 1, word, p);
1192 }
1193 if (r < 0)
1194 return bus_log_create_error(r);
1195
1196 r = sd_bus_message_close_container(m);
1197 if (r < 0)
1198 return bus_log_create_error(r);
1199
1200 r = sd_bus_message_close_container(m);
1201 if (r < 0)
1202 return bus_log_create_error(r);
1203
1204 return 1;
1205 }
1206
1207 if (streq(field, "LogExtraFields")) {
1208 r = sd_bus_message_open_container(m, 'r', "sv");
1209 if (r < 0)
1210 return bus_log_create_error(r);
1211
1212 r = sd_bus_message_append_basic(m, 's', "LogExtraFields");
1213 if (r < 0)
1214 return bus_log_create_error(r);
1215
1216 r = sd_bus_message_open_container(m, 'v', "aay");
1217 if (r < 0)
1218 return bus_log_create_error(r);
1219
1220 r = sd_bus_message_open_container(m, 'a', "ay");
1221 if (r < 0)
1222 return bus_log_create_error(r);
1223
1224 r = sd_bus_message_append_array(m, 'y', eq, strlen(eq));
1225 if (r < 0)
1226 return bus_log_create_error(r);
1227
1228 r = sd_bus_message_close_container(m);
1229 if (r < 0)
1230 return bus_log_create_error(r);
1231
1232 r = sd_bus_message_close_container(m);
1233 if (r < 0)
1234 return bus_log_create_error(r);
1235
1236 r = sd_bus_message_close_container(m);
1237 if (r < 0)
1238 return bus_log_create_error(r);
1239
1240 return 1;
1241 }
1242
1243 if (streq(field, "LogFilterPatterns")) {
1244 r = sd_bus_message_append(m, "(sv)", "LogFilterPatterns", "a(bs)", 1,
1245 eq[0] != '~',
1246 eq[0] != '~' ? eq : eq + 1);
1247 if (r < 0)
1248 return bus_log_create_error(r);
1249
1250 return 1;
1251 }
1252
1253 if (STR_IN_SET(field, "StandardInput",
1254 "StandardOutput",
1255 "StandardError")) {
1256 const char *n, *appended;
1257
1258 if ((n = startswith(eq, "fd:"))) {
1259 appended = strjoina(field, "FileDescriptorName");
1260 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
1261 } else if ((n = startswith(eq, "file:"))) {
1262 appended = strjoina(field, "File");
1263 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
1264 } else if ((n = startswith(eq, "append:"))) {
1265 appended = strjoina(field, "FileToAppend");
1266 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
1267 } else if ((n = startswith(eq, "truncate:"))) {
1268 appended = strjoina(field, "FileToTruncate");
1269 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
1270 } else
1271 r = sd_bus_message_append(m, "(sv)", field, "s", eq);
1272 if (r < 0)
1273 return bus_log_create_error(r);
1274
1275 return 1;
1276 }
1277
1278 if (streq(field, "StandardInputText")) {
1279 _cleanup_free_ char *unescaped = NULL;
1280 ssize_t l;
1281
1282 l = cunescape(eq, 0, &unescaped);
1283 if (l < 0)
1284 return log_error_errno(l, "Failed to unescape text '%s': %m", eq);
1285
1286 if (!strextend(&unescaped, "\n"))
1287 return log_oom();
1288
1289 /* Note that we don't expand specifiers here, but that should be OK, as this is a
1290 * programmatic interface anyway */
1291
1292 return bus_append_byte_array(m, field, unescaped, l + 1);
1293 }
1294
1295 if (streq(field, "StandardInputData")) {
1296 _cleanup_free_ void *decoded = NULL;
1297 size_t sz;
1298
1299 r = unbase64mem(eq, SIZE_MAX, &decoded, &sz);
1300 if (r < 0)
1301 return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq);
1302
1303 return bus_append_byte_array(m, field, decoded, sz);
1304 }
1305
1306 if ((suffix = startswith(field, "Limit"))) {
1307 int rl;
1308
1309 rl = rlimit_from_string(suffix);
1310 if (rl >= 0) {
1311 const char *sn;
1312 struct rlimit l;
1313
1314 r = rlimit_parse(rl, eq, &l);
1315 if (r < 0)
1316 return log_error_errno(r, "Failed to parse resource limit: %s", eq);
1317
1318 r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max);
1319 if (r < 0)
1320 return bus_log_create_error(r);
1321
1322 sn = strjoina(field, "Soft");
1323 r = sd_bus_message_append(m, "(sv)", sn, "t", l.rlim_cur);
1324 if (r < 0)
1325 return bus_log_create_error(r);
1326
1327 return 1;
1328 }
1329 }
1330
1331 if (STR_IN_SET(field, "AppArmorProfile",
1332 "SmackProcessLabel")) {
1333 int ignore = 0;
1334 const char *s = eq;
1335
1336 if (eq[0] == '-') {
1337 ignore = 1;
1338 s = eq + 1;
1339 }
1340
1341 r = sd_bus_message_append(m, "(sv)", field, "(bs)", ignore, s);
1342 if (r < 0)
1343 return bus_log_create_error(r);
1344
1345 return 1;
1346 }
1347
1348 if (STR_IN_SET(field, "CapabilityBoundingSet",
1349 "AmbientCapabilities")) {
1350 uint64_t sum = 0;
1351 bool invert = false;
1352 const char *p = eq;
1353
1354 if (*p == '~') {
1355 invert = true;
1356 p++;
1357 }
1358
1359 r = capability_set_from_string(p, &sum);
1360 if (r < 0)
1361 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
1362
1363 sum = invert ? ~sum : sum;
1364
1365 r = sd_bus_message_append(m, "(sv)", field, "t", sum);
1366 if (r < 0)
1367 return bus_log_create_error(r);
1368
1369 return 1;
1370 }
1371
1372 if (streq(field, "CPUAffinity")) {
1373 _cleanup_(cpu_set_reset) CPUSet cpuset = {};
1374 _cleanup_free_ uint8_t *array = NULL;
1375 size_t allocated;
1376
1377 if (eq && streq(eq, "numa")) {
1378 r = sd_bus_message_append(m, "(sv)", "CPUAffinityFromNUMA", "b", true);
1379 if (r < 0)
1380 return bus_log_create_error(r);
1381 return r;
1382 }
1383
1384 r = parse_cpu_set(eq, &cpuset);
1385 if (r < 0)
1386 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
1387
1388 r = cpu_set_to_dbus(&cpuset, &array, &allocated);
1389 if (r < 0)
1390 return log_error_errno(r, "Failed to serialize CPUAffinity: %m");
1391
1392 return bus_append_byte_array(m, field, array, allocated);
1393 }
1394
1395 if (streq(field, "NUMAPolicy")) {
1396 r = mpol_from_string(eq);
1397 if (r < 0)
1398 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
1399
1400 r = sd_bus_message_append(m, "(sv)", field, "i", (int32_t) r);
1401 if (r < 0)
1402 return bus_log_create_error(r);
1403
1404 return 1;
1405 }
1406
1407 if (streq(field, "NUMAMask")) {
1408 _cleanup_(cpu_set_reset) CPUSet nodes = {};
1409 _cleanup_free_ uint8_t *array = NULL;
1410 size_t allocated;
1411
1412 if (eq && streq(eq, "all")) {
1413 r = numa_mask_add_all(&nodes);
1414 if (r < 0)
1415 return log_error_errno(r, "Failed to create NUMA mask representing \"all\" NUMA nodes: %m");
1416 } else {
1417 r = parse_cpu_set(eq, &nodes);
1418 if (r < 0)
1419 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
1420 }
1421
1422 r = cpu_set_to_dbus(&nodes, &array, &allocated);
1423 if (r < 0)
1424 return log_error_errno(r, "Failed to serialize NUMAMask: %m");
1425
1426 return bus_append_byte_array(m, field, array, allocated);
1427 }
1428
1429 if (STR_IN_SET(field, "RestrictAddressFamilies",
1430 "RestrictFileSystems",
1431 "SystemCallFilter",
1432 "SystemCallLog",
1433 "RestrictNetworkInterfaces")) {
1434 int allow_list = 1;
1435 const char *p = eq;
1436
1437 if (*p == '~') {
1438 allow_list = 0;
1439 p++;
1440 }
1441
1442 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1443 if (r < 0)
1444 return bus_log_create_error(r);
1445
1446 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1447 if (r < 0)
1448 return bus_log_create_error(r);
1449
1450 r = sd_bus_message_open_container(m, 'v', "(bas)");
1451 if (r < 0)
1452 return bus_log_create_error(r);
1453
1454 r = sd_bus_message_open_container(m, 'r', "bas");
1455 if (r < 0)
1456 return bus_log_create_error(r);
1457
1458 r = sd_bus_message_append_basic(m, 'b', &allow_list);
1459 if (r < 0)
1460 return bus_log_create_error(r);
1461
1462 r = sd_bus_message_open_container(m, 'a', "s");
1463 if (r < 0)
1464 return bus_log_create_error(r);
1465
1466 for (;;) {
1467 _cleanup_free_ char *word = NULL;
1468
1469 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
1470 if (r == 0)
1471 break;
1472 if (r == -ENOMEM)
1473 return log_oom();
1474 if (r < 0)
1475 return log_error_errno(r, "Invalid syntax: %s", eq);
1476
1477 r = sd_bus_message_append_basic(m, 's', word);
1478 if (r < 0)
1479 return bus_log_create_error(r);
1480 }
1481
1482 r = sd_bus_message_close_container(m);
1483 if (r < 0)
1484 return bus_log_create_error(r);
1485
1486 r = sd_bus_message_close_container(m);
1487 if (r < 0)
1488 return bus_log_create_error(r);
1489
1490 r = sd_bus_message_close_container(m);
1491 if (r < 0)
1492 return bus_log_create_error(r);
1493
1494 r = sd_bus_message_close_container(m);
1495 if (r < 0)
1496 return bus_log_create_error(r);
1497
1498 return 1;
1499 }
1500
1501 if (streq(field, "RestrictNamespaces")) {
1502 bool invert = false;
1503 unsigned long flags;
1504
1505 r = parse_boolean(eq);
1506 if (r > 0)
1507 flags = 0;
1508 else if (r == 0)
1509 flags = NAMESPACE_FLAGS_ALL;
1510 else {
1511 if (eq[0] == '~') {
1512 invert = true;
1513 eq++;
1514 }
1515
1516 r = namespace_flags_from_string(eq, &flags);
1517 if (r < 0)
1518 return log_error_errno(r, "Failed to parse %s value %s.", field, eq);
1519 }
1520
1521 if (invert)
1522 flags = (~flags) & NAMESPACE_FLAGS_ALL;
1523
1524 r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) flags);
1525 if (r < 0)
1526 return bus_log_create_error(r);
1527
1528 return 1;
1529 }
1530
1531 if (STR_IN_SET(field, "BindPaths",
1532 "BindReadOnlyPaths")) {
1533 const char *p = eq;
1534
1535 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1536 if (r < 0)
1537 return bus_log_create_error(r);
1538
1539 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1540 if (r < 0)
1541 return bus_log_create_error(r);
1542
1543 r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
1544 if (r < 0)
1545 return bus_log_create_error(r);
1546
1547 r = sd_bus_message_open_container(m, 'a', "(ssbt)");
1548 if (r < 0)
1549 return bus_log_create_error(r);
1550
1551 for (;;) {
1552 _cleanup_free_ char *source = NULL, *destination = NULL;
1553 char *s = NULL, *d = NULL;
1554 bool ignore_enoent = false;
1555 uint64_t flags = MS_REC;
1556
1557 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
1558 if (r < 0)
1559 return log_error_errno(r, "Failed to parse argument: %m");
1560 if (r == 0)
1561 break;
1562
1563 s = source;
1564 if (s[0] == '-') {
1565 ignore_enoent = true;
1566 s++;
1567 }
1568
1569 if (p && p[-1] == ':') {
1570 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
1571 if (r < 0)
1572 return log_error_errno(r, "Failed to parse argument: %m");
1573 if (r == 0)
1574 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1575 "Missing argument after ':': %s",
1576 eq);
1577
1578 d = destination;
1579
1580 if (p && p[-1] == ':') {
1581 _cleanup_free_ char *options = NULL;
1582
1583 r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE);
1584 if (r < 0)
1585 return log_error_errno(r, "Failed to parse argument: %m");
1586
1587 if (isempty(options) || streq(options, "rbind"))
1588 flags = MS_REC;
1589 else if (streq(options, "norbind"))
1590 flags = 0;
1591 else
1592 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1593 "Unknown options: %s",
1594 eq);
1595 }
1596 } else
1597 d = s;
1598
1599 r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
1600 if (r < 0)
1601 return bus_log_create_error(r);
1602 }
1603
1604 r = sd_bus_message_close_container(m);
1605 if (r < 0)
1606 return bus_log_create_error(r);
1607
1608 r = sd_bus_message_close_container(m);
1609 if (r < 0)
1610 return bus_log_create_error(r);
1611
1612 r = sd_bus_message_close_container(m);
1613 if (r < 0)
1614 return bus_log_create_error(r);
1615
1616 return 1;
1617 }
1618
1619 if (streq(field, "TemporaryFileSystem")) {
1620 const char *p = eq;
1621
1622 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1623 if (r < 0)
1624 return bus_log_create_error(r);
1625
1626 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1627 if (r < 0)
1628 return bus_log_create_error(r);
1629
1630 r = sd_bus_message_open_container(m, 'v', "a(ss)");
1631 if (r < 0)
1632 return bus_log_create_error(r);
1633
1634 r = sd_bus_message_open_container(m, 'a', "(ss)");
1635 if (r < 0)
1636 return bus_log_create_error(r);
1637
1638 for (;;) {
1639 _cleanup_free_ char *word = NULL, *path = NULL;
1640 const char *w;
1641
1642 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
1643 if (r < 0)
1644 return log_error_errno(r, "Failed to parse argument: %m");
1645 if (r == 0)
1646 break;
1647
1648 w = word;
1649 r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1650 if (r < 0)
1651 return log_error_errno(r, "Failed to parse argument: %m");
1652 if (r == 0)
1653 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1654 "Failed to parse argument: %s",
1655 p);
1656
1657 r = sd_bus_message_append(m, "(ss)", path, w);
1658 if (r < 0)
1659 return bus_log_create_error(r);
1660 }
1661
1662 r = sd_bus_message_close_container(m);
1663 if (r < 0)
1664 return bus_log_create_error(r);
1665
1666 r = sd_bus_message_close_container(m);
1667 if (r < 0)
1668 return bus_log_create_error(r);
1669
1670 r = sd_bus_message_close_container(m);
1671 if (r < 0)
1672 return bus_log_create_error(r);
1673
1674 return 1;
1675 }
1676
1677 if (streq(field, "RootHash")) {
1678 _cleanup_free_ void *roothash_decoded = NULL;
1679 size_t roothash_decoded_size = 0;
1680
1681 /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
1682 if (path_is_absolute(eq))
1683 return bus_append_string(m, "RootHashPath", eq);
1684
1685 /* We have a roothash to decode, eg: RootHash=012345789abcdef */
1686 r = unhexmem(eq, strlen(eq), &roothash_decoded, &roothash_decoded_size);
1687 if (r < 0)
1688 return log_error_errno(r, "Failed to decode RootHash= '%s': %m", eq);
1689 if (roothash_decoded_size < sizeof(sd_id128_t))
1690 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "RootHash= '%s' is too short: %m", eq);
1691
1692 return bus_append_byte_array(m, field, roothash_decoded, roothash_decoded_size);
1693 }
1694
1695 if (streq(field, "RootHashSignature")) {
1696 _cleanup_free_ void *roothash_sig_decoded = NULL;
1697 char *value;
1698 size_t roothash_sig_decoded_size = 0;
1699
1700 /* We have the path to a roothash signature to load and decode, eg: RootHash=/foo/bar.roothash.p7s */
1701 if (path_is_absolute(eq))
1702 return bus_append_string(m, "RootHashSignaturePath", eq);
1703
1704 if (!(value = startswith(eq, "base64:")))
1705 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decode RootHashSignature= '%s', not a path but doesn't start with 'base64:': %m", eq);
1706
1707 /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
1708 r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size);
1709 if (r < 0)
1710 return log_error_errno(r, "Failed to decode RootHashSignature= '%s': %m", eq);
1711
1712 return bus_append_byte_array(m, field, roothash_sig_decoded, roothash_sig_decoded_size);
1713 }
1714
1715 if (streq(field, "RootImageOptions")) {
1716 _cleanup_strv_free_ char **l = NULL;
1717 const char *p = eq;
1718
1719 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1720 if (r < 0)
1721 return bus_log_create_error(r);
1722
1723 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1724 if (r < 0)
1725 return bus_log_create_error(r);
1726
1727 r = sd_bus_message_open_container(m, 'v', "a(ss)");
1728 if (r < 0)
1729 return bus_log_create_error(r);
1730
1731 r = sd_bus_message_open_container(m, 'a', "(ss)");
1732 if (r < 0)
1733 return bus_log_create_error(r);
1734
1735 r = strv_split_colon_pairs(&l, p);
1736 if (r < 0)
1737 return log_error_errno(r, "Failed to parse argument: %m");
1738
1739 STRV_FOREACH_PAIR(first, second, l) {
1740 r = sd_bus_message_append(m, "(ss)",
1741 !isempty(*second) ? *first : "root",
1742 !isempty(*second) ? *second : *first);
1743 if (r < 0)
1744 return bus_log_create_error(r);
1745 }
1746
1747 r = sd_bus_message_close_container(m);
1748 if (r < 0)
1749 return bus_log_create_error(r);
1750
1751 r = sd_bus_message_close_container(m);
1752 if (r < 0)
1753 return bus_log_create_error(r);
1754
1755 r = sd_bus_message_close_container(m);
1756 if (r < 0)
1757 return bus_log_create_error(r);
1758
1759 return 1;
1760 }
1761
1762 if (streq(field, "MountImages")) {
1763 const char *p = eq;
1764
1765 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1766 if (r < 0)
1767 return bus_log_create_error(r);
1768
1769 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1770 if (r < 0)
1771 return bus_log_create_error(r);
1772
1773 r = sd_bus_message_open_container(m, 'v', "a(ssba(ss))");
1774 if (r < 0)
1775 return bus_log_create_error(r);
1776
1777 r = sd_bus_message_open_container(m, 'a', "(ssba(ss))");
1778 if (r < 0)
1779 return bus_log_create_error(r);
1780
1781 for (;;) {
1782 _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL;
1783 const char *q = NULL, *source = NULL;
1784 bool permissive = false;
1785
1786 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
1787 if (r < 0)
1788 return log_error_errno(r, "Failed to parse MountImages= property: %s", eq);
1789 if (r == 0)
1790 break;
1791
1792 q = tuple;
1793 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &first, &second, NULL);
1794 if (r < 0)
1795 return log_error_errno(r, "Failed to parse MountImages= property: %s", eq);
1796 if (r == 0)
1797 continue;
1798
1799 source = first;
1800 if (source[0] == '-') {
1801 permissive = true;
1802 source++;
1803 }
1804
1805 if (isempty(second))
1806 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1807 "Missing argument after ':': %s",
1808 eq);
1809
1810 r = sd_bus_message_open_container(m, 'r', "ssba(ss)");
1811 if (r < 0)
1812 return bus_log_create_error(r);
1813
1814 r = sd_bus_message_append(m, "ssb", source, second, permissive);
1815 if (r < 0)
1816 return bus_log_create_error(r);
1817
1818 r = sd_bus_message_open_container(m, 'a', "(ss)");
1819 if (r < 0)
1820 return bus_log_create_error(r);
1821
1822 for (;;) {
1823 _cleanup_free_ char *partition = NULL, *mount_options = NULL;
1824
1825 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
1826 if (r < 0)
1827 return log_error_errno(r, "Failed to parse MountImages= property: %s", eq);
1828 if (r == 0)
1829 break;
1830 /* Single set of options, applying to the root partition/single filesystem */
1831 if (r == 1) {
1832 r = sd_bus_message_append(m, "(ss)", "root", partition);
1833 if (r < 0)
1834 return bus_log_create_error(r);
1835
1836 break;
1837 }
1838
1839 r = sd_bus_message_append(m, "(ss)", partition, mount_options);
1840 if (r < 0)
1841 return bus_log_create_error(r);
1842 }
1843
1844 r = sd_bus_message_close_container(m);
1845 if (r < 0)
1846 return bus_log_create_error(r);
1847
1848 r = sd_bus_message_close_container(m);
1849 if (r < 0)
1850 return bus_log_create_error(r);
1851 }
1852
1853 r = sd_bus_message_close_container(m);
1854 if (r < 0)
1855 return bus_log_create_error(r);
1856
1857 r = sd_bus_message_close_container(m);
1858 if (r < 0)
1859 return bus_log_create_error(r);
1860
1861 r = sd_bus_message_close_container(m);
1862 if (r < 0)
1863 return bus_log_create_error(r);
1864
1865 return 1;
1866 }
1867
1868 if (streq(field, "ExtensionImages")) {
1869 const char *p = eq;
1870
1871 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1872 if (r < 0)
1873 return bus_log_create_error(r);
1874
1875 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1876 if (r < 0)
1877 return bus_log_create_error(r);
1878
1879 r = sd_bus_message_open_container(m, 'v', "a(sba(ss))");
1880 if (r < 0)
1881 return bus_log_create_error(r);
1882
1883 r = sd_bus_message_open_container(m, 'a', "(sba(ss))");
1884 if (r < 0)
1885 return bus_log_create_error(r);
1886
1887 for (;;) {
1888 _cleanup_free_ char *source = NULL, *tuple = NULL;
1889 const char *q = NULL, *s = NULL;
1890 bool permissive = false;
1891
1892 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
1893 if (r < 0)
1894 return log_error_errno(r, "Failed to parse ExtensionImages= property: %s", eq);
1895 if (r == 0)
1896 break;
1897
1898 q = tuple;
1899 r = extract_first_word(&q, &source, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS);
1900 if (r < 0)
1901 return log_error_errno(r, "Failed to parse ExtensionImages= property: %s", eq);
1902 if (r == 0)
1903 continue;
1904
1905 s = source;
1906 if (s[0] == '-') {
1907 permissive = true;
1908 s++;
1909 }
1910
1911 r = sd_bus_message_open_container(m, 'r', "sba(ss)");
1912 if (r < 0)
1913 return bus_log_create_error(r);
1914
1915 r = sd_bus_message_append(m, "sb", s, permissive);
1916 if (r < 0)
1917 return bus_log_create_error(r);
1918
1919 r = sd_bus_message_open_container(m, 'a', "(ss)");
1920 if (r < 0)
1921 return bus_log_create_error(r);
1922
1923 for (;;) {
1924 _cleanup_free_ char *partition = NULL, *mount_options = NULL;
1925
1926 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
1927 if (r < 0)
1928 return log_error_errno(r, "Failed to parse ExtensionImages= property: %s", eq);
1929 if (r == 0)
1930 break;
1931 /* Single set of options, applying to the root partition/single filesystem */
1932 if (r == 1) {
1933 r = sd_bus_message_append(m, "(ss)", "root", partition);
1934 if (r < 0)
1935 return bus_log_create_error(r);
1936
1937 break;
1938 }
1939
1940 r = sd_bus_message_append(m, "(ss)", partition, mount_options);
1941 if (r < 0)
1942 return bus_log_create_error(r);
1943 }
1944
1945 r = sd_bus_message_close_container(m);
1946 if (r < 0)
1947 return bus_log_create_error(r);
1948
1949 r = sd_bus_message_close_container(m);
1950 if (r < 0)
1951 return bus_log_create_error(r);
1952 }
1953
1954 r = sd_bus_message_close_container(m);
1955 if (r < 0)
1956 return bus_log_create_error(r);
1957
1958 r = sd_bus_message_close_container(m);
1959 if (r < 0)
1960 return bus_log_create_error(r);
1961
1962 r = sd_bus_message_close_container(m);
1963 if (r < 0)
1964 return bus_log_create_error(r);
1965
1966 return 1;
1967 }
1968
1969 if (STR_IN_SET(field, "StateDirectory", "RuntimeDirectory", "CacheDirectory", "LogsDirectory")) {
1970 _cleanup_strv_free_ char **symlinks = NULL, **sources = NULL;
1971 const char *p = eq;
1972
1973 /* Adding new directories is supported from both *DirectorySymlink methods and the
1974 * older ones, so first parse the input, and if we are given a new-style src:dst
1975 * tuple use the new method, else use the old one. */
1976
1977 for (;;) {
1978 _cleanup_free_ char *tuple = NULL, *source = NULL, *destination = NULL;
1979
1980 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE);
1981 if (r < 0)
1982 return log_error_errno(r, "Failed to parse argument: %m");
1983 if (r == 0)
1984 break;
1985
1986 const char *t = tuple;
1987 r = extract_many_words(&t, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL);
1988 if (r <= 0)
1989 return log_error_errno(r ?: SYNTHETIC_ERRNO(EINVAL), "Failed to parse argument: %m");
1990
1991 path_simplify(source);
1992
1993 if (isempty(destination)) {
1994 r = strv_consume(&sources, TAKE_PTR(source));
1995 if (r < 0)
1996 return bus_log_create_error(r);
1997 } else {
1998 path_simplify(destination);
1999
2000 r = strv_consume_pair(&symlinks, TAKE_PTR(source), TAKE_PTR(destination));
2001 if (r < 0)
2002 return log_oom();
2003 }
2004 }
2005
2006 if (!strv_isempty(sources)) {
2007 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
2008 if (r < 0)
2009 return bus_log_create_error(r);
2010
2011 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
2012 if (r < 0)
2013 return bus_log_create_error(r);
2014
2015 r = sd_bus_message_open_container(m, 'v', "as");
2016 if (r < 0)
2017 return bus_log_create_error(r);
2018
2019 r = sd_bus_message_append_strv(m, sources);
2020 if (r < 0)
2021 return bus_log_create_error(r);
2022
2023 r = sd_bus_message_close_container(m);
2024 if (r < 0)
2025 return bus_log_create_error(r);
2026
2027 r = sd_bus_message_close_container(m);
2028 if (r < 0)
2029 return bus_log_create_error(r);
2030 }
2031
2032 /* For State and Runtime directories we support an optional destination parameter, which
2033 * will be used to create a symlink to the source. But it is new so we cannot change the
2034 * old DBUS signatures, so append a new message type. */
2035 if (!strv_isempty(symlinks)) {
2036 const char *symlink_field;
2037
2038 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
2039 if (r < 0)
2040 return bus_log_create_error(r);
2041
2042 if (streq(field, "StateDirectory"))
2043 symlink_field = "StateDirectorySymlink";
2044 else if (streq(field, "RuntimeDirectory"))
2045 symlink_field = "RuntimeDirectorySymlink";
2046 else if (streq(field, "CacheDirectory"))
2047 symlink_field = "CacheDirectorySymlink";
2048 else if (streq(field, "LogsDirectory"))
2049 symlink_field = "LogsDirectorySymlink";
2050 else
2051 assert_not_reached();
2052
2053 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, symlink_field);
2054 if (r < 0)
2055 return bus_log_create_error(r);
2056
2057 r = sd_bus_message_open_container(m, 'v', "a(sst)");
2058 if (r < 0)
2059 return bus_log_create_error(r);
2060
2061 r = sd_bus_message_open_container(m, 'a', "(sst)");
2062 if (r < 0)
2063 return bus_log_create_error(r);
2064
2065 STRV_FOREACH_PAIR(source, destination, symlinks) {
2066 r = sd_bus_message_append(m, "(sst)", *source, *destination, 0);
2067 if (r < 0)
2068 return bus_log_create_error(r);
2069 }
2070
2071 r = sd_bus_message_close_container(m);
2072 if (r < 0)
2073 return bus_log_create_error(r);
2074
2075 r = sd_bus_message_close_container(m);
2076 if (r < 0)
2077 return bus_log_create_error(r);
2078
2079 r = sd_bus_message_close_container(m);
2080 if (r < 0)
2081 return bus_log_create_error(r);
2082 }
2083
2084 return 1;
2085 }
2086
2087 return 0;
2088 }
2089
2090 static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
2091 if (streq(field, "KillMode"))
2092 return bus_append_string(m, field, eq);
2093
2094 if (STR_IN_SET(field, "SendSIGHUP",
2095 "SendSIGKILL"))
2096 return bus_append_parse_boolean(m, field, eq);
2097
2098 if (STR_IN_SET(field, "KillSignal",
2099 "RestartKillSignal",
2100 "FinalKillSignal",
2101 "WatchdogSignal",
2102 "ReloadSignal"))
2103 return bus_append_signal_from_string(m, field, eq);
2104
2105 return 0;
2106 }
2107
2108 static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) {
2109
2110 if (STR_IN_SET(field, "What",
2111 "Where",
2112 "Options",
2113 "Type"))
2114 return bus_append_string(m, field, eq);
2115
2116 if (streq(field, "TimeoutSec"))
2117 return bus_append_parse_sec_rename(m, field, eq);
2118
2119 if (streq(field, "DirectoryMode"))
2120 return bus_append_parse_mode(m, field, eq);
2121
2122 if (STR_IN_SET(field, "SloppyOptions",
2123 "LazyUnmount",
2124 "ForceUnmount",
2125 "ReadwriteOnly"))
2126 return bus_append_parse_boolean(m, field, eq);
2127
2128 return 0;
2129 }
2130
2131 static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) {
2132 int r;
2133
2134 if (streq(field, "MakeDirectory"))
2135 return bus_append_parse_boolean(m, field, eq);
2136
2137 if (streq(field, "DirectoryMode"))
2138 return bus_append_parse_mode(m, field, eq);
2139
2140 if (STR_IN_SET(field, "PathExists",
2141 "PathExistsGlob",
2142 "PathChanged",
2143 "PathModified",
2144 "DirectoryNotEmpty")) {
2145 if (isempty(eq))
2146 r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0);
2147 else
2148 r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 1, field, eq);
2149 if (r < 0)
2150 return bus_log_create_error(r);
2151
2152 return 1;
2153 }
2154
2155 if (streq(field, "TriggerLimitBurst"))
2156 return bus_append_safe_atou(m, field, eq);
2157
2158 if (streq(field, "TriggerLimitIntervalSec"))
2159 return bus_append_parse_sec_rename(m, field, eq);
2160
2161 return 0;
2162 }
2163
2164 static int bus_append_scope_property(sd_bus_message *m, const char *field, const char *eq) {
2165 if (streq(field, "RuntimeMaxSec"))
2166 return bus_append_parse_sec_rename(m, field, eq);
2167
2168 if (streq(field, "RuntimeRandomizedExtraSec"))
2169 return bus_append_parse_sec_rename(m, field, eq);
2170
2171 if (streq(field, "TimeoutStopSec"))
2172 return bus_append_parse_sec_rename(m, field, eq);
2173
2174 /* Scope units don't have execution context but we still want to allow setting these two,
2175 * so let's handle them separately. */
2176 if (STR_IN_SET(field, "User", "Group"))
2177 return bus_append_string(m, field, eq);
2178
2179 if (streq(field, "OOMPolicy"))
2180 return bus_append_string(m, field, eq);
2181
2182 return 0;
2183 }
2184
2185 static int bus_append_service_property(sd_bus_message *m, const char *field, const char *eq) {
2186 int r;
2187
2188 if (STR_IN_SET(field, "PIDFile",
2189 "Type",
2190 "ExitType",
2191 "Restart",
2192 "BusName",
2193 "NotifyAccess",
2194 "USBFunctionDescriptors",
2195 "USBFunctionStrings",
2196 "OOMPolicy",
2197 "TimeoutStartFailureMode",
2198 "TimeoutStopFailureMode"))
2199 return bus_append_string(m, field, eq);
2200
2201 if (STR_IN_SET(field, "PermissionsStartOnly",
2202 "RootDirectoryStartOnly",
2203 "RemainAfterExit",
2204 "GuessMainPID"))
2205 return bus_append_parse_boolean(m, field, eq);
2206
2207 if (STR_IN_SET(field, "RestartSec",
2208 "TimeoutStartSec",
2209 "TimeoutStopSec",
2210 "TimeoutAbortSec",
2211 "RuntimeMaxSec",
2212 "RuntimeRandomizedExtraSec",
2213 "WatchdogSec"))
2214 return bus_append_parse_sec_rename(m, field, eq);
2215
2216 if (streq(field, "TimeoutSec")) {
2217 r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq);
2218 if (r < 0)
2219 return r;
2220
2221 return bus_append_parse_sec_rename(m, "TimeoutStopSec", eq);
2222 }
2223
2224 if (streq(field, "FileDescriptorStoreMax"))
2225 return bus_append_safe_atou(m, field, eq);
2226
2227 if (STR_IN_SET(field, "ExecCondition",
2228 "ExecStartPre",
2229 "ExecStart",
2230 "ExecStartPost",
2231 "ExecConditionEx",
2232 "ExecStartPreEx",
2233 "ExecStartEx",
2234 "ExecStartPostEx",
2235 "ExecReload",
2236 "ExecStop",
2237 "ExecStopPost",
2238 "ExecReloadEx",
2239 "ExecStopEx",
2240 "ExecStopPostEx"))
2241 return bus_append_exec_command(m, field, eq);
2242
2243 if (STR_IN_SET(field, "RestartPreventExitStatus",
2244 "RestartForceExitStatus",
2245 "SuccessExitStatus")) {
2246 _cleanup_free_ int *status = NULL, *signal = NULL;
2247 size_t n_status = 0, n_signal = 0;
2248 const char *p;
2249
2250 for (p = eq;;) {
2251 _cleanup_free_ char *word = NULL;
2252
2253 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
2254 if (r == 0)
2255 break;
2256 if (r == -ENOMEM)
2257 return log_oom();
2258 if (r < 0)
2259 return log_error_errno(r, "Invalid syntax in %s: %s", field, eq);
2260
2261 /* We need to call exit_status_from_string() first, because we want
2262 * to parse numbers as exit statuses, not signals. */
2263
2264 r = exit_status_from_string(word);
2265 if (r >= 0) {
2266 assert(r >= 0 && r < 256);
2267
2268 status = reallocarray(status, n_status + 1, sizeof(int));
2269 if (!status)
2270 return log_oom();
2271
2272 status[n_status++] = r;
2273
2274 } else if ((r = signal_from_string(word)) >= 0) {
2275 signal = reallocarray(signal, n_signal + 1, sizeof(int));
2276 if (!signal)
2277 return log_oom();
2278
2279 signal[n_signal++] = r;
2280
2281 } else
2282 /* original r from exit_status_to_string() */
2283 return log_error_errno(r, "Invalid status or signal %s in %s: %m",
2284 word, field);
2285 }
2286
2287 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
2288 if (r < 0)
2289 return bus_log_create_error(r);
2290
2291 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
2292 if (r < 0)
2293 return bus_log_create_error(r);
2294
2295 r = sd_bus_message_open_container(m, 'v', "(aiai)");
2296 if (r < 0)
2297 return bus_log_create_error(r);
2298
2299 r = sd_bus_message_open_container(m, 'r', "aiai");
2300 if (r < 0)
2301 return bus_log_create_error(r);
2302
2303 r = sd_bus_message_append_array(m, 'i', status, n_status * sizeof(int));
2304 if (r < 0)
2305 return bus_log_create_error(r);
2306
2307 r = sd_bus_message_append_array(m, 'i', signal, n_signal * sizeof(int));
2308 if (r < 0)
2309 return bus_log_create_error(r);
2310
2311 r = sd_bus_message_close_container(m);
2312 if (r < 0)
2313 return bus_log_create_error(r);
2314
2315 r = sd_bus_message_close_container(m);
2316 if (r < 0)
2317 return bus_log_create_error(r);
2318
2319 r = sd_bus_message_close_container(m);
2320 if (r < 0)
2321 return bus_log_create_error(r);
2322
2323 return 1;
2324 }
2325
2326 if (streq(field, "OpenFile"))
2327 return bus_append_open_file(m, field, eq);
2328
2329 return 0;
2330 }
2331
2332 static int bus_append_socket_property(sd_bus_message *m, const char *field, const char *eq) {
2333 int r;
2334
2335 if (STR_IN_SET(field, "Accept",
2336 "FlushPending",
2337 "Writable",
2338 "KeepAlive",
2339 "NoDelay",
2340 "FreeBind",
2341 "Transparent",
2342 "Broadcast",
2343 "PassCredentials",
2344 "PassSecurity",
2345 "PassPacketInfo",
2346 "ReusePort",
2347 "RemoveOnStop",
2348 "SELinuxContextFromNet"))
2349 return bus_append_parse_boolean(m, field, eq);
2350
2351 if (STR_IN_SET(field, "Priority",
2352 "IPTTL",
2353 "Mark"))
2354 return bus_append_safe_atoi(m, field, eq);
2355
2356 if (streq(field, "IPTOS"))
2357 return bus_append_ip_tos_from_string(m, field, eq);
2358
2359 if (STR_IN_SET(field, "Backlog",
2360 "MaxConnections",
2361 "MaxConnectionsPerSource",
2362 "KeepAliveProbes",
2363 "TriggerLimitBurst"))
2364 return bus_append_safe_atou(m, field, eq);
2365
2366 if (STR_IN_SET(field, "SocketMode",
2367 "DirectoryMode"))
2368 return bus_append_parse_mode(m, field, eq);
2369
2370 if (STR_IN_SET(field, "MessageQueueMaxMessages",
2371 "MessageQueueMessageSize"))
2372 return bus_append_safe_atoi64(m, field, eq);
2373
2374 if (STR_IN_SET(field, "TimeoutSec",
2375 "KeepAliveTimeSec",
2376 "KeepAliveIntervalSec",
2377 "DeferAcceptSec",
2378 "TriggerLimitIntervalSec"))
2379 return bus_append_parse_sec_rename(m, field, eq);
2380
2381 if (STR_IN_SET(field, "ReceiveBuffer",
2382 "SendBuffer",
2383 "PipeSize"))
2384 return bus_append_parse_size(m, field, eq, 1024);
2385
2386 if (STR_IN_SET(field, "ExecStartPre",
2387 "ExecStartPost",
2388 "ExecReload",
2389 "ExecStopPost"))
2390 return bus_append_exec_command(m, field, eq);
2391
2392 if (STR_IN_SET(field, "SmackLabel",
2393 "SmackLabelIPIn",
2394 "SmackLabelIPOut",
2395 "TCPCongestion",
2396 "BindToDevice",
2397 "BindIPv6Only",
2398 "FileDescriptorName",
2399 "SocketUser",
2400 "SocketGroup",
2401 "Timestamping"))
2402 return bus_append_string(m, field, eq);
2403
2404 if (streq(field, "Symlinks"))
2405 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
2406
2407 if (streq(field, "SocketProtocol"))
2408 return bus_append_parse_ip_protocol(m, field, eq);
2409
2410 if (STR_IN_SET(field, "ListenStream",
2411 "ListenDatagram",
2412 "ListenSequentialPacket",
2413 "ListenNetlink",
2414 "ListenSpecial",
2415 "ListenMessageQueue",
2416 "ListenFIFO",
2417 "ListenUSBFunction")) {
2418 if (isempty(eq))
2419 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0);
2420 else
2421 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 1, field + STRLEN("Listen"), eq);
2422 if (r < 0)
2423 return bus_log_create_error(r);
2424
2425 return 1;
2426 }
2427
2428 return 0;
2429 }
2430 static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) {
2431 int r;
2432
2433 if (STR_IN_SET(field, "WakeSystem",
2434 "RemainAfterElapse",
2435 "Persistent",
2436 "OnTimezoneChange",
2437 "OnClockChange",
2438 "FixedRandomDelay"))
2439 return bus_append_parse_boolean(m, field, eq);
2440
2441 if (STR_IN_SET(field, "AccuracySec",
2442 "RandomizedDelaySec"))
2443 return bus_append_parse_sec_rename(m, field, eq);
2444
2445 if (STR_IN_SET(field, "OnActiveSec",
2446 "OnBootSec",
2447 "OnStartupSec",
2448 "OnUnitActiveSec",
2449 "OnUnitInactiveSec")) {
2450 if (isempty(eq))
2451 r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0);
2452 else {
2453 usec_t t;
2454 r = parse_sec(eq, &t);
2455 if (r < 0)
2456 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
2457
2458 r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 1, field, t);
2459 }
2460 if (r < 0)
2461 return bus_log_create_error(r);
2462
2463 return 1;
2464 }
2465
2466 if (streq(field, "OnCalendar")) {
2467 if (isempty(eq))
2468 r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0);
2469 else
2470 r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 1, field, eq);
2471 if (r < 0)
2472 return bus_log_create_error(r);
2473
2474 return 1;
2475 }
2476
2477 return 0;
2478 }
2479
2480 static int bus_append_unit_property(sd_bus_message *m, const char *field, const char *eq) {
2481 ConditionType t = _CONDITION_TYPE_INVALID;
2482 bool is_condition = false;
2483 int r;
2484
2485 if (STR_IN_SET(field, "Description",
2486 "SourcePath",
2487 "OnFailureJobMode",
2488 "JobTimeoutAction",
2489 "JobTimeoutRebootArgument",
2490 "StartLimitAction",
2491 "FailureAction",
2492 "SuccessAction",
2493 "RebootArgument",
2494 "CollectMode"))
2495 return bus_append_string(m, field, eq);
2496
2497 if (STR_IN_SET(field, "StopWhenUnneeded",
2498 "RefuseManualStart",
2499 "RefuseManualStop",
2500 "AllowIsolate",
2501 "IgnoreOnIsolate",
2502 "DefaultDependencies"))
2503 return bus_append_parse_boolean(m, field, eq);
2504
2505 if (STR_IN_SET(field, "JobTimeoutSec",
2506 "JobRunningTimeoutSec",
2507 "StartLimitIntervalSec"))
2508 return bus_append_parse_sec_rename(m, field, eq);
2509
2510 if (streq(field, "StartLimitBurst"))
2511 return bus_append_safe_atou(m, field, eq);
2512
2513 if (STR_IN_SET(field, "SuccessActionExitStatus",
2514 "FailureActionExitStatus")) {
2515 if (isempty(eq))
2516 r = sd_bus_message_append(m, "(sv)", field, "i", -1);
2517 else {
2518 uint8_t u;
2519
2520 r = safe_atou8(eq, &u);
2521 if (r < 0)
2522 return log_error_errno(r, "Failed to parse %s=%s", field, eq);
2523
2524 r = sd_bus_message_append(m, "(sv)", field, "i", (int) u);
2525 }
2526 if (r < 0)
2527 return bus_log_create_error(r);
2528
2529 return 1;
2530 }
2531
2532 if (unit_dependency_from_string(field) >= 0 ||
2533 STR_IN_SET(field, "Documentation",
2534 "RequiresMountsFor",
2535 "Markers"))
2536 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
2537
2538 t = condition_type_from_string(field);
2539 if (t >= 0)
2540 is_condition = true;
2541 else
2542 t = assert_type_from_string(field);
2543 if (t >= 0) {
2544 if (isempty(eq))
2545 r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 0);
2546 else {
2547 const char *p = eq;
2548 int trigger, negate;
2549
2550 trigger = *p == '|';
2551 if (trigger)
2552 p++;
2553
2554 negate = *p == '!';
2555 if (negate)
2556 p++;
2557
2558 r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 1,
2559 field, trigger, negate, p);
2560 }
2561 if (r < 0)
2562 return bus_log_create_error(r);
2563
2564 return 1;
2565 }
2566
2567 return 0;
2568 }
2569
2570 int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment) {
2571 const char *eq, *field;
2572 int r;
2573
2574 assert(m);
2575 assert(assignment);
2576
2577 eq = strchr(assignment, '=');
2578 if (!eq)
2579 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2580 "Not an assignment: %s", assignment);
2581
2582 field = strndupa_safe(assignment, eq - assignment);
2583 eq++;
2584
2585 switch (t) {
2586 case UNIT_SERVICE:
2587 r = bus_append_cgroup_property(m, field, eq);
2588 if (r != 0)
2589 return r;
2590
2591 r = bus_append_execute_property(m, field, eq);
2592 if (r != 0)
2593 return r;
2594
2595 r = bus_append_kill_property(m, field, eq);
2596 if (r != 0)
2597 return r;
2598
2599 r = bus_append_service_property(m, field, eq);
2600 if (r != 0)
2601 return r;
2602 break;
2603
2604 case UNIT_SOCKET:
2605 r = bus_append_cgroup_property(m, field, eq);
2606 if (r != 0)
2607 return r;
2608
2609 r = bus_append_execute_property(m, field, eq);
2610 if (r != 0)
2611 return r;
2612
2613 r = bus_append_kill_property(m, field, eq);
2614 if (r != 0)
2615 return r;
2616
2617 r = bus_append_socket_property(m, field, eq);
2618 if (r != 0)
2619 return r;
2620 break;
2621
2622 case UNIT_TIMER:
2623 r = bus_append_timer_property(m, field, eq);
2624 if (r != 0)
2625 return r;
2626 break;
2627
2628 case UNIT_PATH:
2629 r = bus_append_path_property(m, field, eq);
2630 if (r != 0)
2631 return r;
2632 break;
2633
2634 case UNIT_SLICE:
2635 r = bus_append_cgroup_property(m, field, eq);
2636 if (r != 0)
2637 return r;
2638 break;
2639
2640 case UNIT_SCOPE:
2641 r = bus_append_cgroup_property(m, field, eq);
2642 if (r != 0)
2643 return r;
2644
2645 r = bus_append_kill_property(m, field, eq);
2646 if (r != 0)
2647 return r;
2648
2649 r = bus_append_scope_property(m, field, eq);
2650 if (r != 0)
2651 return r;
2652 break;
2653
2654 case UNIT_MOUNT:
2655 r = bus_append_cgroup_property(m, field, eq);
2656 if (r != 0)
2657 return r;
2658
2659 r = bus_append_execute_property(m, field, eq);
2660 if (r != 0)
2661 return r;
2662
2663 r = bus_append_kill_property(m, field, eq);
2664 if (r != 0)
2665 return r;
2666
2667 r = bus_append_mount_property(m, field, eq);
2668 if (r != 0)
2669 return r;
2670
2671 break;
2672
2673 case UNIT_AUTOMOUNT:
2674 r = bus_append_automount_property(m, field, eq);
2675 if (r != 0)
2676 return r;
2677
2678 break;
2679
2680 case UNIT_TARGET:
2681 case UNIT_DEVICE:
2682 case UNIT_SWAP:
2683 break;
2684
2685 default:
2686 assert_not_reached();
2687 }
2688
2689 r = bus_append_unit_property(m, field, eq);
2690 if (r != 0)
2691 return r;
2692
2693 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2694 "Unknown assignment: %s", assignment);
2695 }
2696
2697 int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) {
2698 int r;
2699
2700 assert(m);
2701
2702 STRV_FOREACH(i, l) {
2703 r = bus_append_unit_property_assignment(m, t, *i);
2704 if (r < 0)
2705 return r;
2706 }
2707
2708 return 0;
2709 }
2710
2711 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, InstallChange **changes, size_t *n_changes) {
2712 const char *type, *path, *source;
2713 int r;
2714
2715 /* changes is dereferenced when calling install_changes_dump() later,
2716 * so we have to make sure this is not NULL. */
2717 assert(changes);
2718 assert(n_changes);
2719
2720 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
2721 if (r < 0)
2722 return bus_log_parse_error(r);
2723
2724 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
2725 InstallChangeType t;
2726
2727 /* We expect only "success" changes to be sent over the bus. Hence, reject anything
2728 * negative. */
2729 t = install_change_type_from_string(type);
2730 if (t < 0) {
2731 log_notice_errno(t, "Manager reported unknown change type \"%s\" for path \"%s\", ignoring.",
2732 type, path);
2733 continue;
2734 }
2735
2736 r = install_changes_add(changes, n_changes, t, path, source);
2737 if (r < 0)
2738 return r;
2739 }
2740 if (r < 0)
2741 return bus_log_parse_error(r);
2742
2743 r = sd_bus_message_exit_container(m);
2744 if (r < 0)
2745 return bus_log_parse_error(r);
2746
2747 install_changes_dump(0, NULL, *changes, *n_changes, quiet);
2748 return 0;
2749 }
2750
2751 int unit_load_state(sd_bus *bus, const char *name, char **load_state) {
2752 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2753 _cleanup_free_ char *path = NULL;
2754 int r;
2755
2756 path = unit_dbus_path_from_name(name);
2757 if (!path)
2758 return log_oom();
2759
2760 /* This function warns on it's own, because otherwise it'd be awkward to pass
2761 * the dbus error message around. */
2762
2763 r = sd_bus_get_property_string(
2764 bus,
2765 "org.freedesktop.systemd1",
2766 path,
2767 "org.freedesktop.systemd1.Unit",
2768 "LoadState",
2769 &error,
2770 load_state);
2771 if (r < 0)
2772 return log_error_errno(r, "Failed to get load state of %s: %s", name, bus_error_message(&error, r));
2773
2774 return 0;
2775 }
2776
2777 int unit_info_compare(const UnitInfo *a, const UnitInfo *b) {
2778 int r;
2779
2780 /* First, order by machine */
2781 r = strcasecmp_ptr(a->machine, b->machine);
2782 if (r != 0)
2783 return r;
2784
2785 /* Second, order by unit type */
2786 r = strcasecmp_ptr(strrchr(a->id, '.'), strrchr(b->id, '.'));
2787 if (r != 0)
2788 return r;
2789
2790 /* Third, order by name */
2791 return strcasecmp(a->id, b->id);
2792 }