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