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 r
= sd_bus_message_append_array(m
, 'y', cpuset
, CPU_ALLOC_SIZE(ncpus
));
710 return bus_log_create_error(r
);
712 r
= sd_bus_message_close_container(m
);
714 } else if (streq(field
, "Nice")) {
717 r
= parse_nice(eq
, &n
);
719 return log_error_errno(r
, "Failed to parse nice value: %s", eq
);
721 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
723 } else if (streq(field
, "SystemCallFilter")) {
725 _cleanup_strv_free_
char **l
= NULL
;
735 if (whitelist
!= 0) {
736 r
= strv_extend(&l
, "@default");
742 _cleanup_free_
char *word
= NULL
;
744 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
746 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
750 r
= strv_extend(&l
, word
);
755 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
757 return bus_log_create_error(r
);
759 r
= sd_bus_message_open_container(m
, 'r', "bas");
761 return bus_log_create_error(r
);
763 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
765 return bus_log_create_error(r
);
767 r
= sd_bus_message_append_strv(m
, l
);
769 return bus_log_create_error(r
);
771 r
= sd_bus_message_close_container(m
);
773 return bus_log_create_error(r
);
775 r
= sd_bus_message_close_container(m
);
777 return bus_log_create_error(r
);
779 } else if (streq(field
, "SystemCallArchitectures")) {
782 r
= sd_bus_message_open_container(m
, 'v', "as");
784 return bus_log_create_error(r
);
786 r
= sd_bus_message_open_container(m
, 'a', "s");
788 return bus_log_create_error(r
);
791 _cleanup_free_
char *word
= NULL
;
793 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
795 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
799 r
= sd_bus_message_append_basic(m
, 's', word
);
801 return bus_log_create_error(r
);
804 r
= sd_bus_message_close_container(m
);
806 return bus_log_create_error(r
);
808 r
= sd_bus_message_close_container(m
);
810 } else if (streq(field
, "SystemCallErrorNumber")) {
815 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
817 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
819 } else if (streq(field
, "RestrictAddressFamilies")) {
821 _cleanup_strv_free_
char **l
= NULL
;
831 _cleanup_free_
char *word
= NULL
;
833 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
835 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
839 r
= strv_extend(&l
, word
);
844 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
846 return bus_log_create_error(r
);
848 r
= sd_bus_message_open_container(m
, 'r', "bas");
850 return bus_log_create_error(r
);
852 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
854 return bus_log_create_error(r
);
856 r
= sd_bus_message_append_strv(m
, l
);
858 return bus_log_create_error(r
);
860 r
= sd_bus_message_close_container(m
);
862 return bus_log_create_error(r
);
864 r
= sd_bus_message_close_container(m
);
866 return bus_log_create_error(r
);
868 } else if (streq(field
, "FileDescriptorStoreMax")) {
871 r
= safe_atou(eq
, &u
);
873 return log_error_errno(r
, "Failed to parse file descriptor store limit: %s", eq
);
875 r
= sd_bus_message_append(m
, "v", "u", (uint32_t) u
);
877 } else if (streq(field
, "IOSchedulingClass")) {
880 c
= ioprio_class_from_string(eq
);
882 return log_error_errno(r
, "Failed to parse IO scheduling class: %s", eq
);
884 r
= sd_bus_message_append(m
, "v", "i", (int32_t) c
);
886 } else if (streq(field
, "IOSchedulingPriority")) {
889 r
= ioprio_parse_priority(eq
, &q
);
891 return log_error_errno(r
, "Failed to parse IO scheduling priority: %s", eq
);
893 r
= sd_bus_message_append(m
, "v", "i", (int32_t) q
);
895 } else if (STR_IN_SET(field
, "Environment", "UnsetEnvironment", "PassEnvironment")) {
898 r
= sd_bus_message_open_container(m
, 'v', "as");
900 return bus_log_create_error(r
);
902 r
= sd_bus_message_open_container(m
, 'a', "s");
904 return bus_log_create_error(r
);
907 _cleanup_free_
char *word
= NULL
;
909 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
911 return log_error_errno(r
, "Failed to parse Environment value %s: %m", eq
);
915 if (streq(field
, "Environment")) {
916 if (!env_assignment_is_valid(word
)) {
917 log_error("Invalid environment assignment: %s", word
);
920 } else if (streq(field
, "UnsetEnvironment")) {
921 if (!env_assignment_is_valid(word
) && !env_name_is_valid(word
)) {
922 log_error("Invalid environment name or assignment: %s", word
);
925 } else { /* PassEnvironment */
926 if (!env_name_is_valid(word
)) {
927 log_error("Invalid environment variable name: %s", word
);
932 r
= sd_bus_message_append_basic(m
, 's', word
);
934 return bus_log_create_error(r
);
937 r
= sd_bus_message_close_container(m
);
939 return bus_log_create_error(r
);
941 r
= sd_bus_message_close_container(m
);
943 } else if (streq(field
, "KillSignal")) {
946 sig
= signal_from_string_try_harder(eq
);
948 log_error("Failed to parse %s value %s.", field
, eq
);
952 r
= sd_bus_message_append(m
, "v", "i", sig
);
954 } else if (streq(field
, "TimerSlackNSec")) {
957 r
= parse_nsec(eq
, &n
);
959 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
961 r
= sd_bus_message_append(m
, "v", "t", n
);
962 } else if (streq(field
, "OOMScoreAdjust")) {
965 r
= safe_atoi(eq
, &oa
);
967 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
969 if (!oom_score_adjust_is_valid(oa
)) {
970 log_error("OOM score adjust value out of range");
974 r
= sd_bus_message_append(m
, "v", "i", oa
);
975 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
976 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
979 r
= sd_bus_message_open_container(m
, 'v', "as");
981 return bus_log_create_error(r
);
983 r
= sd_bus_message_open_container(m
, 'a', "s");
985 return bus_log_create_error(r
);
988 _cleanup_free_
char *word
= NULL
;
991 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
993 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
997 if (!utf8_is_valid(word
)) {
998 log_error("Failed to parse %s value %s", field
, eq
);
1002 offset
= word
[0] == '-';
1003 offset
+= word
[offset
] == '+';
1005 if (!path_is_absolute(word
+ offset
)) {
1006 log_error("Failed to parse %s value %s", field
, eq
);
1010 path_kill_slashes(word
+ offset
);
1012 r
= sd_bus_message_append_basic(m
, 's', word
);
1014 return bus_log_create_error(r
);
1017 r
= sd_bus_message_close_container(m
);
1019 return bus_log_create_error(r
);
1021 r
= sd_bus_message_close_container(m
);
1023 } else if (streq(field
, "SupplementaryGroups")) {
1026 r
= sd_bus_message_open_container(m
, 'v', "as");
1028 return bus_log_create_error(r
);
1030 r
= sd_bus_message_open_container(m
, 'a', "s");
1032 return bus_log_create_error(r
);
1035 _cleanup_free_
char *word
= NULL
;
1037 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1039 return log_error_errno(r
, "Failed to parse %s value %s: %m", field
, eq
);
1043 if (!valid_user_group_name_or_id(word
)) {
1044 log_error("Failed to parse %s value %s", field
, eq
);
1048 r
= sd_bus_message_append_basic(m
, 's', word
);
1050 return bus_log_create_error(r
);
1053 r
= sd_bus_message_close_container(m
);
1055 return bus_log_create_error(r
);
1057 r
= sd_bus_message_close_container(m
);
1059 } else if (STR_IN_SET(field
, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) {
1062 r
= parse_mode(eq
, &mode
);
1064 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
1066 r
= sd_bus_message_append(m
, "v", "u", mode
);
1068 } else if (STR_IN_SET(field
, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
1071 r
= sd_bus_message_open_container(m
, 'v', "as");
1073 return bus_log_create_error(r
);
1075 r
= sd_bus_message_open_container(m
, 'a', "s");
1077 return bus_log_create_error(r
);
1080 _cleanup_free_
char *word
= NULL
;
1082 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1086 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
1090 r
= sd_bus_message_append_basic(m
, 's', word
);
1092 return bus_log_create_error(r
);
1095 r
= sd_bus_message_close_container(m
);
1097 return bus_log_create_error(r
);
1099 r
= sd_bus_message_close_container(m
);
1101 } else if (streq(field
, "RestrictNamespaces")) {
1102 bool invert
= false;
1103 unsigned long flags
= 0;
1110 r
= parse_boolean(eq
);
1114 flags
= NAMESPACE_FLAGS_ALL
;
1116 r
= namespace_flag_from_string_many(eq
, &flags
);
1118 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
1122 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
1124 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
1125 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
1126 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
1127 else if (streq(field
, "MountFlags")) {
1130 r
= mount_propagation_flags_from_string(eq
, &f
);
1132 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
1134 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
1135 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
1138 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
1142 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
1147 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
1148 char *s
= NULL
, *d
= NULL
;
1149 bool ignore_enoent
= false;
1150 uint64_t flags
= MS_REC
;
1152 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1154 return log_error_errno(r
, "Failed to parse argument: %m");
1160 ignore_enoent
= true;
1164 if (p
&& p
[-1] == ':') {
1165 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1167 return log_error_errno(r
, "Failed to parse argument: %m");
1169 log_error("Missing argument after ':': %s", eq
);
1175 if (p
&& p
[-1] == ':') {
1176 _cleanup_free_
char *options
= NULL
;
1178 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
1180 return log_error_errno(r
, "Failed to parse argument: %m");
1182 if (isempty(options
) || streq(options
, "rbind"))
1184 else if (streq(options
, "norbind"))
1187 log_error("Unknown options: %s", eq
);
1195 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
1200 r
= sd_bus_message_close_container(m
);
1204 r
= sd_bus_message_close_container(m
);
1206 } else if (STR_IN_SET(field
, "ExecStartPre", "ExecStart", "ExecStartPost",
1207 "ExecReload", "ExecStop", "ExecStopPost")) {
1209 bool ignore_failure
= false, explicit_path
= false, done
= false;
1210 _cleanup_strv_free_
char **l
= NULL
;
1211 _cleanup_free_
char *path
= NULL
;
1220 ignore_failure
= true;
1229 explicit_path
= true;
1236 /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */
1237 log_error("Sorry, but +, ! and !! are currently not supported for transient services.");
1246 if (explicit_path
) {
1247 r
= extract_first_word(&eq
, &path
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
1249 return log_error_errno(r
, "Failed to parse path: %m");
1252 r
= strv_split_extract(&l
, eq
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
1254 return log_error_errno(r
, "Failed to parse command line: %m");
1256 r
= sd_bus_message_open_container(m
, 'v', "a(sasb)");
1260 r
= sd_bus_message_open_container(m
, 'a', "(sasb)");
1264 if (strv_length(l
) > 0) {
1266 r
= sd_bus_message_open_container(m
, 'r', "sasb");
1270 r
= sd_bus_message_append(m
, "s", path
?: l
[0]);
1274 r
= sd_bus_message_append_strv(m
, l
);
1278 r
= sd_bus_message_append(m
, "b", ignore_failure
);
1282 r
= sd_bus_message_close_container(m
);
1287 r
= sd_bus_message_close_container(m
);
1291 r
= sd_bus_message_close_container(m
);
1294 log_error("Unknown assignment: %s", assignment
);
1300 return bus_log_create_error(r
);
1302 r
= sd_bus_message_close_container(m
);
1304 return bus_log_create_error(r
);
1309 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
1315 STRV_FOREACH(i
, l
) {
1316 r
= bus_append_unit_property_assignment(m
, *i
);
1324 typedef struct BusWaitForJobs
{
1331 sd_bus_slot
*slot_job_removed
;
1332 sd_bus_slot
*slot_disconnected
;
1335 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1338 log_error("Warning! D-Bus connection terminated.");
1339 sd_bus_close(sd_bus_message_get_bus(m
));
1344 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1345 const char *path
, *unit
, *result
;
1346 BusWaitForJobs
*d
= userdata
;
1354 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1356 bus_log_parse_error(r
);
1360 found
= set_remove(d
->jobs
, (char*) path
);
1366 if (!isempty(result
))
1367 d
->result
= strdup(result
);
1370 d
->name
= strdup(unit
);
1375 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1379 set_free_free(d
->jobs
);
1381 sd_bus_slot_unref(d
->slot_disconnected
);
1382 sd_bus_slot_unref(d
->slot_job_removed
);
1384 sd_bus_unref(d
->bus
);
1392 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1393 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1399 d
= new0(BusWaitForJobs
, 1);
1403 d
->bus
= sd_bus_ref(bus
);
1405 /* When we are a bus client we match by sender. Direct
1406 * connections OTOH have no initialized sender field, and
1407 * hence we ignore the sender then */
1408 r
= sd_bus_add_match(
1410 &d
->slot_job_removed
,
1413 "sender='org.freedesktop.systemd1',"
1414 "interface='org.freedesktop.systemd1.Manager',"
1415 "member='JobRemoved',"
1416 "path='/org/freedesktop/systemd1'" :
1418 "interface='org.freedesktop.systemd1.Manager',"
1419 "member='JobRemoved',"
1420 "path='/org/freedesktop/systemd1'",
1421 match_job_removed
, d
);
1425 r
= sd_bus_add_match(
1427 &d
->slot_disconnected
,
1429 "sender='org.freedesktop.DBus.Local',"
1430 "interface='org.freedesktop.DBus.Local',"
1431 "member='Disconnected'",
1432 match_disconnected
, d
);
1442 static int bus_process_wait(sd_bus
*bus
) {
1446 r
= sd_bus_process(bus
, NULL
);
1452 r
= sd_bus_wait(bus
, (uint64_t) -1);
1458 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1459 _cleanup_free_
char *dbus_path
= NULL
;
1465 if (!endswith(d
->name
, ".service"))
1468 dbus_path
= unit_dbus_path_from_name(d
->name
);
1472 return sd_bus_get_property_string(d
->bus
,
1473 "org.freedesktop.systemd1",
1475 "org.freedesktop.systemd1.Service",
1481 static const struct {
1482 const char *result
, *explanation
;
1483 } explanations
[] = {
1484 { "resources", "of unavailable resources or another system error" },
1485 { "protocol", "the service did not take the steps required by its unit configuration" },
1486 { "timeout", "a timeout was exceeded" },
1487 { "exit-code", "the control process exited with error code" },
1488 { "signal", "a fatal signal was delivered to the control process" },
1489 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1490 { "watchdog", "the service failed to send watchdog ping" },
1491 { "start-limit", "start of the service was attempted too often" }
1494 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1495 _cleanup_free_
char *service_shell_quoted
= NULL
;
1496 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1500 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1502 if (!strv_isempty((char**) extra_args
)) {
1503 _cleanup_free_
char *t
;
1505 t
= strv_join((char**) extra_args
, " ");
1506 systemctl
= strjoina("systemctl ", t
? : "<args>");
1507 journalctl
= strjoina("journalctl ", t
? : "<args>");
1510 if (!isempty(result
)) {
1513 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1514 if (streq(result
, explanations
[i
].result
))
1517 if (i
< ELEMENTSOF(explanations
)) {
1518 log_error("Job for %s failed because %s.\n"
1519 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1521 explanations
[i
].explanation
,
1523 service_shell_quoted
?: "<service>",
1529 log_error("Job for %s failed.\n"
1530 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1533 service_shell_quoted
?: "<service>",
1537 /* For some results maybe additional explanation is required */
1538 if (streq_ptr(result
, "start-limit"))
1539 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1540 "followed by \"%1$s start %2$s\" again.",
1542 service_shell_quoted
?: "<service>");
1545 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1551 if (streq(d
->result
, "canceled"))
1552 log_error("Job for %s canceled.", strna(d
->name
));
1553 else if (streq(d
->result
, "timeout"))
1554 log_error("Job for %s timed out.", strna(d
->name
));
1555 else if (streq(d
->result
, "dependency"))
1556 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1557 else if (streq(d
->result
, "invalid"))
1558 log_error("%s is not active, cannot reload.", strna(d
->name
));
1559 else if (streq(d
->result
, "assert"))
1560 log_error("Assertion failed on job for %s.", strna(d
->name
));
1561 else if (streq(d
->result
, "unsupported"))
1562 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1563 else if (streq(d
->result
, "collected"))
1564 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1565 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1567 _cleanup_free_
char *result
= NULL
;
1570 q
= bus_job_get_service_result(d
, &result
);
1572 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1574 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1576 log_error("Job failed. See \"journalctl -xe\" for details.");
1580 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1582 else if (streq(d
->result
, "timeout"))
1584 else if (streq(d
->result
, "dependency"))
1586 else if (streq(d
->result
, "invalid"))
1588 else if (streq(d
->result
, "assert"))
1590 else if (streq(d
->result
, "unsupported"))
1592 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1598 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1603 while (!set_isempty(d
->jobs
)) {
1606 q
= bus_process_wait(d
->bus
);
1608 return log_error_errno(q
, "Failed to wait for response: %m");
1611 q
= check_wait_response(d
, quiet
, extra_args
);
1612 /* Return the first error as it is most likely to be
1614 if (q
< 0 && r
== 0)
1617 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1620 d
->name
= mfree(d
->name
);
1621 d
->result
= mfree(d
->result
);
1627 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1632 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1636 return set_put_strdup(d
->jobs
, path
);
1639 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1642 r
= bus_wait_for_jobs_add(d
, path
);
1646 return bus_wait_for_jobs(d
, quiet
, NULL
);
1649 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1650 const char *type
, *path
, *source
;
1653 /* changes is dereferenced when calling unit_file_dump_changes() later,
1654 * so we have to make sure this is not NULL. */
1658 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1660 return bus_log_parse_error(r
);
1662 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1663 /* We expect only "success" changes to be sent over the bus.
1664 Hence, reject anything negative. */
1665 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1668 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1672 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1677 return bus_log_parse_error(r
);
1679 r
= sd_bus_message_exit_container(m
);
1681 return bus_log_parse_error(r
);
1683 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, quiet
);
1689 bool is_const
; /* If false, cgroup_path should be free()'d */
1691 Hashmap
*pids
; /* PID → process name */
1694 struct CGroupInfo
*parent
;
1695 LIST_FIELDS(struct CGroupInfo
, siblings
);
1696 LIST_HEAD(struct CGroupInfo
, children
);
1700 static bool IS_ROOT(const char *p
) {
1701 return isempty(p
) || streq(p
, "/");
1704 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1705 struct CGroupInfo
*parent
= NULL
, *cg
;
1714 cg
= hashmap_get(cgroups
, path
);
1720 if (!IS_ROOT(path
)) {
1723 e
= strrchr(path
, '/');
1727 pp
= strndupa(path
, e
- path
);
1731 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1736 cg
= new0(struct CGroupInfo
, 1);
1741 cg
->cgroup_path
= (char*) path
;
1743 cg
->cgroup_path
= strdup(path
);
1744 if (!cg
->cgroup_path
) {
1750 cg
->is_const
= is_const
;
1751 cg
->parent
= parent
;
1753 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1756 free(cg
->cgroup_path
);
1762 LIST_PREPEND(siblings
, parent
->children
, cg
);
1763 parent
->n_children
++;
1770 static int add_process(
1776 struct CGroupInfo
*cg
;
1783 r
= add_cgroup(cgroups
, path
, true, &cg
);
1787 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1791 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1794 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1798 while (cg
->children
)
1799 remove_cgroup(cgroups
, cg
->children
);
1801 hashmap_remove(cgroups
, cg
->cgroup_path
);
1804 free(cg
->cgroup_path
);
1806 hashmap_free(cg
->pids
);
1809 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1814 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1815 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1820 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1823 static int dump_processes(
1825 const char *cgroup_path
,
1828 OutputFlags flags
) {
1830 struct CGroupInfo
*cg
;
1835 if (IS_ROOT(cgroup_path
))
1838 cg
= hashmap_get(cgroups
, cgroup_path
);
1842 if (!hashmap_isempty(cg
->pids
)) {
1850 /* Order processes by their PID */
1851 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1853 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1854 pids
[n
++] = PTR_TO_PID(pidp
);
1856 assert(n
== hashmap_size(cg
->pids
));
1857 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1859 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1861 for (i
= 0; i
< n
; i
++) {
1862 _cleanup_free_
char *e
= NULL
;
1863 const char *special
;
1866 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1869 if (n_columns
!= 0) {
1872 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1874 e
= ellipsize(name
, k
, 100);
1879 more
= i
+1 < n
|| cg
->children
;
1880 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1882 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1891 struct CGroupInfo
**children
, *child
;
1894 /* Order subcgroups by their name */
1895 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1896 LIST_FOREACH(siblings
, child
, cg
->children
)
1897 children
[n
++] = child
;
1898 assert(n
== cg
->n_children
);
1899 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1902 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1904 for (i
= 0; i
< n
; i
++) {
1905 _cleanup_free_
char *pp
= NULL
;
1906 const char *name
, *special
;
1909 child
= children
[i
];
1911 name
= strrchr(child
->cgroup_path
, '/');
1917 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1919 fputs(prefix
, stdout
);
1920 fputs(special
, stdout
);
1921 fputs(name
, stdout
);
1922 fputc('\n', stdout
);
1924 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1926 pp
= strappend(prefix
, special
);
1930 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1940 static int dump_extra_processes(
1944 OutputFlags flags
) {
1946 _cleanup_free_ pid_t
*pids
= NULL
;
1947 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1948 struct CGroupInfo
*cg
;
1949 size_t n_allocated
= 0, n
= 0, k
;
1953 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1954 * combined, sorted, linear list. */
1956 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1964 if (hashmap_isempty(cg
->pids
))
1967 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1971 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1974 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1975 pids
[n
++] = PTR_TO_PID(pidp
);
1977 r
= hashmap_put(names
, pidp
, (void*) name
);
1986 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1987 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1989 for (k
= 0; k
< n
; k
++) {
1990 _cleanup_free_
char *e
= NULL
;
1993 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1996 if (n_columns
!= 0) {
1999 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
2001 e
= ellipsize(name
, z
, 100);
2006 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
2008 special_glyph(TRIANGULAR_BULLET
),
2016 int unit_show_processes(
2019 const char *cgroup_path
,
2023 sd_bus_error
*error
) {
2025 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2026 Hashmap
*cgroups
= NULL
;
2027 struct CGroupInfo
*cg
;
2033 if (flags
& OUTPUT_FULL_WIDTH
)
2035 else if (n_columns
<= 0)
2036 n_columns
= columns();
2038 prefix
= strempty(prefix
);
2040 r
= sd_bus_call_method(
2042 "org.freedesktop.systemd1",
2043 "/org/freedesktop/systemd1",
2044 "org.freedesktop.systemd1.Manager",
2053 cgroups
= hashmap_new(&string_hash_ops
);
2057 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
2062 const char *path
= NULL
, *name
= NULL
;
2065 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
2071 r
= add_process(cgroups
, path
, pid
, name
);
2076 r
= sd_bus_message_exit_container(reply
);
2080 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
2084 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
2087 while ((cg
= hashmap_first(cgroups
)))
2088 remove_cgroup(cgroups
, cg
);
2090 hashmap_free(cgroups
);