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
) {
1297 if (STR_IN_SET(field
, "Description", "CollectMode", "FailureAction", "SuccessAction"))
1299 return bus_append_string(m
, field
, eq
);
1301 if (streq(field
, "DefaultDependencies"))
1303 return bus_append_parse_boolean(m
, field
, eq
);
1305 if (unit_dependency_from_string(field
) >= 0)
1307 return bus_append_strv(m
, field
, eq
, EXTRACT_QUOTES
);
1312 int bus_append_unit_property_assignment(sd_bus_message
*m
, UnitType t
, const char *assignment
) {
1313 const char *eq
, *field
;
1319 eq
= strchr(assignment
, '=');
1321 log_error("Not an assignment: %s", assignment
);
1325 field
= strndupa(assignment
, eq
- assignment
);
1330 r
= bus_append_cgroup_property(m
, field
, eq
);
1334 r
= bus_append_execute_property(m
, field
, eq
);
1338 r
= bus_append_kill_property(m
, field
, eq
);
1342 r
= bus_append_service_property(m
, field
, eq
);
1348 r
= bus_append_cgroup_property(m
, field
, eq
);
1352 r
= bus_append_execute_property(m
, field
, eq
);
1356 r
= bus_append_kill_property(m
, field
, eq
);
1360 r
= bus_append_socket_property(m
, field
, eq
);
1366 r
= bus_append_timer_property(m
, field
, eq
);
1372 r
= bus_append_path_property(m
, field
, eq
);
1378 r
= bus_append_cgroup_property(m
, field
, eq
);
1384 r
= bus_append_cgroup_property(m
, field
, eq
);
1388 r
= bus_append_kill_property(m
, field
, eq
);
1394 case UNIT_AUTOMOUNT
:
1400 log_error("Not supported unit type");
1404 log_error("Invalid unit type");
1408 r
= bus_append_unit_property(m
, field
, eq
);
1412 log_error("Unknown assignment: %s", assignment
);
1416 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, UnitType t
, char **l
) {
1422 STRV_FOREACH(i
, l
) {
1423 r
= bus_append_unit_property_assignment(m
, t
, *i
);
1431 typedef struct BusWaitForJobs
{
1438 sd_bus_slot
*slot_job_removed
;
1439 sd_bus_slot
*slot_disconnected
;
1442 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1445 log_error("Warning! D-Bus connection terminated.");
1446 sd_bus_close(sd_bus_message_get_bus(m
));
1451 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1452 const char *path
, *unit
, *result
;
1453 BusWaitForJobs
*d
= userdata
;
1461 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1463 bus_log_parse_error(r
);
1467 found
= set_remove(d
->jobs
, (char*) path
);
1473 if (!isempty(result
))
1474 d
->result
= strdup(result
);
1477 d
->name
= strdup(unit
);
1482 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1486 set_free_free(d
->jobs
);
1488 sd_bus_slot_unref(d
->slot_disconnected
);
1489 sd_bus_slot_unref(d
->slot_job_removed
);
1491 sd_bus_unref(d
->bus
);
1499 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1500 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1506 d
= new0(BusWaitForJobs
, 1);
1510 d
->bus
= sd_bus_ref(bus
);
1512 /* When we are a bus client we match by sender. Direct
1513 * connections OTOH have no initialized sender field, and
1514 * hence we ignore the sender then */
1515 r
= sd_bus_add_match(
1517 &d
->slot_job_removed
,
1520 "sender='org.freedesktop.systemd1',"
1521 "interface='org.freedesktop.systemd1.Manager',"
1522 "member='JobRemoved',"
1523 "path='/org/freedesktop/systemd1'" :
1525 "interface='org.freedesktop.systemd1.Manager',"
1526 "member='JobRemoved',"
1527 "path='/org/freedesktop/systemd1'",
1528 match_job_removed
, d
);
1532 r
= sd_bus_add_match(
1534 &d
->slot_disconnected
,
1536 "sender='org.freedesktop.DBus.Local',"
1537 "interface='org.freedesktop.DBus.Local',"
1538 "member='Disconnected'",
1539 match_disconnected
, d
);
1549 static int bus_process_wait(sd_bus
*bus
) {
1553 r
= sd_bus_process(bus
, NULL
);
1559 r
= sd_bus_wait(bus
, (uint64_t) -1);
1565 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1566 _cleanup_free_
char *dbus_path
= NULL
;
1572 if (!endswith(d
->name
, ".service"))
1575 dbus_path
= unit_dbus_path_from_name(d
->name
);
1579 return sd_bus_get_property_string(d
->bus
,
1580 "org.freedesktop.systemd1",
1582 "org.freedesktop.systemd1.Service",
1588 static const struct {
1589 const char *result
, *explanation
;
1590 } explanations
[] = {
1591 { "resources", "of unavailable resources or another system error" },
1592 { "protocol", "the service did not take the steps required by its unit configuration" },
1593 { "timeout", "a timeout was exceeded" },
1594 { "exit-code", "the control process exited with error code" },
1595 { "signal", "a fatal signal was delivered to the control process" },
1596 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1597 { "watchdog", "the service failed to send watchdog ping" },
1598 { "start-limit", "start of the service was attempted too often" }
1601 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1602 _cleanup_free_
char *service_shell_quoted
= NULL
;
1603 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1607 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1609 if (!strv_isempty((char**) extra_args
)) {
1610 _cleanup_free_
char *t
;
1612 t
= strv_join((char**) extra_args
, " ");
1613 systemctl
= strjoina("systemctl ", t
? : "<args>");
1614 journalctl
= strjoina("journalctl ", t
? : "<args>");
1617 if (!isempty(result
)) {
1620 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1621 if (streq(result
, explanations
[i
].result
))
1624 if (i
< ELEMENTSOF(explanations
)) {
1625 log_error("Job for %s failed because %s.\n"
1626 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1628 explanations
[i
].explanation
,
1630 service_shell_quoted
?: "<service>",
1636 log_error("Job for %s failed.\n"
1637 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1640 service_shell_quoted
?: "<service>",
1644 /* For some results maybe additional explanation is required */
1645 if (streq_ptr(result
, "start-limit"))
1646 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1647 "followed by \"%1$s start %2$s\" again.",
1649 service_shell_quoted
?: "<service>");
1652 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1658 if (streq(d
->result
, "canceled"))
1659 log_error("Job for %s canceled.", strna(d
->name
));
1660 else if (streq(d
->result
, "timeout"))
1661 log_error("Job for %s timed out.", strna(d
->name
));
1662 else if (streq(d
->result
, "dependency"))
1663 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1664 else if (streq(d
->result
, "invalid"))
1665 log_error("%s is not active, cannot reload.", strna(d
->name
));
1666 else if (streq(d
->result
, "assert"))
1667 log_error("Assertion failed on job for %s.", strna(d
->name
));
1668 else if (streq(d
->result
, "unsupported"))
1669 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1670 else if (streq(d
->result
, "collected"))
1671 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1672 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1674 _cleanup_free_
char *result
= NULL
;
1677 q
= bus_job_get_service_result(d
, &result
);
1679 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1681 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1683 log_error("Job failed. See \"journalctl -xe\" for details.");
1687 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1689 else if (streq(d
->result
, "timeout"))
1691 else if (streq(d
->result
, "dependency"))
1693 else if (streq(d
->result
, "invalid"))
1695 else if (streq(d
->result
, "assert"))
1697 else if (streq(d
->result
, "unsupported"))
1699 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1705 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1710 while (!set_isempty(d
->jobs
)) {
1713 q
= bus_process_wait(d
->bus
);
1715 return log_error_errno(q
, "Failed to wait for response: %m");
1718 q
= check_wait_response(d
, quiet
, extra_args
);
1719 /* Return the first error as it is most likely to be
1721 if (q
< 0 && r
== 0)
1724 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1727 d
->name
= mfree(d
->name
);
1728 d
->result
= mfree(d
->result
);
1734 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1739 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1743 return set_put_strdup(d
->jobs
, path
);
1746 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1749 r
= bus_wait_for_jobs_add(d
, path
);
1753 return bus_wait_for_jobs(d
, quiet
, NULL
);
1756 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1757 const char *type
, *path
, *source
;
1760 /* changes is dereferenced when calling unit_file_dump_changes() later,
1761 * so we have to make sure this is not NULL. */
1765 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1767 return bus_log_parse_error(r
);
1769 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1770 /* We expect only "success" changes to be sent over the bus.
1771 Hence, reject anything negative. */
1772 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1775 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1779 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1784 return bus_log_parse_error(r
);
1786 r
= sd_bus_message_exit_container(m
);
1788 return bus_log_parse_error(r
);
1790 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, quiet
);
1796 bool is_const
; /* If false, cgroup_path should be free()'d */
1798 Hashmap
*pids
; /* PID → process name */
1801 struct CGroupInfo
*parent
;
1802 LIST_FIELDS(struct CGroupInfo
, siblings
);
1803 LIST_HEAD(struct CGroupInfo
, children
);
1807 static bool IS_ROOT(const char *p
) {
1808 return isempty(p
) || streq(p
, "/");
1811 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1812 struct CGroupInfo
*parent
= NULL
, *cg
;
1821 cg
= hashmap_get(cgroups
, path
);
1827 if (!IS_ROOT(path
)) {
1830 e
= strrchr(path
, '/');
1834 pp
= strndupa(path
, e
- path
);
1838 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1843 cg
= new0(struct CGroupInfo
, 1);
1848 cg
->cgroup_path
= (char*) path
;
1850 cg
->cgroup_path
= strdup(path
);
1851 if (!cg
->cgroup_path
) {
1857 cg
->is_const
= is_const
;
1858 cg
->parent
= parent
;
1860 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1863 free(cg
->cgroup_path
);
1869 LIST_PREPEND(siblings
, parent
->children
, cg
);
1870 parent
->n_children
++;
1877 static int add_process(
1883 struct CGroupInfo
*cg
;
1890 r
= add_cgroup(cgroups
, path
, true, &cg
);
1894 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1898 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1901 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1905 while (cg
->children
)
1906 remove_cgroup(cgroups
, cg
->children
);
1908 hashmap_remove(cgroups
, cg
->cgroup_path
);
1911 free(cg
->cgroup_path
);
1913 hashmap_free(cg
->pids
);
1916 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1921 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1922 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1927 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1930 static int dump_processes(
1932 const char *cgroup_path
,
1935 OutputFlags flags
) {
1937 struct CGroupInfo
*cg
;
1942 if (IS_ROOT(cgroup_path
))
1945 cg
= hashmap_get(cgroups
, cgroup_path
);
1949 if (!hashmap_isempty(cg
->pids
)) {
1957 /* Order processes by their PID */
1958 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1960 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1961 pids
[n
++] = PTR_TO_PID(pidp
);
1963 assert(n
== hashmap_size(cg
->pids
));
1964 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1966 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1968 for (i
= 0; i
< n
; i
++) {
1969 _cleanup_free_
char *e
= NULL
;
1970 const char *special
;
1973 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1976 if (n_columns
!= 0) {
1979 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1981 e
= ellipsize(name
, k
, 100);
1986 more
= i
+1 < n
|| cg
->children
;
1987 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1989 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1998 struct CGroupInfo
**children
, *child
;
2001 /* Order subcgroups by their name */
2002 children
= newa(struct CGroupInfo
*, cg
->n_children
);
2003 LIST_FOREACH(siblings
, child
, cg
->children
)
2004 children
[n
++] = child
;
2005 assert(n
== cg
->n_children
);
2006 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
2009 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
2011 for (i
= 0; i
< n
; i
++) {
2012 _cleanup_free_
char *pp
= NULL
;
2013 const char *name
, *special
;
2016 child
= children
[i
];
2018 name
= strrchr(child
->cgroup_path
, '/');
2024 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
2026 fputs(prefix
, stdout
);
2027 fputs(special
, stdout
);
2028 fputs(name
, stdout
);
2029 fputc('\n', stdout
);
2031 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
2033 pp
= strappend(prefix
, special
);
2037 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
2047 static int dump_extra_processes(
2051 OutputFlags flags
) {
2053 _cleanup_free_ pid_t
*pids
= NULL
;
2054 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
2055 struct CGroupInfo
*cg
;
2056 size_t n_allocated
= 0, n
= 0, k
;
2060 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
2061 * combined, sorted, linear list. */
2063 HASHMAP_FOREACH(cg
, cgroups
, i
) {
2071 if (hashmap_isempty(cg
->pids
))
2074 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
2078 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
2081 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
2082 pids
[n
++] = PTR_TO_PID(pidp
);
2084 r
= hashmap_put(names
, pidp
, (void*) name
);
2093 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
2094 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
2096 for (k
= 0; k
< n
; k
++) {
2097 _cleanup_free_
char *e
= NULL
;
2100 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
2103 if (n_columns
!= 0) {
2106 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
2108 e
= ellipsize(name
, z
, 100);
2113 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
2115 special_glyph(TRIANGULAR_BULLET
),
2123 int unit_show_processes(
2126 const char *cgroup_path
,
2130 sd_bus_error
*error
) {
2132 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2133 Hashmap
*cgroups
= NULL
;
2134 struct CGroupInfo
*cg
;
2140 if (flags
& OUTPUT_FULL_WIDTH
)
2142 else if (n_columns
<= 0)
2143 n_columns
= columns();
2145 prefix
= strempty(prefix
);
2147 r
= sd_bus_call_method(
2149 "org.freedesktop.systemd1",
2150 "/org/freedesktop/systemd1",
2151 "org.freedesktop.systemd1.Manager",
2160 cgroups
= hashmap_new(&string_hash_ops
);
2164 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
2169 const char *path
= NULL
, *name
= NULL
;
2172 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
2178 r
= add_process(cgroups
, path
, pid
, name
);
2183 r
= sd_bus_message_exit_container(reply
);
2187 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
2191 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
2194 while ((cg
= hashmap_first(cgroups
)))
2195 remove_cgroup(cgroups
, cg
);
2197 hashmap_free(cgroups
);