1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2016 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include "alloc-util.h"
22 #include "bus-internal.h"
23 #include "bus-unit-util.h"
26 #include "cgroup-util.h"
27 #include "condition.h"
28 #include "cpu-set-util.h"
30 #include "errno-list.h"
33 #include "hexdecoct.h"
34 #include "hostname-util.h"
35 #include "in-addr-util.h"
37 #include "locale-util.h"
38 #include "mount-util.h"
40 #include "parse-util.h"
41 #include "path-util.h"
42 #include "process-util.h"
43 #include "rlimit-util.h"
44 #include "securebits-util.h"
45 #include "signal-util.h"
46 #include "socket-protocol-list.h"
47 #include "string-util.h"
48 #include "syslog-util.h"
49 #include "terminal-util.h"
51 #include "user-util.h"
55 int bus_parse_unit_info(sd_bus_message
*message
, UnitInfo
*u
) {
61 return sd_bus_message_read(
76 #define DEFINE_BUS_APPEND_PARSE_PTR(bus_type, cast_type, type, parse_func) \
77 static int bus_append_##parse_func( \
84 r = parse_func(eq, &val); \
86 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); \
88 r = sd_bus_message_append(m, "(sv)", field, \
89 bus_type, (cast_type) val); \
91 return bus_log_create_error(r); \
95 struct __useless_struct_to_allow_trailing_semicolon__
97 #define DEFINE_BUS_APPEND_PARSE(bus_type, parse_func) \
98 static int bus_append_##parse_func( \
104 r = parse_func(eq); \
106 log_error("Failed to parse %s: %s", field, eq); \
110 r = sd_bus_message_append(m, "(sv)", field, \
111 bus_type, (int32_t) r); \
113 return bus_log_create_error(r); \
117 struct __useless_struct_to_allow_trailing_semicolon__
119 DEFINE_BUS_APPEND_PARSE("b", parse_boolean
);
120 DEFINE_BUS_APPEND_PARSE("i", ioprio_class_from_string
);
121 DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string
);
122 DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string
);
123 DEFINE_BUS_APPEND_PARSE("i", log_level_from_string
);
124 DEFINE_BUS_APPEND_PARSE("i", parse_errno
);
125 DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string
);
126 DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string
);
127 DEFINE_BUS_APPEND_PARSE("i", signal_from_string_try_harder
);
128 DEFINE_BUS_APPEND_PARSE("i", socket_protocol_from_name
);
129 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, ioprio_parse_priority
);
130 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, parse_nice
);
131 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, safe_atoi
);
132 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, nsec_t
, parse_nsec
);
133 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_blkio_weight_parse
);
134 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_shares_parse
);
135 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_weight_parse
);
136 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, unsigned long, mount_propagation_flags_from_string
);
137 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, safe_atou64
);
138 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t
, parse_mode
);
139 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou
);
140 DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64
);
142 static inline int bus_append_string(sd_bus_message
*m
, const char *field
, const char *eq
) {
145 r
= sd_bus_message_append(m
, "(sv)", field
, "s", eq
);
147 return bus_log_create_error(r
);
152 static int bus_append_strv(sd_bus_message
*m
, const char *field
, const char *eq
, ExtractFlags flags
) {
156 r
= sd_bus_message_open_container(m
, 'r', "sv");
158 return bus_log_create_error(r
);
160 r
= sd_bus_message_append_basic(m
, 's', field
);
162 return bus_log_create_error(r
);
164 r
= sd_bus_message_open_container(m
, 'v', "as");
166 return bus_log_create_error(r
);
168 r
= sd_bus_message_open_container(m
, 'a', "s");
170 return bus_log_create_error(r
);
173 _cleanup_free_
char *word
= NULL
;
175 r
= extract_first_word(&p
, &word
, NULL
, flags
);
181 return log_error_errno(r
, "Invalid syntax: %s", eq
);
183 r
= sd_bus_message_append_basic(m
, 's', word
);
185 return bus_log_create_error(r
);
188 r
= sd_bus_message_close_container(m
);
190 return bus_log_create_error(r
);
192 r
= sd_bus_message_close_container(m
);
194 return bus_log_create_error(r
);
196 r
= sd_bus_message_close_container(m
);
198 return bus_log_create_error(r
);
203 static int bus_append_byte_array(sd_bus_message
*m
, const char *field
, const void *buf
, size_t n
) {
206 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
208 return bus_log_create_error(r
);
210 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
212 return bus_log_create_error(r
);
214 r
= sd_bus_message_open_container(m
, 'v', "ay");
216 return bus_log_create_error(r
);
218 r
= sd_bus_message_append_array(m
, 'y', buf
, n
);
220 return bus_log_create_error(r
);
222 r
= sd_bus_message_close_container(m
);
224 return bus_log_create_error(r
);
226 r
= sd_bus_message_close_container(m
);
228 return bus_log_create_error(r
);
233 static int bus_append_parse_sec_rename(sd_bus_message
*m
, const char *field
, const char *eq
) {
239 r
= parse_sec(eq
, &t
);
241 return log_error_errno(r
, "Failed to parse %s=%s: %m", field
, eq
);
244 n
= newa(char, l
+ 2);
245 /* Change suffix Sec → USec */
246 strcpy(mempcpy(n
, field
, l
- 3), "USec");
248 r
= sd_bus_message_append(m
, "(sv)", n
, "t", t
);
250 return bus_log_create_error(r
);
255 static int bus_append_parse_size(sd_bus_message
*m
, const char *field
, const char *eq
, uint64_t base
) {
259 r
= parse_size(eq
, base
, &v
);
261 return log_error_errno(r
, "Failed to parse %s=%s: %m", field
, eq
);
263 r
= sd_bus_message_append(m
, "(sv)", field
, "t", v
);
265 return bus_log_create_error(r
);
270 static int bus_append_exec_command(sd_bus_message
*m
, const char *field
, const char *eq
) {
271 bool ignore_failure
= false, explicit_path
= false, done
= false;
272 _cleanup_strv_free_
char **l
= NULL
;
273 _cleanup_free_
char *path
= NULL
;
283 ignore_failure
= true;
292 explicit_path
= true;
299 /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */
300 log_error("Sorry, but +, ! and !! are currently not supported for transient services.");
310 r
= extract_first_word(&eq
, &path
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
312 return log_error_errno(r
, "Failed to parse path: %m");
315 r
= strv_split_extract(&l
, eq
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
317 return log_error_errno(r
, "Failed to parse command line: %m");
319 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
321 return bus_log_create_error(r
);
323 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
325 return bus_log_create_error(r
);
327 r
= sd_bus_message_open_container(m
, 'v', "a(sasb)");
329 return bus_log_create_error(r
);
331 r
= sd_bus_message_open_container(m
, 'a', "(sasb)");
333 return bus_log_create_error(r
);
335 if (!strv_isempty(l
)) {
337 r
= sd_bus_message_open_container(m
, 'r', "sasb");
339 return bus_log_create_error(r
);
341 r
= sd_bus_message_append(m
, "s", path
?: l
[0]);
343 return bus_log_create_error(r
);
345 r
= sd_bus_message_append_strv(m
, l
);
347 return bus_log_create_error(r
);
349 r
= sd_bus_message_append(m
, "b", ignore_failure
);
351 return bus_log_create_error(r
);
353 r
= sd_bus_message_close_container(m
);
355 return bus_log_create_error(r
);
358 r
= sd_bus_message_close_container(m
);
360 return bus_log_create_error(r
);
362 r
= sd_bus_message_close_container(m
);
364 return bus_log_create_error(r
);
366 r
= sd_bus_message_close_container(m
);
368 return bus_log_create_error(r
);
373 static int bus_append_ip_address_access(sd_bus_message
*m
, int family
, const union in_addr_union
*prefix
, unsigned char prefixlen
) {
379 r
= sd_bus_message_open_container(m
, 'r', "iayu");
383 r
= sd_bus_message_append(m
, "i", family
);
387 r
= sd_bus_message_append_array(m
, 'y', prefix
, FAMILY_ADDRESS_SIZE(family
));
391 r
= sd_bus_message_append(m
, "u", prefixlen
);
395 return sd_bus_message_close_container(m
);
398 static int bus_append_cgroup_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
401 if (STR_IN_SET(field
, "DevicePolicy", "Slice"))
403 return bus_append_string(m
, field
, eq
);
405 if (STR_IN_SET(field
,
406 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
407 "TasksAccounting", "IPAccounting"))
409 return bus_append_parse_boolean(m
, field
, eq
);
411 if (STR_IN_SET(field
, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight"))
413 return bus_append_cg_weight_parse(m
, field
, eq
);
415 if (STR_IN_SET(field
, "CPUShares", "StartupCPUShares"))
417 return bus_append_cg_cpu_shares_parse(m
, field
, eq
);
419 if (STR_IN_SET(field
, "BlockIOWeight", "StartupBlockIOWeight"))
421 return bus_append_cg_blkio_weight_parse(m
, field
, eq
);
423 if (streq(field
, "Delegate")) {
425 r
= parse_boolean(eq
);
427 return bus_append_strv(m
, "DelegateControllers", eq
, EXTRACT_QUOTES
);
429 r
= sd_bus_message_append(m
, "(sv)", "Delegate", "b", r
);
431 return bus_log_create_error(r
);
436 if (STR_IN_SET(field
, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) {
438 if (isempty(eq
) || streq(eq
, "infinity")) {
439 r
= sd_bus_message_append(m
, "(sv)", field
, "t", CGROUP_LIMIT_MAX
);
441 return bus_log_create_error(r
);
445 r
= parse_percent(eq
);
449 /* When this is a percentage we'll convert this into a relative value in the range
450 * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
451 * ones). This way the physical memory size can be determined server-side */
453 n
= strjoina(field
, "Scale");
454 r
= sd_bus_message_append(m
, "(sv)", n
, "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
456 return bus_log_create_error(r
);
461 if (streq(field
, "TasksMax"))
462 return bus_append_safe_atou64(m
, field
, eq
);
464 return bus_append_parse_size(m
, field
, eq
, 1024);
468 if (streq(field
, "CPUQuota")) {
471 r
= sd_bus_message_append(m
, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY
);
473 r
= parse_percent_unbounded(eq
);
475 log_error_errno(r
, "CPU quota '%s' invalid.", eq
);
479 r
= sd_bus_message_append(m
, "(sv)", "CPUQuotaPerSecUSec", "t", (usec_t
) r
* USEC_PER_SEC
/ 100U);
483 return bus_log_create_error(r
);
488 if (streq(field
, "DeviceAllow")) {
491 r
= sd_bus_message_append(m
, "(sv)", field
, "a(ss)", 0);
493 const char *path
= eq
, *rwm
= NULL
, *e
;
497 path
= strndupa(eq
, e
- eq
);
501 r
= sd_bus_message_append(m
, "(sv)", field
, "a(ss)", 1, path
, strempty(rwm
));
505 return bus_log_create_error(r
);
510 if (cgroup_io_limit_type_from_string(field
) >= 0 || STR_IN_SET(field
, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
513 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 0);
515 const char *path
, *bandwidth
, *e
;
520 log_error("Failed to parse %s value %s.", field
, eq
);
524 path
= strndupa(eq
, e
- eq
);
527 if (streq(bandwidth
, "infinity")) {
528 bytes
= CGROUP_LIMIT_MAX
;
530 r
= parse_size(bandwidth
, 1000, &bytes
);
532 return log_error_errno(r
, "Failed to parse byte value %s: %m", bandwidth
);
535 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 1, path
, bytes
);
539 return bus_log_create_error(r
);
544 if (STR_IN_SET(field
, "IODeviceWeight", "BlockIODeviceWeight")) {
547 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 0);
549 const char *path
, *weight
, *e
;
554 log_error("Failed to parse %s value %s.", field
, eq
);
558 path
= strndupa(eq
, e
- eq
);
561 r
= safe_atou64(weight
, &u
);
563 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, weight
);
565 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 1, path
, u
);
569 return bus_log_create_error(r
);
574 if (STR_IN_SET(field
, "IPAddressAllow", "IPAddressDeny")) {
575 unsigned char prefixlen
;
576 union in_addr_union prefix
= {};
580 r
= sd_bus_message_append(m
, "(sv)", field
, "a(iayu)", 0);
582 return bus_log_create_error(r
);
587 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
589 return bus_log_create_error(r
);
591 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
593 return bus_log_create_error(r
);
595 r
= sd_bus_message_open_container(m
, 'v', "a(iayu)");
597 return bus_log_create_error(r
);
599 r
= sd_bus_message_open_container(m
, 'a', "(iayu)");
601 return bus_log_create_error(r
);
603 if (streq(eq
, "any")) {
604 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
606 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 0);
608 return bus_log_create_error(r
);
610 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 0);
612 return bus_log_create_error(r
);
614 } else if (is_localhost(eq
)) {
615 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
617 prefix
.in
.s_addr
= htobe32(0x7f000000);
618 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 8);
620 return bus_log_create_error(r
);
622 prefix
.in6
= (struct in6_addr
) IN6ADDR_LOOPBACK_INIT
;
623 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 128);
627 } else if (streq(eq
, "link-local")) {
628 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
630 prefix
.in
.s_addr
= htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
631 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 16);
633 return bus_log_create_error(r
);
635 prefix
.in6
= (struct in6_addr
) {
636 .s6_addr32
[0] = htobe32(0xfe800000)
638 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 64);
640 return bus_log_create_error(r
);
642 } else if (streq(eq
, "multicast")) {
643 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
645 prefix
.in
.s_addr
= htobe32((UINT32_C(224) << 24));
646 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 4);
648 return bus_log_create_error(r
);
650 prefix
.in6
= (struct in6_addr
) {
651 .s6_addr32
[0] = htobe32(0xff000000)
653 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 8);
655 return bus_log_create_error(r
);
658 r
= in_addr_prefix_from_string_auto(eq
, &family
, &prefix
, &prefixlen
);
660 return log_error_errno(r
, "Failed to parse IP address prefix: %s", eq
);
662 r
= bus_append_ip_address_access(m
, family
, &prefix
, prefixlen
);
664 return bus_log_create_error(r
);
667 r
= sd_bus_message_close_container(m
);
669 return bus_log_create_error(r
);
671 r
= sd_bus_message_close_container(m
);
673 return bus_log_create_error(r
);
675 r
= sd_bus_message_close_container(m
);
677 return bus_log_create_error(r
);
685 static int bus_append_automount_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
687 if (streq(field
, "Where"))
689 return bus_append_string(m
, field
, eq
);
691 if (streq(field
, "DirectoryMode"))
693 return bus_append_parse_mode(m
, field
, eq
);
695 if (streq(field
, "TimeoutIdleSec"))
697 return bus_append_parse_sec_rename(m
, field
, eq
);
702 static int bus_append_execute_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
705 if (STR_IN_SET(field
,
707 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
708 "WorkingDirectory", "RootDirectory", "SyslogIdentifier",
709 "ProtectSystem", "ProtectHome", "SELinuxContext", "RootImage",
710 "RuntimeDirectoryPreserve", "Personality", "KeyringMode"))
712 return bus_append_string(m
, field
, eq
);
714 if (STR_IN_SET(field
,
715 "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
716 "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
717 "NoNewPrivileges", "SyslogLevelPrefix",
718 "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
719 "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups",
720 "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality"))
722 return bus_append_parse_boolean(m
, field
, eq
);
724 if (STR_IN_SET(field
,
725 "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
726 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths",
727 "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory",
728 "SupplementaryGroups", "SystemCallArchitectures"))
730 return bus_append_strv(m
, field
, eq
, EXTRACT_QUOTES
);
732 if (STR_IN_SET(field
, "SyslogLevel", "LogLevelMax"))
734 return bus_append_log_level_from_string(m
, field
, eq
);
736 if (streq(field
, "SyslogFacility"))
738 return bus_append_log_facility_unshifted_from_string(m
, field
, eq
);
740 if (streq(field
, "SecureBits"))
742 return bus_append_secure_bits_from_string(m
, field
, eq
);
744 if (streq(field
, "CPUSchedulingPolicy"))
746 return bus_append_sched_policy_from_string(m
, field
, eq
);
748 if (STR_IN_SET(field
, "CPUSchedulingPriority", "OOMScoreAdjust"))
750 return bus_append_safe_atoi(m
, field
, eq
);
752 if (streq(field
, "Nice"))
754 return bus_append_parse_nice(m
, field
, eq
);
756 if (streq(field
, "SystemCallErrorNumber"))
758 return bus_append_parse_errno(m
, field
, eq
);
760 if (streq(field
, "IOSchedulingClass"))
762 return bus_append_ioprio_class_from_string(m
, field
, eq
);
764 if (streq(field
, "IOSchedulingPriority"))
766 return bus_append_ioprio_parse_priority(m
, field
, eq
);
768 if (STR_IN_SET(field
,
769 "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode",
770 "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask"))
772 return bus_append_parse_mode(m
, field
, eq
);
774 if (streq(field
, "TimerSlackNSec"))
776 return bus_append_parse_nsec(m
, field
, eq
);
778 if (streq(field
, "MountFlags"))
780 return bus_append_mount_propagation_flags_from_string(m
, field
, eq
);
782 if (STR_IN_SET(field
, "Environment", "UnsetEnvironment", "PassEnvironment"))
784 return bus_append_strv(m
, field
, eq
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
786 if (streq(field
, "EnvironmentFile")) {
789 r
= sd_bus_message_append(m
, "(sv)", "EnvironmentFiles", "a(sb)", 0);
791 r
= sd_bus_message_append(m
, "(sv)", "EnvironmentFiles", "a(sb)", 1,
792 eq
[0] == '-' ? eq
+ 1 : eq
,
795 return bus_log_create_error(r
);
800 if (streq(field
, "LogExtraFields")) {
802 r
= sd_bus_message_open_container(m
, 'r', "sv");
804 return bus_log_create_error(r
);
806 r
= sd_bus_message_append_basic(m
, 's', "LogExtraFields");
808 return bus_log_create_error(r
);
810 r
= sd_bus_message_open_container(m
, 'v', "aay");
812 return bus_log_create_error(r
);
814 r
= sd_bus_message_open_container(m
, 'a', "ay");
816 return bus_log_create_error(r
);
818 r
= sd_bus_message_append_array(m
, 'y', eq
, strlen(eq
));
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
);
830 r
= sd_bus_message_close_container(m
);
832 return bus_log_create_error(r
);
837 if (STR_IN_SET(field
, "StandardInput", "StandardOutput", "StandardError")) {
838 const char *n
, *appended
;
840 if ((n
= startswith(eq
, "fd:"))) {
841 appended
= strjoina(field
, "FileDescriptorName");
842 r
= sd_bus_message_append(m
, "(sv)", appended
, "s", n
);
843 } else if ((n
= startswith(eq
, "file:"))) {
844 appended
= strjoina(field
, "File");
845 r
= sd_bus_message_append(m
, "(sv)", appended
, "s", n
);
847 r
= sd_bus_message_append(m
, "(sv)", field
, "s", eq
);
850 return bus_log_create_error(r
);
855 if (streq(field
, "StandardInputText")) {
856 _cleanup_free_
char *unescaped
= NULL
;
858 r
= cunescape(eq
, 0, &unescaped
);
860 return log_error_errno(r
, "Failed to unescape text '%s': %m", eq
);
862 if (!strextend(&unescaped
, "\n", NULL
))
865 /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
866 * interface anyway */
868 return bus_append_byte_array(m
, field
, unescaped
, strlen(unescaped
));
871 if (streq(field
, "StandardInputData")) {
872 _cleanup_free_
void *decoded
= NULL
;
875 r
= unbase64mem(eq
, (size_t) -1, &decoded
, &sz
);
877 return log_error_errno(r
, "Failed to decode base64 data '%s': %m", eq
);
879 return bus_append_byte_array(m
, field
, decoded
, sz
);
882 rl
= rlimit_from_string(field
);
887 r
= rlimit_parse(rl
, eq
, &l
);
889 return log_error_errno(r
, "Failed to parse resource limit: %s", eq
);
891 r
= sd_bus_message_append(m
, "(sv)", field
, "t", l
.rlim_max
);
893 return bus_log_create_error(r
);
895 sn
= strjoina(field
, "Soft");
896 r
= sd_bus_message_append(m
, "(sv)", sn
, "t", l
.rlim_cur
);
898 return bus_log_create_error(r
);
903 if (STR_IN_SET(field
, "AppArmorProfile", "SmackProcessLabel")) {
912 r
= sd_bus_message_append(m
, "(sv)", field
, "(bs)", ignore
, s
);
914 return bus_log_create_error(r
);
919 if (STR_IN_SET(field
, "CapabilityBoundingSet", "AmbientCapabilities")) {
929 r
= capability_set_from_string(p
, &sum
);
931 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
933 sum
= invert
? ~sum
: sum
;
935 r
= sd_bus_message_append(m
, "(sv)", field
, "t", sum
);
937 return bus_log_create_error(r
);
942 if (streq(field
, "CPUAffinity")) {
943 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
945 r
= parse_cpu_set(eq
, &cpuset
);
947 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
949 return bus_append_byte_array(m
, field
, cpuset
, CPU_ALLOC_SIZE(r
));
952 if (STR_IN_SET(field
, "RestrictAddressFamilies", "SystemCallFilter")) {
961 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
963 return bus_log_create_error(r
);
965 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
967 return bus_log_create_error(r
);
969 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
971 return bus_log_create_error(r
);
973 r
= sd_bus_message_open_container(m
, 'r', "bas");
975 return bus_log_create_error(r
);
977 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
979 return bus_log_create_error(r
);
981 r
= sd_bus_message_open_container(m
, 'a', "s");
983 return bus_log_create_error(r
);
986 _cleanup_free_
char *word
= NULL
;
988 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
994 return log_error_errno(r
, "Invalid syntax: %s", eq
);
996 r
= sd_bus_message_append_basic(m
, 's', word
);
998 return bus_log_create_error(r
);
1001 r
= sd_bus_message_close_container(m
);
1003 return bus_log_create_error(r
);
1005 r
= sd_bus_message_close_container(m
);
1007 return bus_log_create_error(r
);
1009 r
= sd_bus_message_close_container(m
);
1011 return bus_log_create_error(r
);
1013 r
= sd_bus_message_close_container(m
);
1015 return bus_log_create_error(r
);
1020 if (streq(field
, "RestrictNamespaces")) {
1021 bool invert
= false;
1022 unsigned long flags
= 0;
1029 r
= parse_boolean(eq
);
1033 flags
= NAMESPACE_FLAGS_ALL
;
1035 r
= namespace_flag_from_string_many(eq
, &flags
);
1037 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
1041 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
1043 r
= sd_bus_message_append(m
, "(sv)", field
, "t", (uint64_t) flags
);
1045 return bus_log_create_error(r
);
1050 if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
1053 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
1055 return bus_log_create_error(r
);
1057 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
1059 return bus_log_create_error(r
);
1061 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
1063 return bus_log_create_error(r
);
1065 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
1067 return bus_log_create_error(r
);
1070 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
1071 char *s
= NULL
, *d
= NULL
;
1072 bool ignore_enoent
= false;
1073 uint64_t flags
= MS_REC
;
1075 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1077 return log_error_errno(r
, "Failed to parse argument: %m");
1083 ignore_enoent
= true;
1087 if (p
&& p
[-1] == ':') {
1088 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1090 return log_error_errno(r
, "Failed to parse argument: %m");
1092 log_error("Missing argument after ':': %s", eq
);
1098 if (p
&& p
[-1] == ':') {
1099 _cleanup_free_
char *options
= NULL
;
1101 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
1103 return log_error_errno(r
, "Failed to parse argument: %m");
1105 if (isempty(options
) || streq(options
, "rbind"))
1107 else if (streq(options
, "norbind"))
1110 log_error("Unknown options: %s", eq
);
1118 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
1120 return bus_log_create_error(r
);
1123 r
= sd_bus_message_close_container(m
);
1125 return bus_log_create_error(r
);
1127 r
= sd_bus_message_close_container(m
);
1129 return bus_log_create_error(r
);
1131 r
= sd_bus_message_close_container(m
);
1133 return bus_log_create_error(r
);
1138 if (streq(field
, "TemporaryFileSystem")) {
1141 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
1143 return bus_log_create_error(r
);
1145 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
1147 return bus_log_create_error(r
);
1149 r
= sd_bus_message_open_container(m
, 'v', "a(ss)");
1151 return bus_log_create_error(r
);
1153 r
= sd_bus_message_open_container(m
, 'a', "(ss)");
1155 return bus_log_create_error(r
);
1158 _cleanup_free_
char *word
= NULL
, *path
= NULL
;
1161 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1163 return log_error_errno(r
, "Failed to parse argument: %m");
1168 r
= extract_first_word(&w
, &path
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
1170 return log_error_errno(r
, "Failed to parse argument: %m");
1172 return log_error("Failed to parse argument: %m");
1174 r
= sd_bus_message_append(m
, "(ss)", path
, w
);
1176 return bus_log_create_error(r
);
1179 r
= sd_bus_message_close_container(m
);
1181 return bus_log_create_error(r
);
1183 r
= sd_bus_message_close_container(m
);
1185 return bus_log_create_error(r
);
1187 r
= sd_bus_message_close_container(m
);
1189 return bus_log_create_error(r
);
1197 static int bus_append_kill_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1199 if (streq(field
, "KillMode"))
1201 return bus_append_string(m
, field
, eq
);
1203 if (STR_IN_SET(field
, "SendSIGHUP", "SendSIGKILL"))
1205 return bus_append_parse_boolean(m
, field
, eq
);
1207 if (streq(field
, "KillSignal"))
1209 return bus_append_signal_from_string_try_harder(m
, field
, eq
);
1214 static int bus_append_mount_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1216 if (STR_IN_SET(field
, "What", "Where", "Options", "Type"))
1218 return bus_append_string(m
, field
, eq
);
1220 if (streq(field
, "TimeoutSec"))
1222 return bus_append_parse_sec_rename(m
, field
, eq
);
1224 if (streq(field
, "DirectoryMode"))
1226 return bus_append_parse_mode(m
, field
, eq
);
1228 if (STR_IN_SET(field
, "SloppyOptions", "LazyUnmount", "ForceUnmount"))
1230 return bus_append_parse_boolean(m
, field
, eq
);
1235 static int bus_append_path_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1238 if (streq(field
, "MakeDirectory"))
1240 return bus_append_parse_boolean(m
, field
, eq
);
1242 if (streq(field
, "DirectoryMode"))
1244 return bus_append_parse_mode(m
, field
, eq
);
1246 if (STR_IN_SET(field
,
1247 "PathExists", "PathExistsGlob", "PathChanged",
1248 "PathModified", "DirectoryNotEmpty")) {
1251 r
= sd_bus_message_append(m
, "(sv)", "Paths", "a(ss)", 0);
1253 r
= sd_bus_message_append(m
, "(sv)", "Paths", "a(ss)", 1, field
, eq
);
1255 return bus_log_create_error(r
);
1263 static int bus_append_service_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1266 if (STR_IN_SET(field
,
1267 "PIDFile", "Type", "Restart", "BusName", "NotifyAccess",
1268 "USBFunctionDescriptors", "USBFunctionStrings"))
1270 return bus_append_string(m
, field
, eq
);
1272 if (STR_IN_SET(field
, "PermissionsStartOnly", "RootDirectoryStartOnly", "RemainAfterExit", "GuessMainPID"))
1274 return bus_append_parse_boolean(m
, field
, eq
);
1276 if (STR_IN_SET(field
, "RestartSec", "TimeoutStartSec", "TimeoutStopSec", "RuntimeMaxSec", "WatchdogSec"))
1278 return bus_append_parse_sec_rename(m
, field
, eq
);
1280 if (streq(field
, "TimeoutSec")) {
1282 r
= bus_append_parse_sec_rename(m
, "TimeoutStartSec", eq
);
1286 return bus_append_parse_sec_rename(m
, "TimeoutStopSec", eq
);
1289 if (streq(field
, "FileDescriptorStoreMax"))
1291 return bus_append_safe_atou(m
, field
, eq
);
1293 if (STR_IN_SET(field
,
1294 "ExecStartPre", "ExecStart", "ExecStartPost",
1295 "ExecReload", "ExecStop", "ExecStopPost"))
1297 return bus_append_exec_command(m
, field
, eq
);
1299 if (STR_IN_SET(field
, "RestartPreventExitStatus", "RestartForceExitStatus", "SuccessExitStatus")) {
1300 _cleanup_free_
int *status
= NULL
, *signal
= NULL
;
1301 size_t sz_status
= 0, sz_signal
= 0;
1305 _cleanup_free_
char *word
= NULL
;
1308 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1314 return log_error_errno(r
, "Invalid syntax in %s: %s", field
, eq
);
1316 r
= safe_atoi(word
, &val
);
1318 val
= signal_from_string_try_harder(word
);
1320 return log_error_errno(r
, "Invalid status or signal %s in %s: %m", word
, field
);
1322 signal
= reallocarray(signal
, sz_signal
+ 1, sizeof(int));
1326 signal
[sz_signal
++] = val
;
1328 status
= reallocarray(status
, sz_status
+ 1, sizeof(int));
1332 status
[sz_status
++] = val
;
1336 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
1338 return bus_log_create_error(r
);
1340 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
1342 return bus_log_create_error(r
);
1344 r
= sd_bus_message_open_container(m
, 'v', "(aiai)");
1346 return bus_log_create_error(r
);
1348 r
= sd_bus_message_open_container(m
, 'r', "aiai");
1350 return bus_log_create_error(r
);
1352 r
= sd_bus_message_append_array(m
, 'i', status
, sz_status
);
1354 return bus_log_create_error(r
);
1356 r
= sd_bus_message_append_array(m
, 'i', signal
, sz_signal
);
1358 return bus_log_create_error(r
);
1360 r
= sd_bus_message_close_container(m
);
1362 return bus_log_create_error(r
);
1364 r
= sd_bus_message_close_container(m
);
1366 return bus_log_create_error(r
);
1368 r
= sd_bus_message_close_container(m
);
1370 return bus_log_create_error(r
);
1378 static int bus_append_socket_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1381 if (STR_IN_SET(field
,
1382 "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast",
1383 "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet"))
1385 return bus_append_parse_boolean(m
, field
, eq
);
1387 if (STR_IN_SET(field
, "Priority", "IPTTL", "Mark"))
1389 return bus_append_safe_atoi(m
, field
, eq
);
1391 if (streq(field
, "IPTOS"))
1393 return bus_append_ip_tos_from_string(m
, field
, eq
);
1395 if (STR_IN_SET(field
, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst"))
1397 return bus_append_safe_atou(m
, field
, eq
);
1399 if (STR_IN_SET(field
, "SocketMode", "DirectoryMode"))
1401 return bus_append_parse_mode(m
, field
, eq
);
1403 if (STR_IN_SET(field
, "MessageQueueMaxMessages", "MessageQueueMessageSize"))
1405 return bus_append_safe_atoi64(m
, field
, eq
);
1407 if (STR_IN_SET(field
, "TimeoutSec", "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", "TriggerLimitIntervalSec"))
1409 return bus_append_parse_sec_rename(m
, field
, eq
);
1411 if (STR_IN_SET(field
, "ReceiveBuffer", "SendBuffer", "PipeSize"))
1413 return bus_append_parse_size(m
, field
, eq
, 1024);
1415 if (STR_IN_SET(field
, "ExecStartPre", "ExecStartPost", "ExecReload", "ExecStopPost"))
1417 return bus_append_exec_command(m
, field
, eq
);
1419 if (STR_IN_SET(field
,
1420 "SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion",
1421 "BindToDevice", "BindIPv6Only", "FileDescriptorName",
1422 "SocketUser", "SocketGroup"))
1424 return bus_append_string(m
, field
, eq
);
1426 if (streq(field
, "Symlinks"))
1428 return bus_append_strv(m
, field
, eq
, EXTRACT_QUOTES
);
1430 if (streq(field
, "SocketProtocol"))
1432 return bus_append_socket_protocol_from_name(m
, field
, eq
);
1434 if (STR_IN_SET(field
,
1435 "ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink",
1436 "ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) {
1439 r
= sd_bus_message_append(m
, "(sv)", "Listen", "a(ss)", 0);
1441 r
= sd_bus_message_append(m
, "(sv)", "Listen", "a(ss)", 1, field
+ STRLEN("Listen"), eq
);
1443 return bus_log_create_error(r
);
1450 static int bus_append_timer_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1453 if (STR_IN_SET(field
, "WakeSystem", "RemainAfterElapse", "Persistent"))
1455 return bus_append_parse_boolean(m
, field
, eq
);
1457 if (STR_IN_SET(field
, "AccuracySec", "RandomizedDelaySec"))
1459 return bus_append_parse_sec_rename(m
, field
, eq
);
1461 if (STR_IN_SET(field
,
1462 "OnActiveSec", "OnBootSec", "OnStartupSec",
1463 "OnUnitActiveSec","OnUnitInactiveSec")) {
1466 r
= sd_bus_message_append(m
, "(sv)", "TimersMonotonic", "a(st)", 0);
1469 r
= parse_sec(eq
, &t
);
1471 return log_error_errno(r
, "Failed to parse %s=%s: %m", field
, eq
);
1473 r
= sd_bus_message_append(m
, "(sv)", "TimersMonotonic", "a(st)", 1, field
, t
);
1476 return bus_log_create_error(r
);
1481 if (streq(field
, "OnCalendar")) {
1484 r
= sd_bus_message_append(m
, "(sv)", "TimersCalendar", "a(ss)", 0);
1486 r
= sd_bus_message_append(m
, "(sv)", "TimersCalendar", "a(ss)", 1, field
, eq
);
1488 return bus_log_create_error(r
);
1496 static int bus_append_unit_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1497 ConditionType t
= _CONDITION_TYPE_INVALID
;
1498 bool is_condition
= false;
1501 if (STR_IN_SET(field
,
1502 "Description", "SourcePath", "OnFailureJobMode",
1503 "JobTimeoutAction", "JobTimeoutRebootArgument",
1504 "StartLimitAction", "FailureAction", "SuccessAction",
1505 "RebootArgument", "CollectMode"))
1507 return bus_append_string(m
, field
, eq
);
1509 if (STR_IN_SET(field
,
1510 "StopWhenUnneeded", "RefuseManualStart", "RefuseManualStop",
1511 "AllowIsolate", "IgnoreOnIsolate", "DefaultDependencies"))
1513 return bus_append_parse_boolean(m
, field
, eq
);
1515 if (STR_IN_SET(field
, "JobTimeoutSec", "JobRunningTimeoutSec", "StartLimitIntervalSec"))
1517 return bus_append_parse_sec_rename(m
, field
, eq
);
1519 if (streq(field
, "StartLimitBurst"))
1521 return bus_append_safe_atou(m
, field
, eq
);
1523 if (unit_dependency_from_string(field
) >= 0 ||
1524 STR_IN_SET(field
, "Documentation", "RequiresMountsFor"))
1526 return bus_append_strv(m
, field
, eq
, EXTRACT_QUOTES
);
1528 t
= condition_type_from_string(field
);
1530 is_condition
= true;
1532 t
= assert_type_from_string(field
);
1535 r
= sd_bus_message_append(m
, "(sv)", is_condition
? "Conditions" : "Asserts", "a(sbbs)", 0);
1538 int trigger
, negate
;
1540 trigger
= *p
== '|';
1548 r
= sd_bus_message_append(m
, "(sv)", is_condition
? "Conditions" : "Asserts", "a(sbbs)", 1,
1549 field
, trigger
, negate
, p
);
1552 return bus_log_create_error(r
);
1560 int bus_append_unit_property_assignment(sd_bus_message
*m
, UnitType t
, const char *assignment
) {
1561 const char *eq
, *field
;
1567 eq
= strchr(assignment
, '=');
1569 log_error("Not an assignment: %s", assignment
);
1573 field
= strndupa(assignment
, eq
- assignment
);
1578 r
= bus_append_cgroup_property(m
, field
, eq
);
1582 r
= bus_append_execute_property(m
, field
, eq
);
1586 r
= bus_append_kill_property(m
, field
, eq
);
1590 r
= bus_append_service_property(m
, field
, eq
);
1596 r
= bus_append_cgroup_property(m
, field
, eq
);
1600 r
= bus_append_execute_property(m
, field
, eq
);
1604 r
= bus_append_kill_property(m
, field
, eq
);
1608 r
= bus_append_socket_property(m
, field
, eq
);
1614 r
= bus_append_timer_property(m
, field
, eq
);
1620 r
= bus_append_path_property(m
, field
, eq
);
1626 r
= bus_append_cgroup_property(m
, field
, eq
);
1633 if (streq(field
, "TimeoutStopSec"))
1634 return bus_append_parse_sec_rename(m
, field
, eq
);
1636 r
= bus_append_cgroup_property(m
, field
, eq
);
1640 r
= bus_append_kill_property(m
, field
, eq
);
1646 r
= bus_append_cgroup_property(m
, field
, eq
);
1650 r
= bus_append_execute_property(m
, field
, eq
);
1654 r
= bus_append_kill_property(m
, field
, eq
);
1658 r
= bus_append_mount_property(m
, field
, eq
);
1664 case UNIT_AUTOMOUNT
:
1665 r
= bus_append_automount_property(m
, field
, eq
);
1674 log_error("Not supported unit type");
1678 log_error("Invalid unit type");
1682 r
= bus_append_unit_property(m
, field
, eq
);
1686 log_error("Unknown assignment: %s", assignment
);
1690 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, UnitType t
, char **l
) {
1696 STRV_FOREACH(i
, l
) {
1697 r
= bus_append_unit_property_assignment(m
, t
, *i
);
1705 typedef struct BusWaitForJobs
{
1712 sd_bus_slot
*slot_job_removed
;
1713 sd_bus_slot
*slot_disconnected
;
1716 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1719 log_error("Warning! D-Bus connection terminated.");
1720 sd_bus_close(sd_bus_message_get_bus(m
));
1725 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1726 const char *path
, *unit
, *result
;
1727 BusWaitForJobs
*d
= userdata
;
1735 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1737 bus_log_parse_error(r
);
1741 found
= set_remove(d
->jobs
, (char*) path
);
1747 if (!isempty(result
))
1748 d
->result
= strdup(result
);
1751 d
->name
= strdup(unit
);
1756 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1760 set_free_free(d
->jobs
);
1762 sd_bus_slot_unref(d
->slot_disconnected
);
1763 sd_bus_slot_unref(d
->slot_job_removed
);
1765 sd_bus_unref(d
->bus
);
1773 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1774 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1780 d
= new0(BusWaitForJobs
, 1);
1784 d
->bus
= sd_bus_ref(bus
);
1786 /* When we are a bus client we match by sender. Direct
1787 * connections OTOH have no initialized sender field, and
1788 * hence we ignore the sender then */
1789 r
= sd_bus_match_signal_async(
1791 &d
->slot_job_removed
,
1792 bus
->bus_client
? "org.freedesktop.systemd1" : NULL
,
1793 "/org/freedesktop/systemd1",
1794 "org.freedesktop.systemd1.Manager",
1796 match_job_removed
, NULL
, d
);
1800 r
= sd_bus_match_signal_async(
1802 &d
->slot_disconnected
,
1803 "org.freedesktop.DBus.Local",
1805 "org.freedesktop.DBus.Local",
1807 match_disconnected
, NULL
, d
);
1817 static int bus_process_wait(sd_bus
*bus
) {
1821 r
= sd_bus_process(bus
, NULL
);
1827 r
= sd_bus_wait(bus
, (uint64_t) -1);
1833 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1834 _cleanup_free_
char *dbus_path
= NULL
;
1840 if (!endswith(d
->name
, ".service"))
1843 dbus_path
= unit_dbus_path_from_name(d
->name
);
1847 return sd_bus_get_property_string(d
->bus
,
1848 "org.freedesktop.systemd1",
1850 "org.freedesktop.systemd1.Service",
1856 static const struct {
1857 const char *result
, *explanation
;
1858 } explanations
[] = {
1859 { "resources", "of unavailable resources or another system error" },
1860 { "protocol", "the service did not take the steps required by its unit configuration" },
1861 { "timeout", "a timeout was exceeded" },
1862 { "exit-code", "the control process exited with error code" },
1863 { "signal", "a fatal signal was delivered to the control process" },
1864 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1865 { "watchdog", "the service failed to send watchdog ping" },
1866 { "start-limit", "start of the service was attempted too often" }
1869 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1870 _cleanup_free_
char *service_shell_quoted
= NULL
;
1871 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1875 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1877 if (!strv_isempty((char**) extra_args
)) {
1878 _cleanup_free_
char *t
;
1880 t
= strv_join((char**) extra_args
, " ");
1881 systemctl
= strjoina("systemctl ", t
? : "<args>");
1882 journalctl
= strjoina("journalctl ", t
? : "<args>");
1885 if (!isempty(result
)) {
1888 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1889 if (streq(result
, explanations
[i
].result
))
1892 if (i
< ELEMENTSOF(explanations
)) {
1893 log_error("Job for %s failed because %s.\n"
1894 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1896 explanations
[i
].explanation
,
1898 service_shell_quoted
?: "<service>",
1904 log_error("Job for %s failed.\n"
1905 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1908 service_shell_quoted
?: "<service>",
1912 /* For some results maybe additional explanation is required */
1913 if (streq_ptr(result
, "start-limit"))
1914 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1915 "followed by \"%1$s start %2$s\" again.",
1917 service_shell_quoted
?: "<service>");
1920 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1926 if (streq(d
->result
, "canceled"))
1927 log_error("Job for %s canceled.", strna(d
->name
));
1928 else if (streq(d
->result
, "timeout"))
1929 log_error("Job for %s timed out.", strna(d
->name
));
1930 else if (streq(d
->result
, "dependency"))
1931 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1932 else if (streq(d
->result
, "invalid"))
1933 log_error("%s is not active, cannot reload.", strna(d
->name
));
1934 else if (streq(d
->result
, "assert"))
1935 log_error("Assertion failed on job for %s.", strna(d
->name
));
1936 else if (streq(d
->result
, "unsupported"))
1937 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1938 else if (streq(d
->result
, "collected"))
1939 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1940 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1942 _cleanup_free_
char *result
= NULL
;
1945 q
= bus_job_get_service_result(d
, &result
);
1947 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1949 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1951 log_error("Job failed. See \"journalctl -xe\" for details.");
1955 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1957 else if (streq(d
->result
, "timeout"))
1959 else if (streq(d
->result
, "dependency"))
1961 else if (streq(d
->result
, "invalid"))
1963 else if (streq(d
->result
, "assert"))
1965 else if (streq(d
->result
, "unsupported"))
1967 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1973 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1978 while (!set_isempty(d
->jobs
)) {
1981 q
= bus_process_wait(d
->bus
);
1983 return log_error_errno(q
, "Failed to wait for response: %m");
1986 q
= check_wait_response(d
, quiet
, extra_args
);
1987 /* Return the first error as it is most likely to be
1989 if (q
< 0 && r
== 0)
1992 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1995 d
->name
= mfree(d
->name
);
1996 d
->result
= mfree(d
->result
);
2002 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
2007 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
2011 return set_put_strdup(d
->jobs
, path
);
2014 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
2017 r
= bus_wait_for_jobs_add(d
, path
);
2021 return bus_wait_for_jobs(d
, quiet
, NULL
);
2024 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
2025 const char *type
, *path
, *source
;
2028 /* changes is dereferenced when calling unit_file_dump_changes() later,
2029 * so we have to make sure this is not NULL. */
2033 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
2035 return bus_log_parse_error(r
);
2037 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
2038 /* We expect only "success" changes to be sent over the bus.
2039 Hence, reject anything negative. */
2040 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
2043 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
2047 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
2052 return bus_log_parse_error(r
);
2054 r
= sd_bus_message_exit_container(m
);
2056 return bus_log_parse_error(r
);
2058 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, quiet
);
2064 bool is_const
; /* If false, cgroup_path should be free()'d */
2066 Hashmap
*pids
; /* PID → process name */
2069 struct CGroupInfo
*parent
;
2070 LIST_FIELDS(struct CGroupInfo
, siblings
);
2071 LIST_HEAD(struct CGroupInfo
, children
);
2075 static bool IS_ROOT(const char *p
) {
2076 return isempty(p
) || streq(p
, "/");
2079 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
2080 struct CGroupInfo
*parent
= NULL
, *cg
;
2089 cg
= hashmap_get(cgroups
, path
);
2095 if (!IS_ROOT(path
)) {
2098 e
= strrchr(path
, '/');
2102 pp
= strndupa(path
, e
- path
);
2106 r
= add_cgroup(cgroups
, pp
, false, &parent
);
2111 cg
= new0(struct CGroupInfo
, 1);
2116 cg
->cgroup_path
= (char*) path
;
2118 cg
->cgroup_path
= strdup(path
);
2119 if (!cg
->cgroup_path
) {
2125 cg
->is_const
= is_const
;
2126 cg
->parent
= parent
;
2128 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
2131 free(cg
->cgroup_path
);
2137 LIST_PREPEND(siblings
, parent
->children
, cg
);
2138 parent
->n_children
++;
2145 static int add_process(
2151 struct CGroupInfo
*cg
;
2158 r
= add_cgroup(cgroups
, path
, true, &cg
);
2162 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
2166 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
2169 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
2173 while (cg
->children
)
2174 remove_cgroup(cgroups
, cg
->children
);
2176 hashmap_remove(cgroups
, cg
->cgroup_path
);
2179 free(cg
->cgroup_path
);
2181 hashmap_free(cg
->pids
);
2184 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
2189 static int cgroup_info_compare_func(const void *a
, const void *b
) {
2190 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
2195 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
2198 static int dump_processes(
2200 const char *cgroup_path
,
2203 OutputFlags flags
) {
2205 struct CGroupInfo
*cg
;
2210 if (IS_ROOT(cgroup_path
))
2213 cg
= hashmap_get(cgroups
, cgroup_path
);
2217 if (!hashmap_isempty(cg
->pids
)) {
2225 /* Order processes by their PID */
2226 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
2228 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
2229 pids
[n
++] = PTR_TO_PID(pidp
);
2231 assert(n
== hashmap_size(cg
->pids
));
2232 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
2234 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
2236 for (i
= 0; i
< n
; i
++) {
2237 _cleanup_free_
char *e
= NULL
;
2238 const char *special
;
2241 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
2244 if (n_columns
!= 0) {
2247 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
2249 e
= ellipsize(name
, k
, 100);
2254 more
= i
+1 < n
|| cg
->children
;
2255 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
2257 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
2266 struct CGroupInfo
**children
, *child
;
2269 /* Order subcgroups by their name */
2270 children
= newa(struct CGroupInfo
*, cg
->n_children
);
2271 LIST_FOREACH(siblings
, child
, cg
->children
)
2272 children
[n
++] = child
;
2273 assert(n
== cg
->n_children
);
2274 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
2277 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
2279 for (i
= 0; i
< n
; i
++) {
2280 _cleanup_free_
char *pp
= NULL
;
2281 const char *name
, *special
;
2284 child
= children
[i
];
2286 name
= strrchr(child
->cgroup_path
, '/');
2292 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
2294 fputs(prefix
, stdout
);
2295 fputs(special
, stdout
);
2296 fputs(name
, stdout
);
2297 fputc('\n', stdout
);
2299 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
2301 pp
= strappend(prefix
, special
);
2305 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
2315 static int dump_extra_processes(
2319 OutputFlags flags
) {
2321 _cleanup_free_ pid_t
*pids
= NULL
;
2322 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
2323 struct CGroupInfo
*cg
;
2324 size_t n_allocated
= 0, n
= 0, k
;
2328 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
2329 * combined, sorted, linear list. */
2331 HASHMAP_FOREACH(cg
, cgroups
, i
) {
2339 if (hashmap_isempty(cg
->pids
))
2342 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
2346 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
2349 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
2350 pids
[n
++] = PTR_TO_PID(pidp
);
2352 r
= hashmap_put(names
, pidp
, (void*) name
);
2361 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
2362 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
2364 for (k
= 0; k
< n
; k
++) {
2365 _cleanup_free_
char *e
= NULL
;
2368 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
2371 if (n_columns
!= 0) {
2374 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
2376 e
= ellipsize(name
, z
, 100);
2381 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
2383 special_glyph(TRIANGULAR_BULLET
),
2391 int unit_show_processes(
2394 const char *cgroup_path
,
2398 sd_bus_error
*error
) {
2400 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2401 Hashmap
*cgroups
= NULL
;
2402 struct CGroupInfo
*cg
;
2408 if (flags
& OUTPUT_FULL_WIDTH
)
2410 else if (n_columns
<= 0)
2411 n_columns
= columns();
2413 prefix
= strempty(prefix
);
2415 r
= sd_bus_call_method(
2417 "org.freedesktop.systemd1",
2418 "/org/freedesktop/systemd1",
2419 "org.freedesktop.systemd1.Manager",
2428 cgroups
= hashmap_new(&path_hash_ops
);
2432 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
2437 const char *path
= NULL
, *name
= NULL
;
2440 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
2446 r
= add_process(cgroups
, path
, pid
, name
);
2451 r
= sd_bus_message_exit_container(reply
);
2455 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
2459 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
2462 while ((cg
= hashmap_first(cgroups
)))
2463 remove_cgroup(cgroups
, cg
);
2465 hashmap_free(cgroups
);