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 "string-util.h"
46 #include "syslog-util.h"
47 #include "terminal-util.h"
48 #include "user-util.h"
52 int bus_parse_unit_info(sd_bus_message
*message
, UnitInfo
*u
) {
58 return sd_bus_message_read(
73 static int bus_append_ip_address_access(sd_bus_message
*m
, int family
, const union in_addr_union
*prefix
, unsigned char prefixlen
) {
79 r
= sd_bus_message_open_container(m
, 'r', "iayu");
83 r
= sd_bus_message_append(m
, "i", family
);
87 r
= sd_bus_message_append_array(m
, 'y', prefix
, FAMILY_ADDRESS_SIZE(family
));
91 r
= sd_bus_message_append(m
, "u", prefixlen
);
95 return sd_bus_message_close_container(m
);
98 int bus_append_unit_property_assignment(sd_bus_message
*m
, const char *assignment
) {
99 const char *eq
, *field
;
106 eq
= strchr(assignment
, '=');
108 log_error("Not an assignment: %s", assignment
);
112 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
114 return bus_log_create_error(r
);
116 field
= strndupa(assignment
, eq
- assignment
);
119 if (streq(field
, "CPUQuota")) {
122 r
= sd_bus_message_append(m
, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY
);
124 r
= parse_percent_unbounded(eq
);
126 log_error_errno(r
, "CPU quota '%s' invalid.", eq
);
130 r
= sd_bus_message_append(m
, "sv", "CPUQuotaPerSecUSec", "t", (usec_t
) r
* USEC_PER_SEC
/ 100U);
135 } else if (streq(field
, "EnvironmentFile")) {
138 r
= sd_bus_message_append(m
, "sv", "EnvironmentFiles", "a(sb)", 0);
140 r
= sd_bus_message_append(m
, "sv", "EnvironmentFiles", "a(sb)", 1,
141 eq
[0] == '-' ? eq
+ 1 : eq
,
145 } else if (STR_IN_SET(field
, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) {
150 r
= parse_sec(eq
, &t
);
152 return log_error_errno(r
, "Failed to parse %s= parameter: %s", field
, eq
);
155 n
= newa(char, l
+ 2);
157 /* Change suffix Sec → USec */
158 strcpy(mempcpy(n
, field
, l
- 3), "USec");
159 r
= sd_bus_message_append(m
, "sv", n
, "t", t
);
162 } else if (streq(field
, "LogExtraFields")) {
164 r
= sd_bus_message_append(m
, "s", "LogExtraFields");
168 r
= sd_bus_message_open_container(m
, 'v', "aay");
172 r
= sd_bus_message_open_container(m
, 'a', "ay");
176 r
= sd_bus_message_append_array(m
, 'y', eq
, strlen(eq
));
180 r
= sd_bus_message_close_container(m
);
184 r
= sd_bus_message_close_container(m
);
187 } else if (STR_IN_SET(field
, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit")) {
190 if (isempty(eq
) || streq(eq
, "infinity"))
191 bytes
= CGROUP_LIMIT_MAX
;
193 r
= parse_percent(eq
);
197 /* When this is a percentage we'll convert this into a relative value in the range
198 * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
199 * ones). This way the physical memory size can be determined server-side */
201 n
= strjoina(field
, "Scale");
202 r
= sd_bus_message_append(m
, "sv", n
, "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
206 r
= parse_size(eq
, 1024, &bytes
);
208 return log_error_errno(r
, "Failed to parse bytes specification %s", assignment
);
212 r
= sd_bus_message_append(m
, "sv", field
, "t", bytes
);
215 } else if (streq(field
, "Delegate")) {
217 r
= parse_boolean(eq
);
221 r
= sd_bus_message_append(m
, "s", "DelegateControllers");
225 r
= sd_bus_message_open_container(m
, 'v', "as");
229 r
= sd_bus_message_open_container(m
, 'a', "s");
234 _cleanup_free_
char *word
= NULL
;
236 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
242 return log_error_errno(r
, "Invalid syntax: %s", eq
);
244 r
= sd_bus_message_append(m
, "s", word
);
249 r
= sd_bus_message_close_container(m
);
253 r
= sd_bus_message_close_container(m
);
255 r
= sd_bus_message_append(m
, "sv", "Delegate", "b", r
);
259 } else if (streq(field
, "TasksMax")) {
262 if (isempty(eq
) || streq(eq
, "infinity"))
265 r
= parse_percent(eq
);
267 r
= sd_bus_message_append(m
, "sv", "TasksMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
270 r
= safe_atou64(eq
, &t
);
272 return log_error_errno(r
, "Failed to parse maximum tasks specification %s", assignment
);
277 r
= sd_bus_message_append(m
, "sv", "TasksMax", "t", t
);
280 } else if (STR_IN_SET(field
, "StandardInput", "StandardOutput", "StandardError")) {
281 const char *n
, *appended
;
283 n
= startswith(eq
, "fd:");
285 appended
= strjoina(field
, "FileDescriptorName");
286 r
= sd_bus_message_append(m
, "sv", appended
, "s", n
);
288 } else if ((n
= startswith(eq
, "file:"))) {
289 appended
= strjoina(field
, "File");
290 r
= sd_bus_message_append(m
, "sv", appended
, "s", n
);
292 r
= sd_bus_message_append(m
, "sv", field
, "s", eq
);
296 } else if (streq(field
, "StandardInputText")) {
297 _cleanup_free_
char *unescaped
= NULL
;
299 r
= cunescape(eq
, 0, &unescaped
);
301 return log_error_errno(r
, "Failed to unescape text '%s': %m", eq
);
303 if (!strextend(&unescaped
, "\n", NULL
))
306 /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
307 * interface anyway */
309 r
= sd_bus_message_append(m
, "s", "StandardInputData");
311 return bus_log_create_error(r
);
313 r
= sd_bus_message_open_container(m
, 'v', "ay");
315 return bus_log_create_error(r
);
317 r
= sd_bus_message_append_array(m
, 'y', unescaped
, strlen(unescaped
));
319 return bus_log_create_error(r
);
321 r
= sd_bus_message_close_container(m
);
325 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
327 return bus_log_create_error(r
);
329 rl
= rlimit_from_string(field
);
334 r
= rlimit_parse(rl
, eq
, &l
);
336 return log_error_errno(r
, "Failed to parse resource limit: %s", eq
);
338 r
= sd_bus_message_append(m
, "v", "t", l
.rlim_max
);
340 return bus_log_create_error(r
);
342 r
= sd_bus_message_close_container(m
);
344 return bus_log_create_error(r
);
346 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
348 return bus_log_create_error(r
);
350 sn
= strjoina(field
, "Soft");
351 r
= sd_bus_message_append(m
, "sv", sn
, "t", l
.rlim_cur
);
353 } else if (STR_IN_SET(field
,
354 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
355 "TasksAccounting", "IPAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem",
356 "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
357 "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
358 "NoNewPrivileges", "SyslogLevelPrefix", "RemainAfterElapse", "Persistent",
359 "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
360 "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS",
361 "CPUSchedulingResetOnFork", "LockPersonality", "MakeDirectory")) {
363 r
= parse_boolean(eq
);
365 return log_error_errno(r
, "Failed to parse boolean assignment %s.", assignment
);
367 r
= sd_bus_message_append(m
, "v", "b", r
);
369 } else if (STR_IN_SET(field
, "CPUWeight", "StartupCPUWeight")) {
372 r
= cg_weight_parse(eq
, &u
);
374 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
376 r
= sd_bus_message_append(m
, "v", "t", u
);
378 } else if (STR_IN_SET(field
, "CPUShares", "StartupCPUShares")) {
381 r
= cg_cpu_shares_parse(eq
, &u
);
383 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
385 r
= sd_bus_message_append(m
, "v", "t", u
);
387 } else if (STR_IN_SET(field
, "IOWeight", "StartupIOWeight")) {
390 r
= cg_weight_parse(eq
, &u
);
392 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
394 r
= sd_bus_message_append(m
, "v", "t", u
);
396 } else if (STR_IN_SET(field
, "BlockIOWeight", "StartupBlockIOWeight")) {
399 r
= cg_blkio_weight_parse(eq
, &u
);
401 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
403 r
= sd_bus_message_append(m
, "v", "t", u
);
405 } else if (STR_IN_SET(field
,
406 "User", "Group", "DevicePolicy", "KillMode",
407 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
408 "Description", "Slice", "Type", "WorkingDirectory",
409 "RootDirectory", "SyslogIdentifier", "ProtectSystem",
410 "ProtectHome", "SELinuxContext", "Restart", "RootImage",
411 "NotifyAccess", "RuntimeDirectoryPreserve", "Personality",
412 "KeyringMode", "CollectMode", "FailureAction", "SuccessAction",
415 r
= sd_bus_message_append(m
, "v", "s", eq
);
417 else if (streq(field
, "StandardInputData")) {
418 _cleanup_free_
void *decoded
= NULL
;
421 r
= unbase64mem(eq
, (size_t) -1, &decoded
, &sz
);
423 return log_error_errno(r
, "Failed to decode base64 data '%s': %m", eq
);
425 r
= sd_bus_message_open_container(m
, 'v', "ay");
427 return bus_log_create_error(r
);
429 r
= sd_bus_message_append_array(m
, 'y', decoded
, sz
);
431 return bus_log_create_error(r
);
433 r
= sd_bus_message_close_container(m
);
435 } else if (STR_IN_SET(field
, "AppArmorProfile", "SmackProcessLabel")) {
447 r
= sd_bus_message_append(m
, "v", "(bs)", ignore
, s
);
449 } else if (STR_IN_SET(field
, "SyslogLevel", "LogLevelMax")) {
452 level
= log_level_from_string(eq
);
454 log_error("Failed to parse %s value %s.", field
, eq
);
458 r
= sd_bus_message_append(m
, "v", "i", level
);
460 } else if (streq(field
, "SyslogFacility")) {
463 facility
= log_facility_unshifted_from_string(eq
);
465 log_error("Failed to parse %s value %s.", field
, eq
);
469 r
= sd_bus_message_append(m
, "v", "i", facility
);
471 } else if (streq(field
, "SecureBits")) {
473 r
= secure_bits_from_string(eq
);
475 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
477 r
= sd_bus_message_append(m
, "v", "i", r
);
479 } else if (STR_IN_SET(field
, "CapabilityBoundingSet", "AmbientCapabilities")) {
490 r
= capability_set_from_string(p
, &sum
);
492 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
494 sum
= invert
? ~sum
: sum
;
496 r
= sd_bus_message_append(m
, "v", "t", sum
);
498 } else if (streq(field
, "DeviceAllow")) {
501 r
= sd_bus_message_append(m
, "v", "a(ss)", 0);
503 const char *path
, *rwm
, *e
;
507 path
= strndupa(eq
, e
- eq
);
514 if (!is_deviceallow_pattern(path
)) {
515 log_error("%s is not a device file in /dev.", path
);
519 r
= sd_bus_message_append(m
, "v", "a(ss)", 1, path
, rwm
);
522 } else if (cgroup_io_limit_type_from_string(field
) >= 0 || STR_IN_SET(field
, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
525 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
527 const char *path
, *bandwidth
, *e
;
532 path
= strndupa(eq
, e
- eq
);
535 log_error("Failed to parse %s value %s.", field
, eq
);
539 if (!path_startswith(path
, "/dev")) {
540 log_error("%s is not a device file in /dev.", path
);
544 if (streq(bandwidth
, "infinity")) {
545 bytes
= CGROUP_LIMIT_MAX
;
547 r
= parse_size(bandwidth
, 1000, &bytes
);
549 return log_error_errno(r
, "Failed to parse byte value %s: %m", bandwidth
);
552 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, bytes
);
555 } else if (STR_IN_SET(field
, "IODeviceWeight", "BlockIODeviceWeight")) {
558 r
= sd_bus_message_append(m
, "v", "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
, "v", "a(st)", 1, path
, u
);
584 } else if (STR_IN_SET(field
, "IPAddressAllow", "IPAddressDeny")) {
587 r
= sd_bus_message_append(m
, "v", "a(iayu)", 0);
589 unsigned char prefixlen
;
590 union in_addr_union prefix
= {};
593 r
= sd_bus_message_open_container(m
, 'v', "a(iayu)");
595 return bus_log_create_error(r
);
597 r
= sd_bus_message_open_container(m
, 'a', "(iayu)");
599 return bus_log_create_error(r
);
601 if (streq(eq
, "any")) {
602 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
604 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 0);
606 return bus_log_create_error(r
);
608 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 0);
610 return bus_log_create_error(r
);
612 } else if (is_localhost(eq
)) {
613 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
615 prefix
.in
.s_addr
= htobe32(0x7f000000);
616 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 8);
618 return bus_log_create_error(r
);
620 prefix
.in6
= (struct in6_addr
) IN6ADDR_LOOPBACK_INIT
;
621 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 128);
625 } else if (streq(eq
, "link-local")) {
627 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
629 prefix
.in
.s_addr
= htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
630 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 16);
632 return bus_log_create_error(r
);
634 prefix
.in6
= (struct in6_addr
) {
635 .s6_addr32
[0] = htobe32(0xfe800000)
637 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 64);
639 return bus_log_create_error(r
);
641 } else if (streq(eq
, "multicast")) {
643 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
645 prefix
.in
.s_addr
= htobe32((UINT32_C(224) << 24));
646 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 4);
648 return bus_log_create_error(r
);
650 prefix
.in6
= (struct in6_addr
) {
651 .s6_addr32
[0] = htobe32(0xff000000)
653 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 8);
655 return bus_log_create_error(r
);
658 r
= in_addr_prefix_from_string_auto(eq
, &family
, &prefix
, &prefixlen
);
660 return log_error_errno(r
, "Failed to parse IP address prefix: %s", eq
);
662 r
= bus_append_ip_address_access(m
, family
, &prefix
, prefixlen
);
664 return bus_log_create_error(r
);
667 r
= sd_bus_message_close_container(m
);
669 return bus_log_create_error(r
);
671 r
= sd_bus_message_close_container(m
);
673 return bus_log_create_error(r
);
676 } else if (streq(field
, "CPUSchedulingPolicy")) {
679 n
= sched_policy_from_string(eq
);
681 return log_error_errno(r
, "Failed to parse CPUSchedulingPolicy: %s", eq
);
683 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
685 } else if (streq(field
, "CPUSchedulingPriority")) {
688 r
= safe_atoi(eq
, &n
);
690 return log_error_errno(r
, "Failed to parse CPUSchedulingPriority: %s", eq
);
691 if (!sched_priority_is_valid(n
))
692 return log_error_errno(r
, "Invalid CPUSchedulingPriority: %s", eq
);
694 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
696 } else if (streq(field
, "CPUAffinity")) {
697 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
700 ncpus
= parse_cpu_set(eq
, &cpuset
);
702 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
704 r
= sd_bus_message_open_container(m
, 'v', "ay");
706 return bus_log_create_error(r
);
708 r
= sd_bus_message_append_array(m
, 'y', cpuset
, CPU_ALLOC_SIZE(ncpus
));
710 return bus_log_create_error(r
);
712 r
= sd_bus_message_close_container(m
);
714 } else if (streq(field
, "Nice")) {
717 r
= parse_nice(eq
, &n
);
719 return log_error_errno(r
, "Failed to parse nice value: %s", eq
);
721 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
723 } else if (streq(field
, "SystemCallFilter")) {
725 _cleanup_strv_free_
char **l
= NULL
;
735 if (whitelist
!= 0) {
736 r
= strv_extend(&l
, "@default");
742 _cleanup_free_
char *word
= NULL
;
744 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
746 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
750 r
= strv_extend(&l
, word
);
755 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
757 return bus_log_create_error(r
);
759 r
= sd_bus_message_open_container(m
, 'r', "bas");
761 return bus_log_create_error(r
);
763 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
765 return bus_log_create_error(r
);
767 r
= sd_bus_message_append_strv(m
, l
);
769 return bus_log_create_error(r
);
771 r
= sd_bus_message_close_container(m
);
773 return bus_log_create_error(r
);
775 r
= sd_bus_message_close_container(m
);
777 return bus_log_create_error(r
);
779 } else if (streq(field
, "SystemCallArchitectures")) {
782 r
= sd_bus_message_open_container(m
, 'v', "as");
784 return bus_log_create_error(r
);
786 r
= sd_bus_message_open_container(m
, 'a', "s");
788 return bus_log_create_error(r
);
791 _cleanup_free_
char *word
= NULL
;
793 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
795 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
799 r
= sd_bus_message_append_basic(m
, 's', word
);
801 return bus_log_create_error(r
);
804 r
= sd_bus_message_close_container(m
);
806 return bus_log_create_error(r
);
808 r
= sd_bus_message_close_container(m
);
810 } else if (streq(field
, "SystemCallErrorNumber")) {
815 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
817 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
819 } else if (streq(field
, "RestrictAddressFamilies")) {
821 _cleanup_strv_free_
char **l
= NULL
;
831 _cleanup_free_
char *word
= NULL
;
833 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
835 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
839 r
= strv_extend(&l
, word
);
844 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
846 return bus_log_create_error(r
);
848 r
= sd_bus_message_open_container(m
, 'r', "bas");
850 return bus_log_create_error(r
);
852 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
854 return bus_log_create_error(r
);
856 r
= sd_bus_message_append_strv(m
, l
);
858 return bus_log_create_error(r
);
860 r
= sd_bus_message_close_container(m
);
862 return bus_log_create_error(r
);
864 r
= sd_bus_message_close_container(m
);
866 return bus_log_create_error(r
);
868 } else if (streq(field
, "FileDescriptorStoreMax")) {
871 r
= safe_atou(eq
, &u
);
873 return log_error_errno(r
, "Failed to parse file descriptor store limit: %s", eq
);
875 r
= sd_bus_message_append(m
, "v", "u", (uint32_t) u
);
877 } else if (streq(field
, "IOSchedulingClass")) {
880 c
= ioprio_class_from_string(eq
);
882 return log_error_errno(r
, "Failed to parse IO scheduling class: %s", eq
);
884 r
= sd_bus_message_append(m
, "v", "i", (int32_t) c
);
886 } else if (streq(field
, "IOSchedulingPriority")) {
889 r
= ioprio_parse_priority(eq
, &q
);
891 return log_error_errno(r
, "Failed to parse IO scheduling priority: %s", eq
);
893 r
= sd_bus_message_append(m
, "v", "i", (int32_t) q
);
895 } else if (STR_IN_SET(field
, "Environment", "UnsetEnvironment", "PassEnvironment")) {
898 r
= sd_bus_message_open_container(m
, 'v', "as");
900 return bus_log_create_error(r
);
902 r
= sd_bus_message_open_container(m
, 'a', "s");
904 return bus_log_create_error(r
);
907 _cleanup_free_
char *word
= NULL
;
909 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
911 return log_error_errno(r
, "Failed to parse Environment value %s: %m", eq
);
915 if (streq(field
, "Environment")) {
916 if (!env_assignment_is_valid(word
)) {
917 log_error("Invalid environment assignment: %s", word
);
920 } else if (streq(field
, "UnsetEnvironment")) {
921 if (!env_assignment_is_valid(word
) && !env_name_is_valid(word
)) {
922 log_error("Invalid environment name or assignment: %s", word
);
925 } else { /* PassEnvironment */
926 if (!env_name_is_valid(word
)) {
927 log_error("Invalid environment variable name: %s", word
);
932 r
= sd_bus_message_append_basic(m
, 's', word
);
934 return bus_log_create_error(r
);
937 r
= sd_bus_message_close_container(m
);
939 return bus_log_create_error(r
);
941 r
= sd_bus_message_close_container(m
);
943 } else if (streq(field
, "KillSignal")) {
946 sig
= signal_from_string_try_harder(eq
);
948 log_error("Failed to parse %s value %s.", field
, eq
);
952 r
= sd_bus_message_append(m
, "v", "i", sig
);
954 } else if (streq(field
, "TimerSlackNSec")) {
957 r
= parse_nsec(eq
, &n
);
959 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
961 r
= sd_bus_message_append(m
, "v", "t", n
);
962 } else if (streq(field
, "OOMScoreAdjust")) {
965 r
= safe_atoi(eq
, &oa
);
967 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
969 if (!oom_score_adjust_is_valid(oa
)) {
970 log_error("OOM score adjust value out of range");
974 r
= sd_bus_message_append(m
, "v", "i", oa
);
976 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
977 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
980 r
= sd_bus_message_open_container(m
, 'v', "as");
982 return bus_log_create_error(r
);
984 r
= sd_bus_message_open_container(m
, 'a', "s");
986 return bus_log_create_error(r
);
989 _cleanup_free_
char *word
= NULL
;
992 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
994 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
998 if (!utf8_is_valid(word
)) {
999 log_error("Failed to parse %s value %s", field
, eq
);
1003 offset
= word
[0] == '-';
1004 offset
+= word
[offset
] == '+';
1006 if (!path_is_absolute(word
+ offset
)) {
1007 log_error("Path specified by %s is not absolute: %s", field
, eq
);
1011 r
= sd_bus_message_append_basic(m
, 's', word
);
1013 return bus_log_create_error(r
);
1016 r
= sd_bus_message_close_container(m
);
1018 return bus_log_create_error(r
);
1020 r
= sd_bus_message_close_container(m
);
1022 } else if (streq(field
, "SupplementaryGroups")) {
1025 r
= sd_bus_message_open_container(m
, 'v', "as");
1027 return bus_log_create_error(r
);
1029 r
= sd_bus_message_open_container(m
, 'a', "s");
1031 return bus_log_create_error(r
);
1034 _cleanup_free_
char *word
= NULL
;
1036 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1038 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
1042 if (!valid_user_group_name_or_id(word
)) {
1043 log_error("Invalid group name or id is specified by %s: %s", field
, eq
);
1047 r
= sd_bus_message_append_basic(m
, 's', word
);
1049 return bus_log_create_error(r
);
1052 r
= sd_bus_message_close_container(m
);
1054 return bus_log_create_error(r
);
1056 r
= sd_bus_message_close_container(m
);
1058 } else if (STR_IN_SET(field
,
1059 "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode",
1060 "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask",
1064 r
= parse_mode(eq
, &mode
);
1066 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
1068 r
= sd_bus_message_append(m
, "v", "u", mode
);
1070 } else if (STR_IN_SET(field
, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
1073 r
= sd_bus_message_open_container(m
, 'v', "as");
1075 return bus_log_create_error(r
);
1077 r
= sd_bus_message_open_container(m
, 'a', "s");
1079 return bus_log_create_error(r
);
1082 _cleanup_free_
char *word
= NULL
;
1084 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1088 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
1092 r
= sd_bus_message_append_basic(m
, 's', word
);
1094 return bus_log_create_error(r
);
1097 r
= sd_bus_message_close_container(m
);
1099 return bus_log_create_error(r
);
1101 r
= sd_bus_message_close_container(m
);
1103 } else if (streq(field
, "RestrictNamespaces")) {
1104 bool invert
= false;
1105 unsigned long flags
= 0;
1112 r
= parse_boolean(eq
);
1116 flags
= NAMESPACE_FLAGS_ALL
;
1118 r
= namespace_flag_from_string_many(eq
, &flags
);
1120 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
1124 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
1126 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
1128 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
1130 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
1132 else if (streq(field
, "MountFlags")) {
1135 r
= mount_propagation_flags_from_string(eq
, &f
);
1137 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
1139 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
1141 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
1144 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
1148 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
1153 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
1154 char *s
= NULL
, *d
= NULL
;
1155 bool ignore_enoent
= false;
1156 uint64_t flags
= MS_REC
;
1158 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1160 return log_error_errno(r
, "Failed to parse argument: %m");
1166 ignore_enoent
= true;
1170 if (p
&& p
[-1] == ':') {
1171 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1173 return log_error_errno(r
, "Failed to parse argument: %m");
1175 log_error("Missing argument after ':': %s", eq
);
1181 if (p
&& p
[-1] == ':') {
1182 _cleanup_free_
char *options
= NULL
;
1184 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
1186 return log_error_errno(r
, "Failed to parse argument: %m");
1188 if (isempty(options
) || streq(options
, "rbind"))
1190 else if (streq(options
, "norbind"))
1193 log_error("Unknown options: %s", eq
);
1201 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
1206 r
= sd_bus_message_close_container(m
);
1210 r
= sd_bus_message_close_container(m
);
1212 } else if (STR_IN_SET(field
, "ExecStartPre", "ExecStart", "ExecStartPost",
1213 "ExecReload", "ExecStop", "ExecStopPost")) {
1215 bool ignore_failure
= false, explicit_path
= false, done
= false;
1216 _cleanup_strv_free_
char **l
= NULL
;
1217 _cleanup_free_
char *path
= NULL
;
1226 ignore_failure
= true;
1235 explicit_path
= true;
1242 /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */
1243 log_error("Sorry, but +, ! and !! are currently not supported for transient services.");
1252 if (explicit_path
) {
1253 r
= extract_first_word(&eq
, &path
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
1255 return log_error_errno(r
, "Failed to parse path: %m");
1258 r
= strv_split_extract(&l
, eq
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
1260 return log_error_errno(r
, "Failed to parse command line: %m");
1262 r
= sd_bus_message_open_container(m
, 'v', "a(sasb)");
1266 r
= sd_bus_message_open_container(m
, 'a', "(sasb)");
1270 if (!strv_isempty(l
)) {
1272 r
= sd_bus_message_open_container(m
, 'r', "sasb");
1276 r
= sd_bus_message_append(m
, "s", path
?: l
[0]);
1280 r
= sd_bus_message_append_strv(m
, l
);
1284 r
= sd_bus_message_append(m
, "b", ignore_failure
);
1288 r
= sd_bus_message_close_container(m
);
1293 r
= sd_bus_message_close_container(m
);
1297 r
= sd_bus_message_close_container(m
);
1299 } else if (STR_IN_SET(field
,
1300 "OnActiveSec", "OnBootSec", "OnStartupSec",
1301 "OnUnitActiveSec","OnUnitInactiveSec")) {
1304 r
= parse_sec(eq
, &t
);
1306 return log_error_errno(r
, "Failed to parse %s= parameter: %s", field
, eq
);
1308 r
= sd_bus_message_append(m
, "v", "t", t
);
1310 } else if (STR_IN_SET(field
,
1311 "PathExists", "PathExistsGlob", "PathChanged",
1312 "PathModified", "DirectoryNotEmpty")) {
1314 if (!path_is_absolute(eq
)) {
1315 log_error("Path specified by %s= is not absolute: %s", field
, eq
);
1319 r
= sd_bus_message_append(m
, "v", "s", eq
);
1322 log_error("Unknown assignment: %s", assignment
);
1328 return bus_log_create_error(r
);
1330 r
= sd_bus_message_close_container(m
);
1332 return bus_log_create_error(r
);
1337 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
1343 STRV_FOREACH(i
, l
) {
1344 r
= bus_append_unit_property_assignment(m
, *i
);
1352 typedef struct BusWaitForJobs
{
1359 sd_bus_slot
*slot_job_removed
;
1360 sd_bus_slot
*slot_disconnected
;
1363 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1366 log_error("Warning! D-Bus connection terminated.");
1367 sd_bus_close(sd_bus_message_get_bus(m
));
1372 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1373 const char *path
, *unit
, *result
;
1374 BusWaitForJobs
*d
= userdata
;
1382 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1384 bus_log_parse_error(r
);
1388 found
= set_remove(d
->jobs
, (char*) path
);
1394 if (!isempty(result
))
1395 d
->result
= strdup(result
);
1398 d
->name
= strdup(unit
);
1403 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1407 set_free_free(d
->jobs
);
1409 sd_bus_slot_unref(d
->slot_disconnected
);
1410 sd_bus_slot_unref(d
->slot_job_removed
);
1412 sd_bus_unref(d
->bus
);
1420 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1421 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1427 d
= new0(BusWaitForJobs
, 1);
1431 d
->bus
= sd_bus_ref(bus
);
1433 /* When we are a bus client we match by sender. Direct
1434 * connections OTOH have no initialized sender field, and
1435 * hence we ignore the sender then */
1436 r
= sd_bus_add_match(
1438 &d
->slot_job_removed
,
1441 "sender='org.freedesktop.systemd1',"
1442 "interface='org.freedesktop.systemd1.Manager',"
1443 "member='JobRemoved',"
1444 "path='/org/freedesktop/systemd1'" :
1446 "interface='org.freedesktop.systemd1.Manager',"
1447 "member='JobRemoved',"
1448 "path='/org/freedesktop/systemd1'",
1449 match_job_removed
, d
);
1453 r
= sd_bus_add_match(
1455 &d
->slot_disconnected
,
1457 "sender='org.freedesktop.DBus.Local',"
1458 "interface='org.freedesktop.DBus.Local',"
1459 "member='Disconnected'",
1460 match_disconnected
, d
);
1470 static int bus_process_wait(sd_bus
*bus
) {
1474 r
= sd_bus_process(bus
, NULL
);
1480 r
= sd_bus_wait(bus
, (uint64_t) -1);
1486 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1487 _cleanup_free_
char *dbus_path
= NULL
;
1493 if (!endswith(d
->name
, ".service"))
1496 dbus_path
= unit_dbus_path_from_name(d
->name
);
1500 return sd_bus_get_property_string(d
->bus
,
1501 "org.freedesktop.systemd1",
1503 "org.freedesktop.systemd1.Service",
1509 static const struct {
1510 const char *result
, *explanation
;
1511 } explanations
[] = {
1512 { "resources", "of unavailable resources or another system error" },
1513 { "protocol", "the service did not take the steps required by its unit configuration" },
1514 { "timeout", "a timeout was exceeded" },
1515 { "exit-code", "the control process exited with error code" },
1516 { "signal", "a fatal signal was delivered to the control process" },
1517 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1518 { "watchdog", "the service failed to send watchdog ping" },
1519 { "start-limit", "start of the service was attempted too often" }
1522 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1523 _cleanup_free_
char *service_shell_quoted
= NULL
;
1524 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1528 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1530 if (!strv_isempty((char**) extra_args
)) {
1531 _cleanup_free_
char *t
;
1533 t
= strv_join((char**) extra_args
, " ");
1534 systemctl
= strjoina("systemctl ", t
? : "<args>");
1535 journalctl
= strjoina("journalctl ", t
? : "<args>");
1538 if (!isempty(result
)) {
1541 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1542 if (streq(result
, explanations
[i
].result
))
1545 if (i
< ELEMENTSOF(explanations
)) {
1546 log_error("Job for %s failed because %s.\n"
1547 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1549 explanations
[i
].explanation
,
1551 service_shell_quoted
?: "<service>",
1557 log_error("Job for %s failed.\n"
1558 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1561 service_shell_quoted
?: "<service>",
1565 /* For some results maybe additional explanation is required */
1566 if (streq_ptr(result
, "start-limit"))
1567 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1568 "followed by \"%1$s start %2$s\" again.",
1570 service_shell_quoted
?: "<service>");
1573 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1579 if (streq(d
->result
, "canceled"))
1580 log_error("Job for %s canceled.", strna(d
->name
));
1581 else if (streq(d
->result
, "timeout"))
1582 log_error("Job for %s timed out.", strna(d
->name
));
1583 else if (streq(d
->result
, "dependency"))
1584 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1585 else if (streq(d
->result
, "invalid"))
1586 log_error("%s is not active, cannot reload.", strna(d
->name
));
1587 else if (streq(d
->result
, "assert"))
1588 log_error("Assertion failed on job for %s.", strna(d
->name
));
1589 else if (streq(d
->result
, "unsupported"))
1590 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1591 else if (streq(d
->result
, "collected"))
1592 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1593 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1595 _cleanup_free_
char *result
= NULL
;
1598 q
= bus_job_get_service_result(d
, &result
);
1600 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1602 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1604 log_error("Job failed. See \"journalctl -xe\" for details.");
1608 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1610 else if (streq(d
->result
, "timeout"))
1612 else if (streq(d
->result
, "dependency"))
1614 else if (streq(d
->result
, "invalid"))
1616 else if (streq(d
->result
, "assert"))
1618 else if (streq(d
->result
, "unsupported"))
1620 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1626 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1631 while (!set_isempty(d
->jobs
)) {
1634 q
= bus_process_wait(d
->bus
);
1636 return log_error_errno(q
, "Failed to wait for response: %m");
1639 q
= check_wait_response(d
, quiet
, extra_args
);
1640 /* Return the first error as it is most likely to be
1642 if (q
< 0 && r
== 0)
1645 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1648 d
->name
= mfree(d
->name
);
1649 d
->result
= mfree(d
->result
);
1655 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1660 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1664 return set_put_strdup(d
->jobs
, path
);
1667 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1670 r
= bus_wait_for_jobs_add(d
, path
);
1674 return bus_wait_for_jobs(d
, quiet
, NULL
);
1677 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1678 const char *type
, *path
, *source
;
1681 /* changes is dereferenced when calling unit_file_dump_changes() later,
1682 * so we have to make sure this is not NULL. */
1686 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1688 return bus_log_parse_error(r
);
1690 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1691 /* We expect only "success" changes to be sent over the bus.
1692 Hence, reject anything negative. */
1693 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1696 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1700 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1705 return bus_log_parse_error(r
);
1707 r
= sd_bus_message_exit_container(m
);
1709 return bus_log_parse_error(r
);
1711 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, quiet
);
1717 bool is_const
; /* If false, cgroup_path should be free()'d */
1719 Hashmap
*pids
; /* PID → process name */
1722 struct CGroupInfo
*parent
;
1723 LIST_FIELDS(struct CGroupInfo
, siblings
);
1724 LIST_HEAD(struct CGroupInfo
, children
);
1728 static bool IS_ROOT(const char *p
) {
1729 return isempty(p
) || streq(p
, "/");
1732 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1733 struct CGroupInfo
*parent
= NULL
, *cg
;
1742 cg
= hashmap_get(cgroups
, path
);
1748 if (!IS_ROOT(path
)) {
1751 e
= strrchr(path
, '/');
1755 pp
= strndupa(path
, e
- path
);
1759 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1764 cg
= new0(struct CGroupInfo
, 1);
1769 cg
->cgroup_path
= (char*) path
;
1771 cg
->cgroup_path
= strdup(path
);
1772 if (!cg
->cgroup_path
) {
1778 cg
->is_const
= is_const
;
1779 cg
->parent
= parent
;
1781 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1784 free(cg
->cgroup_path
);
1790 LIST_PREPEND(siblings
, parent
->children
, cg
);
1791 parent
->n_children
++;
1798 static int add_process(
1804 struct CGroupInfo
*cg
;
1811 r
= add_cgroup(cgroups
, path
, true, &cg
);
1815 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1819 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1822 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1826 while (cg
->children
)
1827 remove_cgroup(cgroups
, cg
->children
);
1829 hashmap_remove(cgroups
, cg
->cgroup_path
);
1832 free(cg
->cgroup_path
);
1834 hashmap_free(cg
->pids
);
1837 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1842 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1843 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1848 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1851 static int dump_processes(
1853 const char *cgroup_path
,
1856 OutputFlags flags
) {
1858 struct CGroupInfo
*cg
;
1863 if (IS_ROOT(cgroup_path
))
1866 cg
= hashmap_get(cgroups
, cgroup_path
);
1870 if (!hashmap_isempty(cg
->pids
)) {
1878 /* Order processes by their PID */
1879 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1881 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1882 pids
[n
++] = PTR_TO_PID(pidp
);
1884 assert(n
== hashmap_size(cg
->pids
));
1885 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1887 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1889 for (i
= 0; i
< n
; i
++) {
1890 _cleanup_free_
char *e
= NULL
;
1891 const char *special
;
1894 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1897 if (n_columns
!= 0) {
1900 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1902 e
= ellipsize(name
, k
, 100);
1907 more
= i
+1 < n
|| cg
->children
;
1908 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1910 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1919 struct CGroupInfo
**children
, *child
;
1922 /* Order subcgroups by their name */
1923 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1924 LIST_FOREACH(siblings
, child
, cg
->children
)
1925 children
[n
++] = child
;
1926 assert(n
== cg
->n_children
);
1927 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1930 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1932 for (i
= 0; i
< n
; i
++) {
1933 _cleanup_free_
char *pp
= NULL
;
1934 const char *name
, *special
;
1937 child
= children
[i
];
1939 name
= strrchr(child
->cgroup_path
, '/');
1945 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1947 fputs(prefix
, stdout
);
1948 fputs(special
, stdout
);
1949 fputs(name
, stdout
);
1950 fputc('\n', stdout
);
1952 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1954 pp
= strappend(prefix
, special
);
1958 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1968 static int dump_extra_processes(
1972 OutputFlags flags
) {
1974 _cleanup_free_ pid_t
*pids
= NULL
;
1975 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1976 struct CGroupInfo
*cg
;
1977 size_t n_allocated
= 0, n
= 0, k
;
1981 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1982 * combined, sorted, linear list. */
1984 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1992 if (hashmap_isempty(cg
->pids
))
1995 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1999 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
2002 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
2003 pids
[n
++] = PTR_TO_PID(pidp
);
2005 r
= hashmap_put(names
, pidp
, (void*) name
);
2014 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
2015 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
2017 for (k
= 0; k
< n
; k
++) {
2018 _cleanup_free_
char *e
= NULL
;
2021 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
2024 if (n_columns
!= 0) {
2027 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
2029 e
= ellipsize(name
, z
, 100);
2034 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
2036 special_glyph(TRIANGULAR_BULLET
),
2044 int unit_show_processes(
2047 const char *cgroup_path
,
2051 sd_bus_error
*error
) {
2053 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2054 Hashmap
*cgroups
= NULL
;
2055 struct CGroupInfo
*cg
;
2061 if (flags
& OUTPUT_FULL_WIDTH
)
2063 else if (n_columns
<= 0)
2064 n_columns
= columns();
2066 prefix
= strempty(prefix
);
2068 r
= sd_bus_call_method(
2070 "org.freedesktop.systemd1",
2071 "/org/freedesktop/systemd1",
2072 "org.freedesktop.systemd1.Manager",
2081 cgroups
= hashmap_new(&string_hash_ops
);
2085 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
2090 const char *path
= NULL
, *name
= NULL
;
2093 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
2099 r
= add_process(cgroups
, path
, pid
, name
);
2104 r
= sd_bus_message_exit_container(reply
);
2108 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
2112 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
2115 while ((cg
= hashmap_first(cgroups
)))
2116 remove_cgroup(cgroups
, cg
);
2118 hashmap_free(cgroups
);