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