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