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_
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 sd_bus_message_append_array(m
, 'y', cpuset
, CPU_ALLOC_SIZE(ncpus
));
710 r
= sd_bus_message_close_container(m
);
712 } else if (streq(field
, "Nice")) {
715 r
= parse_nice(eq
, &n
);
717 return log_error_errno(r
, "Failed to parse nice value: %s", eq
);
719 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
721 } else if (streq(field
, "SystemCallFilter")) {
723 _cleanup_strv_free_
char **l
= NULL
;
733 if (whitelist
!= 0) {
734 r
= strv_extend(&l
, "@default");
740 _cleanup_free_
char *word
= NULL
;
742 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
744 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
748 r
= strv_extend(&l
, word
);
753 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
755 return bus_log_create_error(r
);
757 r
= sd_bus_message_open_container(m
, 'r', "bas");
759 return bus_log_create_error(r
);
761 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
763 return bus_log_create_error(r
);
765 r
= sd_bus_message_append_strv(m
, l
);
767 return bus_log_create_error(r
);
769 r
= sd_bus_message_close_container(m
);
771 return bus_log_create_error(r
);
773 r
= sd_bus_message_close_container(m
);
775 return bus_log_create_error(r
);
777 } else if (streq(field
, "SystemCallArchitectures")) {
780 r
= sd_bus_message_open_container(m
, 'v', "as");
782 return bus_log_create_error(r
);
784 r
= sd_bus_message_open_container(m
, 'a', "s");
786 return bus_log_create_error(r
);
789 _cleanup_free_
char *word
= NULL
;
791 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
793 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
797 r
= sd_bus_message_append_basic(m
, 's', word
);
799 return bus_log_create_error(r
);
802 r
= sd_bus_message_close_container(m
);
804 return bus_log_create_error(r
);
806 r
= sd_bus_message_close_container(m
);
808 } else if (streq(field
, "SystemCallErrorNumber")) {
813 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
815 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
817 } else if (streq(field
, "RestrictAddressFamilies")) {
819 _cleanup_strv_free_
char **l
= NULL
;
829 _cleanup_free_
char *word
= NULL
;
831 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
833 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
837 r
= strv_extend(&l
, word
);
842 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
844 return bus_log_create_error(r
);
846 r
= sd_bus_message_open_container(m
, 'r', "bas");
848 return bus_log_create_error(r
);
850 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
852 return bus_log_create_error(r
);
854 r
= sd_bus_message_append_strv(m
, l
);
856 return bus_log_create_error(r
);
858 r
= sd_bus_message_close_container(m
);
860 return bus_log_create_error(r
);
862 r
= sd_bus_message_close_container(m
);
864 return bus_log_create_error(r
);
866 } else if (streq(field
, "FileDescriptorStoreMax")) {
869 r
= safe_atou(eq
, &u
);
871 return log_error_errno(r
, "Failed to parse file descriptor store limit: %s", eq
);
873 r
= sd_bus_message_append(m
, "v", "u", (uint32_t) u
);
875 } else if (streq(field
, "IOSchedulingClass")) {
878 c
= ioprio_class_from_string(eq
);
880 return log_error_errno(r
, "Failed to parse IO scheduling class: %s", eq
);
882 r
= sd_bus_message_append(m
, "v", "i", (int32_t) c
);
884 } else if (streq(field
, "IOSchedulingPriority")) {
887 r
= ioprio_parse_priority(eq
, &q
);
889 return log_error_errno(r
, "Failed to parse IO scheduling priority: %s", eq
);
891 r
= sd_bus_message_append(m
, "v", "i", (int32_t) q
);
893 } else if (STR_IN_SET(field
, "Environment", "UnsetEnvironment", "PassEnvironment")) {
896 r
= sd_bus_message_open_container(m
, 'v', "as");
898 return bus_log_create_error(r
);
900 r
= sd_bus_message_open_container(m
, 'a', "s");
902 return bus_log_create_error(r
);
905 _cleanup_free_
char *word
= NULL
;
907 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
909 return log_error_errno(r
, "Failed to parse Environment value %s: %m", eq
);
913 if (streq(field
, "Environment")) {
914 if (!env_assignment_is_valid(word
)) {
915 log_error("Invalid environment assignment: %s", word
);
918 } else if (streq(field
, "UnsetEnvironment")) {
919 if (!env_assignment_is_valid(word
) && !env_name_is_valid(word
)) {
920 log_error("Invalid environment name or assignment: %s", word
);
923 } else { /* PassEnvironment */
924 if (!env_name_is_valid(word
)) {
925 log_error("Invalid environment variable name: %s", word
);
930 r
= sd_bus_message_append_basic(m
, 's', word
);
932 return bus_log_create_error(r
);
935 r
= sd_bus_message_close_container(m
);
937 return bus_log_create_error(r
);
939 r
= sd_bus_message_close_container(m
);
941 } else if (streq(field
, "KillSignal")) {
944 sig
= signal_from_string_try_harder(eq
);
946 log_error("Failed to parse %s value %s.", field
, eq
);
950 r
= sd_bus_message_append(m
, "v", "i", sig
);
952 } else if (streq(field
, "TimerSlackNSec")) {
955 r
= parse_nsec(eq
, &n
);
957 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
959 r
= sd_bus_message_append(m
, "v", "t", n
);
960 } else if (streq(field
, "OOMScoreAdjust")) {
963 r
= safe_atoi(eq
, &oa
);
965 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
967 if (!oom_score_adjust_is_valid(oa
)) {
968 log_error("OOM score adjust value out of range");
972 r
= sd_bus_message_append(m
, "v", "i", oa
);
973 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
974 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
977 r
= sd_bus_message_open_container(m
, 'v', "as");
979 return bus_log_create_error(r
);
981 r
= sd_bus_message_open_container(m
, 'a', "s");
983 return bus_log_create_error(r
);
986 _cleanup_free_
char *word
= NULL
;
989 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
991 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
995 if (!utf8_is_valid(word
)) {
996 log_error("Failed to parse %s value %s", field
, eq
);
1000 offset
= word
[0] == '-';
1001 offset
+= word
[offset
] == '+';
1003 if (!path_is_absolute(word
+ offset
)) {
1004 log_error("Failed to parse %s value %s", field
, eq
);
1008 path_kill_slashes(word
+ offset
);
1010 r
= sd_bus_message_append_basic(m
, 's', word
);
1012 return bus_log_create_error(r
);
1015 r
= sd_bus_message_close_container(m
);
1017 return bus_log_create_error(r
);
1019 r
= sd_bus_message_close_container(m
);
1021 } else if (streq(field
, "SupplementaryGroups")) {
1024 r
= sd_bus_message_open_container(m
, 'v', "as");
1026 return bus_log_create_error(r
);
1028 r
= sd_bus_message_open_container(m
, 'a', "s");
1030 return bus_log_create_error(r
);
1033 _cleanup_free_
char *word
= NULL
;
1035 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1037 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
1041 if (!valid_user_group_name_or_id(word
)) {
1042 log_error("Failed to parse %s value %s", field
, eq
);
1046 r
= sd_bus_message_append_basic(m
, 's', word
);
1048 return bus_log_create_error(r
);
1051 r
= sd_bus_message_close_container(m
);
1053 return bus_log_create_error(r
);
1055 r
= sd_bus_message_close_container(m
);
1057 } else if (STR_IN_SET(field
, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) {
1060 r
= parse_mode(eq
, &mode
);
1062 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
1064 r
= sd_bus_message_append(m
, "v", "u", mode
);
1066 } else if (STR_IN_SET(field
, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
1069 r
= sd_bus_message_open_container(m
, 'v', "as");
1071 return bus_log_create_error(r
);
1073 r
= sd_bus_message_open_container(m
, 'a', "s");
1075 return bus_log_create_error(r
);
1078 _cleanup_free_
char *word
= NULL
;
1080 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1084 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
1088 r
= sd_bus_message_append_basic(m
, 's', word
);
1090 return bus_log_create_error(r
);
1093 r
= sd_bus_message_close_container(m
);
1095 return bus_log_create_error(r
);
1097 r
= sd_bus_message_close_container(m
);
1099 } else if (streq(field
, "RestrictNamespaces")) {
1100 bool invert
= false;
1101 unsigned long flags
= 0;
1108 r
= parse_boolean(eq
);
1112 flags
= NAMESPACE_FLAGS_ALL
;
1114 r
= namespace_flag_from_string_many(eq
, &flags
);
1116 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
1120 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
1122 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
1123 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
1124 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
1125 else if (streq(field
, "MountFlags")) {
1128 r
= mount_propagation_flags_from_string(eq
, &f
);
1130 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
1132 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
1133 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
1136 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
1140 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
1145 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
1146 char *s
= NULL
, *d
= NULL
;
1147 bool ignore_enoent
= false;
1148 uint64_t flags
= MS_REC
;
1150 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1152 return log_error_errno(r
, "Failed to parse argument: %m");
1158 ignore_enoent
= true;
1162 if (p
&& p
[-1] == ':') {
1163 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1165 return log_error_errno(r
, "Failed to parse argument: %m");
1167 log_error("Missing argument after ':': %s", eq
);
1173 if (p
&& p
[-1] == ':') {
1174 _cleanup_free_
char *options
= NULL
;
1176 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
1178 return log_error_errno(r
, "Failed to parse argument: %m");
1180 if (isempty(options
) || streq(options
, "rbind"))
1182 else if (streq(options
, "norbind"))
1185 log_error("Unknown options: %s", eq
);
1193 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
1198 r
= sd_bus_message_close_container(m
);
1202 r
= sd_bus_message_close_container(m
);
1204 } else if (STR_IN_SET(field
, "ExecStartPre", "ExecStart", "ExecStartPost",
1205 "ExecReload", "ExecStop", "ExecStopPost")) {
1207 bool ignore_failure
= false, explicit_path
= false, done
= false;
1208 _cleanup_strv_free_
char **l
= NULL
;
1209 _cleanup_free_
char *path
= NULL
;
1218 ignore_failure
= true;
1227 explicit_path
= true;
1234 /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */
1235 log_error("Sorry, but +, ! and !! are currently not supported for transient services.");
1244 if (explicit_path
) {
1245 r
= extract_first_word(&eq
, &path
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
1247 return log_error_errno(r
, "Failed to parse path: %m");
1250 r
= strv_split_extract(&l
, eq
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
1252 return log_error_errno(r
, "Failed to parse command line: %m");
1254 r
= sd_bus_message_open_container(m
, 'v', "a(sasb)");
1258 r
= sd_bus_message_open_container(m
, 'a', "(sasb)");
1262 if (strv_length(l
) > 0) {
1264 r
= sd_bus_message_open_container(m
, 'r', "sasb");
1268 r
= sd_bus_message_append(m
, "s", path
?: l
[0]);
1272 r
= sd_bus_message_append_strv(m
, l
);
1276 r
= sd_bus_message_append(m
, "b", ignore_failure
);
1280 r
= sd_bus_message_close_container(m
);
1285 r
= sd_bus_message_close_container(m
);
1289 r
= sd_bus_message_close_container(m
);
1292 log_error("Unknown assignment: %s", assignment
);
1298 return bus_log_create_error(r
);
1300 r
= sd_bus_message_close_container(m
);
1302 return bus_log_create_error(r
);
1307 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
1313 STRV_FOREACH(i
, l
) {
1314 r
= bus_append_unit_property_assignment(m
, *i
);
1322 typedef struct BusWaitForJobs
{
1329 sd_bus_slot
*slot_job_removed
;
1330 sd_bus_slot
*slot_disconnected
;
1333 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1336 log_error("Warning! D-Bus connection terminated.");
1337 sd_bus_close(sd_bus_message_get_bus(m
));
1342 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1343 const char *path
, *unit
, *result
;
1344 BusWaitForJobs
*d
= userdata
;
1352 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1354 bus_log_parse_error(r
);
1358 found
= set_remove(d
->jobs
, (char*) path
);
1364 if (!isempty(result
))
1365 d
->result
= strdup(result
);
1368 d
->name
= strdup(unit
);
1373 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1377 set_free_free(d
->jobs
);
1379 sd_bus_slot_unref(d
->slot_disconnected
);
1380 sd_bus_slot_unref(d
->slot_job_removed
);
1382 sd_bus_unref(d
->bus
);
1390 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1391 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1397 d
= new0(BusWaitForJobs
, 1);
1401 d
->bus
= sd_bus_ref(bus
);
1403 /* When we are a bus client we match by sender. Direct
1404 * connections OTOH have no initialized sender field, and
1405 * hence we ignore the sender then */
1406 r
= sd_bus_add_match(
1408 &d
->slot_job_removed
,
1411 "sender='org.freedesktop.systemd1',"
1412 "interface='org.freedesktop.systemd1.Manager',"
1413 "member='JobRemoved',"
1414 "path='/org/freedesktop/systemd1'" :
1416 "interface='org.freedesktop.systemd1.Manager',"
1417 "member='JobRemoved',"
1418 "path='/org/freedesktop/systemd1'",
1419 match_job_removed
, d
);
1423 r
= sd_bus_add_match(
1425 &d
->slot_disconnected
,
1427 "sender='org.freedesktop.DBus.Local',"
1428 "interface='org.freedesktop.DBus.Local',"
1429 "member='Disconnected'",
1430 match_disconnected
, d
);
1440 static int bus_process_wait(sd_bus
*bus
) {
1444 r
= sd_bus_process(bus
, NULL
);
1450 r
= sd_bus_wait(bus
, (uint64_t) -1);
1456 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1457 _cleanup_free_
char *dbus_path
= NULL
;
1463 if (!endswith(d
->name
, ".service"))
1466 dbus_path
= unit_dbus_path_from_name(d
->name
);
1470 return sd_bus_get_property_string(d
->bus
,
1471 "org.freedesktop.systemd1",
1473 "org.freedesktop.systemd1.Service",
1479 static const struct {
1480 const char *result
, *explanation
;
1481 } explanations
[] = {
1482 { "resources", "of unavailable resources or another system error" },
1483 { "protocol", "the service did not take the steps required by its unit configuration" },
1484 { "timeout", "a timeout was exceeded" },
1485 { "exit-code", "the control process exited with error code" },
1486 { "signal", "a fatal signal was delivered to the control process" },
1487 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1488 { "watchdog", "the service failed to send watchdog ping" },
1489 { "start-limit", "start of the service was attempted too often" }
1492 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1493 _cleanup_free_
char *service_shell_quoted
= NULL
;
1494 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1498 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1500 if (!strv_isempty((char**) extra_args
)) {
1501 _cleanup_free_
char *t
;
1503 t
= strv_join((char**) extra_args
, " ");
1504 systemctl
= strjoina("systemctl ", t
? : "<args>");
1505 journalctl
= strjoina("journalctl ", t
? : "<args>");
1508 if (!isempty(result
)) {
1511 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1512 if (streq(result
, explanations
[i
].result
))
1515 if (i
< ELEMENTSOF(explanations
)) {
1516 log_error("Job for %s failed because %s.\n"
1517 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1519 explanations
[i
].explanation
,
1521 service_shell_quoted
?: "<service>",
1527 log_error("Job for %s failed.\n"
1528 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1531 service_shell_quoted
?: "<service>",
1535 /* For some results maybe additional explanation is required */
1536 if (streq_ptr(result
, "start-limit"))
1537 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1538 "followed by \"%1$s start %2$s\" again.",
1540 service_shell_quoted
?: "<service>");
1543 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1549 if (streq(d
->result
, "canceled"))
1550 log_error("Job for %s canceled.", strna(d
->name
));
1551 else if (streq(d
->result
, "timeout"))
1552 log_error("Job for %s timed out.", strna(d
->name
));
1553 else if (streq(d
->result
, "dependency"))
1554 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1555 else if (streq(d
->result
, "invalid"))
1556 log_error("%s is not active, cannot reload.", strna(d
->name
));
1557 else if (streq(d
->result
, "assert"))
1558 log_error("Assertion failed on job for %s.", strna(d
->name
));
1559 else if (streq(d
->result
, "unsupported"))
1560 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1561 else if (streq(d
->result
, "collected"))
1562 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1563 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1565 _cleanup_free_
char *result
= NULL
;
1568 q
= bus_job_get_service_result(d
, &result
);
1570 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1572 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1574 log_error("Job failed. See \"journalctl -xe\" for details.");
1578 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1580 else if (streq(d
->result
, "timeout"))
1582 else if (streq(d
->result
, "dependency"))
1584 else if (streq(d
->result
, "invalid"))
1586 else if (streq(d
->result
, "assert"))
1588 else if (streq(d
->result
, "unsupported"))
1590 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1596 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1601 while (!set_isempty(d
->jobs
)) {
1604 q
= bus_process_wait(d
->bus
);
1606 return log_error_errno(q
, "Failed to wait for response: %m");
1609 q
= check_wait_response(d
, quiet
, extra_args
);
1610 /* Return the first error as it is most likely to be
1612 if (q
< 0 && r
== 0)
1615 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1618 d
->name
= mfree(d
->name
);
1619 d
->result
= mfree(d
->result
);
1625 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1630 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1634 return set_put_strdup(d
->jobs
, path
);
1637 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1640 r
= bus_wait_for_jobs_add(d
, path
);
1644 return bus_wait_for_jobs(d
, quiet
, NULL
);
1647 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1648 const char *type
, *path
, *source
;
1651 /* changes is dereferenced when calling unit_file_dump_changes() later,
1652 * so we have to make sure this is not NULL. */
1656 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1658 return bus_log_parse_error(r
);
1660 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1661 /* We expect only "success" changes to be sent over the bus.
1662 Hence, reject anything negative. */
1663 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1666 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1670 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1675 return bus_log_parse_error(r
);
1677 r
= sd_bus_message_exit_container(m
);
1679 return bus_log_parse_error(r
);
1681 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, quiet
);
1687 bool is_const
; /* If false, cgroup_path should be free()'d */
1689 Hashmap
*pids
; /* PID → process name */
1692 struct CGroupInfo
*parent
;
1693 LIST_FIELDS(struct CGroupInfo
, siblings
);
1694 LIST_HEAD(struct CGroupInfo
, children
);
1698 static bool IS_ROOT(const char *p
) {
1699 return isempty(p
) || streq(p
, "/");
1702 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1703 struct CGroupInfo
*parent
= NULL
, *cg
;
1712 cg
= hashmap_get(cgroups
, path
);
1718 if (!IS_ROOT(path
)) {
1721 e
= strrchr(path
, '/');
1725 pp
= strndupa(path
, e
- path
);
1729 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1734 cg
= new0(struct CGroupInfo
, 1);
1739 cg
->cgroup_path
= (char*) path
;
1741 cg
->cgroup_path
= strdup(path
);
1742 if (!cg
->cgroup_path
) {
1748 cg
->is_const
= is_const
;
1749 cg
->parent
= parent
;
1751 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1754 free(cg
->cgroup_path
);
1760 LIST_PREPEND(siblings
, parent
->children
, cg
);
1761 parent
->n_children
++;
1768 static int add_process(
1774 struct CGroupInfo
*cg
;
1781 r
= add_cgroup(cgroups
, path
, true, &cg
);
1785 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1789 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1792 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1796 while (cg
->children
)
1797 remove_cgroup(cgroups
, cg
->children
);
1799 hashmap_remove(cgroups
, cg
->cgroup_path
);
1802 free(cg
->cgroup_path
);
1804 hashmap_free(cg
->pids
);
1807 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1812 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1813 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1818 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1821 static int dump_processes(
1823 const char *cgroup_path
,
1826 OutputFlags flags
) {
1828 struct CGroupInfo
*cg
;
1833 if (IS_ROOT(cgroup_path
))
1836 cg
= hashmap_get(cgroups
, cgroup_path
);
1840 if (!hashmap_isempty(cg
->pids
)) {
1848 /* Order processes by their PID */
1849 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1851 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1852 pids
[n
++] = PTR_TO_PID(pidp
);
1854 assert(n
== hashmap_size(cg
->pids
));
1855 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1857 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1859 for (i
= 0; i
< n
; i
++) {
1860 _cleanup_free_
char *e
= NULL
;
1861 const char *special
;
1864 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1867 if (n_columns
!= 0) {
1870 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1872 e
= ellipsize(name
, k
, 100);
1877 more
= i
+1 < n
|| cg
->children
;
1878 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1880 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1889 struct CGroupInfo
**children
, *child
;
1892 /* Order subcgroups by their name */
1893 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1894 LIST_FOREACH(siblings
, child
, cg
->children
)
1895 children
[n
++] = child
;
1896 assert(n
== cg
->n_children
);
1897 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1900 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1902 for (i
= 0; i
< n
; i
++) {
1903 _cleanup_free_
char *pp
= NULL
;
1904 const char *name
, *special
;
1907 child
= children
[i
];
1909 name
= strrchr(child
->cgroup_path
, '/');
1915 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1917 fputs(prefix
, stdout
);
1918 fputs(special
, stdout
);
1919 fputs(name
, stdout
);
1920 fputc('\n', stdout
);
1922 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1924 pp
= strappend(prefix
, special
);
1928 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1938 static int dump_extra_processes(
1942 OutputFlags flags
) {
1944 _cleanup_free_ pid_t
*pids
= NULL
;
1945 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1946 struct CGroupInfo
*cg
;
1947 size_t n_allocated
= 0, n
= 0, k
;
1951 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1952 * combined, sorted, linear list. */
1954 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1962 if (hashmap_isempty(cg
->pids
))
1965 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1969 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1972 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1973 pids
[n
++] = PTR_TO_PID(pidp
);
1975 r
= hashmap_put(names
, pidp
, (void*) name
);
1984 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1985 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1987 for (k
= 0; k
< n
; k
++) {
1988 _cleanup_free_
char *e
= NULL
;
1991 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1994 if (n_columns
!= 0) {
1997 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1999 e
= ellipsize(name
, z
, 100);
2004 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
2006 special_glyph(TRIANGULAR_BULLET
),
2014 int unit_show_processes(
2017 const char *cgroup_path
,
2021 sd_bus_error
*error
) {
2023 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2024 Hashmap
*cgroups
= NULL
;
2025 struct CGroupInfo
*cg
;
2031 if (flags
& OUTPUT_FULL_WIDTH
)
2033 else if (n_columns
<= 0)
2034 n_columns
= columns();
2036 prefix
= strempty(prefix
);
2038 r
= sd_bus_call_method(
2040 "org.freedesktop.systemd1",
2041 "/org/freedesktop/systemd1",
2042 "org.freedesktop.systemd1.Manager",
2051 cgroups
= hashmap_new(&string_hash_ops
);
2055 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
2060 const char *path
= NULL
, *name
= NULL
;
2063 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
2069 r
= add_process(cgroups
, path
, pid
, name
);
2074 r
= sd_bus_message_exit_container(reply
);
2078 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
2082 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
2085 while ((cg
= hashmap_first(cgroups
)))
2086 remove_cgroup(cgroups
, cg
);
2088 hashmap_free(cgroups
);