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