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 log_error("Unknown assignment %s.", assignment
);
1215 return bus_log_create_error(r
);
1217 r
= sd_bus_message_close_container(m
);
1219 return bus_log_create_error(r
);
1224 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
1230 STRV_FOREACH(i
, l
) {
1231 r
= bus_append_unit_property_assignment(m
, *i
);
1239 typedef struct BusWaitForJobs
{
1246 sd_bus_slot
*slot_job_removed
;
1247 sd_bus_slot
*slot_disconnected
;
1250 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1253 log_error("Warning! D-Bus connection terminated.");
1254 sd_bus_close(sd_bus_message_get_bus(m
));
1259 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1260 const char *path
, *unit
, *result
;
1261 BusWaitForJobs
*d
= userdata
;
1269 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1271 bus_log_parse_error(r
);
1275 found
= set_remove(d
->jobs
, (char*) path
);
1281 if (!isempty(result
))
1282 d
->result
= strdup(result
);
1285 d
->name
= strdup(unit
);
1290 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1294 set_free_free(d
->jobs
);
1296 sd_bus_slot_unref(d
->slot_disconnected
);
1297 sd_bus_slot_unref(d
->slot_job_removed
);
1299 sd_bus_unref(d
->bus
);
1307 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1308 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1314 d
= new0(BusWaitForJobs
, 1);
1318 d
->bus
= sd_bus_ref(bus
);
1320 /* When we are a bus client we match by sender. Direct
1321 * connections OTOH have no initialized sender field, and
1322 * hence we ignore the sender then */
1323 r
= sd_bus_add_match(
1325 &d
->slot_job_removed
,
1328 "sender='org.freedesktop.systemd1',"
1329 "interface='org.freedesktop.systemd1.Manager',"
1330 "member='JobRemoved',"
1331 "path='/org/freedesktop/systemd1'" :
1333 "interface='org.freedesktop.systemd1.Manager',"
1334 "member='JobRemoved',"
1335 "path='/org/freedesktop/systemd1'",
1336 match_job_removed
, d
);
1340 r
= sd_bus_add_match(
1342 &d
->slot_disconnected
,
1344 "sender='org.freedesktop.DBus.Local',"
1345 "interface='org.freedesktop.DBus.Local',"
1346 "member='Disconnected'",
1347 match_disconnected
, d
);
1357 static int bus_process_wait(sd_bus
*bus
) {
1361 r
= sd_bus_process(bus
, NULL
);
1367 r
= sd_bus_wait(bus
, (uint64_t) -1);
1373 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1374 _cleanup_free_
char *dbus_path
= NULL
;
1380 if (!endswith(d
->name
, ".service"))
1383 dbus_path
= unit_dbus_path_from_name(d
->name
);
1387 return sd_bus_get_property_string(d
->bus
,
1388 "org.freedesktop.systemd1",
1390 "org.freedesktop.systemd1.Service",
1396 static const struct {
1397 const char *result
, *explanation
;
1398 } explanations
[] = {
1399 { "resources", "of unavailable resources or another system error" },
1400 { "protocol", "the service did not take the steps required by its unit configuration" },
1401 { "timeout", "a timeout was exceeded" },
1402 { "exit-code", "the control process exited with error code" },
1403 { "signal", "a fatal signal was delivered to the control process" },
1404 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1405 { "watchdog", "the service failed to send watchdog ping" },
1406 { "start-limit", "start of the service was attempted too often" }
1409 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1410 _cleanup_free_
char *service_shell_quoted
= NULL
;
1411 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1415 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1417 if (!strv_isempty((char**) extra_args
)) {
1418 _cleanup_free_
char *t
;
1420 t
= strv_join((char**) extra_args
, " ");
1421 systemctl
= strjoina("systemctl ", t
? : "<args>");
1422 journalctl
= strjoina("journalctl ", t
? : "<args>");
1425 if (!isempty(result
)) {
1428 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1429 if (streq(result
, explanations
[i
].result
))
1432 if (i
< ELEMENTSOF(explanations
)) {
1433 log_error("Job for %s failed because %s.\n"
1434 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1436 explanations
[i
].explanation
,
1438 service_shell_quoted
?: "<service>",
1444 log_error("Job for %s failed.\n"
1445 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1448 service_shell_quoted
?: "<service>",
1452 /* For some results maybe additional explanation is required */
1453 if (streq_ptr(result
, "start-limit"))
1454 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1455 "followed by \"%1$s start %2$s\" again.",
1457 service_shell_quoted
?: "<service>");
1460 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1466 if (streq(d
->result
, "canceled"))
1467 log_error("Job for %s canceled.", strna(d
->name
));
1468 else if (streq(d
->result
, "timeout"))
1469 log_error("Job for %s timed out.", strna(d
->name
));
1470 else if (streq(d
->result
, "dependency"))
1471 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1472 else if (streq(d
->result
, "invalid"))
1473 log_error("%s is not active, cannot reload.", strna(d
->name
));
1474 else if (streq(d
->result
, "assert"))
1475 log_error("Assertion failed on job for %s.", strna(d
->name
));
1476 else if (streq(d
->result
, "unsupported"))
1477 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1478 else if (streq(d
->result
, "collected"))
1479 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1480 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1482 _cleanup_free_
char *result
= NULL
;
1485 q
= bus_job_get_service_result(d
, &result
);
1487 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1489 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1491 log_error("Job failed. See \"journalctl -xe\" for details.");
1495 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1497 else if (streq(d
->result
, "timeout"))
1499 else if (streq(d
->result
, "dependency"))
1501 else if (streq(d
->result
, "invalid"))
1503 else if (streq(d
->result
, "assert"))
1505 else if (streq(d
->result
, "unsupported"))
1507 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1513 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1518 while (!set_isempty(d
->jobs
)) {
1521 q
= bus_process_wait(d
->bus
);
1523 return log_error_errno(q
, "Failed to wait for response: %m");
1526 q
= check_wait_response(d
, quiet
, extra_args
);
1527 /* Return the first error as it is most likely to be
1529 if (q
< 0 && r
== 0)
1532 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1535 d
->name
= mfree(d
->name
);
1536 d
->result
= mfree(d
->result
);
1542 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1547 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1551 return set_put_strdup(d
->jobs
, path
);
1554 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1557 r
= bus_wait_for_jobs_add(d
, path
);
1561 return bus_wait_for_jobs(d
, quiet
, NULL
);
1564 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1565 const char *type
, *path
, *source
;
1568 /* changes is dereferenced when calling unit_file_dump_changes() later,
1569 * so we have to make sure this is not NULL. */
1573 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1575 return bus_log_parse_error(r
);
1577 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1578 /* We expect only "success" changes to be sent over the bus.
1579 Hence, reject anything negative. */
1580 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1583 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1587 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1592 return bus_log_parse_error(r
);
1594 r
= sd_bus_message_exit_container(m
);
1596 return bus_log_parse_error(r
);
1598 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, quiet
);
1604 bool is_const
; /* If false, cgroup_path should be free()'d */
1606 Hashmap
*pids
; /* PID → process name */
1609 struct CGroupInfo
*parent
;
1610 LIST_FIELDS(struct CGroupInfo
, siblings
);
1611 LIST_HEAD(struct CGroupInfo
, children
);
1615 static bool IS_ROOT(const char *p
) {
1616 return isempty(p
) || streq(p
, "/");
1619 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1620 struct CGroupInfo
*parent
= NULL
, *cg
;
1629 cg
= hashmap_get(cgroups
, path
);
1635 if (!IS_ROOT(path
)) {
1638 e
= strrchr(path
, '/');
1642 pp
= strndupa(path
, e
- path
);
1646 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1651 cg
= new0(struct CGroupInfo
, 1);
1656 cg
->cgroup_path
= (char*) path
;
1658 cg
->cgroup_path
= strdup(path
);
1659 if (!cg
->cgroup_path
) {
1665 cg
->is_const
= is_const
;
1666 cg
->parent
= parent
;
1668 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1671 free(cg
->cgroup_path
);
1677 LIST_PREPEND(siblings
, parent
->children
, cg
);
1678 parent
->n_children
++;
1685 static int add_process(
1691 struct CGroupInfo
*cg
;
1698 r
= add_cgroup(cgroups
, path
, true, &cg
);
1702 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1706 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1709 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1713 while (cg
->children
)
1714 remove_cgroup(cgroups
, cg
->children
);
1716 hashmap_remove(cgroups
, cg
->cgroup_path
);
1719 free(cg
->cgroup_path
);
1721 hashmap_free(cg
->pids
);
1724 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1729 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1730 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1735 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1738 static int dump_processes(
1740 const char *cgroup_path
,
1743 OutputFlags flags
) {
1745 struct CGroupInfo
*cg
;
1750 if (IS_ROOT(cgroup_path
))
1753 cg
= hashmap_get(cgroups
, cgroup_path
);
1757 if (!hashmap_isempty(cg
->pids
)) {
1765 /* Order processes by their PID */
1766 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1768 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1769 pids
[n
++] = PTR_TO_PID(pidp
);
1771 assert(n
== hashmap_size(cg
->pids
));
1772 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1774 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1776 for (i
= 0; i
< n
; i
++) {
1777 _cleanup_free_
char *e
= NULL
;
1778 const char *special
;
1781 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1784 if (n_columns
!= 0) {
1787 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1789 e
= ellipsize(name
, k
, 100);
1794 more
= i
+1 < n
|| cg
->children
;
1795 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1797 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1806 struct CGroupInfo
**children
, *child
;
1809 /* Order subcgroups by their name */
1810 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1811 LIST_FOREACH(siblings
, child
, cg
->children
)
1812 children
[n
++] = child
;
1813 assert(n
== cg
->n_children
);
1814 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1817 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1819 for (i
= 0; i
< n
; i
++) {
1820 _cleanup_free_
char *pp
= NULL
;
1821 const char *name
, *special
;
1824 child
= children
[i
];
1826 name
= strrchr(child
->cgroup_path
, '/');
1832 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1834 fputs(prefix
, stdout
);
1835 fputs(special
, stdout
);
1836 fputs(name
, stdout
);
1837 fputc('\n', stdout
);
1839 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1841 pp
= strappend(prefix
, special
);
1845 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1855 static int dump_extra_processes(
1859 OutputFlags flags
) {
1861 _cleanup_free_ pid_t
*pids
= NULL
;
1862 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1863 struct CGroupInfo
*cg
;
1864 size_t n_allocated
= 0, n
= 0, k
;
1868 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1869 * combined, sorted, linear list. */
1871 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1879 if (hashmap_isempty(cg
->pids
))
1882 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1886 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1889 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1890 pids
[n
++] = PTR_TO_PID(pidp
);
1892 r
= hashmap_put(names
, pidp
, (void*) name
);
1901 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1902 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1904 for (k
= 0; k
< n
; k
++) {
1905 _cleanup_free_
char *e
= NULL
;
1908 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1911 if (n_columns
!= 0) {
1914 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1916 e
= ellipsize(name
, z
, 100);
1921 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1923 special_glyph(TRIANGULAR_BULLET
),
1931 int unit_show_processes(
1934 const char *cgroup_path
,
1938 sd_bus_error
*error
) {
1940 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1941 Hashmap
*cgroups
= NULL
;
1942 struct CGroupInfo
*cg
;
1948 if (flags
& OUTPUT_FULL_WIDTH
)
1950 else if (n_columns
<= 0)
1951 n_columns
= columns();
1953 prefix
= strempty(prefix
);
1955 r
= sd_bus_call_method(
1957 "org.freedesktop.systemd1",
1958 "/org/freedesktop/systemd1",
1959 "org.freedesktop.systemd1.Manager",
1968 cgroups
= hashmap_new(&string_hash_ops
);
1972 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1977 const char *path
= NULL
, *name
= NULL
;
1980 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1986 r
= add_process(cgroups
, path
, pid
, name
);
1991 r
= sd_bus_message_exit_container(reply
);
1995 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1999 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
2002 while ((cg
= hashmap_first(cgroups
)))
2003 remove_cgroup(cgroups
, cg
);
2005 hashmap_free(cgroups
);