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")) {
137 r
= sd_bus_message_append(m
, "sv", "EnvironmentFiles", "a(sb)", 1,
138 eq
[0] == '-' ? eq
+ 1 : eq
,
142 } else if (STR_IN_SET(field
, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) {
147 r
= parse_sec(eq
, &t
);
149 return log_error_errno(r
, "Failed to parse %s= parameter: %s", field
, eq
);
152 n
= newa(char, l
+ 2);
156 /* Change suffix Sec → USec */
157 strcpy(mempcpy(n
, field
, l
- 3), "USec");
158 r
= sd_bus_message_append(m
, "sv", n
, "t", t
);
161 } else if (streq(field
, "LogExtraFields")) {
163 r
= sd_bus_message_append(m
, "s", "LogExtraFields");
167 r
= sd_bus_message_open_container(m
, 'v', "aay");
171 r
= sd_bus_message_open_container(m
, 'a', "ay");
175 r
= sd_bus_message_append_array(m
, 'y', eq
, strlen(eq
));
179 r
= sd_bus_message_close_container(m
);
183 r
= sd_bus_message_close_container(m
);
186 } else if (STR_IN_SET(field
, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) {
189 if (isempty(eq
) || streq(eq
, "infinity"))
190 bytes
= CGROUP_LIMIT_MAX
;
192 r
= parse_percent(eq
);
196 /* When this is a percentage we'll convert this into a relative value in the range
197 * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
198 * ones). This way the physical memory size can be determined server-side */
200 n
= strjoina(field
, "Scale");
201 r
= sd_bus_message_append(m
, "sv", n
, "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
205 r
= parse_size(eq
, 1024, &bytes
);
207 return log_error_errno(r
, "Failed to parse bytes specification %s", assignment
);
211 r
= sd_bus_message_append(m
, "sv", field
, "t", bytes
);
214 } else if (streq(field
, "Delegate")) {
216 r
= parse_boolean(eq
);
220 r
= sd_bus_message_append(m
, "s", "DelegateControllers");
224 r
= sd_bus_message_open_container(m
, 'v', "as");
228 r
= sd_bus_message_open_container(m
, 'a', "s");
233 _cleanup_free_
char *word
= NULL
;
235 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
241 return log_error_errno(r
, "Invalid syntax: %s", eq
);
243 r
= sd_bus_message_append(m
, "s", word
);
248 r
= sd_bus_message_close_container(m
);
252 r
= sd_bus_message_close_container(m
);
254 r
= sd_bus_message_append(m
, "sv", "Delegate", "b", r
);
258 } else if (streq(field
, "TasksMax")) {
261 if (isempty(eq
) || streq(eq
, "infinity"))
264 r
= parse_percent(eq
);
266 r
= sd_bus_message_append(m
, "sv", "TasksMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
269 r
= safe_atou64(eq
, &t
);
271 return log_error_errno(r
, "Failed to parse maximum tasks specification %s", assignment
);
276 r
= sd_bus_message_append(m
, "sv", "TasksMax", "t", t
);
279 } else if (STR_IN_SET(field
, "StandardInput", "StandardOutput", "StandardError")) {
280 const char *n
, *appended
;
282 n
= startswith(eq
, "fd:");
284 appended
= strjoina(field
, "FileDescriptorName");
285 r
= sd_bus_message_append(m
, "sv", appended
, "s", n
);
287 } else if ((n
= startswith(eq
, "file:"))) {
288 appended
= strjoina(field
, "File");
289 r
= sd_bus_message_append(m
, "sv", appended
, "s", n
);
291 r
= sd_bus_message_append(m
, "sv", field
, "s", eq
);
295 } else if (streq(field
, "StandardInputText")) {
296 _cleanup_free_
char *unescaped
= NULL
;
298 r
= cunescape(eq
, 0, &unescaped
);
300 return log_error_errno(r
, "Failed to unescape text '%s': %m", eq
);
302 if (!strextend(&unescaped
, "\n", NULL
))
305 /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
306 * interface anyway */
308 r
= sd_bus_message_append(m
, "s", "StandardInputData");
310 return bus_log_create_error(r
);
312 r
= sd_bus_message_open_container(m
, 'v', "ay");
314 return bus_log_create_error(r
);
316 r
= sd_bus_message_append_array(m
, 'y', unescaped
, strlen(unescaped
));
318 return bus_log_create_error(r
);
320 r
= sd_bus_message_close_container(m
);
324 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
326 return bus_log_create_error(r
);
328 rl
= rlimit_from_string(field
);
333 r
= rlimit_parse(rl
, eq
, &l
);
335 return log_error_errno(r
, "Failed to parse resource limit: %s", eq
);
337 r
= sd_bus_message_append(m
, "v", "t", l
.rlim_max
);
339 return bus_log_create_error(r
);
341 r
= sd_bus_message_close_container(m
);
343 return bus_log_create_error(r
);
345 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
347 return bus_log_create_error(r
);
349 sn
= strjoina(field
, "Soft");
350 r
= sd_bus_message_append(m
, "sv", sn
, "t", l
.rlim_cur
);
352 } else if (STR_IN_SET(field
,
353 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
354 "TasksAccounting", "IPAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem",
355 "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
356 "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
357 "NoNewPrivileges", "SyslogLevelPrefix", "RemainAfterElapse",
358 "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
359 "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS",
360 "CPUSchedulingResetOnFork", "LockPersonality")) {
362 r
= parse_boolean(eq
);
364 return log_error_errno(r
, "Failed to parse boolean assignment %s.", assignment
);
366 r
= sd_bus_message_append(m
, "v", "b", r
);
368 } else if (STR_IN_SET(field
, "CPUWeight", "StartupCPUWeight")) {
371 r
= cg_weight_parse(eq
, &u
);
373 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
375 r
= sd_bus_message_append(m
, "v", "t", u
);
377 } else if (STR_IN_SET(field
, "CPUShares", "StartupCPUShares")) {
380 r
= cg_cpu_shares_parse(eq
, &u
);
382 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
384 r
= sd_bus_message_append(m
, "v", "t", u
);
386 } else if (STR_IN_SET(field
, "IOWeight", "StartupIOWeight")) {
389 r
= cg_weight_parse(eq
, &u
);
391 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
393 r
= sd_bus_message_append(m
, "v", "t", u
);
395 } else if (STR_IN_SET(field
, "BlockIOWeight", "StartupBlockIOWeight")) {
398 r
= cg_blkio_weight_parse(eq
, &u
);
400 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
402 r
= sd_bus_message_append(m
, "v", "t", u
);
404 } else if (STR_IN_SET(field
,
405 "User", "Group", "DevicePolicy", "KillMode",
406 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
407 "Description", "Slice", "Type", "WorkingDirectory",
408 "RootDirectory", "SyslogIdentifier", "ProtectSystem",
409 "ProtectHome", "SELinuxContext", "Restart", "RootImage",
410 "NotifyAccess", "RuntimeDirectoryPreserve", "Personality",
411 "KeyringMode", "CollectMode", "FailureAction", "SuccessAction"))
412 r
= sd_bus_message_append(m
, "v", "s", eq
);
414 else if (streq(field
, "StandardInputData")) {
415 _cleanup_free_
char *cleaned
= NULL
;
416 _cleanup_free_
void *decoded
= NULL
;
419 cleaned
= strdup(eq
);
423 delete_chars(cleaned
, WHITESPACE
);
425 r
= unbase64mem(cleaned
, (size_t) -1, &decoded
, &sz
);
427 return log_error_errno(r
, "Failed to decode base64 data '%s': %m", cleaned
);
429 r
= sd_bus_message_open_container(m
, 'v', "ay");
431 return bus_log_create_error(r
);
433 r
= sd_bus_message_append_array(m
, 'y', decoded
, sz
);
435 return bus_log_create_error(r
);
437 r
= sd_bus_message_close_container(m
);
439 } else if (STR_IN_SET(field
, "AppArmorProfile", "SmackProcessLabel")) {
451 r
= sd_bus_message_append(m
, "v", "(bs)", ignore
, s
);
453 } else if (STR_IN_SET(field
, "SyslogLevel", "LogLevelMax")) {
456 level
= log_level_from_string(eq
);
458 log_error("Failed to parse %s value %s.", field
, eq
);
462 r
= sd_bus_message_append(m
, "v", "i", level
);
464 } else if (streq(field
, "SyslogFacility")) {
467 facility
= log_facility_unshifted_from_string(eq
);
469 log_error("Failed to parse %s value %s.", field
, eq
);
473 r
= sd_bus_message_append(m
, "v", "i", facility
);
475 } else if (streq(field
, "SecureBits")) {
477 r
= secure_bits_from_string(eq
);
479 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
481 r
= sd_bus_message_append(m
, "v", "i", r
);
483 } else if (STR_IN_SET(field
, "CapabilityBoundingSet", "AmbientCapabilities")) {
494 r
= capability_set_from_string(p
, &sum
);
496 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
498 sum
= invert
? ~sum
: sum
;
500 r
= sd_bus_message_append(m
, "v", "t", sum
);
502 } else if (streq(field
, "DeviceAllow")) {
505 r
= sd_bus_message_append(m
, "v", "a(ss)", 0);
507 const char *path
, *rwm
, *e
;
511 path
= strndupa(eq
, e
- eq
);
518 if (!is_deviceallow_pattern(path
)) {
519 log_error("%s is not a device file in /dev.", path
);
523 r
= sd_bus_message_append(m
, "v", "a(ss)", 1, path
, rwm
);
526 } else if (cgroup_io_limit_type_from_string(field
) >= 0 || STR_IN_SET(field
, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
529 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
531 const char *path
, *bandwidth
, *e
;
536 path
= strndupa(eq
, e
- eq
);
539 log_error("Failed to parse %s value %s.", field
, eq
);
543 if (!path_startswith(path
, "/dev")) {
544 log_error("%s is not a device file in /dev.", path
);
548 if (streq(bandwidth
, "infinity")) {
549 bytes
= CGROUP_LIMIT_MAX
;
551 r
= parse_size(bandwidth
, 1000, &bytes
);
553 return log_error_errno(r
, "Failed to parse byte value %s: %m", bandwidth
);
556 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, bytes
);
559 } else if (STR_IN_SET(field
, "IODeviceWeight", "BlockIODeviceWeight")) {
562 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
564 const char *path
, *weight
, *e
;
569 path
= strndupa(eq
, e
- eq
);
572 log_error("Failed to parse %s value %s.", field
, eq
);
576 if (!path_startswith(path
, "/dev")) {
577 log_error("%s is not a device file in /dev.", path
);
581 r
= safe_atou64(weight
, &u
);
583 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, weight
);
585 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, u
);
588 } else if (STR_IN_SET(field
, "IPAddressAllow", "IPAddressDeny")) {
591 r
= sd_bus_message_append(m
, "v", "a(iayu)", 0);
593 unsigned char prefixlen
;
594 union in_addr_union prefix
= {};
597 r
= sd_bus_message_open_container(m
, 'v', "a(iayu)");
599 return bus_log_create_error(r
);
601 r
= sd_bus_message_open_container(m
, 'a', "(iayu)");
603 return bus_log_create_error(r
);
605 if (streq(eq
, "any")) {
606 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
608 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 0);
610 return bus_log_create_error(r
);
612 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 0);
614 return bus_log_create_error(r
);
616 } else if (is_localhost(eq
)) {
617 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
619 prefix
.in
.s_addr
= htobe32(0x7f000000);
620 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 8);
622 return bus_log_create_error(r
);
624 prefix
.in6
= (struct in6_addr
) IN6ADDR_LOOPBACK_INIT
;
625 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 128);
629 } else if (streq(eq
, "link-local")) {
631 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
633 prefix
.in
.s_addr
= htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
634 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 16);
636 return bus_log_create_error(r
);
638 prefix
.in6
= (struct in6_addr
) {
639 .s6_addr32
[0] = htobe32(0xfe800000)
641 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 64);
643 return bus_log_create_error(r
);
645 } else if (streq(eq
, "multicast")) {
647 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
649 prefix
.in
.s_addr
= htobe32((UINT32_C(224) << 24));
650 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 4);
652 return bus_log_create_error(r
);
654 prefix
.in6
= (struct in6_addr
) {
655 .s6_addr32
[0] = htobe32(0xff000000)
657 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 8);
659 return bus_log_create_error(r
);
662 r
= in_addr_prefix_from_string_auto(eq
, &family
, &prefix
, &prefixlen
);
664 return log_error_errno(r
, "Failed to parse IP address prefix: %s", eq
);
666 r
= bus_append_ip_address_access(m
, family
, &prefix
, prefixlen
);
668 return bus_log_create_error(r
);
671 r
= sd_bus_message_close_container(m
);
673 return bus_log_create_error(r
);
675 r
= sd_bus_message_close_container(m
);
677 return bus_log_create_error(r
);
680 } else if (streq(field
, "CPUSchedulingPolicy")) {
683 n
= sched_policy_from_string(eq
);
685 return log_error_errno(r
, "Failed to parse CPUSchedulingPolicy: %s", eq
);
687 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
689 } else if (streq(field
, "CPUSchedulingPriority")) {
692 r
= safe_atoi(eq
, &n
);
694 return log_error_errno(r
, "Failed to parse CPUSchedulingPriority: %s", eq
);
695 if (!sched_priority_is_valid(n
))
696 return log_error_errno(r
, "Invalid CPUSchedulingPriority: %s", eq
);
698 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
700 } else if (streq(field
, "CPUAffinity")) {
701 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
704 ncpus
= parse_cpu_set(eq
, &cpuset
);
706 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
708 r
= sd_bus_message_open_container(m
, 'v', "ay");
710 return bus_log_create_error(r
);
713 sd_bus_message_append_array(m
, 'y', cpuset
, CPU_ALLOC_SIZE(ncpus
));
715 r
= sd_bus_message_close_container(m
);
717 } else if (streq(field
, "Nice")) {
720 r
= parse_nice(eq
, &n
);
722 return log_error_errno(r
, "Failed to parse nice value: %s", eq
);
724 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
726 } else if (streq(field
, "SystemCallFilter")) {
728 _cleanup_strv_free_
char **l
= NULL
;
738 if (whitelist
!= 0) {
739 r
= strv_extend(&l
, "@default");
745 _cleanup_free_
char *word
= NULL
;
747 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
749 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
753 r
= strv_extend(&l
, word
);
758 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
760 return bus_log_create_error(r
);
762 r
= sd_bus_message_open_container(m
, 'r', "bas");
764 return bus_log_create_error(r
);
766 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
768 return bus_log_create_error(r
);
770 r
= sd_bus_message_append_strv(m
, l
);
772 return bus_log_create_error(r
);
774 r
= sd_bus_message_close_container(m
);
776 return bus_log_create_error(r
);
778 r
= sd_bus_message_close_container(m
);
780 return bus_log_create_error(r
);
782 } else if (streq(field
, "SystemCallArchitectures")) {
785 r
= sd_bus_message_open_container(m
, 'v', "as");
787 return bus_log_create_error(r
);
789 r
= sd_bus_message_open_container(m
, 'a', "s");
791 return bus_log_create_error(r
);
794 _cleanup_free_
char *word
= NULL
;
796 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
798 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
802 r
= sd_bus_message_append_basic(m
, 's', word
);
804 return bus_log_create_error(r
);
807 r
= sd_bus_message_close_container(m
);
809 return bus_log_create_error(r
);
811 r
= sd_bus_message_close_container(m
);
813 } else if (streq(field
, "SystemCallErrorNumber")) {
818 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
820 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
822 } else if (streq(field
, "RestrictAddressFamilies")) {
824 _cleanup_strv_free_
char **l
= NULL
;
834 _cleanup_free_
char *word
= NULL
;
836 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
838 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
842 r
= strv_extend(&l
, word
);
847 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
849 return bus_log_create_error(r
);
851 r
= sd_bus_message_open_container(m
, 'r', "bas");
853 return bus_log_create_error(r
);
855 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
857 return bus_log_create_error(r
);
859 r
= sd_bus_message_append_strv(m
, l
);
861 return bus_log_create_error(r
);
863 r
= sd_bus_message_close_container(m
);
865 return bus_log_create_error(r
);
867 r
= sd_bus_message_close_container(m
);
869 return bus_log_create_error(r
);
871 } else if (streq(field
, "FileDescriptorStoreMax")) {
874 r
= safe_atou(eq
, &u
);
876 return log_error_errno(r
, "Failed to parse file descriptor store limit: %s", eq
);
878 r
= sd_bus_message_append(m
, "v", "u", (uint32_t) u
);
880 } else if (streq(field
, "IOSchedulingClass")) {
883 c
= ioprio_class_from_string(eq
);
885 return log_error_errno(r
, "Failed to parse IO scheduling class: %s", eq
);
887 r
= sd_bus_message_append(m
, "v", "i", (int32_t) c
);
889 } else if (streq(field
, "IOSchedulingPriority")) {
892 r
= ioprio_parse_priority(eq
, &q
);
894 return log_error_errno(r
, "Failed to parse IO scheduling priority: %s", eq
);
896 r
= sd_bus_message_append(m
, "v", "i", (int32_t) q
);
898 } else if (STR_IN_SET(field
, "Environment", "UnsetEnvironment", "PassEnvironment")) {
901 r
= sd_bus_message_open_container(m
, 'v', "as");
903 return bus_log_create_error(r
);
905 r
= sd_bus_message_open_container(m
, 'a', "s");
907 return bus_log_create_error(r
);
910 _cleanup_free_
char *word
= NULL
;
912 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
914 return log_error_errno(r
, "Failed to parse Environment value %s: %m", eq
);
918 if (streq(field
, "Environment")) {
919 if (!env_assignment_is_valid(word
)) {
920 log_error("Invalid environment assignment: %s", word
);
923 } else if (streq(field
, "UnsetEnvironment")) {
924 if (!env_assignment_is_valid(word
) && !env_name_is_valid(word
)) {
925 log_error("Invalid environment name or assignment: %s", word
);
928 } else { /* PassEnvironment */
929 if (!env_name_is_valid(word
)) {
930 log_error("Invalid environment variable name: %s", word
);
935 r
= sd_bus_message_append_basic(m
, 's', word
);
937 return bus_log_create_error(r
);
940 r
= sd_bus_message_close_container(m
);
942 return bus_log_create_error(r
);
944 r
= sd_bus_message_close_container(m
);
946 } else if (streq(field
, "KillSignal")) {
949 sig
= signal_from_string_try_harder(eq
);
951 log_error("Failed to parse %s value %s.", field
, eq
);
955 r
= sd_bus_message_append(m
, "v", "i", sig
);
957 } else if (streq(field
, "TimerSlackNSec")) {
960 r
= parse_nsec(eq
, &n
);
962 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
964 r
= sd_bus_message_append(m
, "v", "t", n
);
965 } else if (streq(field
, "OOMScoreAdjust")) {
968 r
= safe_atoi(eq
, &oa
);
970 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
972 if (!oom_score_adjust_is_valid(oa
)) {
973 log_error("OOM score adjust value out of range");
977 r
= sd_bus_message_append(m
, "v", "i", oa
);
978 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
979 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
982 r
= sd_bus_message_open_container(m
, 'v', "as");
984 return bus_log_create_error(r
);
986 r
= sd_bus_message_open_container(m
, 'a', "s");
988 return bus_log_create_error(r
);
991 _cleanup_free_
char *word
= NULL
;
994 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
996 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
1000 if (!utf8_is_valid(word
)) {
1001 log_error("Failed to parse %s value %s", field
, eq
);
1005 offset
= word
[0] == '-';
1006 offset
+= word
[offset
] == '+';
1008 if (!path_is_absolute(word
+ offset
)) {
1009 log_error("Failed to parse %s value %s", field
, eq
);
1013 path_kill_slashes(word
+ offset
);
1015 r
= sd_bus_message_append_basic(m
, 's', word
);
1017 return bus_log_create_error(r
);
1020 r
= sd_bus_message_close_container(m
);
1022 return bus_log_create_error(r
);
1024 r
= sd_bus_message_close_container(m
);
1026 } else if (streq(field
, "SupplementaryGroups")) {
1029 r
= sd_bus_message_open_container(m
, 'v', "as");
1031 return bus_log_create_error(r
);
1033 r
= sd_bus_message_open_container(m
, 'a', "s");
1035 return bus_log_create_error(r
);
1038 _cleanup_free_
char *word
= NULL
;
1040 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1042 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
1046 if (!valid_user_group_name_or_id(word
)) {
1047 log_error("Failed to parse %s value %s", field
, eq
);
1051 r
= sd_bus_message_append_basic(m
, 's', word
);
1053 return bus_log_create_error(r
);
1056 r
= sd_bus_message_close_container(m
);
1058 return bus_log_create_error(r
);
1060 r
= sd_bus_message_close_container(m
);
1062 } else if (STR_IN_SET(field
, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) {
1065 r
= parse_mode(eq
, &mode
);
1067 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
1069 r
= sd_bus_message_append(m
, "v", "u", mode
);
1071 } else if (STR_IN_SET(field
, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
1074 r
= sd_bus_message_open_container(m
, 'v', "as");
1076 return bus_log_create_error(r
);
1078 r
= sd_bus_message_open_container(m
, 'a', "s");
1080 return bus_log_create_error(r
);
1083 _cleanup_free_
char *word
= NULL
;
1085 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1089 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
1093 r
= sd_bus_message_append_basic(m
, 's', word
);
1095 return bus_log_create_error(r
);
1098 r
= sd_bus_message_close_container(m
);
1100 return bus_log_create_error(r
);
1102 r
= sd_bus_message_close_container(m
);
1104 } else if (streq(field
, "RestrictNamespaces")) {
1105 bool invert
= false;
1106 unsigned long flags
= 0;
1113 r
= parse_boolean(eq
);
1117 flags
= NAMESPACE_FLAGS_ALL
;
1119 r
= namespace_flag_from_string_many(eq
, &flags
);
1121 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
1125 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
1127 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
1128 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
1129 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
1130 else if (streq(field
, "MountFlags")) {
1133 r
= mount_propagation_flags_from_string(eq
, &f
);
1135 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
1137 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
1138 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
1141 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
1145 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
1150 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
1151 char *s
= NULL
, *d
= NULL
;
1152 bool ignore_enoent
= false;
1153 uint64_t flags
= MS_REC
;
1155 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1157 return log_error_errno(r
, "Failed to parse argument: %m");
1163 ignore_enoent
= true;
1167 if (p
&& p
[-1] == ':') {
1168 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1170 return log_error_errno(r
, "Failed to parse argument: %m");
1172 log_error("Missing argument after ':': %s", eq
);
1178 if (p
&& p
[-1] == ':') {
1179 _cleanup_free_
char *options
= NULL
;
1181 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
1183 return log_error_errno(r
, "Failed to parse argument: %m");
1185 if (isempty(options
) || streq(options
, "rbind"))
1187 else if (streq(options
, "norbind"))
1190 log_error("Unknown options: %s", eq
);
1198 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
1203 r
= sd_bus_message_close_container(m
);
1207 r
= sd_bus_message_close_container(m
);
1209 } else if (STR_IN_SET(field
, "ExecStartPre", "ExecStart", "ExecStartPost",
1210 "ExecReload", "ExecStop", "ExecStopPost")) {
1212 bool ignore_failure
= false, explicit_path
= false, done
= false;
1213 _cleanup_strv_free_
char **l
= NULL
;
1214 _cleanup_free_
char *path
= NULL
;
1223 ignore_failure
= true;
1232 explicit_path
= true;
1239 /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */
1240 log_error("Sorry, but +, ! and !! are currently not supported for transient services.");
1249 if (explicit_path
) {
1250 r
= extract_first_word(&eq
, &path
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
1252 return log_error_errno(r
, "Failed to parse path: %m");
1255 r
= strv_split_extract(&l
, eq
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
1257 return log_error_errno(r
, "Failed to parse command line: %m");
1259 r
= sd_bus_message_open_container(m
, 'v', "a(sasb)");
1263 r
= sd_bus_message_open_container(m
, 'a', "(sasb)");
1267 if (strv_length(l
) > 0) {
1269 r
= sd_bus_message_open_container(m
, 'r', "sasb");
1273 r
= sd_bus_message_append(m
, "s", path
?: l
[0]);
1277 r
= sd_bus_message_append_strv(m
, l
);
1281 r
= sd_bus_message_append(m
, "b", ignore_failure
);
1285 r
= sd_bus_message_close_container(m
);
1290 r
= sd_bus_message_close_container(m
);
1294 r
= sd_bus_message_close_container(m
);
1297 log_error("Unknown assignment %s.", assignment
);
1303 return bus_log_create_error(r
);
1305 r
= sd_bus_message_close_container(m
);
1307 return bus_log_create_error(r
);
1312 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
1318 STRV_FOREACH(i
, l
) {
1319 r
= bus_append_unit_property_assignment(m
, *i
);
1327 typedef struct BusWaitForJobs
{
1334 sd_bus_slot
*slot_job_removed
;
1335 sd_bus_slot
*slot_disconnected
;
1338 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1341 log_error("Warning! D-Bus connection terminated.");
1342 sd_bus_close(sd_bus_message_get_bus(m
));
1347 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1348 const char *path
, *unit
, *result
;
1349 BusWaitForJobs
*d
= userdata
;
1357 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1359 bus_log_parse_error(r
);
1363 found
= set_remove(d
->jobs
, (char*) path
);
1369 if (!isempty(result
))
1370 d
->result
= strdup(result
);
1373 d
->name
= strdup(unit
);
1378 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1382 set_free_free(d
->jobs
);
1384 sd_bus_slot_unref(d
->slot_disconnected
);
1385 sd_bus_slot_unref(d
->slot_job_removed
);
1387 sd_bus_unref(d
->bus
);
1395 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1396 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1402 d
= new0(BusWaitForJobs
, 1);
1406 d
->bus
= sd_bus_ref(bus
);
1408 /* When we are a bus client we match by sender. Direct
1409 * connections OTOH have no initialized sender field, and
1410 * hence we ignore the sender then */
1411 r
= sd_bus_add_match(
1413 &d
->slot_job_removed
,
1416 "sender='org.freedesktop.systemd1',"
1417 "interface='org.freedesktop.systemd1.Manager',"
1418 "member='JobRemoved',"
1419 "path='/org/freedesktop/systemd1'" :
1421 "interface='org.freedesktop.systemd1.Manager',"
1422 "member='JobRemoved',"
1423 "path='/org/freedesktop/systemd1'",
1424 match_job_removed
, d
);
1428 r
= sd_bus_add_match(
1430 &d
->slot_disconnected
,
1432 "sender='org.freedesktop.DBus.Local',"
1433 "interface='org.freedesktop.DBus.Local',"
1434 "member='Disconnected'",
1435 match_disconnected
, d
);
1445 static int bus_process_wait(sd_bus
*bus
) {
1449 r
= sd_bus_process(bus
, NULL
);
1455 r
= sd_bus_wait(bus
, (uint64_t) -1);
1461 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1462 _cleanup_free_
char *dbus_path
= NULL
;
1468 if (!endswith(d
->name
, ".service"))
1471 dbus_path
= unit_dbus_path_from_name(d
->name
);
1475 return sd_bus_get_property_string(d
->bus
,
1476 "org.freedesktop.systemd1",
1478 "org.freedesktop.systemd1.Service",
1484 static const struct {
1485 const char *result
, *explanation
;
1486 } explanations
[] = {
1487 { "resources", "of unavailable resources or another system error" },
1488 { "protocol", "the service did not take the steps required by its unit configuration" },
1489 { "timeout", "a timeout was exceeded" },
1490 { "exit-code", "the control process exited with error code" },
1491 { "signal", "a fatal signal was delivered to the control process" },
1492 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1493 { "watchdog", "the service failed to send watchdog ping" },
1494 { "start-limit", "start of the service was attempted too often" }
1497 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1498 _cleanup_free_
char *service_shell_quoted
= NULL
;
1499 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1503 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1505 if (!strv_isempty((char**) extra_args
)) {
1506 _cleanup_free_
char *t
;
1508 t
= strv_join((char**) extra_args
, " ");
1509 systemctl
= strjoina("systemctl ", t
? : "<args>");
1510 journalctl
= strjoina("journalctl ", t
? : "<args>");
1513 if (!isempty(result
)) {
1516 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1517 if (streq(result
, explanations
[i
].result
))
1520 if (i
< ELEMENTSOF(explanations
)) {
1521 log_error("Job for %s failed because %s.\n"
1522 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1524 explanations
[i
].explanation
,
1526 service_shell_quoted
?: "<service>",
1532 log_error("Job for %s failed.\n"
1533 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1536 service_shell_quoted
?: "<service>",
1540 /* For some results maybe additional explanation is required */
1541 if (streq_ptr(result
, "start-limit"))
1542 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1543 "followed by \"%1$s start %2$s\" again.",
1545 service_shell_quoted
?: "<service>");
1548 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1554 if (streq(d
->result
, "canceled"))
1555 log_error("Job for %s canceled.", strna(d
->name
));
1556 else if (streq(d
->result
, "timeout"))
1557 log_error("Job for %s timed out.", strna(d
->name
));
1558 else if (streq(d
->result
, "dependency"))
1559 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1560 else if (streq(d
->result
, "invalid"))
1561 log_error("%s is not active, cannot reload.", strna(d
->name
));
1562 else if (streq(d
->result
, "assert"))
1563 log_error("Assertion failed on job for %s.", strna(d
->name
));
1564 else if (streq(d
->result
, "unsupported"))
1565 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1566 else if (streq(d
->result
, "collected"))
1567 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1568 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1570 _cleanup_free_
char *result
= NULL
;
1573 q
= bus_job_get_service_result(d
, &result
);
1575 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1577 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1579 log_error("Job failed. See \"journalctl -xe\" for details.");
1583 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1585 else if (streq(d
->result
, "timeout"))
1587 else if (streq(d
->result
, "dependency"))
1589 else if (streq(d
->result
, "invalid"))
1591 else if (streq(d
->result
, "assert"))
1593 else if (streq(d
->result
, "unsupported"))
1595 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1601 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1606 while (!set_isempty(d
->jobs
)) {
1609 q
= bus_process_wait(d
->bus
);
1611 return log_error_errno(q
, "Failed to wait for response: %m");
1614 q
= check_wait_response(d
, quiet
, extra_args
);
1615 /* Return the first error as it is most likely to be
1617 if (q
< 0 && r
== 0)
1620 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1623 d
->name
= mfree(d
->name
);
1624 d
->result
= mfree(d
->result
);
1630 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1635 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1639 return set_put_strdup(d
->jobs
, path
);
1642 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1645 r
= bus_wait_for_jobs_add(d
, path
);
1649 return bus_wait_for_jobs(d
, quiet
, NULL
);
1652 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1653 const char *type
, *path
, *source
;
1656 /* changes is dereferenced when calling unit_file_dump_changes() later,
1657 * so we have to make sure this is not NULL. */
1661 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1663 return bus_log_parse_error(r
);
1665 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1666 /* We expect only "success" changes to be sent over the bus.
1667 Hence, reject anything negative. */
1668 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1671 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1675 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1680 return bus_log_parse_error(r
);
1682 r
= sd_bus_message_exit_container(m
);
1684 return bus_log_parse_error(r
);
1686 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, quiet
);
1692 bool is_const
; /* If false, cgroup_path should be free()'d */
1694 Hashmap
*pids
; /* PID → process name */
1697 struct CGroupInfo
*parent
;
1698 LIST_FIELDS(struct CGroupInfo
, siblings
);
1699 LIST_HEAD(struct CGroupInfo
, children
);
1703 static bool IS_ROOT(const char *p
) {
1704 return isempty(p
) || streq(p
, "/");
1707 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1708 struct CGroupInfo
*parent
= NULL
, *cg
;
1717 cg
= hashmap_get(cgroups
, path
);
1723 if (!IS_ROOT(path
)) {
1726 e
= strrchr(path
, '/');
1730 pp
= strndupa(path
, e
- path
);
1734 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1739 cg
= new0(struct CGroupInfo
, 1);
1744 cg
->cgroup_path
= (char*) path
;
1746 cg
->cgroup_path
= strdup(path
);
1747 if (!cg
->cgroup_path
) {
1753 cg
->is_const
= is_const
;
1754 cg
->parent
= parent
;
1756 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1759 free(cg
->cgroup_path
);
1765 LIST_PREPEND(siblings
, parent
->children
, cg
);
1766 parent
->n_children
++;
1773 static int add_process(
1779 struct CGroupInfo
*cg
;
1786 r
= add_cgroup(cgroups
, path
, true, &cg
);
1790 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1794 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1797 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1801 while (cg
->children
)
1802 remove_cgroup(cgroups
, cg
->children
);
1804 hashmap_remove(cgroups
, cg
->cgroup_path
);
1807 free(cg
->cgroup_path
);
1809 hashmap_free(cg
->pids
);
1812 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1817 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1818 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1823 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1826 static int dump_processes(
1828 const char *cgroup_path
,
1831 OutputFlags flags
) {
1833 struct CGroupInfo
*cg
;
1838 if (IS_ROOT(cgroup_path
))
1841 cg
= hashmap_get(cgroups
, cgroup_path
);
1845 if (!hashmap_isempty(cg
->pids
)) {
1853 /* Order processes by their PID */
1854 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1856 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1857 pids
[n
++] = PTR_TO_PID(pidp
);
1859 assert(n
== hashmap_size(cg
->pids
));
1860 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1862 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1864 for (i
= 0; i
< n
; i
++) {
1865 _cleanup_free_
char *e
= NULL
;
1866 const char *special
;
1869 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1872 if (n_columns
!= 0) {
1875 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1877 e
= ellipsize(name
, k
, 100);
1882 more
= i
+1 < n
|| cg
->children
;
1883 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1885 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1894 struct CGroupInfo
**children
, *child
;
1897 /* Order subcgroups by their name */
1898 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1899 LIST_FOREACH(siblings
, child
, cg
->children
)
1900 children
[n
++] = child
;
1901 assert(n
== cg
->n_children
);
1902 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1905 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1907 for (i
= 0; i
< n
; i
++) {
1908 _cleanup_free_
char *pp
= NULL
;
1909 const char *name
, *special
;
1912 child
= children
[i
];
1914 name
= strrchr(child
->cgroup_path
, '/');
1920 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1922 fputs(prefix
, stdout
);
1923 fputs(special
, stdout
);
1924 fputs(name
, stdout
);
1925 fputc('\n', stdout
);
1927 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1929 pp
= strappend(prefix
, special
);
1933 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1943 static int dump_extra_processes(
1947 OutputFlags flags
) {
1949 _cleanup_free_ pid_t
*pids
= NULL
;
1950 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1951 struct CGroupInfo
*cg
;
1952 size_t n_allocated
= 0, n
= 0, k
;
1956 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1957 * combined, sorted, linear list. */
1959 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1967 if (hashmap_isempty(cg
->pids
))
1970 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1974 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1977 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1978 pids
[n
++] = PTR_TO_PID(pidp
);
1980 r
= hashmap_put(names
, pidp
, (void*) name
);
1989 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1990 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1992 for (k
= 0; k
< n
; k
++) {
1993 _cleanup_free_
char *e
= NULL
;
1996 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1999 if (n_columns
!= 0) {
2002 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
2004 e
= ellipsize(name
, z
, 100);
2009 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
2011 special_glyph(TRIANGULAR_BULLET
),
2019 int unit_show_processes(
2022 const char *cgroup_path
,
2026 sd_bus_error
*error
) {
2028 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2029 Hashmap
*cgroups
= NULL
;
2030 struct CGroupInfo
*cg
;
2036 if (flags
& OUTPUT_FULL_WIDTH
)
2038 else if (n_columns
<= 0)
2039 n_columns
= columns();
2041 prefix
= strempty(prefix
);
2043 r
= sd_bus_call_method(
2045 "org.freedesktop.systemd1",
2046 "/org/freedesktop/systemd1",
2047 "org.freedesktop.systemd1.Manager",
2056 cgroups
= hashmap_new(&string_hash_ops
);
2060 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
2065 const char *path
= NULL
, *name
= NULL
;
2068 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
2074 r
= add_process(cgroups
, path
, pid
, name
);
2079 r
= sd_bus_message_exit_container(reply
);
2083 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
2087 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
2090 while ((cg
= hashmap_first(cgroups
)))
2091 remove_cgroup(cgroups
, cg
);
2093 hashmap_free(cgroups
);