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("t", uint64_t, uint64_t, safe_atou64
)
128 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t
, parse_mode
)
129 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou
)
130 DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64
)
132 static inline int bus_append_string(sd_bus_message
*m
, const char *field
, const char *eq
) {
135 r
= sd_bus_message_append(m
, "(sv)", field
, "s", eq
);
137 return bus_log_create_error(r
);
142 static int bus_append_strv(sd_bus_message
*m
, const char *field
, const char *eq
, ExtractFlags flags
) {
146 r
= sd_bus_message_open_container(m
, 'r', "sv");
148 return bus_log_create_error(r
);
150 r
= sd_bus_message_append_basic(m
, 's', field
);
152 return bus_log_create_error(r
);
154 r
= sd_bus_message_open_container(m
, 'v', "as");
156 return bus_log_create_error(r
);
158 r
= sd_bus_message_open_container(m
, 'a', "s");
160 return bus_log_create_error(r
);
163 _cleanup_free_
char *word
= NULL
;
165 r
= extract_first_word(&p
, &word
, NULL
, flags
);
171 return log_error_errno(r
, "Invalid syntax: %s", eq
);
173 r
= sd_bus_message_append_basic(m
, 's', word
);
175 return bus_log_create_error(r
);
178 r
= sd_bus_message_close_container(m
);
180 return bus_log_create_error(r
);
182 r
= sd_bus_message_close_container(m
);
184 return bus_log_create_error(r
);
186 r
= sd_bus_message_close_container(m
);
188 return bus_log_create_error(r
);
193 static int bus_append_byte_array(sd_bus_message
*m
, const char *field
, const void *buf
, size_t n
) {
196 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
198 return bus_log_create_error(r
);
200 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
202 return bus_log_create_error(r
);
204 r
= sd_bus_message_open_container(m
, 'v', "ay");
206 return bus_log_create_error(r
);
208 r
= sd_bus_message_append_array(m
, 'y', buf
, n
);
210 return bus_log_create_error(r
);
212 r
= sd_bus_message_close_container(m
);
214 return bus_log_create_error(r
);
216 r
= sd_bus_message_close_container(m
);
218 return bus_log_create_error(r
);
223 static int bus_append_parse_sec_rename(sd_bus_message
*m
, const char *field
, const char *eq
) {
229 r
= parse_sec(eq
, &t
);
231 return log_error_errno(r
, "Failed to parse %s=%s: %m", field
, eq
);
234 n
= newa(char, l
+ 2);
235 /* Change suffix Sec → USec */
236 strcpy(mempcpy(n
, field
, l
- 3), "USec");
238 r
= sd_bus_message_append(m
, "(sv)", n
, "t", t
);
240 return bus_log_create_error(r
);
245 static int bus_append_parse_size(sd_bus_message
*m
, const char *field
, const char *eq
, uint64_t base
) {
249 r
= parse_size(eq
, base
, &v
);
251 return log_error_errno(r
, "Failed to parse %s=%s: %m", field
, eq
);
253 r
= sd_bus_message_append(m
, "(sv)", field
, "t", v
);
255 return bus_log_create_error(r
);
260 static int bus_append_exec_command(sd_bus_message
*m
, const char *field
, const char *eq
) {
261 bool ignore_failure
= false, explicit_path
= false, done
= false;
262 _cleanup_strv_free_
char **l
= NULL
;
263 _cleanup_free_
char *path
= NULL
;
273 ignore_failure
= true;
282 explicit_path
= true;
289 /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */
290 log_error("Sorry, but +, ! and !! are currently not supported for transient services.");
300 r
= extract_first_word(&eq
, &path
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
302 return log_error_errno(r
, "Failed to parse path: %m");
305 r
= strv_split_extract(&l
, eq
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
307 return log_error_errno(r
, "Failed to parse command line: %m");
309 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
311 return bus_log_create_error(r
);
313 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
315 return bus_log_create_error(r
);
317 r
= sd_bus_message_open_container(m
, 'v', "a(sasb)");
319 return bus_log_create_error(r
);
321 r
= sd_bus_message_open_container(m
, 'a', "(sasb)");
323 return bus_log_create_error(r
);
325 if (!strv_isempty(l
)) {
327 r
= sd_bus_message_open_container(m
, 'r', "sasb");
329 return bus_log_create_error(r
);
331 r
= sd_bus_message_append(m
, "s", path
?: l
[0]);
333 return bus_log_create_error(r
);
335 r
= sd_bus_message_append_strv(m
, l
);
337 return bus_log_create_error(r
);
339 r
= sd_bus_message_append(m
, "b", ignore_failure
);
341 return bus_log_create_error(r
);
343 r
= sd_bus_message_close_container(m
);
345 return bus_log_create_error(r
);
348 r
= sd_bus_message_close_container(m
);
350 return bus_log_create_error(r
);
352 r
= sd_bus_message_close_container(m
);
354 return bus_log_create_error(r
);
356 r
= sd_bus_message_close_container(m
);
358 return bus_log_create_error(r
);
363 static int bus_append_ip_address_access(sd_bus_message
*m
, int family
, const union in_addr_union
*prefix
, unsigned char prefixlen
) {
369 r
= sd_bus_message_open_container(m
, 'r', "iayu");
373 r
= sd_bus_message_append(m
, "i", family
);
377 r
= sd_bus_message_append_array(m
, 'y', prefix
, FAMILY_ADDRESS_SIZE(family
));
381 r
= sd_bus_message_append(m
, "u", prefixlen
);
385 return sd_bus_message_close_container(m
);
388 static int bus_append_cgroup_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
391 if (STR_IN_SET(field
, "DevicePolicy", "Slice"))
393 return bus_append_string(m
, field
, eq
);
395 if (STR_IN_SET(field
,
396 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
397 "TasksAccounting", "IPAccounting"))
399 return bus_append_parse_boolean(m
, field
, eq
);
401 if (STR_IN_SET(field
, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight"))
403 return bus_append_cg_weight_parse(m
, field
, eq
);
405 if (STR_IN_SET(field
, "CPUShares", "StartupCPUShares"))
407 return bus_append_cg_cpu_shares_parse(m
, field
, eq
);
409 if (STR_IN_SET(field
, "BlockIOWeight", "StartupBlockIOWeight"))
411 return bus_append_cg_blkio_weight_parse(m
, field
, eq
);
413 if (streq(field
, "Delegate")) {
415 r
= parse_boolean(eq
);
417 return bus_append_strv(m
, "DelegateControllers", eq
, EXTRACT_QUOTES
);
419 r
= sd_bus_message_append(m
, "(sv)", "Delegate", "b", r
);
421 return bus_log_create_error(r
);
426 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 return bus_append_safe_atou64(m
, field
, eq
);
454 return bus_append_parse_size(m
, field
, eq
, 1024);
458 if (streq(field
, "CPUQuota")) {
461 r
= sd_bus_message_append(m
, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY
);
463 r
= parse_percent_unbounded(eq
);
465 log_error_errno(r
, "CPU quota '%s' invalid.", eq
);
469 r
= sd_bus_message_append(m
, "(sv)", "CPUQuotaPerSecUSec", "t", (usec_t
) r
* USEC_PER_SEC
/ 100U);
473 return bus_log_create_error(r
);
478 if (streq(field
, "DeviceAllow")) {
481 r
= sd_bus_message_append(m
, "(sv)", field
, "a(ss)", 0);
483 const char *path
= eq
, *rwm
= NULL
, *e
;
487 path
= strndupa(eq
, e
- eq
);
491 r
= sd_bus_message_append(m
, "(sv)", field
, "a(ss)", 1, path
, strempty(rwm
));
495 return bus_log_create_error(r
);
500 if (cgroup_io_limit_type_from_string(field
) >= 0 || STR_IN_SET(field
, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
503 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 0);
505 const char *path
, *bandwidth
, *e
;
510 log_error("Failed to parse %s value %s.", field
, eq
);
514 path
= strndupa(eq
, e
- eq
);
517 if (streq(bandwidth
, "infinity")) {
518 bytes
= CGROUP_LIMIT_MAX
;
520 r
= parse_size(bandwidth
, 1000, &bytes
);
522 return log_error_errno(r
, "Failed to parse byte value %s: %m", bandwidth
);
525 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 1, path
, bytes
);
529 return bus_log_create_error(r
);
534 if (STR_IN_SET(field
, "IODeviceWeight", "BlockIODeviceWeight")) {
537 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 0);
539 const char *path
, *weight
, *e
;
544 log_error("Failed to parse %s value %s.", field
, eq
);
548 path
= strndupa(eq
, e
- eq
);
551 r
= safe_atou64(weight
, &u
);
553 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, weight
);
555 r
= sd_bus_message_append(m
, "(sv)", field
, "a(st)", 1, path
, u
);
559 return bus_log_create_error(r
);
564 if (STR_IN_SET(field
, "IPAddressAllow", "IPAddressDeny")) {
565 unsigned char prefixlen
;
566 union in_addr_union prefix
= {};
570 r
= sd_bus_message_append(m
, "(sv)", field
, "a(iayu)", 0);
572 return bus_log_create_error(r
);
577 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
579 return bus_log_create_error(r
);
581 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
583 return bus_log_create_error(r
);
585 r
= sd_bus_message_open_container(m
, 'v', "a(iayu)");
587 return bus_log_create_error(r
);
589 r
= sd_bus_message_open_container(m
, 'a', "(iayu)");
591 return bus_log_create_error(r
);
593 if (streq(eq
, "any")) {
594 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
596 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 0);
598 return bus_log_create_error(r
);
600 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 0);
602 return bus_log_create_error(r
);
604 } else if (is_localhost(eq
)) {
605 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
607 prefix
.in
.s_addr
= htobe32(0x7f000000);
608 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 8);
610 return bus_log_create_error(r
);
612 prefix
.in6
= (struct in6_addr
) IN6ADDR_LOOPBACK_INIT
;
613 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 128);
617 } else if (streq(eq
, "link-local")) {
618 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
620 prefix
.in
.s_addr
= htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
621 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 16);
623 return bus_log_create_error(r
);
625 prefix
.in6
= (struct in6_addr
) {
626 .s6_addr32
[0] = htobe32(0xfe800000)
628 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 64);
630 return bus_log_create_error(r
);
632 } else if (streq(eq
, "multicast")) {
633 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
635 prefix
.in
.s_addr
= htobe32((UINT32_C(224) << 24));
636 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 4);
638 return bus_log_create_error(r
);
640 prefix
.in6
= (struct in6_addr
) {
641 .s6_addr32
[0] = htobe32(0xff000000)
643 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 8);
645 return bus_log_create_error(r
);
648 r
= in_addr_prefix_from_string_auto(eq
, &family
, &prefix
, &prefixlen
);
650 return log_error_errno(r
, "Failed to parse IP address prefix: %s", eq
);
652 r
= bus_append_ip_address_access(m
, family
, &prefix
, prefixlen
);
654 return bus_log_create_error(r
);
657 r
= sd_bus_message_close_container(m
);
659 return bus_log_create_error(r
);
661 r
= sd_bus_message_close_container(m
);
663 return bus_log_create_error(r
);
665 r
= sd_bus_message_close_container(m
);
667 return bus_log_create_error(r
);
675 static int bus_append_execute_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
678 if (STR_IN_SET(field
,
680 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
681 "WorkingDirectory", "RootDirectory", "SyslogIdentifier",
682 "ProtectSystem", "ProtectHome", "SELinuxContext", "RootImage",
683 "RuntimeDirectoryPreserve", "Personality", "KeyringMode"))
685 return bus_append_string(m
, field
, eq
);
687 if (STR_IN_SET(field
,
688 "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
689 "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
690 "NoNewPrivileges", "SyslogLevelPrefix",
691 "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
692 "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups",
693 "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality"))
695 return bus_append_parse_boolean(m
, field
, eq
);
697 if (STR_IN_SET(field
,
698 "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
699 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths",
700 "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory",
701 "SupplementaryGroups", "SystemCallArchitectures"))
703 return bus_append_strv(m
, field
, eq
, EXTRACT_QUOTES
);
705 if (STR_IN_SET(field
, "SyslogLevel", "LogLevelMax"))
707 return bus_append_log_level_from_string(m
, field
, eq
);
709 if (streq(field
, "SyslogFacility"))
711 return bus_append_log_facility_unshifted_from_string(m
, field
, eq
);
713 if (streq(field
, "SecureBits"))
715 return bus_append_secure_bits_from_string(m
, field
, eq
);
717 if (streq(field
, "CPUSchedulingPolicy"))
719 return bus_append_sched_policy_from_string(m
, field
, eq
);
721 if (STR_IN_SET(field
, "CPUSchedulingPriority", "OOMScoreAdjust"))
723 return bus_append_safe_atoi(m
, field
, eq
);
725 if (streq(field
, "Nice"))
727 return bus_append_parse_nice(m
, field
, eq
);
729 if (streq(field
, "SystemCallErrorNumber"))
731 return bus_append_parse_errno(m
, field
, eq
);
733 if (streq(field
, "IOSchedulingClass"))
735 return bus_append_ioprio_class_from_string(m
, field
, eq
);
737 if (streq(field
, "IOSchedulingPriority"))
739 return bus_append_ioprio_parse_priority(m
, field
, eq
);
741 if (STR_IN_SET(field
,
742 "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode",
743 "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask"))
745 return bus_append_parse_mode(m
, field
, eq
);
747 if (streq(field
, "TimerSlackNSec"))
749 return bus_append_parse_nsec(m
, field
, eq
);
751 if (streq(field
, "MountFlags"))
753 return bus_append_mount_propagation_flags_from_string(m
, field
, eq
);
755 if (STR_IN_SET(field
, "Environment", "UnsetEnvironment", "PassEnvironment"))
757 return bus_append_strv(m
, field
, eq
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
759 if (streq(field
, "EnvironmentFile")) {
762 r
= sd_bus_message_append(m
, "(sv)", "EnvironmentFiles", "a(sb)", 0);
764 r
= sd_bus_message_append(m
, "(sv)", "EnvironmentFiles", "a(sb)", 1,
765 eq
[0] == '-' ? eq
+ 1 : eq
,
768 return bus_log_create_error(r
);
773 if (streq(field
, "LogExtraFields")) {
775 r
= sd_bus_message_open_container(m
, 'r', "sv");
777 return bus_log_create_error(r
);
779 r
= sd_bus_message_append_basic(m
, 's', "LogExtraFields");
781 return bus_log_create_error(r
);
783 r
= sd_bus_message_open_container(m
, 'v', "aay");
785 return bus_log_create_error(r
);
787 r
= sd_bus_message_open_container(m
, 'a', "ay");
789 return bus_log_create_error(r
);
791 r
= sd_bus_message_append_array(m
, 'y', eq
, strlen(eq
));
793 return bus_log_create_error(r
);
795 r
= sd_bus_message_close_container(m
);
797 return bus_log_create_error(r
);
799 r
= sd_bus_message_close_container(m
);
801 return bus_log_create_error(r
);
803 r
= sd_bus_message_close_container(m
);
805 return bus_log_create_error(r
);
810 if (STR_IN_SET(field
, "StandardInput", "StandardOutput", "StandardError")) {
811 const char *n
, *appended
;
813 if ((n
= startswith(eq
, "fd:"))) {
814 appended
= strjoina(field
, "FileDescriptorName");
815 r
= sd_bus_message_append(m
, "(sv)", appended
, "s", n
);
816 } else if ((n
= startswith(eq
, "file:"))) {
817 appended
= strjoina(field
, "File");
818 r
= sd_bus_message_append(m
, "(sv)", appended
, "s", n
);
820 r
= sd_bus_message_append(m
, "(sv)", field
, "s", eq
);
823 return bus_log_create_error(r
);
828 if (streq(field
, "StandardInputText")) {
829 _cleanup_free_
char *unescaped
= NULL
;
831 r
= cunescape(eq
, 0, &unescaped
);
833 return log_error_errno(r
, "Failed to unescape text '%s': %m", eq
);
835 if (!strextend(&unescaped
, "\n", NULL
))
838 /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
839 * interface anyway */
841 return bus_append_byte_array(m
, field
, unescaped
, strlen(unescaped
));
844 if (streq(field
, "StandardInputData")) {
845 _cleanup_free_
void *decoded
= NULL
;
848 r
= unbase64mem(eq
, (size_t) -1, &decoded
, &sz
);
850 return log_error_errno(r
, "Failed to decode base64 data '%s': %m", eq
);
852 return bus_append_byte_array(m
, field
, decoded
, sz
);
855 rl
= rlimit_from_string(field
);
860 r
= rlimit_parse(rl
, eq
, &l
);
862 return log_error_errno(r
, "Failed to parse resource limit: %s", eq
);
864 r
= sd_bus_message_append(m
, "(sv)", field
, "t", l
.rlim_max
);
866 return bus_log_create_error(r
);
868 sn
= strjoina(field
, "Soft");
869 r
= sd_bus_message_append(m
, "(sv)", sn
, "t", l
.rlim_cur
);
871 return bus_log_create_error(r
);
876 if (STR_IN_SET(field
, "AppArmorProfile", "SmackProcessLabel")) {
885 r
= sd_bus_message_append(m
, "(sv)", field
, "(bs)", ignore
, s
);
887 return bus_log_create_error(r
);
892 if (STR_IN_SET(field
, "CapabilityBoundingSet", "AmbientCapabilities")) {
902 r
= capability_set_from_string(p
, &sum
);
904 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
906 sum
= invert
? ~sum
: sum
;
908 r
= sd_bus_message_append(m
, "(sv)", field
, "t", sum
);
910 return bus_log_create_error(r
);
915 if (streq(field
, "CPUAffinity")) {
916 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
918 r
= parse_cpu_set(eq
, &cpuset
);
920 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
922 return bus_append_byte_array(m
, field
, cpuset
, CPU_ALLOC_SIZE(r
));
925 if (STR_IN_SET(field
, "RestrictAddressFamilies", "SystemCallFilter")) {
934 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
936 return bus_log_create_error(r
);
938 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
940 return bus_log_create_error(r
);
942 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
944 return bus_log_create_error(r
);
946 r
= sd_bus_message_open_container(m
, 'r', "bas");
948 return bus_log_create_error(r
);
950 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
952 return bus_log_create_error(r
);
954 r
= sd_bus_message_open_container(m
, 'a', "s");
956 return bus_log_create_error(r
);
959 _cleanup_free_
char *word
= NULL
;
961 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
967 return log_error_errno(r
, "Invalid syntax: %s", eq
);
969 r
= sd_bus_message_append_basic(m
, 's', word
);
971 return bus_log_create_error(r
);
974 r
= sd_bus_message_close_container(m
);
976 return bus_log_create_error(r
);
978 r
= sd_bus_message_close_container(m
);
980 return bus_log_create_error(r
);
982 r
= sd_bus_message_close_container(m
);
984 return bus_log_create_error(r
);
986 r
= sd_bus_message_close_container(m
);
988 return bus_log_create_error(r
);
993 if (streq(field
, "RestrictNamespaces")) {
995 unsigned long flags
= 0;
1002 r
= parse_boolean(eq
);
1006 flags
= NAMESPACE_FLAGS_ALL
;
1008 r
= namespace_flag_from_string_many(eq
, &flags
);
1010 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
1014 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
1016 r
= sd_bus_message_append(m
, "(sv)", field
, "t", (uint64_t) flags
);
1018 return bus_log_create_error(r
);
1023 if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
1026 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
1028 return bus_log_create_error(r
);
1030 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
1032 return bus_log_create_error(r
);
1034 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
1036 return bus_log_create_error(r
);
1038 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
1040 return bus_log_create_error(r
);
1043 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
1044 char *s
= NULL
, *d
= NULL
;
1045 bool ignore_enoent
= false;
1046 uint64_t flags
= MS_REC
;
1048 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1050 return log_error_errno(r
, "Failed to parse argument: %m");
1056 ignore_enoent
= true;
1060 if (p
&& p
[-1] == ':') {
1061 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1063 return log_error_errno(r
, "Failed to parse argument: %m");
1065 log_error("Missing argument after ':': %s", eq
);
1071 if (p
&& p
[-1] == ':') {
1072 _cleanup_free_
char *options
= NULL
;
1074 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
1076 return log_error_errno(r
, "Failed to parse argument: %m");
1078 if (isempty(options
) || streq(options
, "rbind"))
1080 else if (streq(options
, "norbind"))
1083 log_error("Unknown options: %s", eq
);
1091 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
1093 return bus_log_create_error(r
);
1096 r
= sd_bus_message_close_container(m
);
1098 return bus_log_create_error(r
);
1100 r
= sd_bus_message_close_container(m
);
1102 return bus_log_create_error(r
);
1104 r
= sd_bus_message_close_container(m
);
1106 return bus_log_create_error(r
);
1114 static int bus_append_kill_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1116 if (streq(field
, "KillMode"))
1118 return bus_append_string(m
, field
, eq
);
1120 if (STR_IN_SET(field
, "SendSIGHUP", "SendSIGKILL"))
1122 return bus_append_parse_boolean(m
, field
, eq
);
1124 if (streq(field
, "KillSignal"))
1126 return bus_append_signal_from_string_try_harder(m
, field
, eq
);
1131 static int bus_append_path_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1133 if (STR_IN_SET(field
,
1134 "PathExists", "PathExistsGlob", "PathChanged",
1135 "PathModified", "DirectoryNotEmpty"))
1137 return bus_append_string(m
, field
, eq
);
1139 if (streq(field
, "MakeDirectory"))
1141 return bus_append_parse_boolean(m
, field
, eq
);
1143 if (streq(field
, "DirectoryMode"))
1145 return bus_append_parse_mode(m
, field
, eq
);
1150 static int bus_append_service_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1152 if (STR_IN_SET(field
, "Type", "Restart", "NotifyAccess"))
1154 return bus_append_string(m
, field
, eq
);
1156 if (streq(field
, "RemainAfterExit"))
1158 return bus_append_parse_boolean(m
, field
, eq
);
1160 if (streq(field
, "RuntimeMaxSec"))
1162 return bus_append_parse_sec_rename(m
, field
, eq
);
1164 if (streq(field
, "FileDescriptorStoreMax"))
1166 return bus_append_safe_atou(m
, field
, eq
);
1168 if (STR_IN_SET(field
,
1169 "ExecStartPre", "ExecStart", "ExecStartPost",
1170 "ExecReload", "ExecStop", "ExecStopPost"))
1172 return bus_append_exec_command(m
, field
, eq
);
1177 static int bus_append_socket_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1180 if (STR_IN_SET(field
,
1181 "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast",
1182 "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet"))
1184 return bus_append_parse_boolean(m
, field
, eq
);
1186 if (STR_IN_SET(field
, "Priority", "IPTTL", "Mark"))
1188 return bus_append_safe_atoi(m
, field
, eq
);
1190 if (streq(field
, "IPTOS"))
1192 return bus_append_ip_tos_from_string(m
, field
, eq
);
1194 if (STR_IN_SET(field
, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst"))
1196 return bus_append_safe_atou(m
, field
, eq
);
1198 if (STR_IN_SET(field
, "SocketMode", "DirectoryMode"))
1200 return bus_append_parse_mode(m
, field
, eq
);
1202 if (STR_IN_SET(field
, "MessageQueueMaxMessages", "MessageQueueMessageSize"))
1204 return bus_append_safe_atoi64(m
, field
, eq
);
1206 if (STR_IN_SET(field
, "TimeoutSec", "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", "TriggerLimitIntervalSec"))
1208 return bus_append_parse_sec_rename(m
, field
, eq
);
1210 if (STR_IN_SET(field
, "ReceiveBuffer", "SendBuffer", "PipeSize"))
1212 return bus_append_parse_size(m
, field
, eq
, 1024);
1214 if (STR_IN_SET(field
, "ExecStartPre", "ExecStartPost", "ExecReload", "ExecStopPost"))
1216 return bus_append_exec_command(m
, field
, eq
);
1218 if (STR_IN_SET(field
,
1219 "SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion",
1220 "BindToDevice", "BindIPv6Only", "FileDescriptorName",
1221 "SocketUser", "SocketGroup"))
1223 return bus_append_string(m
, field
, eq
);
1225 if (streq(field
, "Symlinks"))
1227 return bus_append_strv(m
, field
, eq
, EXTRACT_QUOTES
);
1229 if (streq(field
, "SocketProtocol"))
1231 return bus_append_socket_protocol_from_name(m
, field
, eq
);
1233 if (STR_IN_SET(field
,
1234 "ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink",
1235 "ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) {
1237 r
= sd_bus_message_append(m
, "(sv)", "Listen", "a(ss)", 1, field
+ strlen("Listen"), eq
);
1239 return bus_log_create_error(r
);
1246 static int bus_append_timer_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1248 if (streq(field
, "OnCalendar"))
1250 return bus_append_string(m
, field
, eq
);
1252 if (STR_IN_SET(field
, "WakeSystem", "RemainAfterElapse", "Persistent"))
1254 return bus_append_parse_boolean(m
, field
, eq
);
1256 if (STR_IN_SET(field
,
1257 "OnActiveSec", "OnBootSec", "OnStartupSec",
1258 "OnUnitActiveSec","OnUnitInactiveSec"))
1260 return bus_append_parse_sec(m
, field
, eq
);
1262 if (STR_IN_SET(field
, "AccuracySec", "RandomizedDelaySec"))
1264 return bus_append_parse_sec_rename(m
, field
, eq
);
1269 static int bus_append_unit_property(sd_bus_message
*m
, const char *field
, const char *eq
) {
1271 if (STR_IN_SET(field
, "Description", "CollectMode", "FailureAction", "SuccessAction"))
1273 return bus_append_string(m
, field
, eq
);
1275 if (streq(field
, "DefaultDependencies"))
1277 return bus_append_parse_boolean(m
, field
, eq
);
1279 if (unit_dependency_from_string(field
) >= 0)
1281 return bus_append_strv(m
, field
, eq
, EXTRACT_QUOTES
);
1286 int bus_append_unit_property_assignment(sd_bus_message
*m
, UnitType t
, const char *assignment
) {
1287 const char *eq
, *field
;
1293 eq
= strchr(assignment
, '=');
1295 log_error("Not an assignment: %s", assignment
);
1299 field
= strndupa(assignment
, eq
- assignment
);
1304 r
= bus_append_cgroup_property(m
, field
, eq
);
1308 r
= bus_append_execute_property(m
, field
, eq
);
1312 r
= bus_append_kill_property(m
, field
, eq
);
1316 r
= bus_append_service_property(m
, field
, eq
);
1322 r
= bus_append_cgroup_property(m
, field
, eq
);
1326 r
= bus_append_execute_property(m
, field
, eq
);
1330 r
= bus_append_kill_property(m
, field
, eq
);
1334 r
= bus_append_socket_property(m
, field
, eq
);
1340 r
= bus_append_timer_property(m
, field
, eq
);
1346 r
= bus_append_path_property(m
, field
, eq
);
1352 r
= bus_append_cgroup_property(m
, field
, eq
);
1358 r
= bus_append_cgroup_property(m
, field
, eq
);
1362 r
= bus_append_kill_property(m
, field
, eq
);
1368 case UNIT_AUTOMOUNT
:
1374 log_error("Not supported unit type");
1378 log_error("Invalid unit type");
1382 r
= bus_append_unit_property(m
, field
, eq
);
1386 log_error("Unknown assignment: %s", assignment
);
1390 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, UnitType t
, char **l
) {
1396 STRV_FOREACH(i
, l
) {
1397 r
= bus_append_unit_property_assignment(m
, t
, *i
);
1405 typedef struct BusWaitForJobs
{
1412 sd_bus_slot
*slot_job_removed
;
1413 sd_bus_slot
*slot_disconnected
;
1416 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1419 log_error("Warning! D-Bus connection terminated.");
1420 sd_bus_close(sd_bus_message_get_bus(m
));
1425 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1426 const char *path
, *unit
, *result
;
1427 BusWaitForJobs
*d
= userdata
;
1435 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1437 bus_log_parse_error(r
);
1441 found
= set_remove(d
->jobs
, (char*) path
);
1447 if (!isempty(result
))
1448 d
->result
= strdup(result
);
1451 d
->name
= strdup(unit
);
1456 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1460 set_free_free(d
->jobs
);
1462 sd_bus_slot_unref(d
->slot_disconnected
);
1463 sd_bus_slot_unref(d
->slot_job_removed
);
1465 sd_bus_unref(d
->bus
);
1473 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1474 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1480 d
= new0(BusWaitForJobs
, 1);
1484 d
->bus
= sd_bus_ref(bus
);
1486 /* When we are a bus client we match by sender. Direct
1487 * connections OTOH have no initialized sender field, and
1488 * hence we ignore the sender then */
1489 r
= sd_bus_add_match(
1491 &d
->slot_job_removed
,
1494 "sender='org.freedesktop.systemd1',"
1495 "interface='org.freedesktop.systemd1.Manager',"
1496 "member='JobRemoved',"
1497 "path='/org/freedesktop/systemd1'" :
1499 "interface='org.freedesktop.systemd1.Manager',"
1500 "member='JobRemoved',"
1501 "path='/org/freedesktop/systemd1'",
1502 match_job_removed
, d
);
1506 r
= sd_bus_add_match(
1508 &d
->slot_disconnected
,
1510 "sender='org.freedesktop.DBus.Local',"
1511 "interface='org.freedesktop.DBus.Local',"
1512 "member='Disconnected'",
1513 match_disconnected
, d
);
1523 static int bus_process_wait(sd_bus
*bus
) {
1527 r
= sd_bus_process(bus
, NULL
);
1533 r
= sd_bus_wait(bus
, (uint64_t) -1);
1539 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1540 _cleanup_free_
char *dbus_path
= NULL
;
1546 if (!endswith(d
->name
, ".service"))
1549 dbus_path
= unit_dbus_path_from_name(d
->name
);
1553 return sd_bus_get_property_string(d
->bus
,
1554 "org.freedesktop.systemd1",
1556 "org.freedesktop.systemd1.Service",
1562 static const struct {
1563 const char *result
, *explanation
;
1564 } explanations
[] = {
1565 { "resources", "of unavailable resources or another system error" },
1566 { "protocol", "the service did not take the steps required by its unit configuration" },
1567 { "timeout", "a timeout was exceeded" },
1568 { "exit-code", "the control process exited with error code" },
1569 { "signal", "a fatal signal was delivered to the control process" },
1570 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1571 { "watchdog", "the service failed to send watchdog ping" },
1572 { "start-limit", "start of the service was attempted too often" }
1575 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1576 _cleanup_free_
char *service_shell_quoted
= NULL
;
1577 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1581 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1583 if (!strv_isempty((char**) extra_args
)) {
1584 _cleanup_free_
char *t
;
1586 t
= strv_join((char**) extra_args
, " ");
1587 systemctl
= strjoina("systemctl ", t
? : "<args>");
1588 journalctl
= strjoina("journalctl ", t
? : "<args>");
1591 if (!isempty(result
)) {
1594 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1595 if (streq(result
, explanations
[i
].result
))
1598 if (i
< ELEMENTSOF(explanations
)) {
1599 log_error("Job for %s failed because %s.\n"
1600 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1602 explanations
[i
].explanation
,
1604 service_shell_quoted
?: "<service>",
1610 log_error("Job for %s failed.\n"
1611 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1614 service_shell_quoted
?: "<service>",
1618 /* For some results maybe additional explanation is required */
1619 if (streq_ptr(result
, "start-limit"))
1620 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1621 "followed by \"%1$s start %2$s\" again.",
1623 service_shell_quoted
?: "<service>");
1626 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1632 if (streq(d
->result
, "canceled"))
1633 log_error("Job for %s canceled.", strna(d
->name
));
1634 else if (streq(d
->result
, "timeout"))
1635 log_error("Job for %s timed out.", strna(d
->name
));
1636 else if (streq(d
->result
, "dependency"))
1637 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1638 else if (streq(d
->result
, "invalid"))
1639 log_error("%s is not active, cannot reload.", strna(d
->name
));
1640 else if (streq(d
->result
, "assert"))
1641 log_error("Assertion failed on job for %s.", strna(d
->name
));
1642 else if (streq(d
->result
, "unsupported"))
1643 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1644 else if (streq(d
->result
, "collected"))
1645 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1646 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1648 _cleanup_free_
char *result
= NULL
;
1651 q
= bus_job_get_service_result(d
, &result
);
1653 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1655 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1657 log_error("Job failed. See \"journalctl -xe\" for details.");
1661 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1663 else if (streq(d
->result
, "timeout"))
1665 else if (streq(d
->result
, "dependency"))
1667 else if (streq(d
->result
, "invalid"))
1669 else if (streq(d
->result
, "assert"))
1671 else if (streq(d
->result
, "unsupported"))
1673 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1679 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1684 while (!set_isempty(d
->jobs
)) {
1687 q
= bus_process_wait(d
->bus
);
1689 return log_error_errno(q
, "Failed to wait for response: %m");
1692 q
= check_wait_response(d
, quiet
, extra_args
);
1693 /* Return the first error as it is most likely to be
1695 if (q
< 0 && r
== 0)
1698 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1701 d
->name
= mfree(d
->name
);
1702 d
->result
= mfree(d
->result
);
1708 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1713 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1717 return set_put_strdup(d
->jobs
, path
);
1720 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1723 r
= bus_wait_for_jobs_add(d
, path
);
1727 return bus_wait_for_jobs(d
, quiet
, NULL
);
1730 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1731 const char *type
, *path
, *source
;
1734 /* changes is dereferenced when calling unit_file_dump_changes() later,
1735 * so we have to make sure this is not NULL. */
1739 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1741 return bus_log_parse_error(r
);
1743 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1744 /* We expect only "success" changes to be sent over the bus.
1745 Hence, reject anything negative. */
1746 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1749 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1753 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1758 return bus_log_parse_error(r
);
1760 r
= sd_bus_message_exit_container(m
);
1762 return bus_log_parse_error(r
);
1764 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, quiet
);
1770 bool is_const
; /* If false, cgroup_path should be free()'d */
1772 Hashmap
*pids
; /* PID → process name */
1775 struct CGroupInfo
*parent
;
1776 LIST_FIELDS(struct CGroupInfo
, siblings
);
1777 LIST_HEAD(struct CGroupInfo
, children
);
1781 static bool IS_ROOT(const char *p
) {
1782 return isempty(p
) || streq(p
, "/");
1785 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1786 struct CGroupInfo
*parent
= NULL
, *cg
;
1795 cg
= hashmap_get(cgroups
, path
);
1801 if (!IS_ROOT(path
)) {
1804 e
= strrchr(path
, '/');
1808 pp
= strndupa(path
, e
- path
);
1812 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1817 cg
= new0(struct CGroupInfo
, 1);
1822 cg
->cgroup_path
= (char*) path
;
1824 cg
->cgroup_path
= strdup(path
);
1825 if (!cg
->cgroup_path
) {
1831 cg
->is_const
= is_const
;
1832 cg
->parent
= parent
;
1834 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1837 free(cg
->cgroup_path
);
1843 LIST_PREPEND(siblings
, parent
->children
, cg
);
1844 parent
->n_children
++;
1851 static int add_process(
1857 struct CGroupInfo
*cg
;
1864 r
= add_cgroup(cgroups
, path
, true, &cg
);
1868 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1872 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1875 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1879 while (cg
->children
)
1880 remove_cgroup(cgroups
, cg
->children
);
1882 hashmap_remove(cgroups
, cg
->cgroup_path
);
1885 free(cg
->cgroup_path
);
1887 hashmap_free(cg
->pids
);
1890 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1895 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1896 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1901 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1904 static int dump_processes(
1906 const char *cgroup_path
,
1909 OutputFlags flags
) {
1911 struct CGroupInfo
*cg
;
1916 if (IS_ROOT(cgroup_path
))
1919 cg
= hashmap_get(cgroups
, cgroup_path
);
1923 if (!hashmap_isempty(cg
->pids
)) {
1931 /* Order processes by their PID */
1932 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1934 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1935 pids
[n
++] = PTR_TO_PID(pidp
);
1937 assert(n
== hashmap_size(cg
->pids
));
1938 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1940 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1942 for (i
= 0; i
< n
; i
++) {
1943 _cleanup_free_
char *e
= NULL
;
1944 const char *special
;
1947 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1950 if (n_columns
!= 0) {
1953 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1955 e
= ellipsize(name
, k
, 100);
1960 more
= i
+1 < n
|| cg
->children
;
1961 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1963 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1972 struct CGroupInfo
**children
, *child
;
1975 /* Order subcgroups by their name */
1976 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1977 LIST_FOREACH(siblings
, child
, cg
->children
)
1978 children
[n
++] = child
;
1979 assert(n
== cg
->n_children
);
1980 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1983 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1985 for (i
= 0; i
< n
; i
++) {
1986 _cleanup_free_
char *pp
= NULL
;
1987 const char *name
, *special
;
1990 child
= children
[i
];
1992 name
= strrchr(child
->cgroup_path
, '/');
1998 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
2000 fputs(prefix
, stdout
);
2001 fputs(special
, stdout
);
2002 fputs(name
, stdout
);
2003 fputc('\n', stdout
);
2005 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
2007 pp
= strappend(prefix
, special
);
2011 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
2021 static int dump_extra_processes(
2025 OutputFlags flags
) {
2027 _cleanup_free_ pid_t
*pids
= NULL
;
2028 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
2029 struct CGroupInfo
*cg
;
2030 size_t n_allocated
= 0, n
= 0, k
;
2034 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
2035 * combined, sorted, linear list. */
2037 HASHMAP_FOREACH(cg
, cgroups
, i
) {
2045 if (hashmap_isempty(cg
->pids
))
2048 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
2052 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
2055 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
2056 pids
[n
++] = PTR_TO_PID(pidp
);
2058 r
= hashmap_put(names
, pidp
, (void*) name
);
2067 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
2068 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
2070 for (k
= 0; k
< n
; k
++) {
2071 _cleanup_free_
char *e
= NULL
;
2074 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
2077 if (n_columns
!= 0) {
2080 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
2082 e
= ellipsize(name
, z
, 100);
2087 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
2089 special_glyph(TRIANGULAR_BULLET
),
2097 int unit_show_processes(
2100 const char *cgroup_path
,
2104 sd_bus_error
*error
) {
2106 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2107 Hashmap
*cgroups
= NULL
;
2108 struct CGroupInfo
*cg
;
2114 if (flags
& OUTPUT_FULL_WIDTH
)
2116 else if (n_columns
<= 0)
2117 n_columns
= columns();
2119 prefix
= strempty(prefix
);
2121 r
= sd_bus_call_method(
2123 "org.freedesktop.systemd1",
2124 "/org/freedesktop/systemd1",
2125 "org.freedesktop.systemd1.Manager",
2134 cgroups
= hashmap_new(&string_hash_ops
);
2138 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
2143 const char *path
= NULL
, *name
= NULL
;
2146 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
2152 r
= add_process(cgroups
, path
, pid
, name
);
2157 r
= sd_bus_message_exit_container(reply
);
2161 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
2165 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
2168 while ((cg
= hashmap_first(cgroups
)))
2169 remove_cgroup(cgroups
, cg
);
2171 hashmap_free(cgroups
);