1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 #include "alloc-util.h"
6 #include "bus-unit-util.h"
9 #include "cgroup-setup.h"
10 #include "cgroup-util.h"
11 #include "condition.h"
12 #include "coredump-util.h"
13 #include "cpu-set-util.h"
14 #include "dissect-image.h"
16 #include "exec-util.h"
17 #include "exit-status.h"
19 #include "hexdecoct.h"
20 #include "hostname-util.h"
21 #include "in-addr-util.h"
22 #include "ip-protocol-list.h"
23 #include "libmount-util.h"
24 #include "locale-util.h"
26 #include "missing_fs.h"
27 #include "mountpoint-util.h"
29 #include "numa-util.h"
30 #include "parse-util.h"
31 #include "path-util.h"
32 #include "percent-util.h"
33 #include "process-util.h"
34 #include "rlimit-util.h"
36 #include "seccomp-util.h"
38 #include "securebits-util.h"
39 #include "signal-util.h"
40 #include "socket-util.h"
41 #include "sort-util.h"
42 #include "stdio-util.h"
43 #include "string-util.h"
44 #include "syslog-util.h"
45 #include "terminal-util.h"
47 #include "user-util.h"
50 int bus_parse_unit_info(sd_bus_message
*message
, UnitInfo
*u
) {
56 return sd_bus_message_read(
71 #define DEFINE_BUS_APPEND_PARSE_PTR(bus_type, cast_type, type, parse_func) \
72 static int bus_append_##parse_func( \
79 r = parse_func(eq, &val); \
81 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); \
83 r = sd_bus_message_append(m, "(sv)", field, \
84 bus_type, (cast_type) val); \
86 return bus_log_create_error(r); \
91 #define DEFINE_BUS_APPEND_PARSE(bus_type, parse_func) \
92 static int bus_append_##parse_func( \
100 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse %s: %s", field, eq); \
102 r = sd_bus_message_append(m, "(sv)", field, \
103 bus_type, (int32_t) r); \
105 return bus_log_create_error(r); \
110 DEFINE_BUS_APPEND_PARSE("b", parse_boolean
);
111 DEFINE_BUS_APPEND_PARSE("i", ioprio_class_from_string
);
112 DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string
);
113 DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string
);
114 DEFINE_BUS_APPEND_PARSE("i", log_level_from_string
);
116 static inline int seccomp_parse_errno_or_action(const char *eq
) { return -EINVAL
; }
118 DEFINE_BUS_APPEND_PARSE("i", seccomp_parse_errno_or_action
);
119 DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string
);
120 DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string
);
121 DEFINE_BUS_APPEND_PARSE("i", signal_from_string
);
122 DEFINE_BUS_APPEND_PARSE("i", parse_ip_protocol
);
123 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, ioprio_parse_priority
);
124 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, parse_nice
);
125 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, safe_atoi
);
126 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, nsec_t
, parse_nsec
);
127 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_blkio_weight_parse
);
128 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_shares_parse
);
129 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_weight_parse
);
130 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, unsigned long, mount_propagation_flags_from_string
);
131 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, safe_atou64
);
132 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t
, parse_mode
);
133 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou
);
134 DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64
);
135 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, coredump_filter_mask_from_string
);
137 static int bus_append_string(sd_bus_message
*m
, const char *field
, const char *eq
) {
140 r
= sd_bus_message_append(m
, "(sv)", field
, "s", eq
);
142 return bus_log_create_error(r
);
147 static int bus_append_strv(sd_bus_message
*m
, const char *field
, const char *eq
, ExtractFlags flags
) {
151 r
= sd_bus_message_open_container(m
, 'r', "sv");
153 return bus_log_create_error(r
);
155 r
= sd_bus_message_append_basic(m
, 's', field
);
157 return bus_log_create_error(r
);
159 r
= sd_bus_message_open_container(m
, 'v', "as");
161 return bus_log_create_error(r
);
163 r
= sd_bus_message_open_container(m
, 'a', "s");
165 return bus_log_create_error(r
);
168 _cleanup_free_
char *word
= NULL
;
170 r
= extract_first_word(&p
, &word
, NULL
, flags
);
176 return log_error_errno(r
, "Invalid syntax: %s", eq
);
178 r
= sd_bus_message_append_basic(m
, 's', word
);
180 return bus_log_create_error(r
);
183 r
= sd_bus_message_close_container(m
);
185 return bus_log_create_error(r
);
187 r
= sd_bus_message_close_container(m
);
189 return bus_log_create_error(r
);
191 r
= sd_bus_message_close_container(m
);
193 return bus_log_create_error(r
);
198 static int bus_append_byte_array(sd_bus_message
*m
, const char *field
, const void *buf
, size_t n
) {
201 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
203 return bus_log_create_error(r
);
205 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
207 return bus_log_create_error(r
);
209 r
= sd_bus_message_open_container(m
, 'v', "ay");
211 return bus_log_create_error(r
);
213 r
= sd_bus_message_append_array(m
, 'y', buf
, n
);
215 return bus_log_create_error(r
);
217 r
= sd_bus_message_close_container(m
);
219 return bus_log_create_error(r
);
221 r
= sd_bus_message_close_container(m
);
223 return bus_log_create_error(r
);
228 static int bus_append_parse_sec_rename(sd_bus_message
*m
, const char *field
, const char *eq
) {
234 r
= parse_sec(eq
, &t
);
236 return log_error_errno(r
, "Failed to parse %s=%s: %m", field
, eq
);
239 n
= newa(char, l
+ 2);
240 /* Change suffix Sec → USec */
241 strcpy(mempcpy(n
, field
, l
- 3), "USec");
243 r
= sd_bus_message_append(m
, "(sv)", n
, "t", t
);
245 return bus_log_create_error(r
);
250 static int bus_append_parse_size(sd_bus_message
*m
, const char *field
, const char *eq
, uint64_t base
) {
254 r
= parse_size(eq
, base
, &v
);
256 return log_error_errno(r
, "Failed to parse %s=%s: %m", field
, eq
);
258 r
= sd_bus_message_append(m
, "(sv)", field
, "t", v
);
260 return bus_log_create_error(r
);
265 static int bus_append_exec_command(sd_bus_message
*m
, const char *field
, const char *eq
) {
266 bool explicit_path
= false, done
= false;
267 _cleanup_strv_free_
char **l
= NULL
, **ex_opts
= NULL
;
268 _cleanup_free_
char *path
= NULL
, *upgraded_name
= NULL
;
269 ExecCommandFlags flags
= 0;
270 bool is_ex_prop
= endswith(field
, "Ex");
277 if (FLAGS_SET(flags
, EXEC_COMMAND_IGNORE_FAILURE
))
280 flags
|= EXEC_COMMAND_IGNORE_FAILURE
;
289 explicit_path
= true;
295 if (FLAGS_SET(flags
, EXEC_COMMAND_NO_ENV_EXPAND
))
298 flags
|= EXEC_COMMAND_NO_ENV_EXPAND
;
304 if (flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
))
307 flags
|= EXEC_COMMAND_FULLY_PRIVILEGED
;
313 if (flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_AMBIENT_MAGIC
))
315 else if (FLAGS_SET(flags
, EXEC_COMMAND_NO_SETUID
)) {
316 flags
&= ~EXEC_COMMAND_NO_SETUID
;
317 flags
|= EXEC_COMMAND_AMBIENT_MAGIC
;
320 flags
|= EXEC_COMMAND_NO_SETUID
;
331 if (!is_ex_prop
&& (flags
& (EXEC_COMMAND_NO_ENV_EXPAND
|EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
))) {
332 /* Upgrade the ExecXYZ= property to ExecXYZEx= for convenience */
334 upgraded_name
= strjoin(field
, "Ex");
340 r
= exec_command_flags_to_strv(flags
, &ex_opts
);
342 return log_error_errno(r
, "Failed to convert ExecCommandFlags to strv: %m");
346 r
= extract_first_word(&eq
, &path
, NULL
, EXTRACT_UNQUOTE
|EXTRACT_CUNESCAPE
);
348 return log_error_errno(r
, "Failed to parse path: %m");
351 r
= strv_split_full(&l
, eq
, NULL
, EXTRACT_UNQUOTE
|EXTRACT_CUNESCAPE
);
353 return log_error_errno(r
, "Failed to parse command line: %m");
355 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
357 return bus_log_create_error(r
);
359 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, upgraded_name
?: field
);
361 return bus_log_create_error(r
);
363 r
= sd_bus_message_open_container(m
, 'v', is_ex_prop
? "a(sasas)" : "a(sasb)");
365 return bus_log_create_error(r
);
367 r
= sd_bus_message_open_container(m
, 'a', is_ex_prop
? "(sasas)" : "(sasb)");
369 return bus_log_create_error(r
);
371 if (!strv_isempty(l
)) {
373 r
= sd_bus_message_open_container(m
, 'r', is_ex_prop
? "sasas" : "sasb");
375 return bus_log_create_error(r
);
377 r
= sd_bus_message_append(m
, "s", path
?: l
[0]);
379 return bus_log_create_error(r
);
381 r
= sd_bus_message_append_strv(m
, l
);
383 return bus_log_create_error(r
);
385 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
));
387 return bus_log_create_error(r
);
389 r
= sd_bus_message_close_container(m
);
391 return bus_log_create_error(r
);
394 r
= sd_bus_message_close_container(m
);
396 return bus_log_create_error(r
);
398 r
= sd_bus_message_close_container(m
);
400 return bus_log_create_error(r
);
402 r
= sd_bus_message_close_container(m
);
404 return bus_log_create_error(r
);
409 static int bus_append_ip_address_access(sd_bus_message
*m
, int family
, const union in_addr_union
*prefix
, unsigned char prefixlen
) {
415 r
= sd_bus_message_open_container(m
, 'r', "iayu");
419 r
= sd_bus_message_append(m
, "i", family
);
423 r
= sd_bus_message_append_array(m
, 'y', prefix
, FAMILY_ADDRESS_SIZE(family
));
427 r
= sd_bus_message_append(m
, "u", prefixlen
);
431 return sd_bus_message_close_container(m
);
434 static int bus_append_cgroup_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
437 if (STR_IN_SET(field
, "DevicePolicy",
440 "ManagedOOMMemoryPressure",
441 "ManagedOOMPreference"))
442 return bus_append_string(m
, field
, eq
);
444 if (STR_IN_SET(field
, "ManagedOOMMemoryPressureLimit")) {
445 r
= parse_permyriad(eq
);
447 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
449 /* Pass around scaled to 2^32-1 == 100% */
450 r
= sd_bus_message_append(m
, "(sv)", field
, "u", UINT32_SCALE_FROM_PERMYRIAD(r
));
452 return bus_log_create_error(r
);
457 if (STR_IN_SET(field
, "CPUAccounting",
463 return bus_append_parse_boolean(m
, field
, eq
);
465 if (STR_IN_SET(field
, "CPUWeight",
469 return bus_append_cg_weight_parse(m
, field
, eq
);
471 if (STR_IN_SET(field
, "CPUShares",
473 return bus_append_cg_cpu_shares_parse(m
, field
, eq
);
475 if (STR_IN_SET(field
, "AllowedCPUs",
476 "AllowedMemoryNodes")) {
477 _cleanup_(cpu_set_reset
) CPUSet cpuset
= {};
478 _cleanup_free_
uint8_t *array
= NULL
;
481 r
= parse_cpu_set(eq
, &cpuset
);
483 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
485 r
= cpu_set_to_dbus(&cpuset
, &array
, &allocated
);
487 return log_error_errno(r
, "Failed to serialize CPUSet: %m");
489 return bus_append_byte_array(m
, field
, array
, allocated
);
492 if (STR_IN_SET(field
, "BlockIOWeight",
493 "StartupBlockIOWeight"))
494 return bus_append_cg_blkio_weight_parse(m
, field
, eq
);
496 if (streq(field
, "DisableControllers"))
497 return bus_append_strv(m
, "DisableControllers", eq
, EXTRACT_UNQUOTE
);
499 if (streq(field
, "Delegate")) {
500 r
= parse_boolean(eq
);
502 return bus_append_strv(m
, "DelegateControllers", eq
, EXTRACT_UNQUOTE
);
504 r
= sd_bus_message_append(m
, "(sv)", "Delegate", "b", r
);
506 return bus_log_create_error(r
);
511 if (STR_IN_SET(field
, "MemoryMin",
521 if (streq(eq
, "infinity")) {
522 r
= sd_bus_message_append(m
, "(sv)", field
, "t", CGROUP_LIMIT_MAX
);
524 return bus_log_create_error(r
);
526 } else if (isempty(eq
)) {
527 uint64_t empty_value
= STR_IN_SET(field
,
535 r
= sd_bus_message_append(m
, "(sv)", field
, "t", empty_value
);
537 return bus_log_create_error(r
);
541 r
= parse_permyriad(eq
);
545 /* When this is a percentage we'll convert this into a relative value in the range 0…UINT32_MAX
546 * and pass it in the MemoryLowScale property (and related ones). This way the physical memory
547 * size can be determined server-side. */
549 n
= strjoina(field
, "Scale");
550 r
= sd_bus_message_append(m
, "(sv)", n
, "u", UINT32_SCALE_FROM_PERMYRIAD(r
));
552 return bus_log_create_error(r
);
557 if (streq(field
, "TasksMax"))
558 return bus_append_safe_atou64(m
, field
, eq
);
560 return bus_append_parse_size(m
, field
, eq
, 1024);
563 if (streq(field
, "CPUQuota")) {
565 r
= sd_bus_message_append(m
, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY
);
567 r
= parse_permyriad_unbounded(eq
);
569 return log_error_errno(SYNTHETIC_ERRNO(ERANGE
),
570 "CPU quota too small.");
572 return log_error_errno(r
, "CPU quota '%s' invalid.", eq
);
574 r
= sd_bus_message_append(m
, "(sv)", "CPUQuotaPerSecUSec", "t", (((uint64_t) r
* USEC_PER_SEC
) / 10000U));
578 return bus_log_create_error(r
);
583 if (streq(field
, "CPUQuotaPeriodSec")) {
584 usec_t u
= USEC_INFINITY
;
586 r
= parse_sec_def_infinity(eq
, &u
);
588 return log_error_errno(r
, "CPU quota period '%s' invalid.", eq
);
590 r
= sd_bus_message_append(m
, "(sv)", "CPUQuotaPeriodUSec", "t", u
);
592 return bus_log_create_error(r
);
597 if (streq(field
, "DeviceAllow")) {
599 r
= sd_bus_message_append(m
, "(sv)", field
, "a(ss)", 0);
601 const char *path
= eq
, *rwm
= NULL
, *e
;
605 path
= strndupa(eq
, e
- eq
);
609 r
= sd_bus_message_append(m
, "(sv)", field
, "a(ss)", 1, path
, strempty(rwm
));
613 return bus_log_create_error(r
);
618 if (cgroup_io_limit_type_from_string(field
) >= 0 || STR_IN_SET(field
, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
620 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 0);
622 const char *path
, *bandwidth
, *e
;
627 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
628 "Failed to parse %s value %s.",
631 path
= strndupa(eq
, e
- eq
);
634 if (streq(bandwidth
, "infinity"))
635 bytes
= CGROUP_LIMIT_MAX
;
637 r
= parse_size(bandwidth
, 1000, &bytes
);
639 return log_error_errno(r
, "Failed to parse byte value %s: %m", bandwidth
);
642 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 1, path
, bytes
);
646 return bus_log_create_error(r
);
651 if (STR_IN_SET(field
, "IODeviceWeight",
652 "BlockIODeviceWeight")) {
654 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 0);
656 const char *path
, *weight
, *e
;
661 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
662 "Failed to parse %s value %s.",
665 path
= strndupa(eq
, e
- eq
);
668 r
= safe_atou64(weight
, &u
);
670 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, weight
);
672 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 1, path
, u
);
676 return bus_log_create_error(r
);
681 if (streq(field
, "IODeviceLatencyTargetSec")) {
682 const char *field_usec
= "IODeviceLatencyTargetUSec";
685 r
= sd_bus_message_append(m
, "(sv)", field_usec
, "a(st)", USEC_INFINITY
);
687 const char *path
, *target
, *e
;
692 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
693 "Failed to parse %s value %s.",
696 path
= strndupa(eq
, e
- eq
);
699 r
= parse_sec(target
, &usec
);
701 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, target
);
703 r
= sd_bus_message_append(m
, "(sv)", field_usec
, "a(st)", 1, path
, usec
);
707 return bus_log_create_error(r
);
712 if (STR_IN_SET(field
, "IPAddressAllow",
714 unsigned char prefixlen
;
715 union in_addr_union prefix
= {};
719 r
= sd_bus_message_append(m
, "(sv)", field
, "a(iayu)", 0);
721 return bus_log_create_error(r
);
726 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
728 return bus_log_create_error(r
);
730 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
732 return bus_log_create_error(r
);
734 r
= sd_bus_message_open_container(m
, 'v', "a(iayu)");
736 return bus_log_create_error(r
);
738 r
= sd_bus_message_open_container(m
, 'a', "(iayu)");
740 return bus_log_create_error(r
);
742 if (streq(eq
, "any")) {
743 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
745 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 0);
747 return bus_log_create_error(r
);
749 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 0);
751 return bus_log_create_error(r
);
753 } else if (is_localhost(eq
)) {
754 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
756 prefix
.in
.s_addr
= htobe32(0x7f000000);
757 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 8);
759 return bus_log_create_error(r
);
761 prefix
.in6
= (struct in6_addr
) IN6ADDR_LOOPBACK_INIT
;
762 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 128);
766 } else if (streq(eq
, "link-local")) {
767 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
769 prefix
.in
.s_addr
= htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
770 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 16);
772 return bus_log_create_error(r
);
774 prefix
.in6
= (struct in6_addr
) {
775 .s6_addr32
[0] = htobe32(0xfe800000)
777 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 64);
779 return bus_log_create_error(r
);
781 } else if (streq(eq
, "multicast")) {
782 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
784 prefix
.in
.s_addr
= htobe32((UINT32_C(224) << 24));
785 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 4);
787 return bus_log_create_error(r
);
789 prefix
.in6
= (struct in6_addr
) {
790 .s6_addr32
[0] = htobe32(0xff000000)
792 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 8);
794 return bus_log_create_error(r
);
798 _cleanup_free_
char *word
= NULL
;
800 r
= extract_first_word(&eq
, &word
, NULL
, 0);
806 return log_error_errno(r
, "Failed to parse %s: %s", field
, eq
);
808 r
= in_addr_prefix_from_string_auto(word
, &family
, &prefix
, &prefixlen
);
810 return log_error_errno(r
, "Failed to parse IP address prefix: %s", word
);
812 r
= bus_append_ip_address_access(m
, family
, &prefix
, prefixlen
);
814 return bus_log_create_error(r
);
818 r
= sd_bus_message_close_container(m
);
820 return bus_log_create_error(r
);
822 r
= sd_bus_message_close_container(m
);
824 return bus_log_create_error(r
);
826 r
= sd_bus_message_close_container(m
);
828 return bus_log_create_error(r
);
833 if (STR_IN_SET(field
, "IPIngressFilterPath",
834 "IPEgressFilterPath")) {
836 r
= sd_bus_message_append(m
, "(sv)", field
, "as", 0);
838 r
= sd_bus_message_append(m
, "(sv)", field
, "as", 1, eq
);
841 return bus_log_create_error(r
);
846 if (streq(field
, "BPFProgram")) {
848 r
= sd_bus_message_append(m
, "(sv)", field
, "a(ss)", 0);
850 _cleanup_free_
char *word
= NULL
;
852 r
= extract_first_word(&eq
, &word
, ":", 0);
856 return log_error_errno(r
, "Failed to parse %s: %m", field
);
858 r
= sd_bus_message_append(m
, "(sv)", field
, "a(ss)", 1, word
, eq
);
861 return bus_log_create_error(r
);
866 if (STR_IN_SET(field
, "SocketBindAllow",
869 r
= sd_bus_message_append(m
, "(sv)", field
, "a(iiqq)", 0);
871 /* No ip protocol specified for now. */
872 int32_t family
= AF_UNSPEC
, ip_protocol
= 0;
873 const char *address_family
, *user_port
;
874 _cleanup_free_
char *word
= NULL
;
876 r
= extract_first_word(&eq
, &word
, ":", 0);
880 return log_error_errno(r
, "Failed to parse %s: %m", field
);
882 address_family
= eq
? word
: NULL
;
883 if (address_family
) {
884 family
= af_from_ipv4_ipv6(address_family
);
885 if (family
== AF_UNSPEC
)
886 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
887 "Only \"ipv4\" and \"ipv6\" protocols are supported");
890 user_port
= eq
? eq
: word
;
891 if (streq(user_port
, "any")) {
892 r
= sd_bus_message_append(m
, "(sv)", field
, "a(iiqq)", 1, family
, ip_protocol
, 0, 0);
894 return bus_log_create_error(r
);
896 uint16_t port_min
, port_max
;
898 r
= parse_ip_port_range(user_port
, &port_min
, &port_max
);
902 return log_error_errno(r
, "Invalid port or port range: %s", user_port
);
904 r
= sd_bus_message_append(
905 m
, "(sv)", field
, "a(iiqq)", 1, family
, ip_protocol
, port_max
- port_min
+ 1, port_min
);
909 return bus_log_create_error(r
);
917 static int bus_append_automount_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
918 if (streq(field
, "Where"))
919 return bus_append_string(m
, field
, eq
);
921 if (streq(field
, "DirectoryMode"))
922 return bus_append_parse_mode(m
, field
, eq
);
924 if (streq(field
, "TimeoutIdleSec"))
925 return bus_append_parse_sec_rename(m
, field
, eq
);
930 static int bus_append_execute_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
934 if (STR_IN_SET(field
, "User",
948 "RuntimeDirectoryPreserve",
953 "NetworkNamespacePath",
956 return bus_append_string(m
, field
, eq
);
958 if (STR_IN_SET(field
, "IgnoreSIGPIPE",
970 "MemoryDenyWriteExecute",
974 "ProtectKernelTunables",
975 "ProtectKernelModules",
978 "ProtectControlGroups",
980 "CPUSchedulingResetOnFork",
984 return bus_append_parse_boolean(m
, field
, eq
);
986 if (STR_IN_SET(field
, "ReadWriteDirectories",
987 "ReadOnlyDirectories",
988 "InaccessibleDirectories",
998 "ConfigurationDirectory",
999 "SupplementaryGroups",
1000 "SystemCallArchitectures"))
1001 return bus_append_strv(m
, field
, eq
, EXTRACT_UNQUOTE
);
1003 if (STR_IN_SET(field
, "SyslogLevel",
1005 return bus_append_log_level_from_string(m
, field
, eq
);
1007 if (streq(field
, "SyslogFacility"))
1008 return bus_append_log_facility_unshifted_from_string(m
, field
, eq
);
1010 if (streq(field
, "SecureBits"))
1011 return bus_append_secure_bits_from_string(m
, field
, eq
);
1013 if (streq(field
, "CPUSchedulingPolicy"))
1014 return bus_append_sched_policy_from_string(m
, field
, eq
);
1016 if (STR_IN_SET(field
, "CPUSchedulingPriority",
1018 return bus_append_safe_atoi(m
, field
, eq
);
1020 if (streq(field
, "CoredumpFilter"))
1021 return bus_append_coredump_filter_mask_from_string(m
, field
, eq
);
1023 if (streq(field
, "Nice"))
1024 return bus_append_parse_nice(m
, field
, eq
);
1026 if (streq(field
, "SystemCallErrorNumber"))
1027 return bus_append_seccomp_parse_errno_or_action(m
, field
, eq
);
1029 if (streq(field
, "IOSchedulingClass"))
1030 return bus_append_ioprio_class_from_string(m
, field
, eq
);
1032 if (streq(field
, "IOSchedulingPriority"))
1033 return bus_append_ioprio_parse_priority(m
, field
, eq
);
1035 if (STR_IN_SET(field
, "RuntimeDirectoryMode",
1036 "StateDirectoryMode",
1037 "CacheDirectoryMode",
1038 "LogsDirectoryMode",
1039 "ConfigurationDirectoryMode",
1041 return bus_append_parse_mode(m
, field
, eq
);
1043 if (streq(field
, "TimerSlackNSec"))
1044 return bus_append_parse_nsec(m
, field
, eq
);
1046 if (streq(field
, "LogRateLimitIntervalSec"))
1047 return bus_append_parse_sec_rename(m
, field
, eq
);
1049 if (streq(field
, "LogRateLimitBurst"))
1050 return bus_append_safe_atou(m
, field
, eq
);
1052 if (streq(field
, "MountFlags"))
1053 return bus_append_mount_propagation_flags_from_string(m
, field
, eq
);
1055 if (STR_IN_SET(field
, "Environment",
1058 return bus_append_strv(m
, field
, eq
, EXTRACT_UNQUOTE
|EXTRACT_CUNESCAPE
);
1060 if (streq(field
, "EnvironmentFile")) {
1062 r
= sd_bus_message_append(m
, "(sv)", "EnvironmentFiles", "a(sb)", 0);
1064 r
= sd_bus_message_append(m
, "(sv)", "EnvironmentFiles", "a(sb)", 1,
1065 eq
[0] == '-' ? eq
+ 1 : eq
,
1068 return bus_log_create_error(r
);
1073 if (streq(field
, "SetCredential")) {
1074 r
= sd_bus_message_open_container(m
, 'r', "sv");
1076 return bus_log_create_error(r
);
1078 r
= sd_bus_message_append_basic(m
, 's', "SetCredential");
1080 return bus_log_create_error(r
);
1082 r
= sd_bus_message_open_container(m
, 'v', "a(say)");
1084 return bus_log_create_error(r
);
1087 r
= sd_bus_message_append(m
, "a(say)", 0);
1089 _cleanup_free_
char *word
= NULL
, *unescaped
= NULL
;
1093 r
= extract_first_word(&p
, &word
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
1097 return log_error_errno(r
, "Failed to parse SetCredential= parameter: %s", eq
);
1099 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Missing argument to SetCredential=.");
1101 l
= cunescape(p
, UNESCAPE_ACCEPT_NUL
, &unescaped
);
1103 return log_error_errno(l
, "Failed to unescape SetCredential= value: %s", p
);
1105 r
= sd_bus_message_open_container(m
, 'a', "(say)");
1107 return bus_log_create_error(r
);
1109 r
= sd_bus_message_open_container(m
, 'r', "say");
1111 return bus_log_create_error(r
);
1113 r
= sd_bus_message_append(m
, "s", word
);
1115 return bus_log_create_error(r
);
1117 r
= sd_bus_message_append_array(m
, 'y', unescaped
, l
);
1119 return bus_log_create_error(r
);
1121 r
= sd_bus_message_close_container(m
);
1123 return bus_log_create_error(r
);
1125 r
= sd_bus_message_close_container(m
);
1128 return bus_log_create_error(r
);
1130 r
= sd_bus_message_close_container(m
);
1132 return bus_log_create_error(r
);
1134 r
= sd_bus_message_close_container(m
);
1136 return bus_log_create_error(r
);
1141 if (streq(field
, "LoadCredential")) {
1142 r
= sd_bus_message_open_container(m
, 'r', "sv");
1144 return bus_log_create_error(r
);
1146 r
= sd_bus_message_append_basic(m
, 's', "LoadCredential");
1148 return bus_log_create_error(r
);
1150 r
= sd_bus_message_open_container(m
, 'v', "a(ss)");
1152 return bus_log_create_error(r
);
1155 r
= sd_bus_message_append(m
, "a(ss)", 0);
1157 _cleanup_free_
char *word
= NULL
;
1160 r
= extract_first_word(&p
, &word
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
1164 return log_error_errno(r
, "Failed to parse LoadCredential= parameter: %s", eq
);
1166 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Missing argument to LoadCredential=.");
1168 r
= sd_bus_message_append(m
, "a(ss)", 1, word
, p
);
1171 return bus_log_create_error(r
);
1173 r
= sd_bus_message_close_container(m
);
1175 return bus_log_create_error(r
);
1177 r
= sd_bus_message_close_container(m
);
1179 return bus_log_create_error(r
);
1184 if (streq(field
, "LogExtraFields")) {
1185 r
= sd_bus_message_open_container(m
, 'r', "sv");
1187 return bus_log_create_error(r
);
1189 r
= sd_bus_message_append_basic(m
, 's', "LogExtraFields");
1191 return bus_log_create_error(r
);
1193 r
= sd_bus_message_open_container(m
, 'v', "aay");
1195 return bus_log_create_error(r
);
1197 r
= sd_bus_message_open_container(m
, 'a', "ay");
1199 return bus_log_create_error(r
);
1201 r
= sd_bus_message_append_array(m
, 'y', eq
, strlen(eq
));
1203 return bus_log_create_error(r
);
1205 r
= sd_bus_message_close_container(m
);
1207 return bus_log_create_error(r
);
1209 r
= sd_bus_message_close_container(m
);
1211 return bus_log_create_error(r
);
1213 r
= sd_bus_message_close_container(m
);
1215 return bus_log_create_error(r
);
1220 if (STR_IN_SET(field
, "StandardInput",
1223 const char *n
, *appended
;
1225 if ((n
= startswith(eq
, "fd:"))) {
1226 appended
= strjoina(field
, "FileDescriptorName");
1227 r
= sd_bus_message_append(m
, "(sv)", appended
, "s", n
);
1228 } else if ((n
= startswith(eq
, "file:"))) {
1229 appended
= strjoina(field
, "File");
1230 r
= sd_bus_message_append(m
, "(sv)", appended
, "s", n
);
1231 } else if ((n
= startswith(eq
, "append:"))) {
1232 appended
= strjoina(field
, "FileToAppend");
1233 r
= sd_bus_message_append(m
, "(sv)", appended
, "s", n
);
1234 } else if ((n
= startswith(eq
, "truncate:"))) {
1235 appended
= strjoina(field
, "FileToTruncate");
1236 r
= sd_bus_message_append(m
, "(sv)", appended
, "s", n
);
1238 r
= sd_bus_message_append(m
, "(sv)", field
, "s", eq
);
1240 return bus_log_create_error(r
);
1245 if (streq(field
, "StandardInputText")) {
1246 _cleanup_free_
char *unescaped
= NULL
;
1248 r
= cunescape(eq
, 0, &unescaped
);
1250 return log_error_errno(r
, "Failed to unescape text '%s': %m", eq
);
1252 if (!strextend(&unescaped
, "\n"))
1255 /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
1256 * interface anyway */
1258 return bus_append_byte_array(m
, field
, unescaped
, strlen(unescaped
));
1261 if (streq(field
, "StandardInputData")) {
1262 _cleanup_free_
void *decoded
= NULL
;
1265 r
= unbase64mem(eq
, SIZE_MAX
, &decoded
, &sz
);
1267 return log_error_errno(r
, "Failed to decode base64 data '%s': %m", eq
);
1269 return bus_append_byte_array(m
, field
, decoded
, sz
);
1272 if ((suffix
= startswith(field
, "Limit"))) {
1275 rl
= rlimit_from_string(suffix
);
1280 r
= rlimit_parse(rl
, eq
, &l
);
1282 return log_error_errno(r
, "Failed to parse resource limit: %s", eq
);
1284 r
= sd_bus_message_append(m
, "(sv)", field
, "t", l
.rlim_max
);
1286 return bus_log_create_error(r
);
1288 sn
= strjoina(field
, "Soft");
1289 r
= sd_bus_message_append(m
, "(sv)", sn
, "t", l
.rlim_cur
);
1291 return bus_log_create_error(r
);
1297 if (STR_IN_SET(field
, "AppArmorProfile",
1298 "SmackProcessLabel")) {
1307 r
= sd_bus_message_append(m
, "(sv)", field
, "(bs)", ignore
, s
);
1309 return bus_log_create_error(r
);
1314 if (STR_IN_SET(field
, "CapabilityBoundingSet",
1315 "AmbientCapabilities")) {
1317 bool invert
= false;
1325 r
= capability_set_from_string(p
, &sum
);
1327 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
1329 sum
= invert
? ~sum
: sum
;
1331 r
= sd_bus_message_append(m
, "(sv)", field
, "t", sum
);
1333 return bus_log_create_error(r
);
1338 if (streq(field
, "CPUAffinity")) {
1339 _cleanup_(cpu_set_reset
) CPUSet cpuset
= {};
1340 _cleanup_free_
uint8_t *array
= NULL
;
1343 if (eq
&& streq(eq
, "numa")) {
1344 r
= sd_bus_message_append(m
, "(sv)", "CPUAffinityFromNUMA", "b", true);
1346 return bus_log_create_error(r
);
1350 r
= parse_cpu_set(eq
, &cpuset
);
1352 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
1354 r
= cpu_set_to_dbus(&cpuset
, &array
, &allocated
);
1356 return log_error_errno(r
, "Failed to serialize CPUAffinity: %m");
1358 return bus_append_byte_array(m
, field
, array
, allocated
);
1361 if (streq(field
, "NUMAPolicy")) {
1362 r
= mpol_from_string(eq
);
1364 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
1366 r
= sd_bus_message_append(m
, "(sv)", field
, "i", (int32_t) r
);
1368 return bus_log_create_error(r
);
1373 if (streq(field
, "NUMAMask")) {
1374 _cleanup_(cpu_set_reset
) CPUSet nodes
= {};
1375 _cleanup_free_
uint8_t *array
= NULL
;
1378 if (eq
&& streq(eq
, "all")) {
1379 r
= numa_mask_add_all(&nodes
);
1381 return log_error_errno(r
, "Failed to create NUMA mask representing \"all\" NUMA nodes: %m");
1383 r
= parse_cpu_set(eq
, &nodes
);
1385 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
1388 r
= cpu_set_to_dbus(&nodes
, &array
, &allocated
);
1390 return log_error_errno(r
, "Failed to serialize NUMAMask: %m");
1392 return bus_append_byte_array(m
, field
, array
, allocated
);
1395 if (STR_IN_SET(field
, "RestrictAddressFamilies",
1406 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
1408 return bus_log_create_error(r
);
1410 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
1412 return bus_log_create_error(r
);
1414 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
1416 return bus_log_create_error(r
);
1418 r
= sd_bus_message_open_container(m
, 'r', "bas");
1420 return bus_log_create_error(r
);
1422 r
= sd_bus_message_append_basic(m
, 'b', &allow_list
);
1424 return bus_log_create_error(r
);
1426 r
= sd_bus_message_open_container(m
, 'a', "s");
1428 return bus_log_create_error(r
);
1431 _cleanup_free_
char *word
= NULL
;
1433 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_UNQUOTE
);
1439 return log_error_errno(r
, "Invalid syntax: %s", eq
);
1441 r
= sd_bus_message_append_basic(m
, 's', word
);
1443 return bus_log_create_error(r
);
1446 r
= sd_bus_message_close_container(m
);
1448 return bus_log_create_error(r
);
1450 r
= sd_bus_message_close_container(m
);
1452 return bus_log_create_error(r
);
1454 r
= sd_bus_message_close_container(m
);
1456 return bus_log_create_error(r
);
1458 r
= sd_bus_message_close_container(m
);
1460 return bus_log_create_error(r
);
1465 if (streq(field
, "RestrictNamespaces")) {
1466 bool invert
= false;
1467 unsigned long flags
;
1469 r
= parse_boolean(eq
);
1473 flags
= NAMESPACE_FLAGS_ALL
;
1480 r
= namespace_flags_from_string(eq
, &flags
);
1482 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
1486 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
1488 r
= sd_bus_message_append(m
, "(sv)", field
, "t", (uint64_t) flags
);
1490 return bus_log_create_error(r
);
1495 if (STR_IN_SET(field
, "BindPaths",
1496 "BindReadOnlyPaths")) {
1499 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
1501 return bus_log_create_error(r
);
1503 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
1505 return bus_log_create_error(r
);
1507 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
1509 return bus_log_create_error(r
);
1511 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
1513 return bus_log_create_error(r
);
1516 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
1517 char *s
= NULL
, *d
= NULL
;
1518 bool ignore_enoent
= false;
1519 uint64_t flags
= MS_REC
;
1521 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_UNQUOTE
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1523 return log_error_errno(r
, "Failed to parse argument: %m");
1529 ignore_enoent
= true;
1533 if (p
&& p
[-1] == ':') {
1534 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_UNQUOTE
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1536 return log_error_errno(r
, "Failed to parse argument: %m");
1538 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1539 "Missing argument after ':': %s",
1544 if (p
&& p
[-1] == ':') {
1545 _cleanup_free_
char *options
= NULL
;
1547 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_UNQUOTE
);
1549 return log_error_errno(r
, "Failed to parse argument: %m");
1551 if (isempty(options
) || streq(options
, "rbind"))
1553 else if (streq(options
, "norbind"))
1556 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1557 "Unknown options: %s",
1563 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
1565 return bus_log_create_error(r
);
1568 r
= sd_bus_message_close_container(m
);
1570 return bus_log_create_error(r
);
1572 r
= sd_bus_message_close_container(m
);
1574 return bus_log_create_error(r
);
1576 r
= sd_bus_message_close_container(m
);
1578 return bus_log_create_error(r
);
1583 if (streq(field
, "TemporaryFileSystem")) {
1586 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
1588 return bus_log_create_error(r
);
1590 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
1592 return bus_log_create_error(r
);
1594 r
= sd_bus_message_open_container(m
, 'v', "a(ss)");
1596 return bus_log_create_error(r
);
1598 r
= sd_bus_message_open_container(m
, 'a', "(ss)");
1600 return bus_log_create_error(r
);
1603 _cleanup_free_
char *word
= NULL
, *path
= NULL
;
1606 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_UNQUOTE
);
1608 return log_error_errno(r
, "Failed to parse argument: %m");
1613 r
= extract_first_word(&w
, &path
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
1615 return log_error_errno(r
, "Failed to parse argument: %m");
1617 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1618 "Failed to parse argument: %s",
1621 r
= sd_bus_message_append(m
, "(ss)", path
, w
);
1623 return bus_log_create_error(r
);
1626 r
= sd_bus_message_close_container(m
);
1628 return bus_log_create_error(r
);
1630 r
= sd_bus_message_close_container(m
);
1632 return bus_log_create_error(r
);
1634 r
= sd_bus_message_close_container(m
);
1636 return bus_log_create_error(r
);
1641 if (streq(field
, "RootHash")) {
1642 _cleanup_free_
void *roothash_decoded
= NULL
;
1643 size_t roothash_decoded_size
= 0;
1645 /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
1646 if (path_is_absolute(eq
))
1647 return bus_append_string(m
, "RootHashPath", eq
);
1649 /* We have a roothash to decode, eg: RootHash=012345789abcdef */
1650 r
= unhexmem(eq
, strlen(eq
), &roothash_decoded
, &roothash_decoded_size
);
1652 return log_error_errno(r
, "Failed to decode RootHash= '%s': %m", eq
);
1653 if (roothash_decoded_size
< sizeof(sd_id128_t
))
1654 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "RootHash= '%s' is too short: %m", eq
);
1656 return bus_append_byte_array(m
, field
, roothash_decoded
, roothash_decoded_size
);
1659 if (streq(field
, "RootHashSignature")) {
1660 _cleanup_free_
void *roothash_sig_decoded
= NULL
;
1662 size_t roothash_sig_decoded_size
= 0;
1664 /* We have the path to a roothash signature to load and decode, eg: RootHash=/foo/bar.roothash.p7s */
1665 if (path_is_absolute(eq
))
1666 return bus_append_string(m
, "RootHashSignaturePath", eq
);
1668 if (!(value
= startswith(eq
, "base64:")))
1669 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to decode RootHashSignature= '%s', not a path but doesn't start with 'base64:': %m", eq
);
1671 /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
1672 r
= unbase64mem(value
, strlen(value
), &roothash_sig_decoded
, &roothash_sig_decoded_size
);
1674 return log_error_errno(r
, "Failed to decode RootHashSignature= '%s': %m", eq
);
1676 return bus_append_byte_array(m
, field
, roothash_sig_decoded
, roothash_sig_decoded_size
);
1679 if (streq(field
, "RootImageOptions")) {
1680 _cleanup_strv_free_
char **l
= NULL
;
1681 char **first
= NULL
, **second
= NULL
;
1684 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
1686 return bus_log_create_error(r
);
1688 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
1690 return bus_log_create_error(r
);
1692 r
= sd_bus_message_open_container(m
, 'v', "a(ss)");
1694 return bus_log_create_error(r
);
1696 r
= sd_bus_message_open_container(m
, 'a', "(ss)");
1698 return bus_log_create_error(r
);
1700 r
= strv_split_colon_pairs(&l
, p
);
1702 return log_error_errno(r
, "Failed to parse argument: %m");
1704 STRV_FOREACH_PAIR(first
, second
, l
) {
1705 r
= sd_bus_message_append(m
, "(ss)",
1706 !isempty(*second
) ? *first
: "root",
1707 !isempty(*second
) ? *second
: *first
);
1709 return bus_log_create_error(r
);
1712 r
= sd_bus_message_close_container(m
);
1714 return bus_log_create_error(r
);
1716 r
= sd_bus_message_close_container(m
);
1718 return bus_log_create_error(r
);
1720 r
= sd_bus_message_close_container(m
);
1722 return bus_log_create_error(r
);
1727 if (streq(field
, "MountImages")) {
1730 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
1732 return bus_log_create_error(r
);
1734 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
1736 return bus_log_create_error(r
);
1738 r
= sd_bus_message_open_container(m
, 'v', "a(ssba(ss))");
1740 return bus_log_create_error(r
);
1742 r
= sd_bus_message_open_container(m
, 'a', "(ssba(ss))");
1744 return bus_log_create_error(r
);
1747 _cleanup_free_
char *first
= NULL
, *second
= NULL
, *tuple
= NULL
;
1748 const char *q
= NULL
, *source
= NULL
;
1749 bool permissive
= false;
1751 r
= extract_first_word(&p
, &tuple
, NULL
, EXTRACT_UNQUOTE
|EXTRACT_RETAIN_ESCAPE
);
1753 return log_error_errno(r
, "Failed to parse MountImages= property: %s", eq
);
1758 r
= extract_many_words(&q
, ":", EXTRACT_CUNESCAPE
|EXTRACT_UNESCAPE_SEPARATORS
, &first
, &second
, NULL
);
1760 return log_error_errno(r
, "Failed to parse MountImages= property: %s", eq
);
1765 if (source
[0] == '-') {
1770 if (isempty(second
))
1771 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1772 "Missing argument after ':': %s",
1775 r
= sd_bus_message_open_container(m
, 'r', "ssba(ss)");
1777 return bus_log_create_error(r
);
1779 r
= sd_bus_message_append(m
, "ssb", source
, second
, permissive
);
1781 return bus_log_create_error(r
);
1783 r
= sd_bus_message_open_container(m
, 'a', "(ss)");
1785 return bus_log_create_error(r
);
1788 _cleanup_free_
char *partition
= NULL
, *mount_options
= NULL
;
1790 r
= extract_many_words(&q
, ":", EXTRACT_CUNESCAPE
|EXTRACT_UNESCAPE_SEPARATORS
, &partition
, &mount_options
, NULL
);
1792 return log_error_errno(r
, "Failed to parse MountImages= property: %s", eq
);
1795 /* Single set of options, applying to the root partition/single filesystem */
1797 r
= sd_bus_message_append(m
, "(ss)", "root", partition
);
1799 return bus_log_create_error(r
);
1804 r
= sd_bus_message_append(m
, "(ss)", partition
, mount_options
);
1806 return bus_log_create_error(r
);
1809 r
= sd_bus_message_close_container(m
);
1811 return bus_log_create_error(r
);
1813 r
= sd_bus_message_close_container(m
);
1815 return bus_log_create_error(r
);
1818 r
= sd_bus_message_close_container(m
);
1820 return bus_log_create_error(r
);
1822 r
= sd_bus_message_close_container(m
);
1824 return bus_log_create_error(r
);
1826 r
= sd_bus_message_close_container(m
);
1828 return bus_log_create_error(r
);
1833 if (streq(field
, "ExtensionImages")) {
1836 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
1838 return bus_log_create_error(r
);
1840 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
1842 return bus_log_create_error(r
);
1844 r
= sd_bus_message_open_container(m
, 'v', "a(sba(ss))");
1846 return bus_log_create_error(r
);
1848 r
= sd_bus_message_open_container(m
, 'a', "(sba(ss))");
1850 return bus_log_create_error(r
);
1853 _cleanup_free_
char *source
= NULL
, *tuple
= NULL
;
1854 const char *q
= NULL
, *s
= NULL
;
1855 bool permissive
= false;
1857 r
= extract_first_word(&p
, &tuple
, NULL
, EXTRACT_UNQUOTE
|EXTRACT_RETAIN_ESCAPE
);
1859 return log_error_errno(r
, "Failed to parse ExtensionImages= property: %s", eq
);
1864 r
= extract_first_word(&q
, &source
, ":", EXTRACT_CUNESCAPE
|EXTRACT_UNESCAPE_SEPARATORS
);
1866 return log_error_errno(r
, "Failed to parse ExtensionImages= property: %s", eq
);
1876 r
= sd_bus_message_open_container(m
, 'r', "sba(ss)");
1878 return bus_log_create_error(r
);
1880 r
= sd_bus_message_append(m
, "sb", s
, permissive
);
1882 return bus_log_create_error(r
);
1884 r
= sd_bus_message_open_container(m
, 'a', "(ss)");
1886 return bus_log_create_error(r
);
1889 _cleanup_free_
char *partition
= NULL
, *mount_options
= NULL
;
1891 r
= extract_many_words(&q
, ":", EXTRACT_CUNESCAPE
|EXTRACT_UNESCAPE_SEPARATORS
, &partition
, &mount_options
, NULL
);
1893 return log_error_errno(r
, "Failed to parse ExtensionImages= property: %s", eq
);
1896 /* Single set of options, applying to the root partition/single filesystem */
1898 r
= sd_bus_message_append(m
, "(ss)", "root", partition
);
1900 return bus_log_create_error(r
);
1905 r
= sd_bus_message_append(m
, "(ss)", partition
, mount_options
);
1907 return bus_log_create_error(r
);
1910 r
= sd_bus_message_close_container(m
);
1912 return bus_log_create_error(r
);
1914 r
= sd_bus_message_close_container(m
);
1916 return bus_log_create_error(r
);
1919 r
= sd_bus_message_close_container(m
);
1921 return bus_log_create_error(r
);
1923 r
= sd_bus_message_close_container(m
);
1925 return bus_log_create_error(r
);
1927 r
= sd_bus_message_close_container(m
);
1929 return bus_log_create_error(r
);
1937 static int bus_append_kill_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1938 if (streq(field
, "KillMode"))
1939 return bus_append_string(m
, field
, eq
);
1941 if (STR_IN_SET(field
, "SendSIGHUP",
1943 return bus_append_parse_boolean(m
, field
, eq
);
1945 if (STR_IN_SET(field
, "KillSignal",
1946 "RestartKillSignal",
1949 return bus_append_signal_from_string(m
, field
, eq
);
1954 static int bus_append_mount_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1956 if (STR_IN_SET(field
, "What",
1960 return bus_append_string(m
, field
, eq
);
1962 if (streq(field
, "TimeoutSec"))
1963 return bus_append_parse_sec_rename(m
, field
, eq
);
1965 if (streq(field
, "DirectoryMode"))
1966 return bus_append_parse_mode(m
, field
, eq
);
1968 if (STR_IN_SET(field
, "SloppyOptions",
1972 return bus_append_parse_boolean(m
, field
, eq
);
1977 static int bus_append_path_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1980 if (streq(field
, "MakeDirectory"))
1981 return bus_append_parse_boolean(m
, field
, eq
);
1983 if (streq(field
, "DirectoryMode"))
1984 return bus_append_parse_mode(m
, field
, eq
);
1986 if (STR_IN_SET(field
, "PathExists",
1990 "DirectoryNotEmpty")) {
1992 r
= sd_bus_message_append(m
, "(sv)", "Paths", "a(ss)", 0);
1994 r
= sd_bus_message_append(m
, "(sv)", "Paths", "a(ss)", 1, field
, eq
);
1996 return bus_log_create_error(r
);
2004 static int bus_append_scope_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
2005 if (streq(field
, "RuntimeMaxSec"))
2006 return bus_append_parse_sec_rename(m
, field
, eq
);
2008 if (streq(field
, "TimeoutStopSec"))
2009 return bus_append_parse_sec_rename(m
, field
, eq
);
2014 static int bus_append_service_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
2017 if (STR_IN_SET(field
, "PIDFile",
2023 "USBFunctionDescriptors",
2024 "USBFunctionStrings",
2026 "TimeoutStartFailureMode",
2027 "TimeoutStopFailureMode"))
2028 return bus_append_string(m
, field
, eq
);
2030 if (STR_IN_SET(field
, "PermissionsStartOnly",
2031 "RootDirectoryStartOnly",
2034 return bus_append_parse_boolean(m
, field
, eq
);
2036 if (STR_IN_SET(field
, "RestartSec",
2042 return bus_append_parse_sec_rename(m
, field
, eq
);
2044 if (streq(field
, "TimeoutSec")) {
2045 r
= bus_append_parse_sec_rename(m
, "TimeoutStartSec", eq
);
2049 return bus_append_parse_sec_rename(m
, "TimeoutStopSec", eq
);
2052 if (streq(field
, "FileDescriptorStoreMax"))
2053 return bus_append_safe_atou(m
, field
, eq
);
2055 if (STR_IN_SET(field
, "ExecCondition",
2069 return bus_append_exec_command(m
, field
, eq
);
2071 if (STR_IN_SET(field
, "RestartPreventExitStatus",
2072 "RestartForceExitStatus",
2073 "SuccessExitStatus")) {
2074 _cleanup_free_
int *status
= NULL
, *signal
= NULL
;
2075 size_t n_status
= 0, n_signal
= 0;
2079 _cleanup_free_
char *word
= NULL
;
2081 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_UNQUOTE
);
2087 return log_error_errno(r
, "Invalid syntax in %s: %s", field
, eq
);
2089 /* We need to call exit_status_from_string() first, because we want
2090 * to parse numbers as exit statuses, not signals. */
2092 r
= exit_status_from_string(word
);
2094 assert(r
>= 0 && r
< 256);
2096 status
= reallocarray(status
, n_status
+ 1, sizeof(int));
2100 status
[n_status
++] = r
;
2102 } else if ((r
= signal_from_string(word
)) >= 0) {
2103 signal
= reallocarray(signal
, n_signal
+ 1, sizeof(int));
2107 signal
[n_signal
++] = r
;
2110 /* original r from exit_status_to_string() */
2111 return log_error_errno(r
, "Invalid status or signal %s in %s: %m",
2115 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
2117 return bus_log_create_error(r
);
2119 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
2121 return bus_log_create_error(r
);
2123 r
= sd_bus_message_open_container(m
, 'v', "(aiai)");
2125 return bus_log_create_error(r
);
2127 r
= sd_bus_message_open_container(m
, 'r', "aiai");
2129 return bus_log_create_error(r
);
2131 r
= sd_bus_message_append_array(m
, 'i', status
, n_status
* sizeof(int));
2133 return bus_log_create_error(r
);
2135 r
= sd_bus_message_append_array(m
, 'i', signal
, n_signal
* sizeof(int));
2137 return bus_log_create_error(r
);
2139 r
= sd_bus_message_close_container(m
);
2141 return bus_log_create_error(r
);
2143 r
= sd_bus_message_close_container(m
);
2145 return bus_log_create_error(r
);
2147 r
= sd_bus_message_close_container(m
);
2149 return bus_log_create_error(r
);
2157 static int bus_append_socket_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
2160 if (STR_IN_SET(field
, "Accept",
2173 "SELinuxContextFromNet"))
2174 return bus_append_parse_boolean(m
, field
, eq
);
2176 if (STR_IN_SET(field
, "Priority",
2179 return bus_append_safe_atoi(m
, field
, eq
);
2181 if (streq(field
, "IPTOS"))
2182 return bus_append_ip_tos_from_string(m
, field
, eq
);
2184 if (STR_IN_SET(field
, "Backlog",
2186 "MaxConnectionsPerSource",
2188 "TriggerLimitBurst"))
2189 return bus_append_safe_atou(m
, field
, eq
);
2191 if (STR_IN_SET(field
, "SocketMode",
2193 return bus_append_parse_mode(m
, field
, eq
);
2195 if (STR_IN_SET(field
, "MessageQueueMaxMessages",
2196 "MessageQueueMessageSize"))
2197 return bus_append_safe_atoi64(m
, field
, eq
);
2199 if (STR_IN_SET(field
, "TimeoutSec",
2201 "KeepAliveIntervalSec",
2203 "TriggerLimitIntervalSec"))
2204 return bus_append_parse_sec_rename(m
, field
, eq
);
2206 if (STR_IN_SET(field
, "ReceiveBuffer",
2209 return bus_append_parse_size(m
, field
, eq
, 1024);
2211 if (STR_IN_SET(field
, "ExecStartPre",
2215 return bus_append_exec_command(m
, field
, eq
);
2217 if (STR_IN_SET(field
, "SmackLabel",
2223 "FileDescriptorName",
2227 return bus_append_string(m
, field
, eq
);
2229 if (streq(field
, "Symlinks"))
2230 return bus_append_strv(m
, field
, eq
, EXTRACT_UNQUOTE
);
2232 if (streq(field
, "SocketProtocol"))
2233 return bus_append_parse_ip_protocol(m
, field
, eq
);
2235 if (STR_IN_SET(field
, "ListenStream",
2237 "ListenSequentialPacket",
2240 "ListenMessageQueue",
2242 "ListenUSBFunction")) {
2244 r
= sd_bus_message_append(m
, "(sv)", "Listen", "a(ss)", 0);
2246 r
= sd_bus_message_append(m
, "(sv)", "Listen", "a(ss)", 1, field
+ STRLEN("Listen"), eq
);
2248 return bus_log_create_error(r
);
2255 static int bus_append_timer_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
2258 if (STR_IN_SET(field
, "WakeSystem",
2259 "RemainAfterElapse",
2263 "FixedRandomDelay"))
2264 return bus_append_parse_boolean(m
, field
, eq
);
2266 if (STR_IN_SET(field
, "AccuracySec",
2267 "RandomizedDelaySec"))
2268 return bus_append_parse_sec_rename(m
, field
, eq
);
2270 if (STR_IN_SET(field
, "OnActiveSec",
2274 "OnUnitInactiveSec")) {
2276 r
= sd_bus_message_append(m
, "(sv)", "TimersMonotonic", "a(st)", 0);
2279 r
= parse_sec(eq
, &t
);
2281 return log_error_errno(r
, "Failed to parse %s=%s: %m", field
, eq
);
2283 r
= sd_bus_message_append(m
, "(sv)", "TimersMonotonic", "a(st)", 1, field
, t
);
2286 return bus_log_create_error(r
);
2291 if (streq(field
, "OnCalendar")) {
2293 r
= sd_bus_message_append(m
, "(sv)", "TimersCalendar", "a(ss)", 0);
2295 r
= sd_bus_message_append(m
, "(sv)", "TimersCalendar", "a(ss)", 1, field
, eq
);
2297 return bus_log_create_error(r
);
2305 static int bus_append_unit_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
2306 ConditionType t
= _CONDITION_TYPE_INVALID
;
2307 bool is_condition
= false;
2310 if (STR_IN_SET(field
, "Description",
2314 "JobTimeoutRebootArgument",
2320 return bus_append_string(m
, field
, eq
);
2322 if (STR_IN_SET(field
, "StopWhenUnneeded",
2323 "RefuseManualStart",
2327 "DefaultDependencies"))
2328 return bus_append_parse_boolean(m
, field
, eq
);
2330 if (STR_IN_SET(field
, "JobTimeoutSec",
2331 "JobRunningTimeoutSec",
2332 "StartLimitIntervalSec"))
2333 return bus_append_parse_sec_rename(m
, field
, eq
);
2335 if (streq(field
, "StartLimitBurst"))
2336 return bus_append_safe_atou(m
, field
, eq
);
2338 if (STR_IN_SET(field
, "SuccessActionExitStatus",
2339 "FailureActionExitStatus")) {
2341 r
= sd_bus_message_append(m
, "(sv)", field
, "i", -1);
2345 r
= safe_atou8(eq
, &u
);
2347 return log_error_errno(r
, "Failed to parse %s=%s", field
, eq
);
2349 r
= sd_bus_message_append(m
, "(sv)", field
, "i", (int) u
);
2352 return bus_log_create_error(r
);
2357 if (unit_dependency_from_string(field
) >= 0 ||
2358 STR_IN_SET(field
, "Documentation",
2359 "RequiresMountsFor",
2361 return bus_append_strv(m
, field
, eq
, EXTRACT_UNQUOTE
);
2363 t
= condition_type_from_string(field
);
2365 is_condition
= true;
2367 t
= assert_type_from_string(field
);
2370 r
= sd_bus_message_append(m
, "(sv)", is_condition
? "Conditions" : "Asserts", "a(sbbs)", 0);
2373 int trigger
, negate
;
2375 trigger
= *p
== '|';
2383 r
= sd_bus_message_append(m
, "(sv)", is_condition
? "Conditions" : "Asserts", "a(sbbs)", 1,
2384 field
, trigger
, negate
, p
);
2387 return bus_log_create_error(r
);
2395 int bus_append_unit_property_assignment(sd_bus_message
*m
, UnitType t
, const char *assignment
) {
2396 const char *eq
, *field
;
2402 eq
= strchr(assignment
, '=');
2404 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2405 "Not an assignment: %s", assignment
);
2407 field
= strndupa(assignment
, eq
- assignment
);
2412 r
= bus_append_cgroup_property(m
, field
, eq
);
2416 r
= bus_append_execute_property(m
, field
, eq
);
2420 r
= bus_append_kill_property(m
, field
, eq
);
2424 r
= bus_append_service_property(m
, field
, eq
);
2430 r
= bus_append_cgroup_property(m
, field
, eq
);
2434 r
= bus_append_execute_property(m
, field
, eq
);
2438 r
= bus_append_kill_property(m
, field
, eq
);
2442 r
= bus_append_socket_property(m
, field
, eq
);
2448 r
= bus_append_timer_property(m
, field
, eq
);
2454 r
= bus_append_path_property(m
, field
, eq
);
2460 r
= bus_append_cgroup_property(m
, field
, eq
);
2466 r
= bus_append_cgroup_property(m
, field
, eq
);
2470 r
= bus_append_kill_property(m
, field
, eq
);
2474 r
= bus_append_scope_property(m
, field
, eq
);
2480 r
= bus_append_cgroup_property(m
, field
, eq
);
2484 r
= bus_append_execute_property(m
, field
, eq
);
2488 r
= bus_append_kill_property(m
, field
, eq
);
2492 r
= bus_append_mount_property(m
, field
, eq
);
2498 case UNIT_AUTOMOUNT
:
2499 r
= bus_append_automount_property(m
, field
, eq
);
2508 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2509 "Not supported unit type");
2512 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2513 "Invalid unit type");
2516 r
= bus_append_unit_property(m
, field
, eq
);
2520 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2521 "Unknown assignment: %s", assignment
);
2524 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, UnitType t
, char **l
) {
2530 STRV_FOREACH(i
, l
) {
2531 r
= bus_append_unit_property_assignment(m
, t
, *i
);
2539 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, size_t *n_changes
) {
2540 const char *type
, *path
, *source
;
2543 /* changes is dereferenced when calling unit_file_dump_changes() later,
2544 * so we have to make sure this is not NULL. */
2548 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
2550 return bus_log_parse_error(r
);
2552 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
2553 /* We expect only "success" changes to be sent over the bus.
2554 Hence, reject anything negative. */
2555 int ch
= unit_file_change_type_from_string(type
);
2557 log_notice_errno(ch
, "Manager reported unknown change type \"%s\" for path \"%s\", ignoring.",
2562 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
2567 return bus_log_parse_error(r
);
2569 r
= sd_bus_message_exit_container(m
);
2571 return bus_log_parse_error(r
);
2573 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, quiet
);
2577 int unit_load_state(sd_bus
*bus
, const char *name
, char **load_state
) {
2578 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2579 _cleanup_free_
char *path
= NULL
;
2582 path
= unit_dbus_path_from_name(name
);
2586 /* This function warns on it's own, because otherwise it'd be awkward to pass
2587 * the dbus error message around. */
2589 r
= sd_bus_get_property_string(
2591 "org.freedesktop.systemd1",
2593 "org.freedesktop.systemd1.Unit",
2598 return log_error_errno(r
, "Failed to get load state of %s: %s", name
, bus_error_message(&error
, r
));
2603 int unit_info_compare(const UnitInfo
*a
, const UnitInfo
*b
) {
2606 /* First, order by machine */
2607 r
= strcasecmp_ptr(a
->machine
, b
->machine
);
2611 /* Second, order by unit type */
2612 r
= strcasecmp_ptr(strrchr(a
->id
, '.'), strrchr(b
->id
, '.'));
2616 /* Third, order by name */
2617 return strcasecmp(a
->id
, b
->id
);