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);
159 /* Change suffix Sec → USec */
160 strcpy(mempcpy(n
, field
, l
- 3), "USec");
161 r
= sd_bus_message_append(m
, "sv", n
, "t", t
);
164 } else if (streq(field
, "LogExtraFields")) {
166 r
= sd_bus_message_append(m
, "s", "LogExtraFields");
170 r
= sd_bus_message_open_container(m
, 'v', "aay");
174 r
= sd_bus_message_open_container(m
, 'a', "ay");
178 r
= sd_bus_message_append_array(m
, 'y', eq
, strlen(eq
));
182 r
= sd_bus_message_close_container(m
);
186 r
= sd_bus_message_close_container(m
);
189 } else if (STR_IN_SET(field
, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit")) {
192 if (isempty(eq
) || streq(eq
, "infinity"))
193 bytes
= CGROUP_LIMIT_MAX
;
195 r
= parse_percent(eq
);
199 /* When this is a percentage we'll convert this into a relative value in the range
200 * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
201 * ones). This way the physical memory size can be determined server-side */
203 n
= strjoina(field
, "Scale");
204 r
= sd_bus_message_append(m
, "sv", n
, "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
208 r
= parse_size(eq
, 1024, &bytes
);
210 return log_error_errno(r
, "Failed to parse bytes specification %s", assignment
);
214 r
= sd_bus_message_append(m
, "sv", field
, "t", bytes
);
217 } else if (streq(field
, "Delegate")) {
219 r
= parse_boolean(eq
);
223 r
= sd_bus_message_append(m
, "s", "DelegateControllers");
227 r
= sd_bus_message_open_container(m
, 'v', "as");
231 r
= sd_bus_message_open_container(m
, 'a', "s");
236 _cleanup_free_
char *word
= NULL
;
238 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
244 return log_error_errno(r
, "Invalid syntax: %s", eq
);
246 r
= sd_bus_message_append(m
, "s", word
);
251 r
= sd_bus_message_close_container(m
);
255 r
= sd_bus_message_close_container(m
);
257 r
= sd_bus_message_append(m
, "sv", "Delegate", "b", r
);
261 } else if (streq(field
, "TasksMax")) {
264 if (isempty(eq
) || streq(eq
, "infinity"))
267 r
= parse_percent(eq
);
269 r
= sd_bus_message_append(m
, "sv", "TasksMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
272 r
= safe_atou64(eq
, &t
);
274 return log_error_errno(r
, "Failed to parse maximum tasks specification %s", assignment
);
279 r
= sd_bus_message_append(m
, "sv", "TasksMax", "t", t
);
282 } else if (STR_IN_SET(field
, "StandardInput", "StandardOutput", "StandardError")) {
283 const char *n
, *appended
;
285 n
= startswith(eq
, "fd:");
287 appended
= strjoina(field
, "FileDescriptorName");
288 r
= sd_bus_message_append(m
, "sv", appended
, "s", n
);
290 } else if ((n
= startswith(eq
, "file:"))) {
291 appended
= strjoina(field
, "File");
292 r
= sd_bus_message_append(m
, "sv", appended
, "s", n
);
294 r
= sd_bus_message_append(m
, "sv", field
, "s", eq
);
298 } else if (streq(field
, "StandardInputText")) {
299 _cleanup_free_
char *unescaped
= NULL
;
301 r
= cunescape(eq
, 0, &unescaped
);
303 return log_error_errno(r
, "Failed to unescape text '%s': %m", eq
);
305 if (!strextend(&unescaped
, "\n", NULL
))
308 /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
309 * interface anyway */
311 r
= sd_bus_message_append(m
, "s", "StandardInputData");
313 return bus_log_create_error(r
);
315 r
= sd_bus_message_open_container(m
, 'v', "ay");
317 return bus_log_create_error(r
);
319 r
= sd_bus_message_append_array(m
, 'y', unescaped
, strlen(unescaped
));
321 return bus_log_create_error(r
);
323 r
= sd_bus_message_close_container(m
);
327 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
329 return bus_log_create_error(r
);
331 rl
= rlimit_from_string(field
);
336 r
= rlimit_parse(rl
, eq
, &l
);
338 return log_error_errno(r
, "Failed to parse resource limit: %s", eq
);
340 r
= sd_bus_message_append(m
, "v", "t", l
.rlim_max
);
342 return bus_log_create_error(r
);
344 r
= sd_bus_message_close_container(m
);
346 return bus_log_create_error(r
);
348 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
350 return bus_log_create_error(r
);
352 sn
= strjoina(field
, "Soft");
353 r
= sd_bus_message_append(m
, "sv", sn
, "t", l
.rlim_cur
);
355 } else if (STR_IN_SET(field
,
356 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
357 "TasksAccounting", "IPAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem",
358 "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
359 "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
360 "NoNewPrivileges", "SyslogLevelPrefix", "RemainAfterElapse",
361 "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
362 "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS",
363 "CPUSchedulingResetOnFork", "LockPersonality")) {
365 r
= parse_boolean(eq
);
367 return log_error_errno(r
, "Failed to parse boolean assignment %s.", assignment
);
369 r
= sd_bus_message_append(m
, "v", "b", r
);
371 } else if (STR_IN_SET(field
, "CPUWeight", "StartupCPUWeight")) {
374 r
= cg_weight_parse(eq
, &u
);
376 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
378 r
= sd_bus_message_append(m
, "v", "t", u
);
380 } else if (STR_IN_SET(field
, "CPUShares", "StartupCPUShares")) {
383 r
= cg_cpu_shares_parse(eq
, &u
);
385 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
387 r
= sd_bus_message_append(m
, "v", "t", u
);
389 } else if (STR_IN_SET(field
, "IOWeight", "StartupIOWeight")) {
392 r
= cg_weight_parse(eq
, &u
);
394 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
396 r
= sd_bus_message_append(m
, "v", "t", u
);
398 } else if (STR_IN_SET(field
, "BlockIOWeight", "StartupBlockIOWeight")) {
401 r
= cg_blkio_weight_parse(eq
, &u
);
403 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
405 r
= sd_bus_message_append(m
, "v", "t", u
);
407 } else if (STR_IN_SET(field
,
408 "User", "Group", "DevicePolicy", "KillMode",
409 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
410 "Description", "Slice", "Type", "WorkingDirectory",
411 "RootDirectory", "SyslogIdentifier", "ProtectSystem",
412 "ProtectHome", "SELinuxContext", "Restart", "RootImage",
413 "NotifyAccess", "RuntimeDirectoryPreserve", "Personality",
414 "KeyringMode", "CollectMode", "FailureAction", "SuccessAction"))
415 r
= sd_bus_message_append(m
, "v", "s", eq
);
417 else if (streq(field
, "StandardInputData")) {
418 _cleanup_free_
char *cleaned
= NULL
;
419 _cleanup_free_
void *decoded
= NULL
;
422 cleaned
= strdup(eq
);
426 delete_chars(cleaned
, WHITESPACE
);
428 r
= unbase64mem(cleaned
, (size_t) -1, &decoded
, &sz
);
430 return log_error_errno(r
, "Failed to decode base64 data '%s': %m", cleaned
);
432 r
= sd_bus_message_open_container(m
, 'v', "ay");
434 return bus_log_create_error(r
);
436 r
= sd_bus_message_append_array(m
, 'y', decoded
, sz
);
438 return bus_log_create_error(r
);
440 r
= sd_bus_message_close_container(m
);
442 } else if (STR_IN_SET(field
, "AppArmorProfile", "SmackProcessLabel")) {
454 r
= sd_bus_message_append(m
, "v", "(bs)", ignore
, s
);
456 } else if (STR_IN_SET(field
, "SyslogLevel", "LogLevelMax")) {
459 level
= log_level_from_string(eq
);
461 log_error("Failed to parse %s value %s.", field
, eq
);
465 r
= sd_bus_message_append(m
, "v", "i", level
);
467 } else if (streq(field
, "SyslogFacility")) {
470 facility
= log_facility_unshifted_from_string(eq
);
472 log_error("Failed to parse %s value %s.", field
, eq
);
476 r
= sd_bus_message_append(m
, "v", "i", facility
);
478 } else if (streq(field
, "SecureBits")) {
480 r
= secure_bits_from_string(eq
);
482 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
484 r
= sd_bus_message_append(m
, "v", "i", r
);
486 } else if (STR_IN_SET(field
, "CapabilityBoundingSet", "AmbientCapabilities")) {
497 r
= capability_set_from_string(p
, &sum
);
499 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
501 sum
= invert
? ~sum
: sum
;
503 r
= sd_bus_message_append(m
, "v", "t", sum
);
505 } else if (streq(field
, "DeviceAllow")) {
508 r
= sd_bus_message_append(m
, "v", "a(ss)", 0);
510 const char *path
, *rwm
, *e
;
514 path
= strndupa(eq
, e
- eq
);
521 if (!is_deviceallow_pattern(path
)) {
522 log_error("%s is not a device file in /dev.", path
);
526 r
= sd_bus_message_append(m
, "v", "a(ss)", 1, path
, rwm
);
529 } else if (cgroup_io_limit_type_from_string(field
) >= 0 || STR_IN_SET(field
, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
532 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
534 const char *path
, *bandwidth
, *e
;
539 path
= strndupa(eq
, e
- eq
);
542 log_error("Failed to parse %s value %s.", field
, eq
);
546 if (!path_startswith(path
, "/dev")) {
547 log_error("%s is not a device file in /dev.", path
);
551 if (streq(bandwidth
, "infinity")) {
552 bytes
= CGROUP_LIMIT_MAX
;
554 r
= parse_size(bandwidth
, 1000, &bytes
);
556 return log_error_errno(r
, "Failed to parse byte value %s: %m", bandwidth
);
559 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, bytes
);
562 } else if (STR_IN_SET(field
, "IODeviceWeight", "BlockIODeviceWeight")) {
565 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
567 const char *path
, *weight
, *e
;
572 path
= strndupa(eq
, e
- eq
);
575 log_error("Failed to parse %s value %s.", field
, eq
);
579 if (!path_startswith(path
, "/dev")) {
580 log_error("%s is not a device file in /dev.", path
);
584 r
= safe_atou64(weight
, &u
);
586 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, weight
);
588 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, u
);
591 } else if (STR_IN_SET(field
, "IPAddressAllow", "IPAddressDeny")) {
594 r
= sd_bus_message_append(m
, "v", "a(iayu)", 0);
596 unsigned char prefixlen
;
597 union in_addr_union prefix
= {};
600 r
= sd_bus_message_open_container(m
, 'v', "a(iayu)");
602 return bus_log_create_error(r
);
604 r
= sd_bus_message_open_container(m
, 'a', "(iayu)");
606 return bus_log_create_error(r
);
608 if (streq(eq
, "any")) {
609 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
611 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 0);
613 return bus_log_create_error(r
);
615 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 0);
617 return bus_log_create_error(r
);
619 } else if (is_localhost(eq
)) {
620 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
622 prefix
.in
.s_addr
= htobe32(0x7f000000);
623 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 8);
625 return bus_log_create_error(r
);
627 prefix
.in6
= (struct in6_addr
) IN6ADDR_LOOPBACK_INIT
;
628 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 128);
632 } else if (streq(eq
, "link-local")) {
634 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
636 prefix
.in
.s_addr
= htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
637 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 16);
639 return bus_log_create_error(r
);
641 prefix
.in6
= (struct in6_addr
) {
642 .s6_addr32
[0] = htobe32(0xfe800000)
644 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 64);
646 return bus_log_create_error(r
);
648 } else if (streq(eq
, "multicast")) {
650 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
652 prefix
.in
.s_addr
= htobe32((UINT32_C(224) << 24));
653 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 4);
655 return bus_log_create_error(r
);
657 prefix
.in6
= (struct in6_addr
) {
658 .s6_addr32
[0] = htobe32(0xff000000)
660 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 8);
662 return bus_log_create_error(r
);
665 r
= in_addr_prefix_from_string_auto(eq
, &family
, &prefix
, &prefixlen
);
667 return log_error_errno(r
, "Failed to parse IP address prefix: %s", eq
);
669 r
= bus_append_ip_address_access(m
, family
, &prefix
, prefixlen
);
671 return bus_log_create_error(r
);
674 r
= sd_bus_message_close_container(m
);
676 return bus_log_create_error(r
);
678 r
= sd_bus_message_close_container(m
);
680 return bus_log_create_error(r
);
683 } else if (streq(field
, "CPUSchedulingPolicy")) {
686 n
= sched_policy_from_string(eq
);
688 return log_error_errno(r
, "Failed to parse CPUSchedulingPolicy: %s", eq
);
690 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
692 } else if (streq(field
, "CPUSchedulingPriority")) {
695 r
= safe_atoi(eq
, &n
);
697 return log_error_errno(r
, "Failed to parse CPUSchedulingPriority: %s", eq
);
698 if (!sched_priority_is_valid(n
))
699 return log_error_errno(r
, "Invalid CPUSchedulingPriority: %s", eq
);
701 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
703 } else if (streq(field
, "CPUAffinity")) {
704 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
707 ncpus
= parse_cpu_set(eq
, &cpuset
);
709 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
711 r
= sd_bus_message_open_container(m
, 'v', "ay");
713 return bus_log_create_error(r
);
716 sd_bus_message_append_array(m
, 'y', cpuset
, CPU_ALLOC_SIZE(ncpus
));
718 r
= sd_bus_message_close_container(m
);
720 } else if (streq(field
, "Nice")) {
723 r
= parse_nice(eq
, &n
);
725 return log_error_errno(r
, "Failed to parse nice value: %s", eq
);
727 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
729 } else if (streq(field
, "SystemCallFilter")) {
731 _cleanup_strv_free_
char **l
= NULL
;
741 if (whitelist
!= 0) {
742 r
= strv_extend(&l
, "@default");
748 _cleanup_free_
char *word
= NULL
;
750 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
752 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
756 r
= strv_extend(&l
, word
);
761 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
763 return bus_log_create_error(r
);
765 r
= sd_bus_message_open_container(m
, 'r', "bas");
767 return bus_log_create_error(r
);
769 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
771 return bus_log_create_error(r
);
773 r
= sd_bus_message_append_strv(m
, l
);
775 return bus_log_create_error(r
);
777 r
= sd_bus_message_close_container(m
);
779 return bus_log_create_error(r
);
781 r
= sd_bus_message_close_container(m
);
783 return bus_log_create_error(r
);
785 } else if (streq(field
, "SystemCallArchitectures")) {
788 r
= sd_bus_message_open_container(m
, 'v', "as");
790 return bus_log_create_error(r
);
792 r
= sd_bus_message_open_container(m
, 'a', "s");
794 return bus_log_create_error(r
);
797 _cleanup_free_
char *word
= NULL
;
799 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
801 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
805 r
= sd_bus_message_append_basic(m
, 's', word
);
807 return bus_log_create_error(r
);
810 r
= sd_bus_message_close_container(m
);
812 return bus_log_create_error(r
);
814 r
= sd_bus_message_close_container(m
);
816 } else if (streq(field
, "SystemCallErrorNumber")) {
821 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
823 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
825 } else if (streq(field
, "RestrictAddressFamilies")) {
827 _cleanup_strv_free_
char **l
= NULL
;
837 _cleanup_free_
char *word
= NULL
;
839 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
841 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
845 r
= strv_extend(&l
, word
);
850 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
852 return bus_log_create_error(r
);
854 r
= sd_bus_message_open_container(m
, 'r', "bas");
856 return bus_log_create_error(r
);
858 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
860 return bus_log_create_error(r
);
862 r
= sd_bus_message_append_strv(m
, l
);
864 return bus_log_create_error(r
);
866 r
= sd_bus_message_close_container(m
);
868 return bus_log_create_error(r
);
870 r
= sd_bus_message_close_container(m
);
872 return bus_log_create_error(r
);
874 } else if (streq(field
, "FileDescriptorStoreMax")) {
877 r
= safe_atou(eq
, &u
);
879 return log_error_errno(r
, "Failed to parse file descriptor store limit: %s", eq
);
881 r
= sd_bus_message_append(m
, "v", "u", (uint32_t) u
);
883 } else if (streq(field
, "IOSchedulingClass")) {
886 c
= ioprio_class_from_string(eq
);
888 return log_error_errno(r
, "Failed to parse IO scheduling class: %s", eq
);
890 r
= sd_bus_message_append(m
, "v", "i", (int32_t) c
);
892 } else if (streq(field
, "IOSchedulingPriority")) {
895 r
= ioprio_parse_priority(eq
, &q
);
897 return log_error_errno(r
, "Failed to parse IO scheduling priority: %s", eq
);
899 r
= sd_bus_message_append(m
, "v", "i", (int32_t) q
);
901 } else if (STR_IN_SET(field
, "Environment", "UnsetEnvironment", "PassEnvironment")) {
904 r
= sd_bus_message_open_container(m
, 'v', "as");
906 return bus_log_create_error(r
);
908 r
= sd_bus_message_open_container(m
, 'a', "s");
910 return bus_log_create_error(r
);
913 _cleanup_free_
char *word
= NULL
;
915 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
917 return log_error_errno(r
, "Failed to parse Environment value %s: %m", eq
);
921 if (streq(field
, "Environment")) {
922 if (!env_assignment_is_valid(word
)) {
923 log_error("Invalid environment assignment: %s", word
);
926 } else if (streq(field
, "UnsetEnvironment")) {
927 if (!env_assignment_is_valid(word
) && !env_name_is_valid(word
)) {
928 log_error("Invalid environment name or assignment: %s", word
);
931 } else { /* PassEnvironment */
932 if (!env_name_is_valid(word
)) {
933 log_error("Invalid environment variable name: %s", word
);
938 r
= sd_bus_message_append_basic(m
, 's', word
);
940 return bus_log_create_error(r
);
943 r
= sd_bus_message_close_container(m
);
945 return bus_log_create_error(r
);
947 r
= sd_bus_message_close_container(m
);
949 } else if (streq(field
, "KillSignal")) {
952 sig
= signal_from_string_try_harder(eq
);
954 log_error("Failed to parse %s value %s.", field
, eq
);
958 r
= sd_bus_message_append(m
, "v", "i", sig
);
960 } else if (streq(field
, "TimerSlackNSec")) {
963 r
= parse_nsec(eq
, &n
);
965 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
967 r
= sd_bus_message_append(m
, "v", "t", n
);
968 } else if (streq(field
, "OOMScoreAdjust")) {
971 r
= safe_atoi(eq
, &oa
);
973 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
975 if (!oom_score_adjust_is_valid(oa
)) {
976 log_error("OOM score adjust value out of range");
980 r
= sd_bus_message_append(m
, "v", "i", oa
);
981 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
982 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
985 r
= sd_bus_message_open_container(m
, 'v', "as");
987 return bus_log_create_error(r
);
989 r
= sd_bus_message_open_container(m
, 'a', "s");
991 return bus_log_create_error(r
);
994 _cleanup_free_
char *word
= NULL
;
997 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
999 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
1003 if (!utf8_is_valid(word
)) {
1004 log_error("Failed to parse %s value %s", field
, eq
);
1008 offset
= word
[0] == '-';
1009 offset
+= word
[offset
] == '+';
1011 if (!path_is_absolute(word
+ offset
)) {
1012 log_error("Failed to parse %s value %s", field
, eq
);
1016 path_kill_slashes(word
+ offset
);
1018 r
= sd_bus_message_append_basic(m
, 's', word
);
1020 return bus_log_create_error(r
);
1023 r
= sd_bus_message_close_container(m
);
1025 return bus_log_create_error(r
);
1027 r
= sd_bus_message_close_container(m
);
1029 } else if (streq(field
, "SupplementaryGroups")) {
1032 r
= sd_bus_message_open_container(m
, 'v', "as");
1034 return bus_log_create_error(r
);
1036 r
= sd_bus_message_open_container(m
, 'a', "s");
1038 return bus_log_create_error(r
);
1041 _cleanup_free_
char *word
= NULL
;
1043 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1045 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
1049 if (!valid_user_group_name_or_id(word
)) {
1050 log_error("Failed to parse %s value %s", field
, eq
);
1054 r
= sd_bus_message_append_basic(m
, 's', word
);
1056 return bus_log_create_error(r
);
1059 r
= sd_bus_message_close_container(m
);
1061 return bus_log_create_error(r
);
1063 r
= sd_bus_message_close_container(m
);
1065 } else if (STR_IN_SET(field
, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) {
1068 r
= parse_mode(eq
, &mode
);
1070 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
1072 r
= sd_bus_message_append(m
, "v", "u", mode
);
1074 } else if (STR_IN_SET(field
, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
1077 r
= sd_bus_message_open_container(m
, 'v', "as");
1079 return bus_log_create_error(r
);
1081 r
= sd_bus_message_open_container(m
, 'a', "s");
1083 return bus_log_create_error(r
);
1086 _cleanup_free_
char *word
= NULL
;
1088 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1092 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
1096 r
= sd_bus_message_append_basic(m
, 's', word
);
1098 return bus_log_create_error(r
);
1101 r
= sd_bus_message_close_container(m
);
1103 return bus_log_create_error(r
);
1105 r
= sd_bus_message_close_container(m
);
1107 } else if (streq(field
, "RestrictNamespaces")) {
1108 bool invert
= false;
1109 unsigned long flags
= 0;
1116 r
= parse_boolean(eq
);
1120 flags
= NAMESPACE_FLAGS_ALL
;
1122 r
= namespace_flag_from_string_many(eq
, &flags
);
1124 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
1128 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
1130 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
1131 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
1132 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
1133 else if (streq(field
, "MountFlags")) {
1136 r
= mount_propagation_flags_from_string(eq
, &f
);
1138 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
1140 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_length(l
) > 0) {
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
);
1300 log_error("Unknown assignment: %s", assignment
);
1306 return bus_log_create_error(r
);
1308 r
= sd_bus_message_close_container(m
);
1310 return bus_log_create_error(r
);
1315 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
1321 STRV_FOREACH(i
, l
) {
1322 r
= bus_append_unit_property_assignment(m
, *i
);
1330 typedef struct BusWaitForJobs
{
1337 sd_bus_slot
*slot_job_removed
;
1338 sd_bus_slot
*slot_disconnected
;
1341 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1344 log_error("Warning! D-Bus connection terminated.");
1345 sd_bus_close(sd_bus_message_get_bus(m
));
1350 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1351 const char *path
, *unit
, *result
;
1352 BusWaitForJobs
*d
= userdata
;
1360 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1362 bus_log_parse_error(r
);
1366 found
= set_remove(d
->jobs
, (char*) path
);
1372 if (!isempty(result
))
1373 d
->result
= strdup(result
);
1376 d
->name
= strdup(unit
);
1381 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1385 set_free_free(d
->jobs
);
1387 sd_bus_slot_unref(d
->slot_disconnected
);
1388 sd_bus_slot_unref(d
->slot_job_removed
);
1390 sd_bus_unref(d
->bus
);
1398 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1399 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1405 d
= new0(BusWaitForJobs
, 1);
1409 d
->bus
= sd_bus_ref(bus
);
1411 /* When we are a bus client we match by sender. Direct
1412 * connections OTOH have no initialized sender field, and
1413 * hence we ignore the sender then */
1414 r
= sd_bus_add_match(
1416 &d
->slot_job_removed
,
1419 "sender='org.freedesktop.systemd1',"
1420 "interface='org.freedesktop.systemd1.Manager',"
1421 "member='JobRemoved',"
1422 "path='/org/freedesktop/systemd1'" :
1424 "interface='org.freedesktop.systemd1.Manager',"
1425 "member='JobRemoved',"
1426 "path='/org/freedesktop/systemd1'",
1427 match_job_removed
, d
);
1431 r
= sd_bus_add_match(
1433 &d
->slot_disconnected
,
1435 "sender='org.freedesktop.DBus.Local',"
1436 "interface='org.freedesktop.DBus.Local',"
1437 "member='Disconnected'",
1438 match_disconnected
, d
);
1448 static int bus_process_wait(sd_bus
*bus
) {
1452 r
= sd_bus_process(bus
, NULL
);
1458 r
= sd_bus_wait(bus
, (uint64_t) -1);
1464 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1465 _cleanup_free_
char *dbus_path
= NULL
;
1471 if (!endswith(d
->name
, ".service"))
1474 dbus_path
= unit_dbus_path_from_name(d
->name
);
1478 return sd_bus_get_property_string(d
->bus
,
1479 "org.freedesktop.systemd1",
1481 "org.freedesktop.systemd1.Service",
1487 static const struct {
1488 const char *result
, *explanation
;
1489 } explanations
[] = {
1490 { "resources", "of unavailable resources or another system error" },
1491 { "protocol", "the service did not take the steps required by its unit configuration" },
1492 { "timeout", "a timeout was exceeded" },
1493 { "exit-code", "the control process exited with error code" },
1494 { "signal", "a fatal signal was delivered to the control process" },
1495 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1496 { "watchdog", "the service failed to send watchdog ping" },
1497 { "start-limit", "start of the service was attempted too often" }
1500 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1501 _cleanup_free_
char *service_shell_quoted
= NULL
;
1502 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1506 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1508 if (!strv_isempty((char**) extra_args
)) {
1509 _cleanup_free_
char *t
;
1511 t
= strv_join((char**) extra_args
, " ");
1512 systemctl
= strjoina("systemctl ", t
? : "<args>");
1513 journalctl
= strjoina("journalctl ", t
? : "<args>");
1516 if (!isempty(result
)) {
1519 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1520 if (streq(result
, explanations
[i
].result
))
1523 if (i
< ELEMENTSOF(explanations
)) {
1524 log_error("Job for %s failed because %s.\n"
1525 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1527 explanations
[i
].explanation
,
1529 service_shell_quoted
?: "<service>",
1535 log_error("Job for %s failed.\n"
1536 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1539 service_shell_quoted
?: "<service>",
1543 /* For some results maybe additional explanation is required */
1544 if (streq_ptr(result
, "start-limit"))
1545 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1546 "followed by \"%1$s start %2$s\" again.",
1548 service_shell_quoted
?: "<service>");
1551 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1557 if (streq(d
->result
, "canceled"))
1558 log_error("Job for %s canceled.", strna(d
->name
));
1559 else if (streq(d
->result
, "timeout"))
1560 log_error("Job for %s timed out.", strna(d
->name
));
1561 else if (streq(d
->result
, "dependency"))
1562 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1563 else if (streq(d
->result
, "invalid"))
1564 log_error("%s is not active, cannot reload.", strna(d
->name
));
1565 else if (streq(d
->result
, "assert"))
1566 log_error("Assertion failed on job for %s.", strna(d
->name
));
1567 else if (streq(d
->result
, "unsupported"))
1568 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1569 else if (streq(d
->result
, "collected"))
1570 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1571 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1573 _cleanup_free_
char *result
= NULL
;
1576 q
= bus_job_get_service_result(d
, &result
);
1578 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1580 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1582 log_error("Job failed. See \"journalctl -xe\" for details.");
1586 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1588 else if (streq(d
->result
, "timeout"))
1590 else if (streq(d
->result
, "dependency"))
1592 else if (streq(d
->result
, "invalid"))
1594 else if (streq(d
->result
, "assert"))
1596 else if (streq(d
->result
, "unsupported"))
1598 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1604 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1609 while (!set_isempty(d
->jobs
)) {
1612 q
= bus_process_wait(d
->bus
);
1614 return log_error_errno(q
, "Failed to wait for response: %m");
1617 q
= check_wait_response(d
, quiet
, extra_args
);
1618 /* Return the first error as it is most likely to be
1620 if (q
< 0 && r
== 0)
1623 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1626 d
->name
= mfree(d
->name
);
1627 d
->result
= mfree(d
->result
);
1633 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1638 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1642 return set_put_strdup(d
->jobs
, path
);
1645 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1648 r
= bus_wait_for_jobs_add(d
, path
);
1652 return bus_wait_for_jobs(d
, quiet
, NULL
);
1655 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1656 const char *type
, *path
, *source
;
1659 /* changes is dereferenced when calling unit_file_dump_changes() later,
1660 * so we have to make sure this is not NULL. */
1664 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1666 return bus_log_parse_error(r
);
1668 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1669 /* We expect only "success" changes to be sent over the bus.
1670 Hence, reject anything negative. */
1671 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1674 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1678 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1683 return bus_log_parse_error(r
);
1685 r
= sd_bus_message_exit_container(m
);
1687 return bus_log_parse_error(r
);
1689 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, quiet
);
1695 bool is_const
; /* If false, cgroup_path should be free()'d */
1697 Hashmap
*pids
; /* PID → process name */
1700 struct CGroupInfo
*parent
;
1701 LIST_FIELDS(struct CGroupInfo
, siblings
);
1702 LIST_HEAD(struct CGroupInfo
, children
);
1706 static bool IS_ROOT(const char *p
) {
1707 return isempty(p
) || streq(p
, "/");
1710 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1711 struct CGroupInfo
*parent
= NULL
, *cg
;
1720 cg
= hashmap_get(cgroups
, path
);
1726 if (!IS_ROOT(path
)) {
1729 e
= strrchr(path
, '/');
1733 pp
= strndupa(path
, e
- path
);
1737 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1742 cg
= new0(struct CGroupInfo
, 1);
1747 cg
->cgroup_path
= (char*) path
;
1749 cg
->cgroup_path
= strdup(path
);
1750 if (!cg
->cgroup_path
) {
1756 cg
->is_const
= is_const
;
1757 cg
->parent
= parent
;
1759 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1762 free(cg
->cgroup_path
);
1768 LIST_PREPEND(siblings
, parent
->children
, cg
);
1769 parent
->n_children
++;
1776 static int add_process(
1782 struct CGroupInfo
*cg
;
1789 r
= add_cgroup(cgroups
, path
, true, &cg
);
1793 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1797 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1800 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1804 while (cg
->children
)
1805 remove_cgroup(cgroups
, cg
->children
);
1807 hashmap_remove(cgroups
, cg
->cgroup_path
);
1810 free(cg
->cgroup_path
);
1812 hashmap_free(cg
->pids
);
1815 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1820 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1821 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1826 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1829 static int dump_processes(
1831 const char *cgroup_path
,
1834 OutputFlags flags
) {
1836 struct CGroupInfo
*cg
;
1841 if (IS_ROOT(cgroup_path
))
1844 cg
= hashmap_get(cgroups
, cgroup_path
);
1848 if (!hashmap_isempty(cg
->pids
)) {
1856 /* Order processes by their PID */
1857 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1859 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1860 pids
[n
++] = PTR_TO_PID(pidp
);
1862 assert(n
== hashmap_size(cg
->pids
));
1863 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1865 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1867 for (i
= 0; i
< n
; i
++) {
1868 _cleanup_free_
char *e
= NULL
;
1869 const char *special
;
1872 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1875 if (n_columns
!= 0) {
1878 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1880 e
= ellipsize(name
, k
, 100);
1885 more
= i
+1 < n
|| cg
->children
;
1886 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1888 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1897 struct CGroupInfo
**children
, *child
;
1900 /* Order subcgroups by their name */
1901 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1902 LIST_FOREACH(siblings
, child
, cg
->children
)
1903 children
[n
++] = child
;
1904 assert(n
== cg
->n_children
);
1905 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1908 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1910 for (i
= 0; i
< n
; i
++) {
1911 _cleanup_free_
char *pp
= NULL
;
1912 const char *name
, *special
;
1915 child
= children
[i
];
1917 name
= strrchr(child
->cgroup_path
, '/');
1923 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1925 fputs(prefix
, stdout
);
1926 fputs(special
, stdout
);
1927 fputs(name
, stdout
);
1928 fputc('\n', stdout
);
1930 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1932 pp
= strappend(prefix
, special
);
1936 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1946 static int dump_extra_processes(
1950 OutputFlags flags
) {
1952 _cleanup_free_ pid_t
*pids
= NULL
;
1953 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1954 struct CGroupInfo
*cg
;
1955 size_t n_allocated
= 0, n
= 0, k
;
1959 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1960 * combined, sorted, linear list. */
1962 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1970 if (hashmap_isempty(cg
->pids
))
1973 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1977 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1980 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1981 pids
[n
++] = PTR_TO_PID(pidp
);
1983 r
= hashmap_put(names
, pidp
, (void*) name
);
1992 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1993 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1995 for (k
= 0; k
< n
; k
++) {
1996 _cleanup_free_
char *e
= NULL
;
1999 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
2002 if (n_columns
!= 0) {
2005 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
2007 e
= ellipsize(name
, z
, 100);
2012 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
2014 special_glyph(TRIANGULAR_BULLET
),
2022 int unit_show_processes(
2025 const char *cgroup_path
,
2029 sd_bus_error
*error
) {
2031 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2032 Hashmap
*cgroups
= NULL
;
2033 struct CGroupInfo
*cg
;
2039 if (flags
& OUTPUT_FULL_WIDTH
)
2041 else if (n_columns
<= 0)
2042 n_columns
= columns();
2044 prefix
= strempty(prefix
);
2046 r
= sd_bus_call_method(
2048 "org.freedesktop.systemd1",
2049 "/org/freedesktop/systemd1",
2050 "org.freedesktop.systemd1.Manager",
2059 cgroups
= hashmap_new(&string_hash_ops
);
2063 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
2068 const char *path
= NULL
, *name
= NULL
;
2071 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
2077 r
= add_process(cgroups
, path
, pid
, name
);
2082 r
= sd_bus_message_exit_container(reply
);
2086 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
2090 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
2093 while ((cg
= hashmap_first(cgroups
)))
2094 remove_cgroup(cgroups
, cg
);
2096 hashmap_free(cgroups
);