]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bus-unit-util.c
tree-wise: several cleanups for logging
[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);
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 "MemoryZSwapWriteback",
566 "IOAccounting",
567 "BlockIOAccounting",
568 "TasksAccounting",
569 "IPAccounting",
570 "CoredumpReceive"))
571 return bus_append_parse_boolean(m, field, eq);
572
573 if (STR_IN_SET(field, "CPUWeight",
574 "StartupCPUWeight"))
575 return bus_append_cg_cpu_weight_parse(m, field, eq);
576
577 if (STR_IN_SET(field, "IOWeight",
578 "StartupIOWeight"))
579 return bus_append_cg_weight_parse(m, field, eq);
580
581 if (STR_IN_SET(field, "CPUShares",
582 "StartupCPUShares"))
583 return bus_append_cg_cpu_shares_parse(m, field, eq);
584
585 if (STR_IN_SET(field, "AllowedCPUs",
586 "StartupAllowedCPUs",
587 "AllowedMemoryNodes",
588 "StartupAllowedMemoryNodes")) {
589 _cleanup_(cpu_set_reset) CPUSet cpuset = {};
590 _cleanup_free_ uint8_t *array = NULL;
591 size_t allocated;
592
593 r = parse_cpu_set(eq, &cpuset);
594 if (r < 0)
595 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
596
597 r = cpu_set_to_dbus(&cpuset, &array, &allocated);
598 if (r < 0)
599 return log_error_errno(r, "Failed to serialize CPUSet: %m");
600
601 return bus_append_byte_array(m, field, array, allocated);
602 }
603
604 if (STR_IN_SET(field, "BlockIOWeight",
605 "StartupBlockIOWeight"))
606 return bus_append_cg_blkio_weight_parse(m, field, eq);
607
608 if (streq(field, "DisableControllers"))
609 return bus_append_strv(m, "DisableControllers", eq, EXTRACT_UNQUOTE);
610
611 if (streq(field, "Delegate")) {
612 r = parse_boolean(eq);
613 if (r < 0)
614 return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_UNQUOTE);
615
616 r = sd_bus_message_append(m, "(sv)", "Delegate", "b", r);
617 if (r < 0)
618 return bus_log_create_error(r);
619
620 return 1;
621 }
622
623 if (STR_IN_SET(field, "MemoryMin",
624 "DefaultMemoryLow",
625 "DefaultMemoryMin",
626 "MemoryLow",
627 "MemoryHigh",
628 "MemoryMax",
629 "MemorySwapMax",
630 "MemoryZSwapMax",
631 "MemoryLimit",
632 "TasksMax")) {
633
634 if (streq(eq, "infinity")) {
635 r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX);
636 if (r < 0)
637 return bus_log_create_error(r);
638 return 1;
639 } else if (isempty(eq)) {
640 uint64_t empty_value = STR_IN_SET(field,
641 "DefaultMemoryLow",
642 "DefaultMemoryMin",
643 "MemoryLow",
644 "MemoryMin") ?
645 CGROUP_LIMIT_MIN :
646 CGROUP_LIMIT_MAX;
647
648 r = sd_bus_message_append(m, "(sv)", field, "t", empty_value);
649 if (r < 0)
650 return bus_log_create_error(r);
651 return 1;
652 }
653
654 r = parse_permyriad(eq);
655 if (r >= 0) {
656 char *n;
657
658 /* When this is a percentage we'll convert this into a relative value in the range 0…UINT32_MAX
659 * and pass it in the MemoryLowScale property (and related ones). This way the physical memory
660 * size can be determined server-side. */
661
662 n = strjoina(field, "Scale");
663 r = sd_bus_message_append(m, "(sv)", n, "u", UINT32_SCALE_FROM_PERMYRIAD(r));
664 if (r < 0)
665 return bus_log_create_error(r);
666
667 return 1;
668 }
669
670 if (streq(field, "TasksMax"))
671 return bus_append_safe_atou64(m, field, eq);
672
673 return bus_append_parse_size(m, field, eq, 1024);
674 }
675
676 if (streq(field, "CPUQuota")) {
677 if (isempty(eq))
678 r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
679 else {
680 r = parse_permyriad_unbounded(eq);
681 if (r == 0)
682 return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "CPU quota too small.");
683 if (r < 0)
684 return log_error_errno(r, "CPU quota '%s' invalid.", eq);
685
686 r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (((uint64_t) r * USEC_PER_SEC) / 10000U));
687 }
688
689 if (r < 0)
690 return bus_log_create_error(r);
691
692 return 1;
693 }
694
695 if (streq(field, "CPUQuotaPeriodSec")) {
696 usec_t u = USEC_INFINITY;
697
698 r = parse_sec_def_infinity(eq, &u);
699 if (r < 0)
700 return log_error_errno(r, "CPU quota period '%s' invalid.", eq);
701
702 r = sd_bus_message_append(m, "(sv)", "CPUQuotaPeriodUSec", "t", u);
703 if (r < 0)
704 return bus_log_create_error(r);
705
706 return 1;
707 }
708
709 if (streq(field, "DeviceAllow")) {
710 if (isempty(eq))
711 r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
712 else {
713 const char *path = eq, *rwm = NULL, *e;
714
715 e = strchr(eq, ' ');
716 if (e) {
717 path = strndupa_safe(eq, e - eq);
718 rwm = e+1;
719 }
720
721 r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, path, strempty(rwm));
722 }
723
724 if (r < 0)
725 return bus_log_create_error(r);
726
727 return 1;
728 }
729
730 if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
731 if (isempty(eq))
732 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
733 else {
734 const char *path, *bandwidth, *e;
735 uint64_t bytes;
736
737 e = strchr(eq, ' ');
738 if (!e)
739 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
740 "Failed to parse %s value %s.",
741 field, eq);
742
743 path = strndupa_safe(eq, e - eq);
744 bandwidth = e+1;
745
746 if (streq(bandwidth, "infinity"))
747 bytes = CGROUP_LIMIT_MAX;
748 else {
749 r = parse_size(bandwidth, 1000, &bytes);
750 if (r < 0)
751 return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth);
752 }
753
754 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, bytes);
755 }
756
757 if (r < 0)
758 return bus_log_create_error(r);
759
760 return 1;
761 }
762
763 if (STR_IN_SET(field, "IODeviceWeight",
764 "BlockIODeviceWeight")) {
765 if (isempty(eq))
766 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
767 else {
768 const char *path, *weight, *e;
769 uint64_t u;
770
771 e = strchr(eq, ' ');
772 if (!e)
773 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
774 "Failed to parse %s value %s.",
775 field, eq);
776
777 path = strndupa_safe(eq, e - eq);
778 weight = e+1;
779
780 r = safe_atou64(weight, &u);
781 if (r < 0)
782 return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight);
783
784 r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, u);
785 }
786
787 if (r < 0)
788 return bus_log_create_error(r);
789
790 return 1;
791 }
792
793 if (streq(field, "IODeviceLatencyTargetSec")) {
794 const char *field_usec = "IODeviceLatencyTargetUSec";
795
796 if (isempty(eq))
797 r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", USEC_INFINITY);
798 else {
799 const char *path, *target, *e;
800 usec_t usec;
801
802 e = strchr(eq, ' ');
803 if (!e)
804 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
805 "Failed to parse %s value %s.",
806 field, eq);
807
808 path = strndupa_safe(eq, e - eq);
809 target = e+1;
810
811 r = parse_sec(target, &usec);
812 if (r < 0)
813 return log_error_errno(r, "Failed to parse %s value %s: %m", field, target);
814
815 r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", 1, path, usec);
816 }
817
818 if (r < 0)
819 return bus_log_create_error(r);
820
821 return 1;
822 }
823
824 if (STR_IN_SET(field, "IPAddressAllow",
825 "IPAddressDeny")) {
826 unsigned char prefixlen;
827 union in_addr_union prefix = {};
828 int family;
829
830 if (isempty(eq)) {
831 r = sd_bus_message_append(m, "(sv)", field, "a(iayu)", 0);
832 if (r < 0)
833 return bus_log_create_error(r);
834
835 return 1;
836 }
837
838 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
839 if (r < 0)
840 return bus_log_create_error(r);
841
842 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
843 if (r < 0)
844 return bus_log_create_error(r);
845
846 r = sd_bus_message_open_container(m, 'v', "a(iayu)");
847 if (r < 0)
848 return bus_log_create_error(r);
849
850 r = sd_bus_message_open_container(m, 'a', "(iayu)");
851 if (r < 0)
852 return bus_log_create_error(r);
853
854 if (streq(eq, "any")) {
855 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
856
857 r = bus_append_ip_address_access(m, AF_INET, &prefix, 0);
858 if (r < 0)
859 return bus_log_create_error(r);
860
861 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0);
862 if (r < 0)
863 return bus_log_create_error(r);
864
865 } else if (is_localhost(eq)) {
866 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
867
868 prefix.in.s_addr = htobe32(0x7f000000);
869 r = bus_append_ip_address_access(m, AF_INET, &prefix, 8);
870 if (r < 0)
871 return bus_log_create_error(r);
872
873 prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
874 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128);
875 if (r < 0)
876 return r;
877
878 } else if (streq(eq, "link-local")) {
879 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
880
881 prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
882 r = bus_append_ip_address_access(m, AF_INET, &prefix, 16);
883 if (r < 0)
884 return bus_log_create_error(r);
885
886 prefix.in6 = (struct in6_addr) {
887 .s6_addr32[0] = htobe32(0xfe800000)
888 };
889 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64);
890 if (r < 0)
891 return bus_log_create_error(r);
892
893 } else if (streq(eq, "multicast")) {
894 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
895
896 prefix.in.s_addr = htobe32((UINT32_C(224) << 24));
897 r = bus_append_ip_address_access(m, AF_INET, &prefix, 4);
898 if (r < 0)
899 return bus_log_create_error(r);
900
901 prefix.in6 = (struct in6_addr) {
902 .s6_addr32[0] = htobe32(0xff000000)
903 };
904 r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8);
905 if (r < 0)
906 return bus_log_create_error(r);
907
908 } else {
909 for (;;) {
910 _cleanup_free_ char *word = NULL;
911
912 r = extract_first_word(&eq, &word, NULL, 0);
913 if (r == 0)
914 break;
915 if (r == -ENOMEM)
916 return log_oom();
917 if (r < 0)
918 return log_error_errno(r, "Failed to parse %s: %s", field, eq);
919
920 r = in_addr_prefix_from_string_auto(word, &family, &prefix, &prefixlen);
921 if (r < 0)
922 return log_error_errno(r, "Failed to parse IP address prefix: %s", word);
923
924 r = bus_append_ip_address_access(m, family, &prefix, prefixlen);
925 if (r < 0)
926 return bus_log_create_error(r);
927 }
928 }
929
930 r = sd_bus_message_close_container(m);
931 if (r < 0)
932 return bus_log_create_error(r);
933
934 r = sd_bus_message_close_container(m);
935 if (r < 0)
936 return bus_log_create_error(r);
937
938 r = sd_bus_message_close_container(m);
939 if (r < 0)
940 return bus_log_create_error(r);
941
942 return 1;
943 }
944
945 if (STR_IN_SET(field, "IPIngressFilterPath",
946 "IPEgressFilterPath")) {
947 if (isempty(eq))
948 r = sd_bus_message_append(m, "(sv)", field, "as", 0);
949 else
950 r = sd_bus_message_append(m, "(sv)", field, "as", 1, eq);
951
952 if (r < 0)
953 return bus_log_create_error(r);
954
955 return 1;
956 }
957
958 if (streq(field, "BPFProgram")) {
959 if (isempty(eq))
960 r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
961 else {
962 _cleanup_free_ char *word = NULL;
963
964 r = extract_first_word(&eq, &word, ":", 0);
965 if (r == -ENOMEM)
966 return log_oom();
967 if (r < 0)
968 return log_error_errno(r, "Failed to parse %s: %m", field);
969
970 r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, word, eq);
971 }
972 if (r < 0)
973 return bus_log_create_error(r);
974
975 return 1;
976 }
977
978 if (STR_IN_SET(field, "SocketBindAllow",
979 "SocketBindDeny")) {
980 if (isempty(eq))
981 r = sd_bus_message_append(m, "(sv)", field, "a(iiqq)", 0);
982 else {
983 int32_t family, ip_protocol;
984 uint16_t nr_ports, port_min;
985
986 r = parse_socket_bind_item(eq, &family, &ip_protocol, &nr_ports, &port_min);
987 if (r == -ENOMEM)
988 return log_oom();
989 if (r < 0)
990 return log_error_errno(r, "Failed to parse %s", field);
991
992 r = sd_bus_message_append(
993 m, "(sv)", field, "a(iiqq)", 1, family, ip_protocol, nr_ports, port_min);
994 }
995 if (r < 0)
996 return bus_log_create_error(r);
997
998 return 1;
999 }
1000
1001 if (streq(field, "MemoryPressureThresholdSec"))
1002 return bus_append_parse_sec_rename(m, field, eq);
1003
1004 if (streq(field, "NFTSet"))
1005 return bus_append_nft_set(m, field, eq);
1006
1007 return 0;
1008 }
1009
1010 static int bus_append_automount_property(sd_bus_message *m, const char *field, const char *eq) {
1011 if (STR_IN_SET(field, "Where",
1012 "ExtraOptions"))
1013 return bus_append_string(m, field, eq);
1014
1015 if (streq(field, "DirectoryMode"))
1016 return bus_append_parse_mode(m, field, eq);
1017
1018 if (streq(field, "TimeoutIdleSec"))
1019 return bus_append_parse_sec_rename(m, field, eq);
1020
1021 return 0;
1022 }
1023
1024 static int bus_append_execute_property(sd_bus_message *m, const char *field, const char *eq) {
1025 const char *suffix;
1026 int r;
1027
1028 if (STR_IN_SET(field, "User",
1029 "Group",
1030 "UtmpIdentifier",
1031 "UtmpMode",
1032 "PAMName",
1033 "TTYPath",
1034 "WorkingDirectory",
1035 "RootDirectory",
1036 "SyslogIdentifier",
1037 "ProtectSystem",
1038 "ProtectHome",
1039 "SELinuxContext",
1040 "RootImage",
1041 "RootVerity",
1042 "RuntimeDirectoryPreserve",
1043 "Personality",
1044 "KeyringMode",
1045 "ProtectProc",
1046 "ProcSubset",
1047 "NetworkNamespacePath",
1048 "IPCNamespacePath",
1049 "LogNamespace",
1050 "RootImagePolicy",
1051 "MountImagePolicy",
1052 "ExtensionImagePolicy"))
1053 return bus_append_string(m, field, eq);
1054
1055 if (STR_IN_SET(field, "IgnoreSIGPIPE",
1056 "TTYVHangup",
1057 "TTYReset",
1058 "TTYVTDisallocate",
1059 "PrivateTmp",
1060 "PrivateDevices",
1061 "PrivateNetwork",
1062 "PrivateUsers",
1063 "PrivateMounts",
1064 "PrivateIPC",
1065 "NoNewPrivileges",
1066 "SyslogLevelPrefix",
1067 "MemoryDenyWriteExecute",
1068 "RestrictRealtime",
1069 "DynamicUser",
1070 "RemoveIPC",
1071 "ProtectKernelTunables",
1072 "ProtectKernelModules",
1073 "ProtectKernelLogs",
1074 "ProtectClock",
1075 "ProtectControlGroups",
1076 "MountAPIVFS",
1077 "CPUSchedulingResetOnFork",
1078 "LockPersonality",
1079 "ProtectHostname",
1080 "MemoryKSM",
1081 "RestrictSUIDSGID",
1082 "RootEphemeral",
1083 "SetLoginEnvironment"))
1084 return bus_append_parse_boolean(m, field, eq);
1085
1086 if (STR_IN_SET(field, "ReadWriteDirectories",
1087 "ReadOnlyDirectories",
1088 "InaccessibleDirectories",
1089 "ReadWritePaths",
1090 "ReadOnlyPaths",
1091 "InaccessiblePaths",
1092 "ExecPaths",
1093 "NoExecPaths",
1094 "ExecSearchPath",
1095 "ExtensionDirectories",
1096 "ConfigurationDirectory",
1097 "SupplementaryGroups",
1098 "SystemCallArchitectures"))
1099 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
1100
1101 if (STR_IN_SET(field, "SyslogLevel",
1102 "LogLevelMax"))
1103 return bus_append_log_level_from_string(m, field, eq);
1104
1105 if (streq(field, "SyslogFacility"))
1106 return bus_append_log_facility_unshifted_from_string(m, field, eq);
1107
1108 if (streq(field, "SecureBits"))
1109 return bus_append_secure_bits_from_string(m, field, eq);
1110
1111 if (streq(field, "CPUSchedulingPolicy"))
1112 return bus_append_sched_policy_from_string(m, field, eq);
1113
1114 if (STR_IN_SET(field, "CPUSchedulingPriority",
1115 "OOMScoreAdjust"))
1116 return bus_append_safe_atoi(m, field, eq);
1117
1118 if (streq(field, "CoredumpFilter"))
1119 return bus_append_coredump_filter_mask_from_string(m, field, eq);
1120
1121 if (streq(field, "Nice"))
1122 return bus_append_parse_nice(m, field, eq);
1123
1124 if (streq(field, "SystemCallErrorNumber"))
1125 return bus_append_seccomp_parse_errno_or_action(m, field, eq);
1126
1127 if (streq(field, "IOSchedulingClass"))
1128 return bus_append_ioprio_class_from_string(m, field, eq);
1129
1130 if (streq(field, "IOSchedulingPriority"))
1131 return bus_append_ioprio_parse_priority(m, field, eq);
1132
1133 if (STR_IN_SET(field, "RuntimeDirectoryMode",
1134 "StateDirectoryMode",
1135 "CacheDirectoryMode",
1136 "LogsDirectoryMode",
1137 "ConfigurationDirectoryMode",
1138 "UMask"))
1139 return bus_append_parse_mode(m, field, eq);
1140
1141 if (streq(field, "TimerSlackNSec"))
1142 return bus_append_parse_nsec(m, field, eq);
1143
1144 if (streq(field, "LogRateLimitIntervalSec"))
1145 return bus_append_parse_sec_rename(m, field, eq);
1146
1147 if (STR_IN_SET(field, "LogRateLimitBurst",
1148 "TTYRows",
1149 "TTYColumns"))
1150 return bus_append_safe_atou(m, field, eq);
1151
1152 if (streq(field, "MountFlags"))
1153 return bus_append_mount_propagation_flag_from_string(m, field, eq);
1154
1155 if (STR_IN_SET(field, "Environment",
1156 "UnsetEnvironment",
1157 "PassEnvironment"))
1158 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
1159
1160 if (streq(field, "EnvironmentFile")) {
1161 if (isempty(eq))
1162 r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0);
1163 else
1164 r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 1,
1165 eq[0] == '-' ? eq + 1 : eq,
1166 eq[0] == '-');
1167 if (r < 0)
1168 return bus_log_create_error(r);
1169
1170 return 1;
1171 }
1172
1173 if (STR_IN_SET(field, "SetCredential", "SetCredentialEncrypted")) {
1174 r = sd_bus_message_open_container(m, 'r', "sv");
1175 if (r < 0)
1176 return bus_log_create_error(r);
1177
1178 r = sd_bus_message_append_basic(m, 's', field);
1179 if (r < 0)
1180 return bus_log_create_error(r);
1181
1182 r = sd_bus_message_open_container(m, 'v', "a(say)");
1183 if (r < 0)
1184 return bus_log_create_error(r);
1185
1186 if (isempty(eq))
1187 r = sd_bus_message_append(m, "a(say)", 0);
1188 else {
1189 _cleanup_free_ char *word = NULL;
1190 const char *p = eq;
1191
1192 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1193 if (r == -ENOMEM)
1194 return log_oom();
1195 if (r < 0)
1196 return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
1197 if (r == 0 || !p)
1198 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to %s=.", field);
1199
1200 r = sd_bus_message_open_container(m, 'a', "(say)");
1201 if (r < 0)
1202 return bus_log_create_error(r);
1203
1204 r = sd_bus_message_open_container(m, 'r', "say");
1205 if (r < 0)
1206 return bus_log_create_error(r);
1207
1208 r = sd_bus_message_append(m, "s", word);
1209 if (r < 0)
1210 return bus_log_create_error(r);
1211
1212 if (streq(field, "SetCredentialEncrypted")) {
1213 _cleanup_free_ void *decoded = NULL;
1214 size_t decoded_size;
1215
1216 r = unbase64mem(p, &decoded, &decoded_size);
1217 if (r < 0)
1218 return log_error_errno(r, "Failed to base64 decode encrypted credential: %m");
1219
1220 r = sd_bus_message_append_array(m, 'y', decoded, decoded_size);
1221 } else {
1222 _cleanup_free_ char *unescaped = NULL;
1223 ssize_t l;
1224
1225 l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped);
1226 if (l < 0)
1227 return log_error_errno(l, "Failed to unescape %s= value: %s", field, p);
1228
1229 r = sd_bus_message_append_array(m, 'y', unescaped, l);
1230 }
1231 if (r < 0)
1232 return bus_log_create_error(r);
1233
1234 r = sd_bus_message_close_container(m);
1235 if (r < 0)
1236 return bus_log_create_error(r);
1237
1238 r = sd_bus_message_close_container(m);
1239 }
1240 if (r < 0)
1241 return bus_log_create_error(r);
1242
1243 r = sd_bus_message_close_container(m);
1244 if (r < 0)
1245 return bus_log_create_error(r);
1246
1247 r = sd_bus_message_close_container(m);
1248 if (r < 0)
1249 return bus_log_create_error(r);
1250
1251 return 1;
1252 }
1253
1254 if (STR_IN_SET(field, "LoadCredential", "LoadCredentialEncrypted")) {
1255 r = sd_bus_message_open_container(m, 'r', "sv");
1256 if (r < 0)
1257 return bus_log_create_error(r);
1258
1259 r = sd_bus_message_append_basic(m, 's', field);
1260 if (r < 0)
1261 return bus_log_create_error(r);
1262
1263 r = sd_bus_message_open_container(m, 'v', "a(ss)");
1264 if (r < 0)
1265 return bus_log_create_error(r);
1266
1267 if (isempty(eq))
1268 r = sd_bus_message_append(m, "a(ss)", 0);
1269 else {
1270 _cleanup_free_ char *word = NULL;
1271 const char *p = eq;
1272
1273 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1274 if (r == -ENOMEM)
1275 return log_oom();
1276 if (r < 0)
1277 return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
1278 if (r == 0)
1279 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to %s=.", field);
1280
1281 if (isempty(p)) /* If only one field is specified, then this means "inherit from above" */
1282 p = eq;
1283
1284 r = sd_bus_message_append(m, "a(ss)", 1, word, p);
1285 }
1286 if (r < 0)
1287 return bus_log_create_error(r);
1288
1289 r = sd_bus_message_close_container(m);
1290 if (r < 0)
1291 return bus_log_create_error(r);
1292
1293 r = sd_bus_message_close_container(m);
1294 if (r < 0)
1295 return bus_log_create_error(r);
1296
1297 return 1;
1298 }
1299
1300 if (streq(field, "ImportCredential")) {
1301 if (isempty(eq))
1302 r = sd_bus_message_append(m, "(sv)", field, "as", 0);
1303 else
1304 r = sd_bus_message_append(m, "(sv)", field, "as", 1, eq);
1305 if (r < 0)
1306 return bus_log_create_error(r);
1307
1308 return 1;
1309 }
1310
1311 if (streq(field, "LogExtraFields")) {
1312 r = sd_bus_message_open_container(m, 'r', "sv");
1313 if (r < 0)
1314 return bus_log_create_error(r);
1315
1316 r = sd_bus_message_append_basic(m, 's', "LogExtraFields");
1317 if (r < 0)
1318 return bus_log_create_error(r);
1319
1320 r = sd_bus_message_open_container(m, 'v', "aay");
1321 if (r < 0)
1322 return bus_log_create_error(r);
1323
1324 r = sd_bus_message_open_container(m, 'a', "ay");
1325 if (r < 0)
1326 return bus_log_create_error(r);
1327
1328 r = sd_bus_message_append_array(m, 'y', eq, strlen(eq));
1329 if (r < 0)
1330 return bus_log_create_error(r);
1331
1332 r = sd_bus_message_close_container(m);
1333 if (r < 0)
1334 return bus_log_create_error(r);
1335
1336 r = sd_bus_message_close_container(m);
1337 if (r < 0)
1338 return bus_log_create_error(r);
1339
1340 r = sd_bus_message_close_container(m);
1341 if (r < 0)
1342 return bus_log_create_error(r);
1343
1344 return 1;
1345 }
1346
1347 if (streq(field, "LogFilterPatterns")) {
1348 r = sd_bus_message_append(m, "(sv)", "LogFilterPatterns", "a(bs)", 1,
1349 eq[0] != '~',
1350 eq[0] != '~' ? eq : eq + 1);
1351 if (r < 0)
1352 return bus_log_create_error(r);
1353
1354 return 1;
1355 }
1356
1357 if (STR_IN_SET(field, "StandardInput",
1358 "StandardOutput",
1359 "StandardError")) {
1360 const char *n, *appended;
1361
1362 if ((n = startswith(eq, "fd:"))) {
1363 appended = strjoina(field, "FileDescriptorName");
1364 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
1365 } else if ((n = startswith(eq, "file:"))) {
1366 appended = strjoina(field, "File");
1367 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
1368 } else if ((n = startswith(eq, "append:"))) {
1369 appended = strjoina(field, "FileToAppend");
1370 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
1371 } else if ((n = startswith(eq, "truncate:"))) {
1372 appended = strjoina(field, "FileToTruncate");
1373 r = sd_bus_message_append(m, "(sv)", appended, "s", n);
1374 } else
1375 r = sd_bus_message_append(m, "(sv)", field, "s", eq);
1376 if (r < 0)
1377 return bus_log_create_error(r);
1378
1379 return 1;
1380 }
1381
1382 if (streq(field, "StandardInputText")) {
1383 _cleanup_free_ char *unescaped = NULL;
1384 ssize_t l;
1385
1386 l = cunescape(eq, 0, &unescaped);
1387 if (l < 0)
1388 return log_error_errno(l, "Failed to unescape text '%s': %m", eq);
1389
1390 if (!strextend(&unescaped, "\n"))
1391 return log_oom();
1392
1393 /* Note that we don't expand specifiers here, but that should be OK, as this is a
1394 * programmatic interface anyway */
1395
1396 return bus_append_byte_array(m, field, unescaped, l + 1);
1397 }
1398
1399 if (streq(field, "StandardInputData")) {
1400 _cleanup_free_ void *decoded = NULL;
1401 size_t sz;
1402
1403 r = unbase64mem(eq, &decoded, &sz);
1404 if (r < 0)
1405 return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq);
1406
1407 return bus_append_byte_array(m, field, decoded, sz);
1408 }
1409
1410 if ((suffix = startswith(field, "Limit"))) {
1411 int rl;
1412
1413 rl = rlimit_from_string(suffix);
1414 if (rl >= 0) {
1415 const char *sn;
1416 struct rlimit l;
1417
1418 r = rlimit_parse(rl, eq, &l);
1419 if (r < 0)
1420 return log_error_errno(r, "Failed to parse resource limit: %s", eq);
1421
1422 r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) l.rlim_max);
1423 if (r < 0)
1424 return bus_log_create_error(r);
1425
1426 sn = strjoina(field, "Soft");
1427 r = sd_bus_message_append(m, "(sv)", sn, "t", (uint64_t) l.rlim_cur);
1428 if (r < 0)
1429 return bus_log_create_error(r);
1430
1431 return 1;
1432 }
1433 }
1434
1435 if (STR_IN_SET(field, "AppArmorProfile",
1436 "SmackProcessLabel")) {
1437 int ignore = 0;
1438 const char *s = eq;
1439
1440 if (eq[0] == '-') {
1441 ignore = 1;
1442 s = eq + 1;
1443 }
1444
1445 r = sd_bus_message_append(m, "(sv)", field, "(bs)", ignore, s);
1446 if (r < 0)
1447 return bus_log_create_error(r);
1448
1449 return 1;
1450 }
1451
1452 if (STR_IN_SET(field, "CapabilityBoundingSet",
1453 "AmbientCapabilities")) {
1454 uint64_t sum = 0;
1455 bool invert = false;
1456 const char *p = eq;
1457
1458 if (*p == '~') {
1459 invert = true;
1460 p++;
1461 }
1462
1463 r = capability_set_from_string(p, &sum);
1464 if (r < 0)
1465 return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
1466
1467 sum = invert ? ~sum : sum;
1468
1469 r = sd_bus_message_append(m, "(sv)", field, "t", sum);
1470 if (r < 0)
1471 return bus_log_create_error(r);
1472
1473 return 1;
1474 }
1475
1476 if (streq(field, "CPUAffinity")) {
1477 _cleanup_(cpu_set_reset) CPUSet cpuset = {};
1478 _cleanup_free_ uint8_t *array = NULL;
1479 size_t allocated;
1480
1481 if (eq && streq(eq, "numa")) {
1482 r = sd_bus_message_append(m, "(sv)", "CPUAffinityFromNUMA", "b", true);
1483 if (r < 0)
1484 return bus_log_create_error(r);
1485 return r;
1486 }
1487
1488 r = parse_cpu_set(eq, &cpuset);
1489 if (r < 0)
1490 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
1491
1492 r = cpu_set_to_dbus(&cpuset, &array, &allocated);
1493 if (r < 0)
1494 return log_error_errno(r, "Failed to serialize CPUAffinity: %m");
1495
1496 return bus_append_byte_array(m, field, array, allocated);
1497 }
1498
1499 if (streq(field, "NUMAPolicy")) {
1500 r = mpol_from_string(eq);
1501 if (r < 0)
1502 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
1503
1504 r = sd_bus_message_append(m, "(sv)", field, "i", (int32_t) r);
1505 if (r < 0)
1506 return bus_log_create_error(r);
1507
1508 return 1;
1509 }
1510
1511 if (streq(field, "NUMAMask")) {
1512 _cleanup_(cpu_set_reset) CPUSet nodes = {};
1513 _cleanup_free_ uint8_t *array = NULL;
1514 size_t allocated;
1515
1516 if (eq && streq(eq, "all")) {
1517 r = numa_mask_add_all(&nodes);
1518 if (r < 0)
1519 return log_error_errno(r, "Failed to create NUMA mask representing \"all\" NUMA nodes: %m");
1520 } else {
1521 r = parse_cpu_set(eq, &nodes);
1522 if (r < 0)
1523 return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
1524 }
1525
1526 r = cpu_set_to_dbus(&nodes, &array, &allocated);
1527 if (r < 0)
1528 return log_error_errno(r, "Failed to serialize NUMAMask: %m");
1529
1530 return bus_append_byte_array(m, field, array, allocated);
1531 }
1532
1533 if (STR_IN_SET(field, "RestrictAddressFamilies",
1534 "RestrictFileSystems",
1535 "SystemCallFilter",
1536 "SystemCallLog",
1537 "RestrictNetworkInterfaces")) {
1538 int allow_list = 1;
1539 const char *p = eq;
1540
1541 if (*p == '~') {
1542 allow_list = 0;
1543 p++;
1544 }
1545
1546 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1547 if (r < 0)
1548 return bus_log_create_error(r);
1549
1550 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1551 if (r < 0)
1552 return bus_log_create_error(r);
1553
1554 r = sd_bus_message_open_container(m, 'v', "(bas)");
1555 if (r < 0)
1556 return bus_log_create_error(r);
1557
1558 r = sd_bus_message_open_container(m, 'r', "bas");
1559 if (r < 0)
1560 return bus_log_create_error(r);
1561
1562 r = sd_bus_message_append_basic(m, 'b', &allow_list);
1563 if (r < 0)
1564 return bus_log_create_error(r);
1565
1566 r = sd_bus_message_open_container(m, 'a', "s");
1567 if (r < 0)
1568 return bus_log_create_error(r);
1569
1570 for (;;) {
1571 _cleanup_free_ char *word = NULL;
1572
1573 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
1574 if (r == 0)
1575 break;
1576 if (r == -ENOMEM)
1577 return log_oom();
1578 if (r < 0)
1579 return log_error_errno(r, "Invalid syntax: %s", eq);
1580
1581 r = sd_bus_message_append_basic(m, 's', word);
1582 if (r < 0)
1583 return bus_log_create_error(r);
1584 }
1585
1586 r = sd_bus_message_close_container(m);
1587 if (r < 0)
1588 return bus_log_create_error(r);
1589
1590 r = sd_bus_message_close_container(m);
1591 if (r < 0)
1592 return bus_log_create_error(r);
1593
1594 r = sd_bus_message_close_container(m);
1595 if (r < 0)
1596 return bus_log_create_error(r);
1597
1598 r = sd_bus_message_close_container(m);
1599 if (r < 0)
1600 return bus_log_create_error(r);
1601
1602 return 1;
1603 }
1604
1605 if (streq(field, "RestrictNamespaces")) {
1606 bool invert = false;
1607 unsigned long flags;
1608
1609 r = parse_boolean(eq);
1610 if (r > 0)
1611 flags = 0;
1612 else if (r == 0)
1613 flags = NAMESPACE_FLAGS_ALL;
1614 else {
1615 if (eq[0] == '~') {
1616 invert = true;
1617 eq++;
1618 }
1619
1620 r = namespace_flags_from_string(eq, &flags);
1621 if (r < 0)
1622 return log_error_errno(r, "Failed to parse %s value %s.", field, eq);
1623 }
1624
1625 if (invert)
1626 flags = (~flags) & NAMESPACE_FLAGS_ALL;
1627
1628 r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) flags);
1629 if (r < 0)
1630 return bus_log_create_error(r);
1631
1632 return 1;
1633 }
1634
1635 if (STR_IN_SET(field, "BindPaths",
1636 "BindReadOnlyPaths")) {
1637 const char *p = eq;
1638
1639 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1640 if (r < 0)
1641 return bus_log_create_error(r);
1642
1643 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1644 if (r < 0)
1645 return bus_log_create_error(r);
1646
1647 r = sd_bus_message_open_container(m, 'v', "a(ssbt)");
1648 if (r < 0)
1649 return bus_log_create_error(r);
1650
1651 r = sd_bus_message_open_container(m, 'a', "(ssbt)");
1652 if (r < 0)
1653 return bus_log_create_error(r);
1654
1655 for (;;) {
1656 _cleanup_free_ char *source = NULL, *destination = NULL;
1657 char *s = NULL, *d = NULL;
1658 bool ignore_enoent = false;
1659 uint64_t flags = MS_REC;
1660
1661 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
1662 if (r < 0)
1663 return log_error_errno(r, "Failed to parse argument: %m");
1664 if (r == 0)
1665 break;
1666
1667 s = source;
1668 if (s[0] == '-') {
1669 ignore_enoent = true;
1670 s++;
1671 }
1672
1673 if (p && p[-1] == ':') {
1674 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
1675 if (r < 0)
1676 return log_error_errno(r, "Failed to parse argument: %m");
1677 if (r == 0)
1678 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1679 "Missing argument after ':': %s",
1680 eq);
1681
1682 d = destination;
1683
1684 if (p && p[-1] == ':') {
1685 _cleanup_free_ char *options = NULL;
1686
1687 r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE);
1688 if (r < 0)
1689 return log_error_errno(r, "Failed to parse argument: %m");
1690
1691 if (isempty(options) || streq(options, "rbind"))
1692 flags = MS_REC;
1693 else if (streq(options, "norbind"))
1694 flags = 0;
1695 else
1696 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1697 "Unknown options: %s",
1698 eq);
1699 }
1700 } else
1701 d = s;
1702
1703 r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags);
1704 if (r < 0)
1705 return bus_log_create_error(r);
1706 }
1707
1708 r = sd_bus_message_close_container(m);
1709 if (r < 0)
1710 return bus_log_create_error(r);
1711
1712 r = sd_bus_message_close_container(m);
1713 if (r < 0)
1714 return bus_log_create_error(r);
1715
1716 r = sd_bus_message_close_container(m);
1717 if (r < 0)
1718 return bus_log_create_error(r);
1719
1720 return 1;
1721 }
1722
1723 if (streq(field, "TemporaryFileSystem")) {
1724 const char *p = eq;
1725
1726 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1727 if (r < 0)
1728 return bus_log_create_error(r);
1729
1730 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1731 if (r < 0)
1732 return bus_log_create_error(r);
1733
1734 r = sd_bus_message_open_container(m, 'v', "a(ss)");
1735 if (r < 0)
1736 return bus_log_create_error(r);
1737
1738 r = sd_bus_message_open_container(m, 'a', "(ss)");
1739 if (r < 0)
1740 return bus_log_create_error(r);
1741
1742 for (;;) {
1743 _cleanup_free_ char *word = NULL, *path = NULL;
1744 const char *w;
1745
1746 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
1747 if (r < 0)
1748 return log_error_errno(r, "Failed to parse argument: %m");
1749 if (r == 0)
1750 break;
1751
1752 w = word;
1753 r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
1754 if (r < 0)
1755 return log_error_errno(r, "Failed to parse argument: %m");
1756 if (r == 0)
1757 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1758 "Failed to parse argument: %s",
1759 p);
1760
1761 r = sd_bus_message_append(m, "(ss)", path, w);
1762 if (r < 0)
1763 return bus_log_create_error(r);
1764 }
1765
1766 r = sd_bus_message_close_container(m);
1767 if (r < 0)
1768 return bus_log_create_error(r);
1769
1770 r = sd_bus_message_close_container(m);
1771 if (r < 0)
1772 return bus_log_create_error(r);
1773
1774 r = sd_bus_message_close_container(m);
1775 if (r < 0)
1776 return bus_log_create_error(r);
1777
1778 return 1;
1779 }
1780
1781 if (streq(field, "RootHash")) {
1782 _cleanup_free_ void *roothash_decoded = NULL;
1783 size_t roothash_decoded_size = 0;
1784
1785 /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
1786 if (path_is_absolute(eq))
1787 return bus_append_string(m, "RootHashPath", eq);
1788
1789 /* We have a roothash to decode, eg: RootHash=012345789abcdef */
1790 r = unhexmem(eq, &roothash_decoded, &roothash_decoded_size);
1791 if (r < 0)
1792 return log_error_errno(r, "Failed to decode RootHash= '%s': %m", eq);
1793 if (roothash_decoded_size < sizeof(sd_id128_t))
1794 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "RootHash= '%s' is too short.", eq);
1795
1796 return bus_append_byte_array(m, field, roothash_decoded, roothash_decoded_size);
1797 }
1798
1799 if (streq(field, "RootHashSignature")) {
1800 _cleanup_free_ void *roothash_sig_decoded = NULL;
1801 char *value;
1802 size_t roothash_sig_decoded_size = 0;
1803
1804 /* We have the path to a roothash signature to load and decode, eg: RootHash=/foo/bar.roothash.p7s */
1805 if (path_is_absolute(eq))
1806 return bus_append_string(m, "RootHashSignaturePath", eq);
1807
1808 if (!(value = startswith(eq, "base64:")))
1809 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decode RootHashSignature= '%s', not a path but doesn't start with 'base64:'.", eq);
1810
1811 /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
1812 r = unbase64mem(value, &roothash_sig_decoded, &roothash_sig_decoded_size);
1813 if (r < 0)
1814 return log_error_errno(r, "Failed to decode RootHashSignature= '%s': %m", eq);
1815
1816 return bus_append_byte_array(m, field, roothash_sig_decoded, roothash_sig_decoded_size);
1817 }
1818
1819 if (streq(field, "RootImageOptions")) {
1820 _cleanup_strv_free_ char **l = NULL;
1821 const char *p = eq;
1822
1823 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1824 if (r < 0)
1825 return bus_log_create_error(r);
1826
1827 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1828 if (r < 0)
1829 return bus_log_create_error(r);
1830
1831 r = sd_bus_message_open_container(m, 'v', "a(ss)");
1832 if (r < 0)
1833 return bus_log_create_error(r);
1834
1835 r = sd_bus_message_open_container(m, 'a', "(ss)");
1836 if (r < 0)
1837 return bus_log_create_error(r);
1838
1839 r = strv_split_colon_pairs(&l, p);
1840 if (r < 0)
1841 return log_error_errno(r, "Failed to parse argument: %m");
1842
1843 STRV_FOREACH_PAIR(first, second, l) {
1844 r = sd_bus_message_append(m, "(ss)",
1845 !isempty(*second) ? *first : "root",
1846 !isempty(*second) ? *second : *first);
1847 if (r < 0)
1848 return bus_log_create_error(r);
1849 }
1850
1851 r = sd_bus_message_close_container(m);
1852 if (r < 0)
1853 return bus_log_create_error(r);
1854
1855 r = sd_bus_message_close_container(m);
1856 if (r < 0)
1857 return bus_log_create_error(r);
1858
1859 r = sd_bus_message_close_container(m);
1860 if (r < 0)
1861 return bus_log_create_error(r);
1862
1863 return 1;
1864 }
1865
1866 if (streq(field, "MountImages")) {
1867 const char *p = eq;
1868
1869 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1870 if (r < 0)
1871 return bus_log_create_error(r);
1872
1873 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1874 if (r < 0)
1875 return bus_log_create_error(r);
1876
1877 r = sd_bus_message_open_container(m, 'v', "a(ssba(ss))");
1878 if (r < 0)
1879 return bus_log_create_error(r);
1880
1881 r = sd_bus_message_open_container(m, 'a', "(ssba(ss))");
1882 if (r < 0)
1883 return bus_log_create_error(r);
1884
1885 for (;;) {
1886 _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL;
1887 const char *q = NULL, *source = NULL;
1888 bool permissive = false;
1889
1890 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
1891 if (r < 0)
1892 return log_error_errno(r, "Failed to parse MountImages= property: %s", eq);
1893 if (r == 0)
1894 break;
1895
1896 q = tuple;
1897 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &first, &second);
1898 if (r < 0)
1899 return log_error_errno(r, "Failed to parse MountImages= property: %s", eq);
1900 if (r == 0)
1901 continue;
1902
1903 source = first;
1904 if (source[0] == '-') {
1905 permissive = true;
1906 source++;
1907 }
1908
1909 if (isempty(second))
1910 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1911 "Missing argument after ':': %s",
1912 eq);
1913
1914 r = sd_bus_message_open_container(m, 'r', "ssba(ss)");
1915 if (r < 0)
1916 return bus_log_create_error(r);
1917
1918 r = sd_bus_message_append(m, "ssb", source, second, permissive);
1919 if (r < 0)
1920 return bus_log_create_error(r);
1921
1922 r = sd_bus_message_open_container(m, 'a', "(ss)");
1923 if (r < 0)
1924 return bus_log_create_error(r);
1925
1926 for (;;) {
1927 _cleanup_free_ char *partition = NULL, *mount_options = NULL;
1928
1929 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options);
1930 if (r < 0)
1931 return log_error_errno(r, "Failed to parse MountImages= property: %s", eq);
1932 if (r == 0)
1933 break;
1934 /* Single set of options, applying to the root partition/single filesystem */
1935 if (r == 1) {
1936 r = sd_bus_message_append(m, "(ss)", "root", partition);
1937 if (r < 0)
1938 return bus_log_create_error(r);
1939
1940 break;
1941 }
1942
1943 r = sd_bus_message_append(m, "(ss)", partition, mount_options);
1944 if (r < 0)
1945 return bus_log_create_error(r);
1946 }
1947
1948 r = sd_bus_message_close_container(m);
1949 if (r < 0)
1950 return bus_log_create_error(r);
1951
1952 r = sd_bus_message_close_container(m);
1953 if (r < 0)
1954 return bus_log_create_error(r);
1955 }
1956
1957 r = sd_bus_message_close_container(m);
1958 if (r < 0)
1959 return bus_log_create_error(r);
1960
1961 r = sd_bus_message_close_container(m);
1962 if (r < 0)
1963 return bus_log_create_error(r);
1964
1965 r = sd_bus_message_close_container(m);
1966 if (r < 0)
1967 return bus_log_create_error(r);
1968
1969 return 1;
1970 }
1971
1972 if (streq(field, "ExtensionImages")) {
1973 const char *p = eq;
1974
1975 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
1976 if (r < 0)
1977 return bus_log_create_error(r);
1978
1979 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
1980 if (r < 0)
1981 return bus_log_create_error(r);
1982
1983 r = sd_bus_message_open_container(m, 'v', "a(sba(ss))");
1984 if (r < 0)
1985 return bus_log_create_error(r);
1986
1987 r = sd_bus_message_open_container(m, 'a', "(sba(ss))");
1988 if (r < 0)
1989 return bus_log_create_error(r);
1990
1991 for (;;) {
1992 _cleanup_free_ char *source = NULL, *tuple = NULL;
1993 const char *q = NULL, *s = NULL;
1994 bool permissive = false;
1995
1996 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
1997 if (r < 0)
1998 return log_error_errno(r, "Failed to parse ExtensionImages= property: %s", eq);
1999 if (r == 0)
2000 break;
2001
2002 q = tuple;
2003 r = extract_first_word(&q, &source, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS);
2004 if (r < 0)
2005 return log_error_errno(r, "Failed to parse ExtensionImages= property: %s", eq);
2006 if (r == 0)
2007 continue;
2008
2009 s = source;
2010 if (s[0] == '-') {
2011 permissive = true;
2012 s++;
2013 }
2014
2015 r = sd_bus_message_open_container(m, 'r', "sba(ss)");
2016 if (r < 0)
2017 return bus_log_create_error(r);
2018
2019 r = sd_bus_message_append(m, "sb", s, permissive);
2020 if (r < 0)
2021 return bus_log_create_error(r);
2022
2023 r = sd_bus_message_open_container(m, 'a', "(ss)");
2024 if (r < 0)
2025 return bus_log_create_error(r);
2026
2027 for (;;) {
2028 _cleanup_free_ char *partition = NULL, *mount_options = NULL;
2029
2030 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options);
2031 if (r < 0)
2032 return log_error_errno(r, "Failed to parse ExtensionImages= property: %s", eq);
2033 if (r == 0)
2034 break;
2035 /* Single set of options, applying to the root partition/single filesystem */
2036 if (r == 1) {
2037 r = sd_bus_message_append(m, "(ss)", "root", partition);
2038 if (r < 0)
2039 return bus_log_create_error(r);
2040
2041 break;
2042 }
2043
2044 r = sd_bus_message_append(m, "(ss)", partition, mount_options);
2045 if (r < 0)
2046 return bus_log_create_error(r);
2047 }
2048
2049 r = sd_bus_message_close_container(m);
2050 if (r < 0)
2051 return bus_log_create_error(r);
2052
2053 r = sd_bus_message_close_container(m);
2054 if (r < 0)
2055 return bus_log_create_error(r);
2056 }
2057
2058 r = sd_bus_message_close_container(m);
2059 if (r < 0)
2060 return bus_log_create_error(r);
2061
2062 r = sd_bus_message_close_container(m);
2063 if (r < 0)
2064 return bus_log_create_error(r);
2065
2066 r = sd_bus_message_close_container(m);
2067 if (r < 0)
2068 return bus_log_create_error(r);
2069
2070 return 1;
2071 }
2072
2073 if (STR_IN_SET(field, "StateDirectory", "RuntimeDirectory", "CacheDirectory", "LogsDirectory")) {
2074 _cleanup_strv_free_ char **symlinks = NULL, **sources = NULL;
2075 const char *p = eq;
2076
2077 /* Adding new directories is supported from both *DirectorySymlink methods and the
2078 * older ones, so first parse the input, and if we are given a new-style src:dst
2079 * tuple use the new method, else use the old one. */
2080
2081 for (;;) {
2082 _cleanup_free_ char *tuple = NULL, *source = NULL, *destination = NULL;
2083
2084 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE);
2085 if (r < 0)
2086 return log_error_errno(r, "Failed to parse argument: %m");
2087 if (r == 0)
2088 break;
2089
2090 const char *t = tuple;
2091 r = extract_many_words(&t, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination);
2092 if (r <= 0)
2093 return log_error_errno(r ?: SYNTHETIC_ERRNO(EINVAL), "Failed to parse argument: %m");
2094
2095 path_simplify(source);
2096
2097 if (isempty(destination)) {
2098 r = strv_consume(&sources, TAKE_PTR(source));
2099 if (r < 0)
2100 return bus_log_create_error(r);
2101 } else {
2102 path_simplify(destination);
2103
2104 r = strv_consume_pair(&symlinks, TAKE_PTR(source), TAKE_PTR(destination));
2105 if (r < 0)
2106 return log_oom();
2107 }
2108 }
2109
2110 if (!strv_isempty(sources)) {
2111 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
2112 if (r < 0)
2113 return bus_log_create_error(r);
2114
2115 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
2116 if (r < 0)
2117 return bus_log_create_error(r);
2118
2119 r = sd_bus_message_open_container(m, 'v', "as");
2120 if (r < 0)
2121 return bus_log_create_error(r);
2122
2123 r = sd_bus_message_append_strv(m, sources);
2124 if (r < 0)
2125 return bus_log_create_error(r);
2126
2127 r = sd_bus_message_close_container(m);
2128 if (r < 0)
2129 return bus_log_create_error(r);
2130
2131 r = sd_bus_message_close_container(m);
2132 if (r < 0)
2133 return bus_log_create_error(r);
2134 }
2135
2136 /* For State and Runtime directories we support an optional destination parameter, which
2137 * will be used to create a symlink to the source. But it is new so we cannot change the
2138 * old DBUS signatures, so append a new message type. */
2139 if (!strv_isempty(symlinks)) {
2140 const char *symlink_field;
2141
2142 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
2143 if (r < 0)
2144 return bus_log_create_error(r);
2145
2146 if (streq(field, "StateDirectory"))
2147 symlink_field = "StateDirectorySymlink";
2148 else if (streq(field, "RuntimeDirectory"))
2149 symlink_field = "RuntimeDirectorySymlink";
2150 else if (streq(field, "CacheDirectory"))
2151 symlink_field = "CacheDirectorySymlink";
2152 else if (streq(field, "LogsDirectory"))
2153 symlink_field = "LogsDirectorySymlink";
2154 else
2155 assert_not_reached();
2156
2157 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, symlink_field);
2158 if (r < 0)
2159 return bus_log_create_error(r);
2160
2161 r = sd_bus_message_open_container(m, 'v', "a(sst)");
2162 if (r < 0)
2163 return bus_log_create_error(r);
2164
2165 r = sd_bus_message_open_container(m, 'a', "(sst)");
2166 if (r < 0)
2167 return bus_log_create_error(r);
2168
2169 STRV_FOREACH_PAIR(source, destination, symlinks) {
2170 r = sd_bus_message_append(m, "(sst)", *source, *destination, UINT64_C(0));
2171 if (r < 0)
2172 return bus_log_create_error(r);
2173 }
2174
2175 r = sd_bus_message_close_container(m);
2176 if (r < 0)
2177 return bus_log_create_error(r);
2178
2179 r = sd_bus_message_close_container(m);
2180 if (r < 0)
2181 return bus_log_create_error(r);
2182
2183 r = sd_bus_message_close_container(m);
2184 if (r < 0)
2185 return bus_log_create_error(r);
2186 }
2187
2188 return 1;
2189 }
2190
2191 return 0;
2192 }
2193
2194 static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
2195 if (streq(field, "KillMode"))
2196 return bus_append_string(m, field, eq);
2197
2198 if (STR_IN_SET(field, "SendSIGHUP",
2199 "SendSIGKILL"))
2200 return bus_append_parse_boolean(m, field, eq);
2201
2202 if (STR_IN_SET(field, "KillSignal",
2203 "RestartKillSignal",
2204 "FinalKillSignal",
2205 "WatchdogSignal",
2206 "ReloadSignal"))
2207 return bus_append_signal_from_string(m, field, eq);
2208
2209 return 0;
2210 }
2211
2212 static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) {
2213
2214 if (STR_IN_SET(field, "What",
2215 "Where",
2216 "Options",
2217 "Type"))
2218 return bus_append_string(m, field, eq);
2219
2220 if (streq(field, "TimeoutSec"))
2221 return bus_append_parse_sec_rename(m, field, eq);
2222
2223 if (streq(field, "DirectoryMode"))
2224 return bus_append_parse_mode(m, field, eq);
2225
2226 if (STR_IN_SET(field, "SloppyOptions",
2227 "LazyUnmount",
2228 "ForceUnmount",
2229 "ReadwriteOnly"))
2230 return bus_append_parse_boolean(m, field, eq);
2231
2232 return 0;
2233 }
2234
2235 static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) {
2236 int r;
2237
2238 if (streq(field, "MakeDirectory"))
2239 return bus_append_parse_boolean(m, field, eq);
2240
2241 if (streq(field, "DirectoryMode"))
2242 return bus_append_parse_mode(m, field, eq);
2243
2244 if (STR_IN_SET(field, "PathExists",
2245 "PathExistsGlob",
2246 "PathChanged",
2247 "PathModified",
2248 "DirectoryNotEmpty")) {
2249 if (isempty(eq))
2250 r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0);
2251 else
2252 r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 1, field, eq);
2253 if (r < 0)
2254 return bus_log_create_error(r);
2255
2256 return 1;
2257 }
2258
2259 if (STR_IN_SET(field, "TriggerLimitBurst", "PollLimitBurst"))
2260 return bus_append_safe_atou(m, field, eq);
2261
2262 if (STR_IN_SET(field, "TriggerLimitIntervalSec", "PollLimitIntervalSec"))
2263 return bus_append_parse_sec_rename(m, field, eq);
2264
2265 return 0;
2266 }
2267
2268 static int bus_append_scope_property(sd_bus_message *m, const char *field, const char *eq) {
2269 if (streq(field, "RuntimeMaxSec"))
2270 return bus_append_parse_sec_rename(m, field, eq);
2271
2272 if (streq(field, "RuntimeRandomizedExtraSec"))
2273 return bus_append_parse_sec_rename(m, field, eq);
2274
2275 if (streq(field, "TimeoutStopSec"))
2276 return bus_append_parse_sec_rename(m, field, eq);
2277
2278 /* Scope units don't have execution context but we still want to allow setting these two,
2279 * so let's handle them separately. */
2280 if (STR_IN_SET(field, "User", "Group"))
2281 return bus_append_string(m, field, eq);
2282
2283 if (streq(field, "OOMPolicy"))
2284 return bus_append_string(m, field, eq);
2285
2286 return 0;
2287 }
2288
2289 static int bus_append_service_property(sd_bus_message *m, const char *field, const char *eq) {
2290 int r;
2291
2292 if (STR_IN_SET(field, "PIDFile",
2293 "Type",
2294 "ExitType",
2295 "Restart",
2296 "RestartMode",
2297 "BusName",
2298 "NotifyAccess",
2299 "USBFunctionDescriptors",
2300 "USBFunctionStrings",
2301 "OOMPolicy",
2302 "TimeoutStartFailureMode",
2303 "TimeoutStopFailureMode",
2304 "FileDescriptorStorePreserve"))
2305 return bus_append_string(m, field, eq);
2306
2307 if (STR_IN_SET(field, "PermissionsStartOnly",
2308 "RootDirectoryStartOnly",
2309 "RemainAfterExit",
2310 "GuessMainPID"))
2311 return bus_append_parse_boolean(m, field, eq);
2312
2313 if (STR_IN_SET(field, "RestartSec",
2314 "RestartMaxDelaySec",
2315 "TimeoutStartSec",
2316 "TimeoutStopSec",
2317 "TimeoutAbortSec",
2318 "RuntimeMaxSec",
2319 "RuntimeRandomizedExtraSec",
2320 "WatchdogSec"))
2321 return bus_append_parse_sec_rename(m, field, eq);
2322
2323 if (streq(field, "TimeoutSec")) {
2324 r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq);
2325 if (r < 0)
2326 return r;
2327
2328 return bus_append_parse_sec_rename(m, "TimeoutStopSec", eq);
2329 }
2330
2331 if (STR_IN_SET(field, "FileDescriptorStoreMax",
2332 "RestartSteps"))
2333 return bus_append_safe_atou(m, field, eq);
2334
2335 if (STR_IN_SET(field, "ExecCondition",
2336 "ExecStartPre",
2337 "ExecStart",
2338 "ExecStartPost",
2339 "ExecConditionEx",
2340 "ExecStartPreEx",
2341 "ExecStartEx",
2342 "ExecStartPostEx",
2343 "ExecReload",
2344 "ExecStop",
2345 "ExecStopPost",
2346 "ExecReloadEx",
2347 "ExecStopEx",
2348 "ExecStopPostEx"))
2349 return bus_append_exec_command(m, field, eq);
2350
2351 if (STR_IN_SET(field, "RestartPreventExitStatus",
2352 "RestartForceExitStatus",
2353 "SuccessExitStatus")) {
2354 _cleanup_free_ int *status = NULL, *signal = NULL;
2355 size_t n_status = 0, n_signal = 0;
2356 const char *p;
2357
2358 for (p = eq;;) {
2359 _cleanup_free_ char *word = NULL;
2360
2361 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
2362 if (r == 0)
2363 break;
2364 if (r == -ENOMEM)
2365 return log_oom();
2366 if (r < 0)
2367 return log_error_errno(r, "Invalid syntax in %s: %s", field, eq);
2368
2369 /* We need to call exit_status_from_string() first, because we want
2370 * to parse numbers as exit statuses, not signals. */
2371
2372 r = exit_status_from_string(word);
2373 if (r >= 0) {
2374 assert(r >= 0 && r < 256);
2375
2376 status = reallocarray(status, n_status + 1, sizeof(int));
2377 if (!status)
2378 return log_oom();
2379
2380 status[n_status++] = r;
2381
2382 } else if ((r = signal_from_string(word)) >= 0) {
2383 signal = reallocarray(signal, n_signal + 1, sizeof(int));
2384 if (!signal)
2385 return log_oom();
2386
2387 signal[n_signal++] = r;
2388
2389 } else
2390 /* original r from exit_status_to_string() */
2391 return log_error_errno(r, "Invalid status or signal %s in %s: %m",
2392 word, field);
2393 }
2394
2395 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
2396 if (r < 0)
2397 return bus_log_create_error(r);
2398
2399 r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
2400 if (r < 0)
2401 return bus_log_create_error(r);
2402
2403 r = sd_bus_message_open_container(m, 'v', "(aiai)");
2404 if (r < 0)
2405 return bus_log_create_error(r);
2406
2407 r = sd_bus_message_open_container(m, 'r', "aiai");
2408 if (r < 0)
2409 return bus_log_create_error(r);
2410
2411 r = sd_bus_message_append_array(m, 'i', status, n_status * sizeof(int));
2412 if (r < 0)
2413 return bus_log_create_error(r);
2414
2415 r = sd_bus_message_append_array(m, 'i', signal, n_signal * sizeof(int));
2416 if (r < 0)
2417 return bus_log_create_error(r);
2418
2419 r = sd_bus_message_close_container(m);
2420 if (r < 0)
2421 return bus_log_create_error(r);
2422
2423 r = sd_bus_message_close_container(m);
2424 if (r < 0)
2425 return bus_log_create_error(r);
2426
2427 r = sd_bus_message_close_container(m);
2428 if (r < 0)
2429 return bus_log_create_error(r);
2430
2431 return 1;
2432 }
2433
2434 if (streq(field, "OpenFile"))
2435 return bus_append_open_file(m, field, eq);
2436
2437 return 0;
2438 }
2439
2440 static int bus_append_socket_property(sd_bus_message *m, const char *field, const char *eq) {
2441 int r;
2442
2443 if (STR_IN_SET(field, "Accept",
2444 "FlushPending",
2445 "Writable",
2446 "KeepAlive",
2447 "NoDelay",
2448 "FreeBind",
2449 "Transparent",
2450 "Broadcast",
2451 "PassCredentials",
2452 "PassFileDescriptorsToExec",
2453 "PassSecurity",
2454 "PassPacketInfo",
2455 "ReusePort",
2456 "RemoveOnStop",
2457 "SELinuxContextFromNet"))
2458 return bus_append_parse_boolean(m, field, eq);
2459
2460 if (STR_IN_SET(field, "Priority",
2461 "IPTTL",
2462 "Mark"))
2463 return bus_append_safe_atoi(m, field, eq);
2464
2465 if (streq(field, "IPTOS"))
2466 return bus_append_ip_tos_from_string(m, field, eq);
2467
2468 if (STR_IN_SET(field, "Backlog",
2469 "MaxConnections",
2470 "MaxConnectionsPerSource",
2471 "KeepAliveProbes",
2472 "TriggerLimitBurst",
2473 "PollLimitBurst"))
2474 return bus_append_safe_atou(m, field, eq);
2475
2476 if (STR_IN_SET(field, "SocketMode",
2477 "DirectoryMode"))
2478 return bus_append_parse_mode(m, field, eq);
2479
2480 if (STR_IN_SET(field, "MessageQueueMaxMessages",
2481 "MessageQueueMessageSize"))
2482 return bus_append_safe_atoi64(m, field, eq);
2483
2484 if (STR_IN_SET(field, "TimeoutSec",
2485 "KeepAliveTimeSec",
2486 "KeepAliveIntervalSec",
2487 "DeferAcceptSec",
2488 "TriggerLimitIntervalSec",
2489 "PollLimitIntervalSec"))
2490 return bus_append_parse_sec_rename(m, field, eq);
2491
2492 if (STR_IN_SET(field, "ReceiveBuffer",
2493 "SendBuffer",
2494 "PipeSize"))
2495 return bus_append_parse_size(m, field, eq, 1024);
2496
2497 if (STR_IN_SET(field, "ExecStartPre",
2498 "ExecStartPost",
2499 "ExecReload",
2500 "ExecStopPost"))
2501 return bus_append_exec_command(m, field, eq);
2502
2503 if (STR_IN_SET(field, "SmackLabel",
2504 "SmackLabelIPIn",
2505 "SmackLabelIPOut",
2506 "TCPCongestion",
2507 "BindToDevice",
2508 "BindIPv6Only",
2509 "FileDescriptorName",
2510 "SocketUser",
2511 "SocketGroup",
2512 "Timestamping"))
2513 return bus_append_string(m, field, eq);
2514
2515 if (streq(field, "Symlinks"))
2516 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
2517
2518 if (streq(field, "SocketProtocol"))
2519 return bus_append_parse_ip_protocol(m, field, eq);
2520
2521 if (STR_IN_SET(field, "ListenStream",
2522 "ListenDatagram",
2523 "ListenSequentialPacket",
2524 "ListenNetlink",
2525 "ListenSpecial",
2526 "ListenMessageQueue",
2527 "ListenFIFO",
2528 "ListenUSBFunction")) {
2529 if (isempty(eq))
2530 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0);
2531 else
2532 r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 1, field + STRLEN("Listen"), eq);
2533 if (r < 0)
2534 return bus_log_create_error(r);
2535
2536 return 1;
2537 }
2538
2539 return 0;
2540 }
2541 static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) {
2542 int r;
2543
2544 if (STR_IN_SET(field, "WakeSystem",
2545 "RemainAfterElapse",
2546 "Persistent",
2547 "OnTimezoneChange",
2548 "OnClockChange",
2549 "FixedRandomDelay"))
2550 return bus_append_parse_boolean(m, field, eq);
2551
2552 if (STR_IN_SET(field, "AccuracySec",
2553 "RandomizedDelaySec"))
2554 return bus_append_parse_sec_rename(m, field, eq);
2555
2556 if (STR_IN_SET(field, "OnActiveSec",
2557 "OnBootSec",
2558 "OnStartupSec",
2559 "OnUnitActiveSec",
2560 "OnUnitInactiveSec")) {
2561 if (isempty(eq))
2562 r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0);
2563 else {
2564 usec_t t;
2565 r = parse_sec(eq, &t);
2566 if (r < 0)
2567 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq);
2568
2569 r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 1, field, t);
2570 }
2571 if (r < 0)
2572 return bus_log_create_error(r);
2573
2574 return 1;
2575 }
2576
2577 if (streq(field, "OnCalendar")) {
2578 if (isempty(eq))
2579 r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0);
2580 else
2581 r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 1, field, eq);
2582 if (r < 0)
2583 return bus_log_create_error(r);
2584
2585 return 1;
2586 }
2587
2588 return 0;
2589 }
2590
2591 static int bus_append_unit_property(sd_bus_message *m, const char *field, const char *eq) {
2592 ConditionType t = _CONDITION_TYPE_INVALID;
2593 bool is_condition = false;
2594 int r;
2595
2596 if (STR_IN_SET(field, "Description",
2597 "SourcePath",
2598 "OnFailureJobMode",
2599 "JobTimeoutAction",
2600 "JobTimeoutRebootArgument",
2601 "StartLimitAction",
2602 "FailureAction",
2603 "SuccessAction",
2604 "RebootArgument",
2605 "CollectMode"))
2606 return bus_append_string(m, field, eq);
2607
2608 if (STR_IN_SET(field, "StopWhenUnneeded",
2609 "RefuseManualStart",
2610 "RefuseManualStop",
2611 "AllowIsolate",
2612 "IgnoreOnIsolate",
2613 "SurviveFinalKillSignal",
2614 "DefaultDependencies"))
2615 return bus_append_parse_boolean(m, field, eq);
2616
2617 if (STR_IN_SET(field, "JobTimeoutSec",
2618 "JobRunningTimeoutSec",
2619 "StartLimitIntervalSec"))
2620 return bus_append_parse_sec_rename(m, field, eq);
2621
2622 if (streq(field, "StartLimitBurst"))
2623 return bus_append_safe_atou(m, field, eq);
2624
2625 if (STR_IN_SET(field, "SuccessActionExitStatus",
2626 "FailureActionExitStatus")) {
2627 if (isempty(eq))
2628 r = sd_bus_message_append(m, "(sv)", field, "i", -1);
2629 else {
2630 uint8_t u;
2631
2632 r = safe_atou8(eq, &u);
2633 if (r < 0)
2634 return log_error_errno(r, "Failed to parse %s=%s", field, eq);
2635
2636 r = sd_bus_message_append(m, "(sv)", field, "i", (int) u);
2637 }
2638 if (r < 0)
2639 return bus_log_create_error(r);
2640
2641 return 1;
2642 }
2643
2644 if (unit_dependency_from_string(field) >= 0 ||
2645 STR_IN_SET(field, "Documentation",
2646 "RequiresMountsFor",
2647 "WantsMountsFor",
2648 "Markers"))
2649 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
2650
2651 t = condition_type_from_string(field);
2652 if (t >= 0)
2653 is_condition = true;
2654 else
2655 t = assert_type_from_string(field);
2656 if (t >= 0) {
2657 if (isempty(eq))
2658 r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 0);
2659 else {
2660 const char *p = eq;
2661 int trigger, negate;
2662
2663 trigger = *p == '|';
2664 if (trigger)
2665 p++;
2666
2667 negate = *p == '!';
2668 if (negate)
2669 p++;
2670
2671 r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 1,
2672 field, trigger, negate, p);
2673 }
2674 if (r < 0)
2675 return bus_log_create_error(r);
2676
2677 return 1;
2678 }
2679
2680 return 0;
2681 }
2682
2683 int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment) {
2684 const char *eq, *field;
2685 int r;
2686
2687 assert(m);
2688 assert(assignment);
2689
2690 eq = strchr(assignment, '=');
2691 if (!eq)
2692 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2693 "Not an assignment: %s", assignment);
2694
2695 field = strndupa_safe(assignment, eq - assignment);
2696 eq++;
2697
2698 switch (t) {
2699 case UNIT_SERVICE:
2700 r = bus_append_cgroup_property(m, field, eq);
2701 if (r != 0)
2702 return r;
2703
2704 r = bus_append_execute_property(m, field, eq);
2705 if (r != 0)
2706 return r;
2707
2708 r = bus_append_kill_property(m, field, eq);
2709 if (r != 0)
2710 return r;
2711
2712 r = bus_append_service_property(m, field, eq);
2713 if (r != 0)
2714 return r;
2715 break;
2716
2717 case UNIT_SOCKET:
2718 r = bus_append_cgroup_property(m, field, eq);
2719 if (r != 0)
2720 return r;
2721
2722 r = bus_append_execute_property(m, field, eq);
2723 if (r != 0)
2724 return r;
2725
2726 r = bus_append_kill_property(m, field, eq);
2727 if (r != 0)
2728 return r;
2729
2730 r = bus_append_socket_property(m, field, eq);
2731 if (r != 0)
2732 return r;
2733 break;
2734
2735 case UNIT_TIMER:
2736 r = bus_append_timer_property(m, field, eq);
2737 if (r != 0)
2738 return r;
2739 break;
2740
2741 case UNIT_PATH:
2742 r = bus_append_path_property(m, field, eq);
2743 if (r != 0)
2744 return r;
2745 break;
2746
2747 case UNIT_SLICE:
2748 r = bus_append_cgroup_property(m, field, eq);
2749 if (r != 0)
2750 return r;
2751 break;
2752
2753 case UNIT_SCOPE:
2754 r = bus_append_cgroup_property(m, field, eq);
2755 if (r != 0)
2756 return r;
2757
2758 r = bus_append_kill_property(m, field, eq);
2759 if (r != 0)
2760 return r;
2761
2762 r = bus_append_scope_property(m, field, eq);
2763 if (r != 0)
2764 return r;
2765 break;
2766
2767 case UNIT_MOUNT:
2768 r = bus_append_cgroup_property(m, field, eq);
2769 if (r != 0)
2770 return r;
2771
2772 r = bus_append_execute_property(m, field, eq);
2773 if (r != 0)
2774 return r;
2775
2776 r = bus_append_kill_property(m, field, eq);
2777 if (r != 0)
2778 return r;
2779
2780 r = bus_append_mount_property(m, field, eq);
2781 if (r != 0)
2782 return r;
2783
2784 break;
2785
2786 case UNIT_AUTOMOUNT:
2787 r = bus_append_automount_property(m, field, eq);
2788 if (r != 0)
2789 return r;
2790
2791 break;
2792
2793 case UNIT_TARGET:
2794 case UNIT_DEVICE:
2795 case UNIT_SWAP:
2796 break;
2797
2798 default:
2799 assert_not_reached();
2800 }
2801
2802 r = bus_append_unit_property(m, field, eq);
2803 if (r != 0)
2804 return r;
2805
2806 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2807 "Unknown assignment: %s", assignment);
2808 }
2809
2810 int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) {
2811 int r;
2812
2813 assert(m);
2814
2815 STRV_FOREACH(i, l) {
2816 r = bus_append_unit_property_assignment(m, t, *i);
2817 if (r < 0)
2818 return r;
2819 }
2820
2821 return 0;
2822 }
2823
2824 int bus_append_scope_pidref(sd_bus_message *m, const PidRef *pidref, bool allow_pidfd) {
2825 assert(m);
2826
2827 if (!pidref_is_set(pidref))
2828 return -ESRCH;
2829
2830 if (pidref->fd >= 0 && allow_pidfd)
2831 return sd_bus_message_append(
2832 m, "(sv)",
2833 "PIDFDs", "ah", 1, pidref->fd);
2834
2835 return sd_bus_message_append(
2836 m, "(sv)",
2837 "PIDs", "au", 1, pidref->pid);
2838 }
2839
2840 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet) {
2841 const char *type, *path, *source;
2842 InstallChange *changes = NULL;
2843 size_t n_changes = 0;
2844 int r;
2845
2846 CLEANUP_ARRAY(changes, n_changes, install_changes_free);
2847
2848 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
2849 if (r < 0)
2850 return bus_log_parse_error(r);
2851
2852 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
2853 InstallChangeType t;
2854
2855 /* We expect only "success" changes to be sent over the bus. Hence, reject anything
2856 * negative. */
2857 t = install_change_type_from_string(type);
2858 if (t < 0) {
2859 log_notice_errno(t, "Manager reported unknown change type \"%s\" for path \"%s\", ignoring.",
2860 type, path);
2861 continue;
2862 }
2863
2864 r = install_changes_add(&changes, &n_changes, t, path, source);
2865 if (r < 0)
2866 return r;
2867 }
2868 if (r < 0)
2869 return bus_log_parse_error(r);
2870
2871 r = sd_bus_message_exit_container(m);
2872 if (r < 0)
2873 return bus_log_parse_error(r);
2874
2875 install_changes_dump(0, NULL, changes, n_changes, quiet);
2876
2877 return 0;
2878 }
2879
2880 int unit_load_state(sd_bus *bus, const char *name, char **load_state) {
2881 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2882 _cleanup_free_ char *path = NULL;
2883 int r;
2884
2885 path = unit_dbus_path_from_name(name);
2886 if (!path)
2887 return log_oom();
2888
2889 /* This function warns on its own, because otherwise it'd be awkward to pass
2890 * the dbus error message around. */
2891
2892 r = sd_bus_get_property_string(
2893 bus,
2894 "org.freedesktop.systemd1",
2895 path,
2896 "org.freedesktop.systemd1.Unit",
2897 "LoadState",
2898 &error,
2899 load_state);
2900 if (r < 0)
2901 return log_error_errno(r, "Failed to get load state of %s: %s", name, bus_error_message(&error, r));
2902
2903 return 0;
2904 }
2905
2906 int unit_info_compare(const UnitInfo *a, const UnitInfo *b) {
2907 int r;
2908
2909 /* First, order by machine */
2910 r = strcasecmp_ptr(a->machine, b->machine);
2911 if (r != 0)
2912 return r;
2913
2914 /* Second, order by unit type */
2915 r = strcasecmp_ptr(strrchr(a->id, '.'), strrchr(b->id, '.'));
2916 if (r != 0)
2917 return r;
2918
2919 /* Third, order by name */
2920 return strcasecmp(a->id, b->id);
2921 }
2922
2923 int bus_service_manager_reload(sd_bus *bus) {
2924 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2925 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2926 int r;
2927
2928 assert(bus);
2929
2930 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "Reload");
2931 if (r < 0)
2932 return bus_log_create_error(r);
2933
2934 /* Reloading the daemon may take long, hence set a longer timeout here */
2935 r = sd_bus_call(bus, m, DAEMON_RELOAD_TIMEOUT_SEC, &error, NULL);
2936 if (r < 0)
2937 return log_error_errno(r, "Failed to reload service manager: %s", bus_error_message(&error, r));
2938
2939 return 0;
2940 }
2941
2942 /* Wait for 1.5 seconds at maximum for freeze operation */
2943 #define FREEZE_BUS_CALL_TIMEOUT (1500 * USEC_PER_MSEC)
2944
2945 int unit_freezer_new(const char *name, UnitFreezer *ret) {
2946 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2947 _cleanup_free_ char *namedup = NULL;
2948 int r;
2949
2950 assert(name);
2951 assert(ret);
2952
2953 namedup = strdup(name);
2954 if (!namedup)
2955 return log_oom_debug();
2956
2957 r = bus_connect_system_systemd(&bus);
2958 if (r < 0)
2959 return log_debug_errno(r, "Failed to open connection to systemd: %m");
2960
2961 (void) sd_bus_set_method_call_timeout(bus, FREEZE_BUS_CALL_TIMEOUT);
2962
2963 *ret = (UnitFreezer) {
2964 .name = TAKE_PTR(namedup),
2965 .bus = TAKE_PTR(bus),
2966 };
2967 return 0;
2968 }
2969
2970 void unit_freezer_done(UnitFreezer *f) {
2971 assert(f);
2972
2973 f->name = mfree(f->name);
2974 f->bus = sd_bus_flush_close_unref(f->bus);
2975 }
2976
2977 static int unit_freezer_action(UnitFreezer *f, bool freeze) {
2978 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2979 int r;
2980
2981 assert(f);
2982 assert(f->name);
2983 assert(f->bus);
2984
2985 r = bus_call_method(f->bus, bus_systemd_mgr,
2986 freeze ? "FreezeUnit" : "ThawUnit",
2987 &error,
2988 /* reply = */ NULL,
2989 "s",
2990 f->name);
2991 if (r < 0)
2992 return log_debug_errno(r, "Failed to %s unit %s: %s",
2993 freeze ? "freeze" : "thaw", f->name, bus_error_message(&error, r));
2994
2995 return 0;
2996 }
2997
2998 int unit_freezer_freeze(UnitFreezer *f) {
2999 return unit_freezer_action(f, true);
3000 }
3001
3002 int unit_freezer_thaw(UnitFreezer *f) {
3003 return unit_freezer_action(f, false);
3004 }
3005
3006 int unit_freezer_new_freeze(const char *name, UnitFreezer *ret) {
3007 _cleanup_(unit_freezer_done) UnitFreezer f = {};
3008 int r;
3009
3010 assert(name);
3011 assert(ret);
3012
3013 r = unit_freezer_new(name, &f);
3014 if (r < 0)
3015 return r;
3016
3017 r = unit_freezer_freeze(&f);
3018 if (r < 0)
3019 return r;
3020
3021 *ret = TAKE_STRUCT(f);
3022 return 0;
3023 }
3024
3025 void unit_freezer_done_thaw(UnitFreezer *f) {
3026 assert(f);
3027
3028 if (!f->name)
3029 return;
3030
3031 (void) unit_freezer_thaw(f);
3032 unit_freezer_done(f);
3033 }