1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "alloc-util.h"
4 #include "bus-internal.h"
5 #include "bus-unit-util.h"
8 #include "cgroup-util.h"
10 #include "cpu-set-util.h"
12 #include "errno-list.h"
15 #include "hexdecoct.h"
16 #include "hostname-util.h"
17 #include "in-addr-util.h"
18 #include "ip-protocol-list.h"
20 #include "locale-util.h"
21 #include "missing_fs.h"
22 #include "mountpoint-util.h"
24 #include "parse-util.h"
25 #include "path-util.h"
26 #include "process-util.h"
27 #include "rlimit-util.h"
28 #include "securebits-util.h"
29 #include "signal-util.h"
30 #include "string-util.h"
31 #include "syslog-util.h"
32 #include "terminal-util.h"
34 #include "user-util.h"
38 int bus_parse_unit_info(sd_bus_message
*message
, UnitInfo
*u
) {
44 return sd_bus_message_read(
59 #define DEFINE_BUS_APPEND_PARSE_PTR(bus_type, cast_type, type, parse_func) \
60 static int bus_append_##parse_func( \
67 r = parse_func(eq, &val); \
69 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); \
71 r = sd_bus_message_append(m, "(sv)", field, \
72 bus_type, (cast_type) val); \
74 return bus_log_create_error(r); \
79 #define DEFINE_BUS_APPEND_PARSE(bus_type, parse_func) \
80 static int bus_append_##parse_func( \
88 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse %s: %s", field, eq); \
90 r = sd_bus_message_append(m, "(sv)", field, \
91 bus_type, (int32_t) r); \
93 return bus_log_create_error(r); \
98 DEFINE_BUS_APPEND_PARSE("b", parse_boolean
);
99 DEFINE_BUS_APPEND_PARSE("i", ioprio_class_from_string
);
100 DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string
);
101 DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string
);
102 DEFINE_BUS_APPEND_PARSE("i", log_level_from_string
);
103 DEFINE_BUS_APPEND_PARSE("i", parse_errno
);
104 DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string
);
105 DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string
);
106 DEFINE_BUS_APPEND_PARSE("i", signal_from_string
);
107 DEFINE_BUS_APPEND_PARSE("i", parse_ip_protocol
);
108 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, ioprio_parse_priority
);
109 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, parse_nice
);
110 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, safe_atoi
);
111 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, nsec_t
, parse_nsec
);
112 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_blkio_weight_parse
);
113 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_shares_parse
);
114 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_weight_parse
);
115 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, unsigned long, mount_propagation_flags_from_string
);
116 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, safe_atou64
);
117 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t
, parse_mode
);
118 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou
);
119 DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64
);
121 static int bus_append_string(sd_bus_message
*m
, const char *field
, const char *eq
) {
124 r
= sd_bus_message_append(m
, "(sv)", field
, "s", eq
);
126 return bus_log_create_error(r
);
131 static int bus_append_strv(sd_bus_message
*m
, const char *field
, const char *eq
, ExtractFlags flags
) {
135 r
= sd_bus_message_open_container(m
, 'r', "sv");
137 return bus_log_create_error(r
);
139 r
= sd_bus_message_append_basic(m
, 's', field
);
141 return bus_log_create_error(r
);
143 r
= sd_bus_message_open_container(m
, 'v', "as");
145 return bus_log_create_error(r
);
147 r
= sd_bus_message_open_container(m
, 'a', "s");
149 return bus_log_create_error(r
);
152 _cleanup_free_
char *word
= NULL
;
154 r
= extract_first_word(&p
, &word
, NULL
, flags
);
160 return log_error_errno(r
, "Invalid syntax: %s", eq
);
162 r
= sd_bus_message_append_basic(m
, 's', word
);
164 return bus_log_create_error(r
);
167 r
= sd_bus_message_close_container(m
);
169 return bus_log_create_error(r
);
171 r
= sd_bus_message_close_container(m
);
173 return bus_log_create_error(r
);
175 r
= sd_bus_message_close_container(m
);
177 return bus_log_create_error(r
);
182 static int bus_append_byte_array(sd_bus_message
*m
, const char *field
, const void *buf
, size_t n
) {
185 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
187 return bus_log_create_error(r
);
189 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
191 return bus_log_create_error(r
);
193 r
= sd_bus_message_open_container(m
, 'v', "ay");
195 return bus_log_create_error(r
);
197 r
= sd_bus_message_append_array(m
, 'y', buf
, n
);
199 return bus_log_create_error(r
);
201 r
= sd_bus_message_close_container(m
);
203 return bus_log_create_error(r
);
205 r
= sd_bus_message_close_container(m
);
207 return bus_log_create_error(r
);
212 static int bus_append_parse_sec_rename(sd_bus_message
*m
, const char *field
, const char *eq
) {
218 r
= parse_sec(eq
, &t
);
220 return log_error_errno(r
, "Failed to parse %s=%s: %m", field
, eq
);
223 n
= newa(char, l
+ 2);
224 /* Change suffix Sec → USec */
225 strcpy(mempcpy(n
, field
, l
- 3), "USec");
227 r
= sd_bus_message_append(m
, "(sv)", n
, "t", t
);
229 return bus_log_create_error(r
);
234 static int bus_append_parse_size(sd_bus_message
*m
, const char *field
, const char *eq
, uint64_t base
) {
238 r
= parse_size(eq
, base
, &v
);
240 return log_error_errno(r
, "Failed to parse %s=%s: %m", field
, eq
);
242 r
= sd_bus_message_append(m
, "(sv)", field
, "t", v
);
244 return bus_log_create_error(r
);
249 static int bus_append_exec_command(sd_bus_message
*m
, const char *field
, const char *eq
) {
250 bool ignore_failure
= false, explicit_path
= false, done
= false;
251 _cleanup_strv_free_
char **l
= NULL
;
252 _cleanup_free_
char *path
= NULL
;
262 ignore_failure
= true;
271 explicit_path
= true;
278 /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */
279 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
280 "Sorry, but +, ! and !! are currently not supported for transient services.");
289 r
= extract_first_word(&eq
, &path
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
291 return log_error_errno(r
, "Failed to parse path: %m");
294 r
= strv_split_extract(&l
, eq
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
296 return log_error_errno(r
, "Failed to parse command line: %m");
298 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
300 return bus_log_create_error(r
);
302 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
304 return bus_log_create_error(r
);
306 r
= sd_bus_message_open_container(m
, 'v', "a(sasb)");
308 return bus_log_create_error(r
);
310 r
= sd_bus_message_open_container(m
, 'a', "(sasb)");
312 return bus_log_create_error(r
);
314 if (!strv_isempty(l
)) {
316 r
= sd_bus_message_open_container(m
, 'r', "sasb");
318 return bus_log_create_error(r
);
320 r
= sd_bus_message_append(m
, "s", path
?: l
[0]);
322 return bus_log_create_error(r
);
324 r
= sd_bus_message_append_strv(m
, l
);
326 return bus_log_create_error(r
);
328 r
= sd_bus_message_append(m
, "b", ignore_failure
);
330 return bus_log_create_error(r
);
332 r
= sd_bus_message_close_container(m
);
334 return bus_log_create_error(r
);
337 r
= sd_bus_message_close_container(m
);
339 return bus_log_create_error(r
);
341 r
= sd_bus_message_close_container(m
);
343 return bus_log_create_error(r
);
345 r
= sd_bus_message_close_container(m
);
347 return bus_log_create_error(r
);
352 static int bus_append_ip_address_access(sd_bus_message
*m
, int family
, const union in_addr_union
*prefix
, unsigned char prefixlen
) {
358 r
= sd_bus_message_open_container(m
, 'r', "iayu");
362 r
= sd_bus_message_append(m
, "i", family
);
366 r
= sd_bus_message_append_array(m
, 'y', prefix
, FAMILY_ADDRESS_SIZE(family
));
370 r
= sd_bus_message_append(m
, "u", prefixlen
);
374 return sd_bus_message_close_container(m
);
377 static int bus_append_cgroup_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
380 if (STR_IN_SET(field
, "DevicePolicy", "Slice"))
382 return bus_append_string(m
, field
, eq
);
384 if (STR_IN_SET(field
,
385 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
386 "TasksAccounting", "IPAccounting"))
388 return bus_append_parse_boolean(m
, field
, eq
);
390 if (STR_IN_SET(field
, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight"))
392 return bus_append_cg_weight_parse(m
, field
, eq
);
394 if (STR_IN_SET(field
, "CPUShares", "StartupCPUShares"))
396 return bus_append_cg_cpu_shares_parse(m
, field
, eq
);
398 if (STR_IN_SET(field
, "BlockIOWeight", "StartupBlockIOWeight"))
400 return bus_append_cg_blkio_weight_parse(m
, field
, eq
);
402 if (streq(field
, "Delegate")) {
404 r
= parse_boolean(eq
);
406 return bus_append_strv(m
, "DelegateControllers", eq
, EXTRACT_QUOTES
);
408 r
= sd_bus_message_append(m
, "(sv)", "Delegate", "b", r
);
410 return bus_log_create_error(r
);
415 if (STR_IN_SET(field
, "MemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) {
417 if (isempty(eq
) || streq(eq
, "infinity")) {
418 r
= sd_bus_message_append(m
, "(sv)", field
, "t", CGROUP_LIMIT_MAX
);
420 return bus_log_create_error(r
);
424 r
= parse_permille(eq
);
428 /* When this is a percentage we'll convert this into a relative value in the range 0…UINT32_MAX
429 * and pass it in the MemoryLowScale property (and related ones). This way the physical memory
430 * size can be determined server-side. */
432 n
= strjoina(field
, "Scale");
433 r
= sd_bus_message_append(m
, "(sv)", n
, "u", (uint32_t) (((uint64_t) r
* UINT32_MAX
) / 1000U));
435 return bus_log_create_error(r
);
440 if (streq(field
, "TasksMax"))
441 return bus_append_safe_atou64(m
, field
, eq
);
443 return bus_append_parse_size(m
, field
, eq
, 1024);
446 if (streq(field
, "CPUQuota")) {
449 r
= sd_bus_message_append(m
, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY
);
451 r
= parse_permille_unbounded(eq
);
453 return log_error_errno(SYNTHETIC_ERRNO(ERANGE
),
454 "CPU quota too small.");
456 return log_error_errno(r
, "CPU quota '%s' invalid.", eq
);
458 r
= sd_bus_message_append(m
, "(sv)", "CPUQuotaPerSecUSec", "t", (((uint64_t) r
* USEC_PER_SEC
) / 1000U));
462 return bus_log_create_error(r
);
467 if (streq(field
, "DeviceAllow")) {
470 r
= sd_bus_message_append(m
, "(sv)", field
, "a(ss)", 0);
472 const char *path
= eq
, *rwm
= NULL
, *e
;
476 path
= strndupa(eq
, e
- eq
);
480 r
= sd_bus_message_append(m
, "(sv)", field
, "a(ss)", 1, path
, strempty(rwm
));
484 return bus_log_create_error(r
);
489 if (cgroup_io_limit_type_from_string(field
) >= 0 || STR_IN_SET(field
, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
492 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 0);
494 const char *path
, *bandwidth
, *e
;
499 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
500 "Failed to parse %s value %s.",
503 path
= strndupa(eq
, e
- eq
);
506 if (streq(bandwidth
, "infinity"))
507 bytes
= CGROUP_LIMIT_MAX
;
509 r
= parse_size(bandwidth
, 1000, &bytes
);
511 return log_error_errno(r
, "Failed to parse byte value %s: %m", bandwidth
);
514 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 1, path
, bytes
);
518 return bus_log_create_error(r
);
523 if (STR_IN_SET(field
, "IODeviceWeight", "BlockIODeviceWeight")) {
526 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 0);
528 const char *path
, *weight
, *e
;
533 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
534 "Failed to parse %s value %s.",
537 path
= strndupa(eq
, e
- eq
);
540 r
= safe_atou64(weight
, &u
);
542 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, weight
);
544 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 1, path
, u
);
548 return bus_log_create_error(r
);
553 if (streq(field
, "IODeviceLatencyTargetSec")) {
554 const char *field_usec
= "IODeviceLatencyTargetUSec";
557 r
= sd_bus_message_append(m
, "(sv)", field_usec
, "a(st)", USEC_INFINITY
);
559 const char *path
, *target
, *e
;
564 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
565 "Failed to parse %s value %s.",
568 path
= strndupa(eq
, e
- eq
);
571 r
= parse_sec(target
, &usec
);
573 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, target
);
575 r
= sd_bus_message_append(m
, "(sv)", field_usec
, "a(st)", 1, path
, usec
);
579 return bus_log_create_error(r
);
584 if (STR_IN_SET(field
, "IPAddressAllow", "IPAddressDeny")) {
585 unsigned char prefixlen
;
586 union in_addr_union prefix
= {};
590 r
= sd_bus_message_append(m
, "(sv)", field
, "a(iayu)", 0);
592 return bus_log_create_error(r
);
597 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
599 return bus_log_create_error(r
);
601 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
603 return bus_log_create_error(r
);
605 r
= sd_bus_message_open_container(m
, 'v', "a(iayu)");
607 return bus_log_create_error(r
);
609 r
= sd_bus_message_open_container(m
, 'a', "(iayu)");
611 return bus_log_create_error(r
);
613 if (streq(eq
, "any")) {
614 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
616 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 0);
618 return bus_log_create_error(r
);
620 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 0);
622 return bus_log_create_error(r
);
624 } else if (is_localhost(eq
)) {
625 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
627 prefix
.in
.s_addr
= htobe32(0x7f000000);
628 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 8);
630 return bus_log_create_error(r
);
632 prefix
.in6
= (struct in6_addr
) IN6ADDR_LOOPBACK_INIT
;
633 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 128);
637 } else if (streq(eq
, "link-local")) {
638 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
640 prefix
.in
.s_addr
= htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
641 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 16);
643 return bus_log_create_error(r
);
645 prefix
.in6
= (struct in6_addr
) {
646 .s6_addr32
[0] = htobe32(0xfe800000)
648 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 64);
650 return bus_log_create_error(r
);
652 } else if (streq(eq
, "multicast")) {
653 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
655 prefix
.in
.s_addr
= htobe32((UINT32_C(224) << 24));
656 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 4);
658 return bus_log_create_error(r
);
660 prefix
.in6
= (struct in6_addr
) {
661 .s6_addr32
[0] = htobe32(0xff000000)
663 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 8);
665 return bus_log_create_error(r
);
669 _cleanup_free_
char *word
= NULL
;
671 r
= extract_first_word(&eq
, &word
, NULL
, 0);
677 return log_error_errno(r
, "Failed to parse %s: %s", field
, eq
);
679 r
= in_addr_prefix_from_string_auto(word
, &family
, &prefix
, &prefixlen
);
681 return log_error_errno(r
, "Failed to parse IP address prefix: %s", word
);
683 r
= bus_append_ip_address_access(m
, family
, &prefix
, prefixlen
);
685 return bus_log_create_error(r
);
689 r
= sd_bus_message_close_container(m
);
691 return bus_log_create_error(r
);
693 r
= sd_bus_message_close_container(m
);
695 return bus_log_create_error(r
);
697 r
= sd_bus_message_close_container(m
);
699 return bus_log_create_error(r
);
707 static int bus_append_automount_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
709 if (streq(field
, "Where"))
711 return bus_append_string(m
, field
, eq
);
713 if (streq(field
, "DirectoryMode"))
715 return bus_append_parse_mode(m
, field
, eq
);
717 if (streq(field
, "TimeoutIdleSec"))
719 return bus_append_parse_sec_rename(m
, field
, eq
);
724 static int bus_append_execute_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
728 if (STR_IN_SET(field
,
730 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
731 "WorkingDirectory", "RootDirectory", "SyslogIdentifier",
732 "ProtectSystem", "ProtectHome", "SELinuxContext", "RootImage",
733 "RuntimeDirectoryPreserve", "Personality", "KeyringMode"))
735 return bus_append_string(m
, field
, eq
);
737 if (STR_IN_SET(field
,
738 "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
739 "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
740 "PrivateMounts", "NoNewPrivileges", "SyslogLevelPrefix",
741 "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
742 "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups",
743 "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality"))
745 return bus_append_parse_boolean(m
, field
, eq
);
747 if (STR_IN_SET(field
,
748 "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
749 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths",
750 "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory",
751 "SupplementaryGroups", "SystemCallArchitectures"))
753 return bus_append_strv(m
, field
, eq
, EXTRACT_QUOTES
);
755 if (STR_IN_SET(field
, "SyslogLevel", "LogLevelMax"))
757 return bus_append_log_level_from_string(m
, field
, eq
);
759 if (streq(field
, "SyslogFacility"))
761 return bus_append_log_facility_unshifted_from_string(m
, field
, eq
);
763 if (streq(field
, "SecureBits"))
765 return bus_append_secure_bits_from_string(m
, field
, eq
);
767 if (streq(field
, "CPUSchedulingPolicy"))
769 return bus_append_sched_policy_from_string(m
, field
, eq
);
771 if (STR_IN_SET(field
, "CPUSchedulingPriority", "OOMScoreAdjust"))
773 return bus_append_safe_atoi(m
, field
, eq
);
775 if (streq(field
, "Nice"))
777 return bus_append_parse_nice(m
, field
, eq
);
779 if (streq(field
, "SystemCallErrorNumber"))
781 return bus_append_parse_errno(m
, field
, eq
);
783 if (streq(field
, "IOSchedulingClass"))
785 return bus_append_ioprio_class_from_string(m
, field
, eq
);
787 if (streq(field
, "IOSchedulingPriority"))
789 return bus_append_ioprio_parse_priority(m
, field
, eq
);
791 if (STR_IN_SET(field
,
792 "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode",
793 "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask"))
795 return bus_append_parse_mode(m
, field
, eq
);
797 if (streq(field
, "TimerSlackNSec"))
799 return bus_append_parse_nsec(m
, field
, eq
);
801 if (streq(field
, "LogRateLimitIntervalSec"))
803 return bus_append_parse_sec_rename(m
, field
, eq
);
805 if (streq(field
, "LogRateLimitBurst"))
807 return bus_append_safe_atou(m
, field
, eq
);
809 if (streq(field
, "MountFlags"))
811 return bus_append_mount_propagation_flags_from_string(m
, field
, eq
);
813 if (STR_IN_SET(field
, "Environment", "UnsetEnvironment", "PassEnvironment"))
815 return bus_append_strv(m
, field
, eq
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
817 if (streq(field
, "EnvironmentFile")) {
820 r
= sd_bus_message_append(m
, "(sv)", "EnvironmentFiles", "a(sb)", 0);
822 r
= sd_bus_message_append(m
, "(sv)", "EnvironmentFiles", "a(sb)", 1,
823 eq
[0] == '-' ? eq
+ 1 : eq
,
826 return bus_log_create_error(r
);
831 if (streq(field
, "LogExtraFields")) {
833 r
= sd_bus_message_open_container(m
, 'r', "sv");
835 return bus_log_create_error(r
);
837 r
= sd_bus_message_append_basic(m
, 's', "LogExtraFields");
839 return bus_log_create_error(r
);
841 r
= sd_bus_message_open_container(m
, 'v', "aay");
843 return bus_log_create_error(r
);
845 r
= sd_bus_message_open_container(m
, 'a', "ay");
847 return bus_log_create_error(r
);
849 r
= sd_bus_message_append_array(m
, 'y', eq
, strlen(eq
));
851 return bus_log_create_error(r
);
853 r
= sd_bus_message_close_container(m
);
855 return bus_log_create_error(r
);
857 r
= sd_bus_message_close_container(m
);
859 return bus_log_create_error(r
);
861 r
= sd_bus_message_close_container(m
);
863 return bus_log_create_error(r
);
868 if (STR_IN_SET(field
, "StandardInput", "StandardOutput", "StandardError")) {
869 const char *n
, *appended
;
871 if ((n
= startswith(eq
, "fd:"))) {
872 appended
= strjoina(field
, "FileDescriptorName");
873 r
= sd_bus_message_append(m
, "(sv)", appended
, "s", n
);
874 } else if ((n
= startswith(eq
, "file:"))) {
875 appended
= strjoina(field
, "File");
876 r
= sd_bus_message_append(m
, "(sv)", appended
, "s", n
);
877 } else if ((n
= startswith(eq
, "append:"))) {
878 appended
= strjoina(field
, "FileToAppend");
879 r
= sd_bus_message_append(m
, "(sv)", appended
, "s", n
);
881 r
= sd_bus_message_append(m
, "(sv)", field
, "s", eq
);
883 return bus_log_create_error(r
);
888 if (streq(field
, "StandardInputText")) {
889 _cleanup_free_
char *unescaped
= NULL
;
891 r
= cunescape(eq
, 0, &unescaped
);
893 return log_error_errno(r
, "Failed to unescape text '%s': %m", eq
);
895 if (!strextend(&unescaped
, "\n", NULL
))
898 /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
899 * interface anyway */
901 return bus_append_byte_array(m
, field
, unescaped
, strlen(unescaped
));
904 if (streq(field
, "StandardInputData")) {
905 _cleanup_free_
void *decoded
= NULL
;
908 r
= unbase64mem(eq
, (size_t) -1, &decoded
, &sz
);
910 return log_error_errno(r
, "Failed to decode base64 data '%s': %m", eq
);
912 return bus_append_byte_array(m
, field
, decoded
, sz
);
915 if ((suffix
= startswith(field
, "Limit"))) {
918 rl
= rlimit_from_string(suffix
);
923 r
= rlimit_parse(rl
, eq
, &l
);
925 return log_error_errno(r
, "Failed to parse resource limit: %s", eq
);
927 r
= sd_bus_message_append(m
, "(sv)", field
, "t", l
.rlim_max
);
929 return bus_log_create_error(r
);
931 sn
= strjoina(field
, "Soft");
932 r
= sd_bus_message_append(m
, "(sv)", sn
, "t", l
.rlim_cur
);
934 return bus_log_create_error(r
);
940 if (STR_IN_SET(field
, "AppArmorProfile", "SmackProcessLabel")) {
949 r
= sd_bus_message_append(m
, "(sv)", field
, "(bs)", ignore
, s
);
951 return bus_log_create_error(r
);
956 if (STR_IN_SET(field
, "CapabilityBoundingSet", "AmbientCapabilities")) {
966 r
= capability_set_from_string(p
, &sum
);
968 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
970 sum
= invert
? ~sum
: sum
;
972 r
= sd_bus_message_append(m
, "(sv)", field
, "t", sum
);
974 return bus_log_create_error(r
);
979 if (streq(field
, "CPUAffinity")) {
980 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
982 r
= parse_cpu_set(eq
, &cpuset
);
984 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
986 return bus_append_byte_array(m
, field
, cpuset
, CPU_ALLOC_SIZE(r
));
989 if (STR_IN_SET(field
, "RestrictAddressFamilies", "SystemCallFilter")) {
998 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
1000 return bus_log_create_error(r
);
1002 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
1004 return bus_log_create_error(r
);
1006 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
1008 return bus_log_create_error(r
);
1010 r
= sd_bus_message_open_container(m
, 'r', "bas");
1012 return bus_log_create_error(r
);
1014 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
1016 return bus_log_create_error(r
);
1018 r
= sd_bus_message_open_container(m
, 'a', "s");
1020 return bus_log_create_error(r
);
1023 _cleanup_free_
char *word
= NULL
;
1025 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1031 return log_error_errno(r
, "Invalid syntax: %s", eq
);
1033 r
= sd_bus_message_append_basic(m
, 's', word
);
1035 return bus_log_create_error(r
);
1038 r
= sd_bus_message_close_container(m
);
1040 return bus_log_create_error(r
);
1042 r
= sd_bus_message_close_container(m
);
1044 return bus_log_create_error(r
);
1046 r
= sd_bus_message_close_container(m
);
1048 return bus_log_create_error(r
);
1050 r
= sd_bus_message_close_container(m
);
1052 return bus_log_create_error(r
);
1057 if (streq(field
, "RestrictNamespaces")) {
1058 bool invert
= false;
1059 unsigned long flags
;
1061 r
= parse_boolean(eq
);
1065 flags
= NAMESPACE_FLAGS_ALL
;
1072 r
= namespace_flags_from_string(eq
, &flags
);
1074 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
1078 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
1080 r
= sd_bus_message_append(m
, "(sv)", field
, "t", (uint64_t) flags
);
1082 return bus_log_create_error(r
);
1087 if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
1090 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
1092 return bus_log_create_error(r
);
1094 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
1096 return bus_log_create_error(r
);
1098 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
1100 return bus_log_create_error(r
);
1102 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
1104 return bus_log_create_error(r
);
1107 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
1108 char *s
= NULL
, *d
= NULL
;
1109 bool ignore_enoent
= false;
1110 uint64_t flags
= MS_REC
;
1112 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1114 return log_error_errno(r
, "Failed to parse argument: %m");
1120 ignore_enoent
= true;
1124 if (p
&& p
[-1] == ':') {
1125 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1127 return log_error_errno(r
, "Failed to parse argument: %m");
1129 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1130 "Missing argument after ':': %s",
1135 if (p
&& p
[-1] == ':') {
1136 _cleanup_free_
char *options
= NULL
;
1138 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
1140 return log_error_errno(r
, "Failed to parse argument: %m");
1142 if (isempty(options
) || streq(options
, "rbind"))
1144 else if (streq(options
, "norbind"))
1147 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1148 "Unknown options: %s",
1154 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
1156 return bus_log_create_error(r
);
1159 r
= sd_bus_message_close_container(m
);
1161 return bus_log_create_error(r
);
1163 r
= sd_bus_message_close_container(m
);
1165 return bus_log_create_error(r
);
1167 r
= sd_bus_message_close_container(m
);
1169 return bus_log_create_error(r
);
1174 if (streq(field
, "TemporaryFileSystem")) {
1177 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
1179 return bus_log_create_error(r
);
1181 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
1183 return bus_log_create_error(r
);
1185 r
= sd_bus_message_open_container(m
, 'v', "a(ss)");
1187 return bus_log_create_error(r
);
1189 r
= sd_bus_message_open_container(m
, 'a', "(ss)");
1191 return bus_log_create_error(r
);
1194 _cleanup_free_
char *word
= NULL
, *path
= NULL
;
1197 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1199 return log_error_errno(r
, "Failed to parse argument: %m");
1204 r
= extract_first_word(&w
, &path
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
1206 return log_error_errno(r
, "Failed to parse argument: %m");
1208 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1209 "Failed to parse argument: %s",
1212 r
= sd_bus_message_append(m
, "(ss)", path
, w
);
1214 return bus_log_create_error(r
);
1217 r
= sd_bus_message_close_container(m
);
1219 return bus_log_create_error(r
);
1221 r
= sd_bus_message_close_container(m
);
1223 return bus_log_create_error(r
);
1225 r
= sd_bus_message_close_container(m
);
1227 return bus_log_create_error(r
);
1235 static int bus_append_kill_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1237 if (streq(field
, "KillMode"))
1239 return bus_append_string(m
, field
, eq
);
1241 if (STR_IN_SET(field
, "SendSIGHUP", "SendSIGKILL"))
1243 return bus_append_parse_boolean(m
, field
, eq
);
1245 if (STR_IN_SET(field
, "KillSignal", "FinalKillSignal", "WatchdogSignal"))
1247 return bus_append_signal_from_string(m
, field
, eq
);
1252 static int bus_append_mount_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1254 if (STR_IN_SET(field
, "What", "Where", "Options", "Type"))
1256 return bus_append_string(m
, field
, eq
);
1258 if (streq(field
, "TimeoutSec"))
1260 return bus_append_parse_sec_rename(m
, field
, eq
);
1262 if (streq(field
, "DirectoryMode"))
1264 return bus_append_parse_mode(m
, field
, eq
);
1266 if (STR_IN_SET(field
, "SloppyOptions", "LazyUnmount", "ForceUnmount"))
1268 return bus_append_parse_boolean(m
, field
, eq
);
1273 static int bus_append_path_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1276 if (streq(field
, "MakeDirectory"))
1278 return bus_append_parse_boolean(m
, field
, eq
);
1280 if (streq(field
, "DirectoryMode"))
1282 return bus_append_parse_mode(m
, field
, eq
);
1284 if (STR_IN_SET(field
,
1285 "PathExists", "PathExistsGlob", "PathChanged",
1286 "PathModified", "DirectoryNotEmpty")) {
1289 r
= sd_bus_message_append(m
, "(sv)", "Paths", "a(ss)", 0);
1291 r
= sd_bus_message_append(m
, "(sv)", "Paths", "a(ss)", 1, field
, eq
);
1293 return bus_log_create_error(r
);
1301 static int bus_append_service_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1304 if (STR_IN_SET(field
,
1305 "PIDFile", "Type", "Restart", "BusName", "NotifyAccess",
1306 "USBFunctionDescriptors", "USBFunctionStrings"))
1308 return bus_append_string(m
, field
, eq
);
1310 if (STR_IN_SET(field
, "PermissionsStartOnly", "RootDirectoryStartOnly", "RemainAfterExit", "GuessMainPID"))
1312 return bus_append_parse_boolean(m
, field
, eq
);
1314 if (STR_IN_SET(field
, "RestartSec", "TimeoutStartSec", "TimeoutStopSec", "RuntimeMaxSec", "WatchdogSec"))
1316 return bus_append_parse_sec_rename(m
, field
, eq
);
1318 if (streq(field
, "TimeoutSec")) {
1320 r
= bus_append_parse_sec_rename(m
, "TimeoutStartSec", eq
);
1324 return bus_append_parse_sec_rename(m
, "TimeoutStopSec", eq
);
1327 if (streq(field
, "FileDescriptorStoreMax"))
1329 return bus_append_safe_atou(m
, field
, eq
);
1331 if (STR_IN_SET(field
,
1332 "ExecStartPre", "ExecStart", "ExecStartPost",
1333 "ExecReload", "ExecStop", "ExecStopPost"))
1335 return bus_append_exec_command(m
, field
, eq
);
1337 if (STR_IN_SET(field
, "RestartPreventExitStatus", "RestartForceExitStatus", "SuccessExitStatus")) {
1338 _cleanup_free_
int *status
= NULL
, *signal
= NULL
;
1339 size_t sz_status
= 0, sz_signal
= 0;
1343 _cleanup_free_
char *word
= NULL
;
1346 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1352 return log_error_errno(r
, "Invalid syntax in %s: %s", field
, eq
);
1354 r
= safe_atoi(word
, &val
);
1356 val
= signal_from_string(word
);
1358 return log_error_errno(r
, "Invalid status or signal %s in %s: %m", word
, field
);
1360 signal
= reallocarray(signal
, sz_signal
+ 1, sizeof(int));
1364 signal
[sz_signal
++] = val
;
1366 status
= reallocarray(status
, sz_status
+ 1, sizeof(int));
1370 status
[sz_status
++] = val
;
1374 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
1376 return bus_log_create_error(r
);
1378 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
1380 return bus_log_create_error(r
);
1382 r
= sd_bus_message_open_container(m
, 'v', "(aiai)");
1384 return bus_log_create_error(r
);
1386 r
= sd_bus_message_open_container(m
, 'r', "aiai");
1388 return bus_log_create_error(r
);
1390 r
= sd_bus_message_append_array(m
, 'i', status
, sz_status
);
1392 return bus_log_create_error(r
);
1394 r
= sd_bus_message_append_array(m
, 'i', signal
, sz_signal
);
1396 return bus_log_create_error(r
);
1398 r
= sd_bus_message_close_container(m
);
1400 return bus_log_create_error(r
);
1402 r
= sd_bus_message_close_container(m
);
1404 return bus_log_create_error(r
);
1406 r
= sd_bus_message_close_container(m
);
1408 return bus_log_create_error(r
);
1416 static int bus_append_socket_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1419 if (STR_IN_SET(field
,
1420 "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast",
1421 "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet"))
1423 return bus_append_parse_boolean(m
, field
, eq
);
1425 if (STR_IN_SET(field
, "Priority", "IPTTL", "Mark"))
1427 return bus_append_safe_atoi(m
, field
, eq
);
1429 if (streq(field
, "IPTOS"))
1431 return bus_append_ip_tos_from_string(m
, field
, eq
);
1433 if (STR_IN_SET(field
, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst"))
1435 return bus_append_safe_atou(m
, field
, eq
);
1437 if (STR_IN_SET(field
, "SocketMode", "DirectoryMode"))
1439 return bus_append_parse_mode(m
, field
, eq
);
1441 if (STR_IN_SET(field
, "MessageQueueMaxMessages", "MessageQueueMessageSize"))
1443 return bus_append_safe_atoi64(m
, field
, eq
);
1445 if (STR_IN_SET(field
, "TimeoutSec", "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", "TriggerLimitIntervalSec"))
1447 return bus_append_parse_sec_rename(m
, field
, eq
);
1449 if (STR_IN_SET(field
, "ReceiveBuffer", "SendBuffer", "PipeSize"))
1451 return bus_append_parse_size(m
, field
, eq
, 1024);
1453 if (STR_IN_SET(field
, "ExecStartPre", "ExecStartPost", "ExecReload", "ExecStopPost"))
1455 return bus_append_exec_command(m
, field
, eq
);
1457 if (STR_IN_SET(field
,
1458 "SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion",
1459 "BindToDevice", "BindIPv6Only", "FileDescriptorName",
1460 "SocketUser", "SocketGroup"))
1462 return bus_append_string(m
, field
, eq
);
1464 if (streq(field
, "Symlinks"))
1466 return bus_append_strv(m
, field
, eq
, EXTRACT_QUOTES
);
1468 if (streq(field
, "SocketProtocol"))
1470 return bus_append_parse_ip_protocol(m
, field
, eq
);
1472 if (STR_IN_SET(field
,
1473 "ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink",
1474 "ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) {
1477 r
= sd_bus_message_append(m
, "(sv)", "Listen", "a(ss)", 0);
1479 r
= sd_bus_message_append(m
, "(sv)", "Listen", "a(ss)", 1, field
+ STRLEN("Listen"), eq
);
1481 return bus_log_create_error(r
);
1488 static int bus_append_timer_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1491 if (STR_IN_SET(field
, "WakeSystem", "RemainAfterElapse", "Persistent"))
1493 return bus_append_parse_boolean(m
, field
, eq
);
1495 if (STR_IN_SET(field
, "AccuracySec", "RandomizedDelaySec"))
1497 return bus_append_parse_sec_rename(m
, field
, eq
);
1499 if (STR_IN_SET(field
,
1500 "OnActiveSec", "OnBootSec", "OnStartupSec",
1501 "OnUnitActiveSec","OnUnitInactiveSec")) {
1504 r
= sd_bus_message_append(m
, "(sv)", "TimersMonotonic", "a(st)", 0);
1507 r
= parse_sec(eq
, &t
);
1509 return log_error_errno(r
, "Failed to parse %s=%s: %m", field
, eq
);
1511 r
= sd_bus_message_append(m
, "(sv)", "TimersMonotonic", "a(st)", 1, field
, t
);
1514 return bus_log_create_error(r
);
1519 if (streq(field
, "OnCalendar")) {
1522 r
= sd_bus_message_append(m
, "(sv)", "TimersCalendar", "a(ss)", 0);
1524 r
= sd_bus_message_append(m
, "(sv)", "TimersCalendar", "a(ss)", 1, field
, eq
);
1526 return bus_log_create_error(r
);
1534 static int bus_append_unit_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1535 ConditionType t
= _CONDITION_TYPE_INVALID
;
1536 bool is_condition
= false;
1539 if (STR_IN_SET(field
,
1540 "Description", "SourcePath", "OnFailureJobMode",
1541 "JobTimeoutAction", "JobTimeoutRebootArgument",
1542 "StartLimitAction", "FailureAction", "SuccessAction",
1543 "RebootArgument", "CollectMode"))
1545 return bus_append_string(m
, field
, eq
);
1547 if (STR_IN_SET(field
,
1548 "StopWhenUnneeded", "RefuseManualStart", "RefuseManualStop",
1549 "AllowIsolate", "IgnoreOnIsolate", "DefaultDependencies"))
1551 return bus_append_parse_boolean(m
, field
, eq
);
1553 if (STR_IN_SET(field
, "JobTimeoutSec", "JobRunningTimeoutSec", "StartLimitIntervalSec"))
1555 return bus_append_parse_sec_rename(m
, field
, eq
);
1557 if (streq(field
, "StartLimitBurst"))
1559 return bus_append_safe_atou(m
, field
, eq
);
1561 if (STR_IN_SET(field
, "SuccessActionExitStatus", "FailureActionExitStatus")) {
1564 r
= sd_bus_message_append(m
, "(sv)", field
, "i", -1);
1568 r
= safe_atou8(eq
, &u
);
1570 return log_error_errno(r
, "Failed to parse %s=%s", field
, eq
);
1572 r
= sd_bus_message_append(m
, "(sv)", field
, "i", (int) u
);
1575 return bus_log_create_error(r
);
1580 if (unit_dependency_from_string(field
) >= 0 ||
1581 STR_IN_SET(field
, "Documentation", "RequiresMountsFor"))
1583 return bus_append_strv(m
, field
, eq
, EXTRACT_QUOTES
);
1585 t
= condition_type_from_string(field
);
1587 is_condition
= true;
1589 t
= assert_type_from_string(field
);
1592 r
= sd_bus_message_append(m
, "(sv)", is_condition
? "Conditions" : "Asserts", "a(sbbs)", 0);
1595 int trigger
, negate
;
1597 trigger
= *p
== '|';
1605 r
= sd_bus_message_append(m
, "(sv)", is_condition
? "Conditions" : "Asserts", "a(sbbs)", 1,
1606 field
, trigger
, negate
, p
);
1609 return bus_log_create_error(r
);
1617 int bus_append_unit_property_assignment(sd_bus_message
*m
, UnitType t
, const char *assignment
) {
1618 const char *eq
, *field
;
1624 eq
= strchr(assignment
, '=');
1626 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1627 "Not an assignment: %s", assignment
);
1629 field
= strndupa(assignment
, eq
- assignment
);
1634 r
= bus_append_cgroup_property(m
, field
, eq
);
1638 r
= bus_append_execute_property(m
, field
, eq
);
1642 r
= bus_append_kill_property(m
, field
, eq
);
1646 r
= bus_append_service_property(m
, field
, eq
);
1652 r
= bus_append_cgroup_property(m
, field
, eq
);
1656 r
= bus_append_execute_property(m
, field
, eq
);
1660 r
= bus_append_kill_property(m
, field
, eq
);
1664 r
= bus_append_socket_property(m
, field
, eq
);
1670 r
= bus_append_timer_property(m
, field
, eq
);
1676 r
= bus_append_path_property(m
, field
, eq
);
1682 r
= bus_append_cgroup_property(m
, field
, eq
);
1689 if (streq(field
, "TimeoutStopSec"))
1690 return bus_append_parse_sec_rename(m
, field
, eq
);
1692 r
= bus_append_cgroup_property(m
, field
, eq
);
1696 r
= bus_append_kill_property(m
, field
, eq
);
1702 r
= bus_append_cgroup_property(m
, field
, eq
);
1706 r
= bus_append_execute_property(m
, field
, eq
);
1710 r
= bus_append_kill_property(m
, field
, eq
);
1714 r
= bus_append_mount_property(m
, field
, eq
);
1720 case UNIT_AUTOMOUNT
:
1721 r
= bus_append_automount_property(m
, field
, eq
);
1730 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1731 "Not supported unit type");
1734 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1735 "Invalid unit type");
1738 r
= bus_append_unit_property(m
, field
, eq
);
1742 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1743 "Unknown assignment: %s", assignment
);
1746 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, UnitType t
, char **l
) {
1752 STRV_FOREACH(i
, l
) {
1753 r
= bus_append_unit_property_assignment(m
, t
, *i
);
1761 typedef struct BusWaitForJobs
{
1768 sd_bus_slot
*slot_job_removed
;
1769 sd_bus_slot
*slot_disconnected
;
1772 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1775 log_error("Warning! D-Bus connection terminated.");
1776 sd_bus_close(sd_bus_message_get_bus(m
));
1781 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1782 const char *path
, *unit
, *result
;
1783 BusWaitForJobs
*d
= userdata
;
1791 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1793 bus_log_parse_error(r
);
1797 found
= set_remove(d
->jobs
, (char*) path
);
1803 if (!isempty(result
))
1804 d
->result
= strdup(result
);
1807 d
->name
= strdup(unit
);
1812 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1816 set_free_free(d
->jobs
);
1818 sd_bus_slot_unref(d
->slot_disconnected
);
1819 sd_bus_slot_unref(d
->slot_job_removed
);
1821 sd_bus_unref(d
->bus
);
1829 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1830 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1836 d
= new0(BusWaitForJobs
, 1);
1840 d
->bus
= sd_bus_ref(bus
);
1842 /* When we are a bus client we match by sender. Direct
1843 * connections OTOH have no initialized sender field, and
1844 * hence we ignore the sender then */
1845 r
= sd_bus_match_signal_async(
1847 &d
->slot_job_removed
,
1848 bus
->bus_client
? "org.freedesktop.systemd1" : NULL
,
1849 "/org/freedesktop/systemd1",
1850 "org.freedesktop.systemd1.Manager",
1852 match_job_removed
, NULL
, d
);
1856 r
= sd_bus_match_signal_async(
1858 &d
->slot_disconnected
,
1859 "org.freedesktop.DBus.Local",
1861 "org.freedesktop.DBus.Local",
1863 match_disconnected
, NULL
, d
);
1872 static int bus_process_wait(sd_bus
*bus
) {
1876 r
= sd_bus_process(bus
, NULL
);
1882 r
= sd_bus_wait(bus
, (uint64_t) -1);
1888 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1889 _cleanup_free_
char *dbus_path
= NULL
;
1895 if (!endswith(d
->name
, ".service"))
1898 dbus_path
= unit_dbus_path_from_name(d
->name
);
1902 return sd_bus_get_property_string(d
->bus
,
1903 "org.freedesktop.systemd1",
1905 "org.freedesktop.systemd1.Service",
1911 static const struct {
1912 const char *result
, *explanation
;
1913 } explanations
[] = {
1914 { "resources", "of unavailable resources or another system error" },
1915 { "protocol", "the service did not take the steps required by its unit configuration" },
1916 { "timeout", "a timeout was exceeded" },
1917 { "exit-code", "the control process exited with error code" },
1918 { "signal", "a fatal signal was delivered to the control process" },
1919 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1920 { "watchdog", "the service failed to send watchdog ping" },
1921 { "start-limit", "start of the service was attempted too often" }
1924 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1925 _cleanup_free_
char *service_shell_quoted
= NULL
;
1926 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1930 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1932 if (!strv_isempty((char**) extra_args
)) {
1933 _cleanup_free_
char *t
;
1935 t
= strv_join((char**) extra_args
, " ");
1936 systemctl
= strjoina("systemctl ", t
? : "<args>");
1937 journalctl
= strjoina("journalctl ", t
? : "<args>");
1940 if (!isempty(result
)) {
1943 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1944 if (streq(result
, explanations
[i
].result
))
1947 if (i
< ELEMENTSOF(explanations
)) {
1948 log_error("Job for %s failed because %s.\n"
1949 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1951 explanations
[i
].explanation
,
1953 service_shell_quoted
?: "<service>",
1959 log_error("Job for %s failed.\n"
1960 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1963 service_shell_quoted
?: "<service>",
1967 /* For some results maybe additional explanation is required */
1968 if (streq_ptr(result
, "start-limit"))
1969 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1970 "followed by \"%1$s start %2$s\" again.",
1972 service_shell_quoted
?: "<service>");
1975 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1979 if (streq(d
->result
, "canceled"))
1980 log_error("Job for %s canceled.", strna(d
->name
));
1981 else if (streq(d
->result
, "timeout"))
1982 log_error("Job for %s timed out.", strna(d
->name
));
1983 else if (streq(d
->result
, "dependency"))
1984 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1985 else if (streq(d
->result
, "invalid"))
1986 log_error("%s is not active, cannot reload.", strna(d
->name
));
1987 else if (streq(d
->result
, "assert"))
1988 log_error("Assertion failed on job for %s.", strna(d
->name
));
1989 else if (streq(d
->result
, "unsupported"))
1990 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1991 else if (streq(d
->result
, "collected"))
1992 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1993 else if (streq(d
->result
, "once"))
1994 log_error("Unit %s was started already once and can't be started again.", strna(d
->name
));
1995 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1997 _cleanup_free_
char *result
= NULL
;
2000 q
= bus_job_get_service_result(d
, &result
);
2002 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
2004 log_job_error_with_service_result(d
->name
, result
, extra_args
);
2006 log_error("Job failed. See \"journalctl -xe\" for details.");
2010 if (STR_IN_SET(d
->result
, "canceled", "collected"))
2012 else if (streq(d
->result
, "timeout"))
2014 else if (streq(d
->result
, "dependency"))
2016 else if (streq(d
->result
, "invalid"))
2018 else if (streq(d
->result
, "assert"))
2020 else if (streq(d
->result
, "unsupported"))
2022 else if (streq(d
->result
, "once"))
2024 else if (STR_IN_SET(d
->result
, "done", "skipped"))
2027 return log_debug_errno(SYNTHETIC_ERRNO(EIO
),
2028 "Unexpected job result, assuming server side newer than us: %s", d
->result
);
2031 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
2036 while (!set_isempty(d
->jobs
)) {
2039 q
= bus_process_wait(d
->bus
);
2041 return log_error_errno(q
, "Failed to wait for response: %m");
2044 q
= check_wait_response(d
, quiet
, extra_args
);
2045 /* Return the first error as it is most likely to be
2047 if (q
< 0 && r
== 0)
2050 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
2053 d
->name
= mfree(d
->name
);
2054 d
->result
= mfree(d
->result
);
2060 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
2065 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
2069 return set_put_strdup(d
->jobs
, path
);
2072 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
2075 r
= bus_wait_for_jobs_add(d
, path
);
2079 return bus_wait_for_jobs(d
, quiet
, NULL
);
2082 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, size_t *n_changes
) {
2083 const char *type
, *path
, *source
;
2086 /* changes is dereferenced when calling unit_file_dump_changes() later,
2087 * so we have to make sure this is not NULL. */
2091 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
2093 return bus_log_parse_error(r
);
2095 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
2096 /* We expect only "success" changes to be sent over the bus.
2097 Hence, reject anything negative. */
2098 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
2101 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
2105 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
2110 return bus_log_parse_error(r
);
2112 r
= sd_bus_message_exit_container(m
);
2114 return bus_log_parse_error(r
);
2116 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, quiet
);
2122 bool is_const
; /* If false, cgroup_path should be free()'d */
2124 Hashmap
*pids
; /* PID → process name */
2127 struct CGroupInfo
*parent
;
2128 LIST_FIELDS(struct CGroupInfo
, siblings
);
2129 LIST_HEAD(struct CGroupInfo
, children
);
2133 static bool IS_ROOT(const char *p
) {
2134 return isempty(p
) || streq(p
, "/");
2137 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
2138 struct CGroupInfo
*parent
= NULL
, *cg
;
2147 cg
= hashmap_get(cgroups
, path
);
2153 if (!IS_ROOT(path
)) {
2156 e
= strrchr(path
, '/');
2160 pp
= strndupa(path
, e
- path
);
2164 r
= add_cgroup(cgroups
, pp
, false, &parent
);
2169 cg
= new0(struct CGroupInfo
, 1);
2174 cg
->cgroup_path
= (char*) path
;
2176 cg
->cgroup_path
= strdup(path
);
2177 if (!cg
->cgroup_path
) {
2183 cg
->is_const
= is_const
;
2184 cg
->parent
= parent
;
2186 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
2189 free(cg
->cgroup_path
);
2195 LIST_PREPEND(siblings
, parent
->children
, cg
);
2196 parent
->n_children
++;
2203 static int add_process(
2209 struct CGroupInfo
*cg
;
2216 r
= add_cgroup(cgroups
, path
, true, &cg
);
2220 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
2224 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
2227 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
2231 while (cg
->children
)
2232 remove_cgroup(cgroups
, cg
->children
);
2234 hashmap_remove(cgroups
, cg
->cgroup_path
);
2237 free(cg
->cgroup_path
);
2239 hashmap_free(cg
->pids
);
2242 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
2247 static int cgroup_info_compare_func(struct CGroupInfo
* const *a
, struct CGroupInfo
* const *b
) {
2248 return strcmp((*a
)->cgroup_path
, (*b
)->cgroup_path
);
2251 static int dump_processes(
2253 const char *cgroup_path
,
2256 OutputFlags flags
) {
2258 struct CGroupInfo
*cg
;
2263 if (IS_ROOT(cgroup_path
))
2266 cg
= hashmap_get(cgroups
, cgroup_path
);
2270 if (!hashmap_isempty(cg
->pids
)) {
2278 /* Order processes by their PID */
2279 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
2281 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
2282 pids
[n
++] = PTR_TO_PID(pidp
);
2284 assert(n
== hashmap_size(cg
->pids
));
2285 typesafe_qsort(pids
, n
, pid_compare_func
);
2287 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
2289 for (i
= 0; i
< n
; i
++) {
2290 _cleanup_free_
char *e
= NULL
;
2291 const char *special
;
2294 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
2297 if (n_columns
!= 0) {
2300 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
2302 e
= ellipsize(name
, k
, 100);
2307 more
= i
+1 < n
|| cg
->children
;
2308 special
= special_glyph(more
? SPECIAL_GLYPH_TREE_BRANCH
: SPECIAL_GLYPH_TREE_RIGHT
);
2310 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
2319 struct CGroupInfo
**children
, *child
;
2322 /* Order subcgroups by their name */
2323 children
= newa(struct CGroupInfo
*, cg
->n_children
);
2324 LIST_FOREACH(siblings
, child
, cg
->children
)
2325 children
[n
++] = child
;
2326 assert(n
== cg
->n_children
);
2327 typesafe_qsort(children
, n
, cgroup_info_compare_func
);
2330 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
2332 for (i
= 0; i
< n
; i
++) {
2333 _cleanup_free_
char *pp
= NULL
;
2334 const char *name
, *special
;
2337 child
= children
[i
];
2339 name
= strrchr(child
->cgroup_path
, '/');
2345 special
= special_glyph(more
? SPECIAL_GLYPH_TREE_BRANCH
: SPECIAL_GLYPH_TREE_RIGHT
);
2347 fputs(prefix
, stdout
);
2348 fputs(special
, stdout
);
2349 fputs(name
, stdout
);
2350 fputc('\n', stdout
);
2352 special
= special_glyph(more
? SPECIAL_GLYPH_TREE_VERTICAL
: SPECIAL_GLYPH_TREE_SPACE
);
2354 pp
= strappend(prefix
, special
);
2358 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
2368 static int dump_extra_processes(
2372 OutputFlags flags
) {
2374 _cleanup_free_ pid_t
*pids
= NULL
;
2375 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
2376 struct CGroupInfo
*cg
;
2377 size_t n_allocated
= 0, n
= 0, k
;
2381 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
2382 * combined, sorted, linear list. */
2384 HASHMAP_FOREACH(cg
, cgroups
, i
) {
2392 if (hashmap_isempty(cg
->pids
))
2395 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
2399 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
2402 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
2403 pids
[n
++] = PTR_TO_PID(pidp
);
2405 r
= hashmap_put(names
, pidp
, (void*) name
);
2414 typesafe_qsort(pids
, n
, pid_compare_func
);
2415 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
2417 for (k
= 0; k
< n
; k
++) {
2418 _cleanup_free_
char *e
= NULL
;
2421 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
2424 if (n_columns
!= 0) {
2427 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
2429 e
= ellipsize(name
, z
, 100);
2434 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
2436 special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET
),
2444 int unit_show_processes(
2447 const char *cgroup_path
,
2451 sd_bus_error
*error
) {
2453 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2454 Hashmap
*cgroups
= NULL
;
2455 struct CGroupInfo
*cg
;
2461 if (flags
& OUTPUT_FULL_WIDTH
)
2463 else if (n_columns
<= 0)
2464 n_columns
= columns();
2466 prefix
= strempty(prefix
);
2468 r
= sd_bus_call_method(
2470 "org.freedesktop.systemd1",
2471 "/org/freedesktop/systemd1",
2472 "org.freedesktop.systemd1.Manager",
2481 cgroups
= hashmap_new(&path_hash_ops
);
2485 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
2490 const char *path
= NULL
, *name
= NULL
;
2493 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
2499 r
= add_process(cgroups
, path
, pid
, name
);
2504 r
= sd_bus_message_exit_container(reply
);
2508 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
2512 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
2515 while ((cg
= hashmap_first(cgroups
)))
2516 remove_cgroup(cgroups
, cg
);
2518 hashmap_free(cgroups
);
2523 int unit_load_state(sd_bus
*bus
, const char *name
, char **load_state
) {
2524 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2525 _cleanup_free_
char *path
= NULL
;
2528 path
= unit_dbus_path_from_name(name
);
2532 /* This function warns on it's own, because otherwise it'd be awkward to pass
2533 * the dbus error message around. */
2535 r
= sd_bus_get_property_string(
2537 "org.freedesktop.systemd1",
2539 "org.freedesktop.systemd1.Unit",
2544 return log_error_errno(r
, "Failed to get load state of %s: %s", name
, bus_error_message(&error
, r
));