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 "cpu-set-util.h"
29 #include "errno-list.h"
32 #include "hexdecoct.h"
33 #include "hostname-util.h"
34 #include "in-addr-util.h"
36 #include "locale-util.h"
37 #include "mount-util.h"
39 #include "parse-util.h"
40 #include "path-util.h"
41 #include "process-util.h"
42 #include "rlimit-util.h"
43 #include "securebits-util.h"
44 #include "signal-util.h"
45 #include "socket-protocol-list.h"
46 #include "string-util.h"
47 #include "syslog-util.h"
48 #include "terminal-util.h"
50 #include "user-util.h"
54 int bus_parse_unit_info(sd_bus_message
*message
, UnitInfo
*u
) {
60 return sd_bus_message_read(
75 #define DEFINE_BUS_APPEND_PARSE_PTR(bus_type, cast_type, type, parse_func) \
76 static int bus_append_##parse_func(sd_bus_message *m, const char *field, const char *eq) { \
80 r = parse_func(eq, &val); \
82 return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); \
84 r = sd_bus_message_append(m, "(sv)", field, bus_type, (cast_type) val); \
86 return bus_log_create_error(r); \
91 #define DEFINE_BUS_APPEND_PARSE(bus_type, parse_func) \
92 static int bus_append_##parse_func(sd_bus_message *m, const char *field, const char *eq) { \
97 log_error("Failed to parse %s: %s", field, eq); \
101 r = sd_bus_message_append(m, "(sv)", field, bus_type, (int32_t) r); \
103 return bus_log_create_error(r); \
108 DEFINE_BUS_APPEND_PARSE("b", parse_boolean
)
109 DEFINE_BUS_APPEND_PARSE("i", ioprio_class_from_string
)
110 DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string
)
111 DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string
)
112 DEFINE_BUS_APPEND_PARSE("i", log_level_from_string
)
113 DEFINE_BUS_APPEND_PARSE("i", parse_errno
)
114 DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string
)
115 DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string
)
116 DEFINE_BUS_APPEND_PARSE("i", signal_from_string_try_harder
)
117 DEFINE_BUS_APPEND_PARSE("i", socket_protocol_from_name
)
118 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, ioprio_parse_priority
)
119 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, parse_nice
)
120 DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, safe_atoi
)
121 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, nsec_t
, parse_nsec
)
122 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_blkio_weight_parse
)
123 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_shares_parse
)
124 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_weight_parse
)
125 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, unsigned long, mount_propagation_flags_from_string
)
126 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, usec_t
, parse_sec
)
127 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t
, parse_mode
)
128 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou
)
129 DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64
)
131 static inline int bus_append_string(sd_bus_message
*m
, const char *field
, const char *eq
) {
134 r
= sd_bus_message_append(m
, "(sv)", field
, "s", eq
);
136 return bus_log_create_error(r
);
141 static int bus_append_strv(sd_bus_message
*m
, const char *field
, const char *eq
, ExtractFlags flags
) {
145 r
= sd_bus_message_open_container(m
, 'r', "sv");
147 return bus_log_create_error(r
);
149 r
= sd_bus_message_append_basic(m
, 's', field
);
151 return bus_log_create_error(r
);
153 r
= sd_bus_message_open_container(m
, 'v', "as");
155 return bus_log_create_error(r
);
157 r
= sd_bus_message_open_container(m
, 'a', "s");
159 return bus_log_create_error(r
);
162 _cleanup_free_
char *word
= NULL
;
164 r
= extract_first_word(&p
, &word
, NULL
, flags
);
170 return log_error_errno(r
, "Invalid syntax: %s", eq
);
172 r
= sd_bus_message_append_basic(m
, 's', word
);
174 return bus_log_create_error(r
);
177 r
= sd_bus_message_close_container(m
);
179 return bus_log_create_error(r
);
181 r
= sd_bus_message_close_container(m
);
183 return bus_log_create_error(r
);
185 r
= sd_bus_message_close_container(m
);
187 return bus_log_create_error(r
);
192 static int bus_append_byte_array(sd_bus_message
*m
, const char *field
, const void *buf
, size_t n
) {
195 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
197 return bus_log_create_error(r
);
199 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
201 return bus_log_create_error(r
);
203 r
= sd_bus_message_open_container(m
, 'v', "ay");
205 return bus_log_create_error(r
);
207 r
= sd_bus_message_append_array(m
, 'y', buf
, n
);
209 return bus_log_create_error(r
);
211 r
= sd_bus_message_close_container(m
);
213 return bus_log_create_error(r
);
215 r
= sd_bus_message_close_container(m
);
217 return bus_log_create_error(r
);
222 static int bus_append_parse_sec_rename(sd_bus_message
*m
, const char *field
, const char *eq
) {
228 r
= parse_sec(eq
, &t
);
230 return log_error_errno(r
, "Failed to parse %s=%s: %m", field
, eq
);
233 n
= newa(char, l
+ 2);
234 /* Change suffix Sec → USec */
235 strcpy(mempcpy(n
, field
, l
- 3), "USec");
237 r
= sd_bus_message_append(m
, "(sv)", n
, "t", t
);
239 return bus_log_create_error(r
);
244 static int bus_append_parse_size(sd_bus_message
*m
, const char *field
, const char *eq
, uint64_t base
) {
248 r
= parse_size(eq
, base
, &v
);
250 return log_error_errno(r
, "Failed to parse %s=%s: %m", field
, eq
);
252 r
= sd_bus_message_append(m
, "(sv)", field
, "t", v
);
254 return bus_log_create_error(r
);
259 static int bus_append_exec_command(sd_bus_message
*m
, const char *field
, const char *eq
) {
260 bool ignore_failure
= false, explicit_path
= false, done
= false;
261 _cleanup_strv_free_
char **l
= NULL
;
262 _cleanup_free_
char *path
= NULL
;
272 ignore_failure
= true;
281 explicit_path
= true;
288 /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */
289 log_error("Sorry, but +, ! and !! are currently not supported for transient services.");
299 r
= extract_first_word(&eq
, &path
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
301 return log_error_errno(r
, "Failed to parse path: %m");
304 r
= strv_split_extract(&l
, eq
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
306 return log_error_errno(r
, "Failed to parse command line: %m");
308 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
310 return bus_log_create_error(r
);
312 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
314 return bus_log_create_error(r
);
316 r
= sd_bus_message_open_container(m
, 'v', "a(sasb)");
318 return bus_log_create_error(r
);
320 r
= sd_bus_message_open_container(m
, 'a', "(sasb)");
322 return bus_log_create_error(r
);
324 if (!strv_isempty(l
)) {
326 r
= sd_bus_message_open_container(m
, 'r', "sasb");
328 return bus_log_create_error(r
);
330 r
= sd_bus_message_append(m
, "s", path
?: l
[0]);
332 return bus_log_create_error(r
);
334 r
= sd_bus_message_append_strv(m
, l
);
336 return bus_log_create_error(r
);
338 r
= sd_bus_message_append(m
, "b", ignore_failure
);
340 return bus_log_create_error(r
);
342 r
= sd_bus_message_close_container(m
);
344 return bus_log_create_error(r
);
347 r
= sd_bus_message_close_container(m
);
349 return bus_log_create_error(r
);
351 r
= sd_bus_message_close_container(m
);
353 return bus_log_create_error(r
);
355 r
= sd_bus_message_close_container(m
);
357 return bus_log_create_error(r
);
362 static int bus_append_ip_address_access(sd_bus_message
*m
, int family
, const union in_addr_union
*prefix
, unsigned char prefixlen
) {
368 r
= sd_bus_message_open_container(m
, 'r', "iayu");
372 r
= sd_bus_message_append(m
, "i", family
);
376 r
= sd_bus_message_append_array(m
, 'y', prefix
, FAMILY_ADDRESS_SIZE(family
));
380 r
= sd_bus_message_append(m
, "u", prefixlen
);
384 return sd_bus_message_close_container(m
);
387 static int bus_append_cgroup_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
390 if (STR_IN_SET(field
, "DevicePolicy", "Slice"))
392 return bus_append_string(m
, field
, eq
);
394 if (STR_IN_SET(field
,
395 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
396 "TasksAccounting", "IPAccounting"))
398 return bus_append_parse_boolean(m
, field
, eq
);
400 if (STR_IN_SET(field
, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight"))
402 return bus_append_cg_weight_parse(m
, field
, eq
);
404 if (STR_IN_SET(field
, "CPUShares", "StartupCPUShares"))
406 return bus_append_cg_cpu_shares_parse(m
, field
, eq
);
408 if (STR_IN_SET(field
, "BlockIOWeight", "StartupBlockIOWeight"))
410 return bus_append_cg_blkio_weight_parse(m
, field
, eq
);
412 if (streq(field
, "Delegate")) {
414 r
= parse_boolean(eq
);
416 return bus_append_strv(m
, "DelegateControllers", eq
, EXTRACT_QUOTES
);
418 r
= sd_bus_message_append(m
, "(sv)", "Delegate", "b", r
);
420 return bus_log_create_error(r
);
425 if (STR_IN_SET(field
, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) {
428 if (isempty(eq
) || streq(eq
, "infinity")) {
429 r
= sd_bus_message_append(m
, "(sv)", field
, "t", CGROUP_LIMIT_MAX
);
431 return bus_log_create_error(r
);
435 r
= parse_percent(eq
);
439 /* When this is a percentage we'll convert this into a relative value in the range
440 * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
441 * ones). This way the physical memory size can be determined server-side */
443 n
= strjoina(field
, "Scale");
444 r
= sd_bus_message_append(m
, "(sv)", n
, "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
446 return bus_log_create_error(r
);
451 if (streq(field
, "TasksMax"))
452 r
= safe_atou64(eq
, &val
);
454 r
= parse_size(eq
, 1024, &val
);
457 return log_error_errno(r
, "Failed to parse %s=%s: %m", field
, eq
);
459 r
= sd_bus_message_append(m
, "(sv)", field
, "t", val
);
461 return bus_log_create_error(r
);
466 if (streq(field
, "CPUQuota")) {
469 r
= sd_bus_message_append(m
, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY
);
471 r
= parse_percent_unbounded(eq
);
473 log_error_errno(r
, "CPU quota '%s' invalid.", eq
);
477 r
= sd_bus_message_append(m
, "(sv)", "CPUQuotaPerSecUSec", "t", (usec_t
) r
* USEC_PER_SEC
/ 100U);
481 return bus_log_create_error(r
);
486 if (streq(field
, "DeviceAllow")) {
489 r
= sd_bus_message_append(m
, "(sv)", field
, "a(ss)", 0);
491 const char *path
, *rwm
, *e
;
495 path
= strndupa(eq
, e
- eq
);
502 if (!is_deviceallow_pattern(path
)) {
503 log_error("%s is not a device file in /dev.", path
);
507 r
= sd_bus_message_append(m
, "(sv)", field
, "a(ss)", 1, path
, rwm
);
511 return bus_log_create_error(r
);
516 if (cgroup_io_limit_type_from_string(field
) >= 0 || STR_IN_SET(field
, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
519 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 0);
521 const char *path
, *bandwidth
, *e
;
526 path
= strndupa(eq
, e
- eq
);
529 log_error("Failed to parse %s value %s.", field
, eq
);
533 if (!path_startswith(path
, "/dev")) {
534 log_error("%s is not a device file in /dev.", path
);
538 if (streq(bandwidth
, "infinity")) {
539 bytes
= CGROUP_LIMIT_MAX
;
541 r
= parse_size(bandwidth
, 1000, &bytes
);
543 return log_error_errno(r
, "Failed to parse byte value %s: %m", bandwidth
);
546 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 1, path
, bytes
);
550 return bus_log_create_error(r
);
555 if (STR_IN_SET(field
, "IODeviceWeight", "BlockIODeviceWeight")) {
558 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 0);
560 const char *path
, *weight
, *e
;
565 path
= strndupa(eq
, e
- eq
);
568 log_error("Failed to parse %s value %s.", field
, eq
);
572 if (!path_startswith(path
, "/dev")) {
573 log_error("%s is not a device file in /dev.", path
);
577 r
= safe_atou64(weight
, &u
);
579 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, weight
);
581 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 1, path
, u
);
585 return bus_log_create_error(r
);
590 if (STR_IN_SET(field
, "IPAddressAllow", "IPAddressDeny")) {
591 unsigned char prefixlen
;
592 union in_addr_union prefix
= {};
596 r
= sd_bus_message_append(m
, "(sv)", field
, "a(iayu)", 0);
598 return bus_log_create_error(r
);
603 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
605 return bus_log_create_error(r
);
607 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
609 return bus_log_create_error(r
);
611 r
= sd_bus_message_open_container(m
, 'v', "a(iayu)");
613 return bus_log_create_error(r
);
615 r
= sd_bus_message_open_container(m
, 'a', "(iayu)");
617 return bus_log_create_error(r
);
619 if (streq(eq
, "any")) {
620 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
622 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 0);
624 return bus_log_create_error(r
);
626 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 0);
628 return bus_log_create_error(r
);
630 } else if (is_localhost(eq
)) {
631 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
633 prefix
.in
.s_addr
= htobe32(0x7f000000);
634 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 8);
636 return bus_log_create_error(r
);
638 prefix
.in6
= (struct in6_addr
) IN6ADDR_LOOPBACK_INIT
;
639 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 128);
643 } else if (streq(eq
, "link-local")) {
644 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
646 prefix
.in
.s_addr
= htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
647 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 16);
649 return bus_log_create_error(r
);
651 prefix
.in6
= (struct in6_addr
) {
652 .s6_addr32
[0] = htobe32(0xfe800000)
654 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 64);
656 return bus_log_create_error(r
);
658 } else if (streq(eq
, "multicast")) {
659 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
661 prefix
.in
.s_addr
= htobe32((UINT32_C(224) << 24));
662 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 4);
664 return bus_log_create_error(r
);
666 prefix
.in6
= (struct in6_addr
) {
667 .s6_addr32
[0] = htobe32(0xff000000)
669 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 8);
671 return bus_log_create_error(r
);
674 r
= in_addr_prefix_from_string_auto(eq
, &family
, &prefix
, &prefixlen
);
676 return log_error_errno(r
, "Failed to parse IP address prefix: %s", eq
);
678 r
= bus_append_ip_address_access(m
, family
, &prefix
, prefixlen
);
680 return bus_log_create_error(r
);
683 r
= sd_bus_message_close_container(m
);
685 return bus_log_create_error(r
);
687 r
= sd_bus_message_close_container(m
);
689 return bus_log_create_error(r
);
691 r
= sd_bus_message_close_container(m
);
693 return bus_log_create_error(r
);
701 static int bus_append_execute_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
704 if (STR_IN_SET(field
,
706 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
707 "WorkingDirectory", "RootDirectory", "SyslogIdentifier",
708 "ProtectSystem", "ProtectHome", "SELinuxContext", "RootImage",
709 "RuntimeDirectoryPreserve", "Personality", "KeyringMode"))
711 return bus_append_string(m
, field
, eq
);
713 if (STR_IN_SET(field
,
714 "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
715 "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
716 "NoNewPrivileges", "SyslogLevelPrefix",
717 "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
718 "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups",
719 "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality"))
721 return bus_append_parse_boolean(m
, field
, eq
);
723 if (STR_IN_SET(field
,
724 "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
725 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths",
726 "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory",
727 "SupplementaryGroups", "SystemCallArchitectures"))
729 return bus_append_strv(m
, field
, eq
, EXTRACT_QUOTES
);
731 if (STR_IN_SET(field
, "SyslogLevel", "LogLevelMax"))
733 return bus_append_log_level_from_string(m
, field
, eq
);
735 if (streq(field
, "SyslogFacility"))
737 return bus_append_log_facility_unshifted_from_string(m
, field
, eq
);
739 if (streq(field
, "SecureBits"))
741 return bus_append_secure_bits_from_string(m
, field
, eq
);
743 if (streq(field
, "CPUSchedulingPolicy"))
745 return bus_append_sched_policy_from_string(m
, field
, eq
);
747 if (STR_IN_SET(field
, "CPUSchedulingPriority", "OOMScoreAdjust"))
749 return bus_append_safe_atoi(m
, field
, eq
);
751 if (streq(field
, "Nice"))
753 return bus_append_parse_nice(m
, field
, eq
);
755 if (streq(field
, "SystemCallErrorNumber"))
757 return bus_append_parse_errno(m
, field
, eq
);
759 if (streq(field
, "IOSchedulingClass"))
761 return bus_append_ioprio_class_from_string(m
, field
, eq
);
763 if (streq(field
, "IOSchedulingPriority"))
765 return bus_append_ioprio_parse_priority(m
, field
, eq
);
767 if (STR_IN_SET(field
,
768 "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode",
769 "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask"))
771 return bus_append_parse_mode(m
, field
, eq
);
773 if (streq(field
, "TimerSlackNSec"))
775 return bus_append_parse_nsec(m
, field
, eq
);
777 if (streq(field
, "MountFlags"))
779 return bus_append_mount_propagation_flags_from_string(m
, field
, eq
);
781 if (STR_IN_SET(field
, "Environment", "UnsetEnvironment", "PassEnvironment"))
783 return bus_append_strv(m
, field
, eq
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
785 if (streq(field
, "EnvironmentFile")) {
788 r
= sd_bus_message_append(m
, "(sv)", "EnvironmentFiles", "a(sb)", 0);
790 r
= sd_bus_message_append(m
, "(sv)", "EnvironmentFiles", "a(sb)", 1,
791 eq
[0] == '-' ? eq
+ 1 : eq
,
794 return bus_log_create_error(r
);
799 if (streq(field
, "LogExtraFields")) {
801 r
= sd_bus_message_open_container(m
, 'r', "sv");
803 return bus_log_create_error(r
);
805 r
= sd_bus_message_append_basic(m
, 's', "LogExtraFields");
807 return bus_log_create_error(r
);
809 r
= sd_bus_message_open_container(m
, 'v', "aay");
811 return bus_log_create_error(r
);
813 r
= sd_bus_message_open_container(m
, 'a', "ay");
815 return bus_log_create_error(r
);
817 r
= sd_bus_message_append_array(m
, 'y', eq
, strlen(eq
));
819 return bus_log_create_error(r
);
821 r
= sd_bus_message_close_container(m
);
823 return bus_log_create_error(r
);
825 r
= sd_bus_message_close_container(m
);
827 return bus_log_create_error(r
);
829 r
= sd_bus_message_close_container(m
);
831 return bus_log_create_error(r
);
836 if (STR_IN_SET(field
, "StandardInput", "StandardOutput", "StandardError")) {
837 const char *n
, *appended
;
839 if ((n
= startswith(eq
, "fd:"))) {
840 appended
= strjoina(field
, "FileDescriptorName");
841 r
= sd_bus_message_append(m
, "(sv)", appended
, "s", n
);
842 } else if ((n
= startswith(eq
, "file:"))) {
843 appended
= strjoina(field
, "File");
844 r
= sd_bus_message_append(m
, "(sv)", appended
, "s", n
);
846 r
= sd_bus_message_append(m
, "(sv)", field
, "s", eq
);
849 return bus_log_create_error(r
);
854 if (streq(field
, "StandardInputText")) {
855 _cleanup_free_
char *unescaped
= NULL
;
857 r
= cunescape(eq
, 0, &unescaped
);
859 return log_error_errno(r
, "Failed to unescape text '%s': %m", eq
);
861 if (!strextend(&unescaped
, "\n", NULL
))
864 /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
865 * interface anyway */
867 return bus_append_byte_array(m
, field
, unescaped
, strlen(unescaped
));
870 if (streq(field
, "StandardInputData")) {
871 _cleanup_free_
void *decoded
= NULL
;
874 r
= unbase64mem(eq
, (size_t) -1, &decoded
, &sz
);
876 return log_error_errno(r
, "Failed to decode base64 data '%s': %m", eq
);
878 return bus_append_byte_array(m
, field
, decoded
, sz
);
881 rl
= rlimit_from_string(field
);
886 r
= rlimit_parse(rl
, eq
, &l
);
888 return log_error_errno(r
, "Failed to parse resource limit: %s", eq
);
890 r
= sd_bus_message_append(m
, "(sv)", field
, "t", l
.rlim_max
);
892 return bus_log_create_error(r
);
894 sn
= strjoina(field
, "Soft");
895 r
= sd_bus_message_append(m
, "(sv)", sn
, "t", l
.rlim_cur
);
897 return bus_log_create_error(r
);
902 if (STR_IN_SET(field
, "AppArmorProfile", "SmackProcessLabel")) {
911 r
= sd_bus_message_append(m
, "(sv)", field
, "(bs)", ignore
, s
);
913 return bus_log_create_error(r
);
918 if (STR_IN_SET(field
, "CapabilityBoundingSet", "AmbientCapabilities")) {
928 r
= capability_set_from_string(p
, &sum
);
930 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
932 sum
= invert
? ~sum
: sum
;
934 r
= sd_bus_message_append(m
, "(sv)", field
, "t", sum
);
936 return bus_log_create_error(r
);
941 if (streq(field
, "CPUAffinity")) {
942 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
944 r
= parse_cpu_set(eq
, &cpuset
);
946 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
948 return bus_append_byte_array(m
, field
, cpuset
, CPU_ALLOC_SIZE(r
));
951 if (STR_IN_SET(field
, "RestrictAddressFamilies", "SystemCallFilter")) {
960 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
962 return bus_log_create_error(r
);
964 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
966 return bus_log_create_error(r
);
968 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
970 return bus_log_create_error(r
);
972 r
= sd_bus_message_open_container(m
, 'r', "bas");
974 return bus_log_create_error(r
);
976 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
978 return bus_log_create_error(r
);
980 r
= sd_bus_message_open_container(m
, 'a', "s");
982 return bus_log_create_error(r
);
985 _cleanup_free_
char *word
= NULL
;
987 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
993 return log_error_errno(r
, "Invalid syntax: %s", eq
);
995 r
= sd_bus_message_append_basic(m
, 's', word
);
997 return bus_log_create_error(r
);
1000 r
= sd_bus_message_close_container(m
);
1002 return bus_log_create_error(r
);
1004 r
= sd_bus_message_close_container(m
);
1006 return bus_log_create_error(r
);
1008 r
= sd_bus_message_close_container(m
);
1010 return bus_log_create_error(r
);
1012 r
= sd_bus_message_close_container(m
);
1014 return bus_log_create_error(r
);
1019 if (streq(field
, "RestrictNamespaces")) {
1020 bool invert
= false;
1021 unsigned long flags
= 0;
1028 r
= parse_boolean(eq
);
1032 flags
= NAMESPACE_FLAGS_ALL
;
1034 r
= namespace_flag_from_string_many(eq
, &flags
);
1036 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
1040 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
1042 r
= sd_bus_message_append(m
, "(sv)", field
, "t", (uint64_t) flags
);
1044 return bus_log_create_error(r
);
1049 if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
1052 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
1054 return bus_log_create_error(r
);
1056 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
1058 return bus_log_create_error(r
);
1060 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
1062 return bus_log_create_error(r
);
1064 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
1066 return bus_log_create_error(r
);
1069 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
1070 char *s
= NULL
, *d
= NULL
;
1071 bool ignore_enoent
= false;
1072 uint64_t flags
= MS_REC
;
1074 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1076 return log_error_errno(r
, "Failed to parse argument: %m");
1082 ignore_enoent
= true;
1086 if (p
&& p
[-1] == ':') {
1087 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1089 return log_error_errno(r
, "Failed to parse argument: %m");
1091 log_error("Missing argument after ':': %s", eq
);
1097 if (p
&& p
[-1] == ':') {
1098 _cleanup_free_
char *options
= NULL
;
1100 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
1102 return log_error_errno(r
, "Failed to parse argument: %m");
1104 if (isempty(options
) || streq(options
, "rbind"))
1106 else if (streq(options
, "norbind"))
1109 log_error("Unknown options: %s", eq
);
1117 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
1119 return bus_log_create_error(r
);
1122 r
= sd_bus_message_close_container(m
);
1124 return bus_log_create_error(r
);
1126 r
= sd_bus_message_close_container(m
);
1128 return bus_log_create_error(r
);
1130 r
= sd_bus_message_close_container(m
);
1132 return bus_log_create_error(r
);
1140 static int bus_append_kill_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1142 if (streq(field
, "KillMode"))
1144 return bus_append_string(m
, field
, eq
);
1146 if (STR_IN_SET(field
, "SendSIGHUP", "SendSIGKILL"))
1148 return bus_append_parse_boolean(m
, field
, eq
);
1150 if (streq(field
, "KillSignal"))
1152 return bus_append_signal_from_string_try_harder(m
, field
, eq
);
1157 static int bus_append_path_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1159 if (STR_IN_SET(field
,
1160 "PathExists", "PathExistsGlob", "PathChanged",
1161 "PathModified", "DirectoryNotEmpty"))
1163 return bus_append_string(m
, field
, eq
);
1165 if (streq(field
, "MakeDirectory"))
1167 return bus_append_parse_boolean(m
, field
, eq
);
1169 if (streq(field
, "DirectoryMode"))
1171 return bus_append_parse_mode(m
, field
, eq
);
1176 static int bus_append_service_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1178 if (STR_IN_SET(field
, "Type", "Restart", "NotifyAccess"))
1180 return bus_append_string(m
, field
, eq
);
1182 if (streq(field
, "RemainAfterExit"))
1184 return bus_append_parse_boolean(m
, field
, eq
);
1186 if (streq(field
, "RuntimeMaxSec"))
1188 return bus_append_parse_sec_rename(m
, field
, eq
);
1190 if (streq(field
, "FileDescriptorStoreMax"))
1192 return bus_append_safe_atou(m
, field
, eq
);
1194 if (STR_IN_SET(field
,
1195 "ExecStartPre", "ExecStart", "ExecStartPost",
1196 "ExecReload", "ExecStop", "ExecStopPost"))
1198 return bus_append_exec_command(m
, field
, eq
);
1203 static int bus_append_socket_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1206 if (STR_IN_SET(field
,
1207 "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast",
1208 "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet"))
1210 return bus_append_parse_boolean(m
, field
, eq
);
1212 if (STR_IN_SET(field
, "Priority", "IPTTL", "Mark"))
1214 return bus_append_safe_atoi(m
, field
, eq
);
1216 if (streq(field
, "IPTOS"))
1218 return bus_append_ip_tos_from_string(m
, field
, eq
);
1220 if (STR_IN_SET(field
, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst"))
1222 return bus_append_safe_atou(m
, field
, eq
);
1224 if (STR_IN_SET(field
, "SocketMode", "DirectoryMode"))
1226 return bus_append_parse_mode(m
, field
, eq
);
1228 if (STR_IN_SET(field
, "MessageQueueMaxMessages", "MessageQueueMessageSize"))
1230 return bus_append_safe_atoi64(m
, field
, eq
);
1232 if (STR_IN_SET(field
, "TimeoutSec", "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", "TriggerLimitIntervalSec"))
1234 return bus_append_parse_sec_rename(m
, field
, eq
);
1236 if (STR_IN_SET(field
, "ReceiveBuffer", "SendBuffer", "PipeSize"))
1238 return bus_append_parse_size(m
, field
, eq
, 1024);
1240 if (STR_IN_SET(field
, "ExecStartPre", "ExecStartPost", "ExecReload", "ExecStopPost"))
1242 return bus_append_exec_command(m
, field
, eq
);
1244 if (STR_IN_SET(field
,
1245 "SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion",
1246 "BindToDevice", "BindIPv6Only", "FileDescriptorName",
1247 "SocketUser", "SocketGroup"))
1249 return bus_append_string(m
, field
, eq
);
1251 if (streq(field
, "Symlinks"))
1253 return bus_append_strv(m
, field
, eq
, EXTRACT_QUOTES
);
1255 if (streq(field
, "SocketProtocol"))
1257 return bus_append_socket_protocol_from_name(m
, field
, eq
);
1259 if (STR_IN_SET(field
,
1260 "ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink",
1261 "ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) {
1263 r
= sd_bus_message_append(m
, "(sv)", "Listen", "a(ss)", 1, field
+ strlen("Listen"), eq
);
1265 return bus_log_create_error(r
);
1272 static int bus_append_timer_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1274 if (streq(field
, "OnCalendar"))
1276 return bus_append_string(m
, field
, eq
);
1278 if (STR_IN_SET(field
, "WakeSystem", "RemainAfterElapse", "Persistent"))
1280 return bus_append_parse_boolean(m
, field
, eq
);
1282 if (STR_IN_SET(field
,
1283 "OnActiveSec", "OnBootSec", "OnStartupSec",
1284 "OnUnitActiveSec","OnUnitInactiveSec"))
1286 return bus_append_parse_sec(m
, field
, eq
);
1288 if (STR_IN_SET(field
, "AccuracySec", "RandomizedDelaySec"))
1290 return bus_append_parse_sec_rename(m
, field
, eq
);
1295 static int bus_append_unit_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1299 if (STR_IN_SET(field
, "Description", "CollectMode", "FailureAction", "SuccessAction"))
1301 return bus_append_string(m
, field
, eq
);
1303 if (streq(field
, "DefaultDependencies"))
1305 return bus_append_parse_boolean(m
, field
, eq
);
1307 if ((dep
= unit_dependency_from_string(field
)) >= 0) {
1309 r
= sd_bus_message_append(m
, "(sv)", field
, "as", 1, eq
);
1311 return bus_log_create_error(r
);
1319 int bus_append_unit_property_assignment(sd_bus_message
*m
, UnitType t
, const char *assignment
) {
1320 const char *eq
, *field
;
1326 eq
= strchr(assignment
, '=');
1328 log_error("Not an assignment: %s", assignment
);
1332 field
= strndupa(assignment
, eq
- assignment
);
1337 r
= bus_append_cgroup_property(m
, field
, eq
);
1341 r
= bus_append_execute_property(m
, field
, eq
);
1345 r
= bus_append_kill_property(m
, field
, eq
);
1349 r
= bus_append_service_property(m
, field
, eq
);
1355 r
= bus_append_cgroup_property(m
, field
, eq
);
1359 r
= bus_append_execute_property(m
, field
, eq
);
1363 r
= bus_append_kill_property(m
, field
, eq
);
1367 r
= bus_append_socket_property(m
, field
, eq
);
1373 r
= bus_append_timer_property(m
, field
, eq
);
1379 r
= bus_append_path_property(m
, field
, eq
);
1385 r
= bus_append_cgroup_property(m
, field
, eq
);
1391 r
= bus_append_cgroup_property(m
, field
, eq
);
1395 r
= bus_append_kill_property(m
, field
, eq
);
1401 case UNIT_AUTOMOUNT
:
1407 log_error("Not supported unit type");
1411 log_error("Invalid unit type");
1415 r
= bus_append_unit_property(m
, field
, eq
);
1419 log_error("Unknown assignment: %s", assignment
);
1423 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, UnitType t
, char **l
) {
1429 STRV_FOREACH(i
, l
) {
1430 r
= bus_append_unit_property_assignment(m
, t
, *i
);
1438 typedef struct BusWaitForJobs
{
1445 sd_bus_slot
*slot_job_removed
;
1446 sd_bus_slot
*slot_disconnected
;
1449 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1452 log_error("Warning! D-Bus connection terminated.");
1453 sd_bus_close(sd_bus_message_get_bus(m
));
1458 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1459 const char *path
, *unit
, *result
;
1460 BusWaitForJobs
*d
= userdata
;
1468 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1470 bus_log_parse_error(r
);
1474 found
= set_remove(d
->jobs
, (char*) path
);
1480 if (!isempty(result
))
1481 d
->result
= strdup(result
);
1484 d
->name
= strdup(unit
);
1489 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1493 set_free_free(d
->jobs
);
1495 sd_bus_slot_unref(d
->slot_disconnected
);
1496 sd_bus_slot_unref(d
->slot_job_removed
);
1498 sd_bus_unref(d
->bus
);
1506 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1507 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1513 d
= new0(BusWaitForJobs
, 1);
1517 d
->bus
= sd_bus_ref(bus
);
1519 /* When we are a bus client we match by sender. Direct
1520 * connections OTOH have no initialized sender field, and
1521 * hence we ignore the sender then */
1522 r
= sd_bus_add_match(
1524 &d
->slot_job_removed
,
1527 "sender='org.freedesktop.systemd1',"
1528 "interface='org.freedesktop.systemd1.Manager',"
1529 "member='JobRemoved',"
1530 "path='/org/freedesktop/systemd1'" :
1532 "interface='org.freedesktop.systemd1.Manager',"
1533 "member='JobRemoved',"
1534 "path='/org/freedesktop/systemd1'",
1535 match_job_removed
, d
);
1539 r
= sd_bus_add_match(
1541 &d
->slot_disconnected
,
1543 "sender='org.freedesktop.DBus.Local',"
1544 "interface='org.freedesktop.DBus.Local',"
1545 "member='Disconnected'",
1546 match_disconnected
, d
);
1556 static int bus_process_wait(sd_bus
*bus
) {
1560 r
= sd_bus_process(bus
, NULL
);
1566 r
= sd_bus_wait(bus
, (uint64_t) -1);
1572 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1573 _cleanup_free_
char *dbus_path
= NULL
;
1579 if (!endswith(d
->name
, ".service"))
1582 dbus_path
= unit_dbus_path_from_name(d
->name
);
1586 return sd_bus_get_property_string(d
->bus
,
1587 "org.freedesktop.systemd1",
1589 "org.freedesktop.systemd1.Service",
1595 static const struct {
1596 const char *result
, *explanation
;
1597 } explanations
[] = {
1598 { "resources", "of unavailable resources or another system error" },
1599 { "protocol", "the service did not take the steps required by its unit configuration" },
1600 { "timeout", "a timeout was exceeded" },
1601 { "exit-code", "the control process exited with error code" },
1602 { "signal", "a fatal signal was delivered to the control process" },
1603 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1604 { "watchdog", "the service failed to send watchdog ping" },
1605 { "start-limit", "start of the service was attempted too often" }
1608 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1609 _cleanup_free_
char *service_shell_quoted
= NULL
;
1610 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1614 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1616 if (!strv_isempty((char**) extra_args
)) {
1617 _cleanup_free_
char *t
;
1619 t
= strv_join((char**) extra_args
, " ");
1620 systemctl
= strjoina("systemctl ", t
? : "<args>");
1621 journalctl
= strjoina("journalctl ", t
? : "<args>");
1624 if (!isempty(result
)) {
1627 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1628 if (streq(result
, explanations
[i
].result
))
1631 if (i
< ELEMENTSOF(explanations
)) {
1632 log_error("Job for %s failed because %s.\n"
1633 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1635 explanations
[i
].explanation
,
1637 service_shell_quoted
?: "<service>",
1643 log_error("Job for %s failed.\n"
1644 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1647 service_shell_quoted
?: "<service>",
1651 /* For some results maybe additional explanation is required */
1652 if (streq_ptr(result
, "start-limit"))
1653 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1654 "followed by \"%1$s start %2$s\" again.",
1656 service_shell_quoted
?: "<service>");
1659 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1665 if (streq(d
->result
, "canceled"))
1666 log_error("Job for %s canceled.", strna(d
->name
));
1667 else if (streq(d
->result
, "timeout"))
1668 log_error("Job for %s timed out.", strna(d
->name
));
1669 else if (streq(d
->result
, "dependency"))
1670 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1671 else if (streq(d
->result
, "invalid"))
1672 log_error("%s is not active, cannot reload.", strna(d
->name
));
1673 else if (streq(d
->result
, "assert"))
1674 log_error("Assertion failed on job for %s.", strna(d
->name
));
1675 else if (streq(d
->result
, "unsupported"))
1676 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1677 else if (streq(d
->result
, "collected"))
1678 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1679 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1681 _cleanup_free_
char *result
= NULL
;
1684 q
= bus_job_get_service_result(d
, &result
);
1686 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1688 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1690 log_error("Job failed. See \"journalctl -xe\" for details.");
1694 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1696 else if (streq(d
->result
, "timeout"))
1698 else if (streq(d
->result
, "dependency"))
1700 else if (streq(d
->result
, "invalid"))
1702 else if (streq(d
->result
, "assert"))
1704 else if (streq(d
->result
, "unsupported"))
1706 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1712 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1717 while (!set_isempty(d
->jobs
)) {
1720 q
= bus_process_wait(d
->bus
);
1722 return log_error_errno(q
, "Failed to wait for response: %m");
1725 q
= check_wait_response(d
, quiet
, extra_args
);
1726 /* Return the first error as it is most likely to be
1728 if (q
< 0 && r
== 0)
1731 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1734 d
->name
= mfree(d
->name
);
1735 d
->result
= mfree(d
->result
);
1741 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1746 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1750 return set_put_strdup(d
->jobs
, path
);
1753 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1756 r
= bus_wait_for_jobs_add(d
, path
);
1760 return bus_wait_for_jobs(d
, quiet
, NULL
);
1763 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1764 const char *type
, *path
, *source
;
1767 /* changes is dereferenced when calling unit_file_dump_changes() later,
1768 * so we have to make sure this is not NULL. */
1772 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1774 return bus_log_parse_error(r
);
1776 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1777 /* We expect only "success" changes to be sent over the bus.
1778 Hence, reject anything negative. */
1779 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1782 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1786 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1791 return bus_log_parse_error(r
);
1793 r
= sd_bus_message_exit_container(m
);
1795 return bus_log_parse_error(r
);
1797 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, quiet
);
1803 bool is_const
; /* If false, cgroup_path should be free()'d */
1805 Hashmap
*pids
; /* PID → process name */
1808 struct CGroupInfo
*parent
;
1809 LIST_FIELDS(struct CGroupInfo
, siblings
);
1810 LIST_HEAD(struct CGroupInfo
, children
);
1814 static bool IS_ROOT(const char *p
) {
1815 return isempty(p
) || streq(p
, "/");
1818 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1819 struct CGroupInfo
*parent
= NULL
, *cg
;
1828 cg
= hashmap_get(cgroups
, path
);
1834 if (!IS_ROOT(path
)) {
1837 e
= strrchr(path
, '/');
1841 pp
= strndupa(path
, e
- path
);
1845 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1850 cg
= new0(struct CGroupInfo
, 1);
1855 cg
->cgroup_path
= (char*) path
;
1857 cg
->cgroup_path
= strdup(path
);
1858 if (!cg
->cgroup_path
) {
1864 cg
->is_const
= is_const
;
1865 cg
->parent
= parent
;
1867 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1870 free(cg
->cgroup_path
);
1876 LIST_PREPEND(siblings
, parent
->children
, cg
);
1877 parent
->n_children
++;
1884 static int add_process(
1890 struct CGroupInfo
*cg
;
1897 r
= add_cgroup(cgroups
, path
, true, &cg
);
1901 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1905 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1908 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1912 while (cg
->children
)
1913 remove_cgroup(cgroups
, cg
->children
);
1915 hashmap_remove(cgroups
, cg
->cgroup_path
);
1918 free(cg
->cgroup_path
);
1920 hashmap_free(cg
->pids
);
1923 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1928 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1929 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1934 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1937 static int dump_processes(
1939 const char *cgroup_path
,
1942 OutputFlags flags
) {
1944 struct CGroupInfo
*cg
;
1949 if (IS_ROOT(cgroup_path
))
1952 cg
= hashmap_get(cgroups
, cgroup_path
);
1956 if (!hashmap_isempty(cg
->pids
)) {
1964 /* Order processes by their PID */
1965 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1967 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1968 pids
[n
++] = PTR_TO_PID(pidp
);
1970 assert(n
== hashmap_size(cg
->pids
));
1971 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1973 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1975 for (i
= 0; i
< n
; i
++) {
1976 _cleanup_free_
char *e
= NULL
;
1977 const char *special
;
1980 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1983 if (n_columns
!= 0) {
1986 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1988 e
= ellipsize(name
, k
, 100);
1993 more
= i
+1 < n
|| cg
->children
;
1994 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1996 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
2005 struct CGroupInfo
**children
, *child
;
2008 /* Order subcgroups by their name */
2009 children
= newa(struct CGroupInfo
*, cg
->n_children
);
2010 LIST_FOREACH(siblings
, child
, cg
->children
)
2011 children
[n
++] = child
;
2012 assert(n
== cg
->n_children
);
2013 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
2016 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
2018 for (i
= 0; i
< n
; i
++) {
2019 _cleanup_free_
char *pp
= NULL
;
2020 const char *name
, *special
;
2023 child
= children
[i
];
2025 name
= strrchr(child
->cgroup_path
, '/');
2031 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
2033 fputs(prefix
, stdout
);
2034 fputs(special
, stdout
);
2035 fputs(name
, stdout
);
2036 fputc('\n', stdout
);
2038 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
2040 pp
= strappend(prefix
, special
);
2044 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
2054 static int dump_extra_processes(
2058 OutputFlags flags
) {
2060 _cleanup_free_ pid_t
*pids
= NULL
;
2061 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
2062 struct CGroupInfo
*cg
;
2063 size_t n_allocated
= 0, n
= 0, k
;
2067 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
2068 * combined, sorted, linear list. */
2070 HASHMAP_FOREACH(cg
, cgroups
, i
) {
2078 if (hashmap_isempty(cg
->pids
))
2081 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
2085 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
2088 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
2089 pids
[n
++] = PTR_TO_PID(pidp
);
2091 r
= hashmap_put(names
, pidp
, (void*) name
);
2100 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
2101 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
2103 for (k
= 0; k
< n
; k
++) {
2104 _cleanup_free_
char *e
= NULL
;
2107 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
2110 if (n_columns
!= 0) {
2113 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
2115 e
= ellipsize(name
, z
, 100);
2120 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
2122 special_glyph(TRIANGULAR_BULLET
),
2130 int unit_show_processes(
2133 const char *cgroup_path
,
2137 sd_bus_error
*error
) {
2139 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2140 Hashmap
*cgroups
= NULL
;
2141 struct CGroupInfo
*cg
;
2147 if (flags
& OUTPUT_FULL_WIDTH
)
2149 else if (n_columns
<= 0)
2150 n_columns
= columns();
2152 prefix
= strempty(prefix
);
2154 r
= sd_bus_call_method(
2156 "org.freedesktop.systemd1",
2157 "/org/freedesktop/systemd1",
2158 "org.freedesktop.systemd1.Manager",
2167 cgroups
= hashmap_new(&string_hash_ops
);
2171 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
2176 const char *path
= NULL
, *name
= NULL
;
2179 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
2185 r
= add_process(cgroups
, path
, pid
, name
);
2190 r
= sd_bus_message_exit_container(reply
);
2194 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
2198 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
2201 while ((cg
= hashmap_first(cgroups
)))
2202 remove_cgroup(cgroups
, cg
);
2204 hashmap_free(cgroups
);