2 This file is part of systemd.
4 Copyright 2016 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 #include "alloc-util.h"
21 #include "bus-internal.h"
22 #include "bus-unit-util.h"
25 #include "cgroup-util.h"
26 #include "cpu-set-util.h"
28 #include "errno-list.h"
32 #include "locale-util.h"
33 #include "mount-util.h"
35 #include "parse-util.h"
36 #include "path-util.h"
37 #include "process-util.h"
38 #include "rlimit-util.h"
39 #include "securebits-util.h"
40 #include "signal-util.h"
41 #include "string-util.h"
42 #include "syslog-util.h"
43 #include "terminal-util.h"
44 #include "user-util.h"
48 int bus_parse_unit_info(sd_bus_message
*message
, UnitInfo
*u
) {
54 return sd_bus_message_read(
69 int bus_append_unit_property_assignment(sd_bus_message
*m
, const char *assignment
) {
70 const char *eq
, *field
;
77 eq
= strchr(assignment
, '=');
79 log_error("Not an assignment: %s", assignment
);
83 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
85 return bus_log_create_error(r
);
87 field
= strndupa(assignment
, eq
- assignment
);
90 if (streq(field
, "CPUQuota")) {
93 r
= sd_bus_message_append(m
, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY
);
95 r
= parse_percent_unbounded(eq
);
97 log_error_errno(r
, "CPU quota '%s' invalid.", eq
);
101 r
= sd_bus_message_append(m
, "sv", "CPUQuotaPerSecUSec", "t", (usec_t
) r
* USEC_PER_SEC
/ 100U);
106 } else if (streq(field
, "EnvironmentFile")) {
108 r
= sd_bus_message_append(m
, "sv", "EnvironmentFiles", "a(sb)", 1,
109 eq
[0] == '-' ? eq
+ 1 : eq
,
113 } else if (STR_IN_SET(field
, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) {
118 r
= parse_sec(eq
, &t
);
120 return log_error_errno(r
, "Failed to parse %s= parameter: %s", field
, eq
);
123 n
= newa(char, l
+ 2);
127 /* Change suffix Sec → USec */
128 strcpy(mempcpy(n
, field
, l
- 3), "USec");
129 r
= sd_bus_message_append(m
, "sv", n
, "t", t
);
132 } else if (STR_IN_SET(field
, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) {
135 if (isempty(eq
) || streq(eq
, "infinity"))
136 bytes
= CGROUP_LIMIT_MAX
;
138 r
= parse_percent(eq
);
142 /* When this is a percentage we'll convert this into a relative value in the range
143 * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
144 * ones). This way the physical memory size can be determined server-side */
146 n
= strjoina(field
, "Scale");
147 r
= sd_bus_message_append(m
, "sv", n
, "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
151 r
= parse_size(eq
, 1024, &bytes
);
153 return log_error_errno(r
, "Failed to parse bytes specification %s", assignment
);
157 r
= sd_bus_message_append(m
, "sv", field
, "t", bytes
);
159 } else if (streq(field
, "TasksMax")) {
162 if (isempty(eq
) || streq(eq
, "infinity"))
165 r
= parse_percent(eq
);
167 r
= sd_bus_message_append(m
, "sv", "TasksMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
170 r
= safe_atou64(eq
, &t
);
172 return log_error_errno(r
, "Failed to parse maximum tasks specification %s", assignment
);
177 r
= sd_bus_message_append(m
, "sv", "TasksMax", "t", t
);
181 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
183 return bus_log_create_error(r
);
185 rl
= rlimit_from_string(field
);
190 r
= rlimit_parse(rl
, eq
, &l
);
192 return log_error_errno(r
, "Failed to parse resource limit: %s", eq
);
194 r
= sd_bus_message_append(m
, "v", "t", l
.rlim_max
);
196 return bus_log_create_error(r
);
198 r
= sd_bus_message_close_container(m
);
200 return bus_log_create_error(r
);
202 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
204 return bus_log_create_error(r
);
206 sn
= strjoina(field
, "Soft");
207 r
= sd_bus_message_append(m
, "sv", sn
, "t", l
.rlim_cur
);
209 } else if (STR_IN_SET(field
,
210 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting", "TasksAccounting",
211 "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
212 "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate", "RemainAfterExit",
213 "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges",
214 "SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute",
215 "RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables",
216 "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS",
217 "CPUSchedulingResetOnFork", "LockPersonality")) {
219 r
= parse_boolean(eq
);
221 return log_error_errno(r
, "Failed to parse boolean assignment %s.", assignment
);
223 r
= sd_bus_message_append(m
, "v", "b", r
);
225 } else if (STR_IN_SET(field
, "CPUWeight", "StartupCPUWeight")) {
228 r
= cg_weight_parse(eq
, &u
);
230 log_error("Failed to parse %s value %s.", field
, eq
);
234 r
= sd_bus_message_append(m
, "v", "t", u
);
236 } else if (STR_IN_SET(field
, "CPUShares", "StartupCPUShares")) {
239 r
= cg_cpu_shares_parse(eq
, &u
);
241 log_error("Failed to parse %s value %s.", field
, eq
);
245 r
= sd_bus_message_append(m
, "v", "t", u
);
247 } else if (STR_IN_SET(field
, "IOWeight", "StartupIOWeight")) {
250 r
= cg_weight_parse(eq
, &u
);
252 log_error("Failed to parse %s value %s.", field
, eq
);
256 r
= sd_bus_message_append(m
, "v", "t", u
);
258 } else if (STR_IN_SET(field
, "BlockIOWeight", "StartupBlockIOWeight")) {
261 r
= cg_blkio_weight_parse(eq
, &u
);
263 log_error("Failed to parse %s value %s.", field
, eq
);
267 r
= sd_bus_message_append(m
, "v", "t", u
);
269 } else if (STR_IN_SET(field
,
270 "User", "Group", "DevicePolicy", "KillMode",
271 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
272 "StandardInput", "StandardOutput", "StandardError",
273 "Description", "Slice", "Type", "WorkingDirectory",
274 "RootDirectory", "SyslogIdentifier", "ProtectSystem",
275 "ProtectHome", "SELinuxContext", "Restart", "RootImage",
276 "NotifyAccess", "RuntimeDirectoryPreserve", "Personality"))
277 r
= sd_bus_message_append(m
, "v", "s", eq
);
279 else if (STR_IN_SET(field
, "AppArmorProfile", "SmackProcessLabel")) {
291 r
= sd_bus_message_append(m
, "v", "(bs)", ignore
, s
);
293 } else if (streq(field
, "SyslogLevel")) {
296 level
= log_level_from_string(eq
);
298 log_error("Failed to parse %s value %s.", field
, eq
);
302 r
= sd_bus_message_append(m
, "v", "i", level
);
304 } else if (streq(field
, "SyslogFacility")) {
307 facility
= log_facility_unshifted_from_string(eq
);
309 log_error("Failed to parse %s value %s.", field
, eq
);
313 r
= sd_bus_message_append(m
, "v", "i", facility
);
315 } else if (streq(field
, "SecureBits")) {
317 r
= secure_bits_from_string(eq
);
319 log_error("Failed to parse %s value %s.", field
, eq
);
323 r
= sd_bus_message_append(m
, "v", "i", r
);
325 } else if (STR_IN_SET(field
, "CapabilityBoundingSet", "AmbientCapabilities")) {
336 r
= capability_set_from_string(p
, &sum
);
338 log_error("Failed to parse %s value %s.", field
, eq
);
342 sum
= invert
? ~sum
: sum
;
344 r
= sd_bus_message_append(m
, "v", "t", sum
);
346 } else if (streq(field
, "DeviceAllow")) {
349 r
= sd_bus_message_append(m
, "v", "a(ss)", 0);
351 const char *path
, *rwm
, *e
;
355 path
= strndupa(eq
, e
- eq
);
362 if (!is_deviceallow_pattern(path
)) {
363 log_error("%s is not a device file in /dev.", path
);
367 r
= sd_bus_message_append(m
, "v", "a(ss)", 1, path
, rwm
);
370 } else if (cgroup_io_limit_type_from_string(field
) >= 0 || STR_IN_SET(field
, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
373 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
375 const char *path
, *bandwidth
, *e
;
380 path
= strndupa(eq
, e
- eq
);
383 log_error("Failed to parse %s value %s.", field
, eq
);
387 if (!path_startswith(path
, "/dev")) {
388 log_error("%s is not a device file in /dev.", path
);
392 if (streq(bandwidth
, "infinity")) {
393 bytes
= CGROUP_LIMIT_MAX
;
395 r
= parse_size(bandwidth
, 1000, &bytes
);
397 log_error("Failed to parse byte value %s.", bandwidth
);
402 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, bytes
);
405 } else if (STR_IN_SET(field
, "IODeviceWeight", "BlockIODeviceWeight")) {
408 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
410 const char *path
, *weight
, *e
;
415 path
= strndupa(eq
, e
- eq
);
418 log_error("Failed to parse %s value %s.", field
, eq
);
422 if (!path_startswith(path
, "/dev")) {
423 log_error("%s is not a device file in /dev.", path
);
427 r
= safe_atou64(weight
, &u
);
429 log_error("Failed to parse %s value %s.", field
, weight
);
432 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, u
);
435 } else if (streq(field
, "CPUSchedulingPolicy")) {
438 n
= sched_policy_from_string(eq
);
440 return log_error_errno(r
, "Failed to parse CPUSchedulingPolicy: %s", eq
);
442 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
444 } else if (streq(field
, "CPUSchedulingPriority")) {
447 r
= safe_atoi(eq
, &n
);
449 return log_error_errno(r
, "Failed to parse CPUSchedulingPriority: %s", eq
);
450 if (!sched_priority_is_valid(n
))
451 return log_error_errno(r
, "Invalid CPUSchedulingPriority: %s", eq
);
453 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
455 } else if (streq(field
, "CPUAffinity")) {
456 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
459 ncpus
= parse_cpu_set(eq
, &cpuset
);
461 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
463 r
= sd_bus_message_open_container(m
, 'v', "ay");
465 return bus_log_create_error(r
);
468 sd_bus_message_append_array(m
, 'y', cpuset
, CPU_ALLOC_SIZE(ncpus
));
470 r
= sd_bus_message_close_container(m
);
472 } else if (streq(field
, "Nice")) {
475 r
= parse_nice(eq
, &n
);
477 return log_error_errno(r
, "Failed to parse nice value: %s", eq
);
479 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
483 } else if (streq(field
, "SystemCallFilter")) {
487 r
= sd_bus_message_open_container(m
, 'v', "bas");
489 return bus_log_create_error(r
);
498 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
500 return bus_log_create_error(r
);
502 r
= sd_bus_message_open_container(m
, 'a', "s");
504 return bus_log_create_error(r
);
506 if (whitelist
!= 0) {
507 r
= sd_bus_message_append_basic(m
, 's', "@default");
509 return bus_log_create_error(r
);
513 _cleanup_free_
char *word
= NULL
;
515 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
517 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
521 r
= sd_bus_message_append_basic(m
, 's', word
);
523 return bus_log_create_error(r
);
526 r
= sd_bus_message_close_container(m
);
528 return bus_log_create_error(r
);
530 r
= sd_bus_message_close_container(m
);
532 } else if (streq(field
, "SystemCallArchitectures")) {
535 r
= sd_bus_message_open_container(m
, 'v', "as");
537 return bus_log_create_error(r
);
539 r
= sd_bus_message_open_container(m
, 'a', "s");
541 return bus_log_create_error(r
);
544 _cleanup_free_
char *word
= NULL
;
546 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
548 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
552 r
= sd_bus_message_append_basic(m
, 's', word
);
554 return bus_log_create_error(r
);
557 r
= sd_bus_message_close_container(m
);
559 return bus_log_create_error(r
);
561 r
= sd_bus_message_close_container(m
);
563 } else if (streq(field
, "SystemCallErrorNumber")) {
566 n
= errno_from_name(eq
);
568 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
570 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
572 } else if (streq(field
, "RestrictAddressFamilies")) {
576 r
= sd_bus_message_open_container(m
, 'v', "bas");
578 return bus_log_create_error(r
);
587 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
589 return bus_log_create_error(r
);
591 r
= sd_bus_message_open_container(m
, 'a', "s");
593 return bus_log_create_error(r
);
596 _cleanup_free_
char *word
= NULL
;
598 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
600 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
604 r
= sd_bus_message_append_basic(m
, 's', word
);
606 return bus_log_create_error(r
);
609 r
= sd_bus_message_close_container(m
);
611 return bus_log_create_error(r
);
613 r
= sd_bus_message_close_container(m
);
617 } else if (streq(field
, "FileDescriptorStoreMax")) {
620 r
= safe_atou(eq
, &u
);
622 return log_error_errno(r
, "Failed to parse file descriptor store limit: %s", eq
);
624 r
= sd_bus_message_append(m
, "v", "u", (uint32_t) u
);
626 } else if (streq(field
, "IOSchedulingClass")) {
629 c
= ioprio_class_from_string(eq
);
631 return log_error_errno(r
, "Failed to parse IO scheduling class: %s", eq
);
633 r
= sd_bus_message_append(m
, "v", "i", (int32_t) c
);
635 } else if (streq(field
, "IOSchedulingPriority")) {
638 r
= ioprio_parse_priority(eq
, &q
);
640 return log_error_errno(r
, "Failed to parse IO scheduling priority: %s", eq
);
642 r
= sd_bus_message_append(m
, "v", "i", (int32_t) q
);
644 } else if (STR_IN_SET(field
, "Environment", "PassEnvironment")) {
647 r
= sd_bus_message_open_container(m
, 'v', "as");
649 return bus_log_create_error(r
);
651 r
= sd_bus_message_open_container(m
, 'a', "s");
653 return bus_log_create_error(r
);
656 _cleanup_free_
char *word
= NULL
;
658 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
660 log_error("Failed to parse Environment value %s", eq
);
666 if (streq(field
, "Environment")) {
667 if (!env_assignment_is_valid(word
)) {
668 log_error("Invalid environment assignment: %s", word
);
671 } else { /* PassEnvironment */
672 if (!env_name_is_valid(word
)) {
673 log_error("Invalid environment variable name: %s", word
);
678 r
= sd_bus_message_append_basic(m
, 's', word
);
680 return bus_log_create_error(r
);
683 r
= sd_bus_message_close_container(m
);
685 return bus_log_create_error(r
);
687 r
= sd_bus_message_close_container(m
);
689 } else if (streq(field
, "KillSignal")) {
692 sig
= signal_from_string_try_harder(eq
);
694 log_error("Failed to parse %s value %s.", field
, eq
);
698 r
= sd_bus_message_append(m
, "v", "i", sig
);
700 } else if (streq(field
, "TimerSlackNSec")) {
703 r
= parse_nsec(eq
, &n
);
705 log_error("Failed to parse %s value %s", field
, eq
);
709 r
= sd_bus_message_append(m
, "v", "t", n
);
710 } else if (streq(field
, "OOMScoreAdjust")) {
713 r
= safe_atoi(eq
, &oa
);
715 log_error("Failed to parse %s value %s", field
, eq
);
719 if (!oom_score_adjust_is_valid(oa
)) {
720 log_error("OOM score adjust value out of range");
724 r
= sd_bus_message_append(m
, "v", "i", oa
);
725 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
726 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
729 r
= sd_bus_message_open_container(m
, 'v', "as");
731 return bus_log_create_error(r
);
733 r
= sd_bus_message_open_container(m
, 'a', "s");
735 return bus_log_create_error(r
);
738 _cleanup_free_
char *word
= NULL
;
741 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
743 log_error("Failed to parse %s value %s", field
, eq
);
749 if (!utf8_is_valid(word
)) {
750 log_error("Failed to parse %s value %s", field
, eq
);
754 offset
= word
[0] == '-';
755 offset
+= word
[offset
] == '+';
757 if (!path_is_absolute(word
+ offset
)) {
758 log_error("Failed to parse %s value %s", field
, eq
);
762 path_kill_slashes(word
+ offset
);
764 r
= sd_bus_message_append_basic(m
, 's', word
);
766 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 } else if (streq(field
, "SupplementaryGroups")) {
778 r
= sd_bus_message_open_container(m
, 'v', "as");
780 return bus_log_create_error(r
);
782 r
= sd_bus_message_open_container(m
, 'a', "s");
784 return bus_log_create_error(r
);
787 _cleanup_free_
char *word
= NULL
;
789 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
791 log_error("Failed to parse %s value %s", field
, eq
);
797 if (!valid_user_group_name_or_id(word
)) {
798 log_error("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 (STR_IN_SET(field
, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) {
816 r
= parse_mode(eq
, &mode
);
818 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
820 r
= sd_bus_message_append(m
, "v", "u", mode
);
822 } else if (STR_IN_SET(field
, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
825 r
= sd_bus_message_open_container(m
, 'v', "as");
827 return bus_log_create_error(r
);
829 r
= sd_bus_message_open_container(m
, 'a', "s");
831 return bus_log_create_error(r
);
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
);
843 r
= sd_bus_message_append_basic(m
, 's', word
);
845 return bus_log_create_error(r
);
848 r
= sd_bus_message_close_container(m
);
850 return bus_log_create_error(r
);
852 r
= sd_bus_message_close_container(m
);
854 } else if (streq(field
, "RestrictNamespaces")) {
856 unsigned long flags
= 0;
863 r
= parse_boolean(eq
);
867 flags
= NAMESPACE_FLAGS_ALL
;
869 r
= namespace_flag_from_string_many(eq
, &flags
);
871 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
875 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
877 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
878 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
879 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
880 else if (streq(field
, "MountFlags")) {
883 r
= mount_propagation_flags_from_string(eq
, &f
);
885 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
887 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
888 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
891 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
895 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
900 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
901 char *s
= NULL
, *d
= NULL
;
902 bool ignore_enoent
= false;
903 uint64_t flags
= MS_REC
;
905 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
907 return log_error_errno(r
, "Failed to parse argument: %m");
913 ignore_enoent
= true;
917 if (p
&& p
[-1] == ':') {
918 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
920 return log_error_errno(r
, "Failed to parse argument: %m");
922 log_error("Missing argument after ':': %s", eq
);
928 if (p
&& p
[-1] == ':') {
929 _cleanup_free_
char *options
= NULL
;
931 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
933 return log_error_errno(r
, "Failed to parse argument: %m");
935 if (isempty(options
) || streq(options
, "rbind"))
937 else if (streq(options
, "norbind"))
940 log_error("Unknown options: %s", eq
);
948 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
953 r
= sd_bus_message_close_container(m
);
957 r
= sd_bus_message_close_container(m
);
959 log_error("Unknown assignment %s.", assignment
);
965 return bus_log_create_error(r
);
967 r
= sd_bus_message_close_container(m
);
969 return bus_log_create_error(r
);
974 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
981 r
= bus_append_unit_property_assignment(m
, *i
);
989 typedef struct BusWaitForJobs
{
996 sd_bus_slot
*slot_job_removed
;
997 sd_bus_slot
*slot_disconnected
;
1000 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1003 log_error("Warning! D-Bus connection terminated.");
1004 sd_bus_close(sd_bus_message_get_bus(m
));
1009 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1010 const char *path
, *unit
, *result
;
1011 BusWaitForJobs
*d
= userdata
;
1019 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1021 bus_log_parse_error(r
);
1025 found
= set_remove(d
->jobs
, (char*) path
);
1031 if (!isempty(result
))
1032 d
->result
= strdup(result
);
1035 d
->name
= strdup(unit
);
1040 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1044 set_free_free(d
->jobs
);
1046 sd_bus_slot_unref(d
->slot_disconnected
);
1047 sd_bus_slot_unref(d
->slot_job_removed
);
1049 sd_bus_unref(d
->bus
);
1057 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1058 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1064 d
= new0(BusWaitForJobs
, 1);
1068 d
->bus
= sd_bus_ref(bus
);
1070 /* When we are a bus client we match by sender. Direct
1071 * connections OTOH have no initialized sender field, and
1072 * hence we ignore the sender then */
1073 r
= sd_bus_add_match(
1075 &d
->slot_job_removed
,
1078 "sender='org.freedesktop.systemd1',"
1079 "interface='org.freedesktop.systemd1.Manager',"
1080 "member='JobRemoved',"
1081 "path='/org/freedesktop/systemd1'" :
1083 "interface='org.freedesktop.systemd1.Manager',"
1084 "member='JobRemoved',"
1085 "path='/org/freedesktop/systemd1'",
1086 match_job_removed
, d
);
1090 r
= sd_bus_add_match(
1092 &d
->slot_disconnected
,
1094 "sender='org.freedesktop.DBus.Local',"
1095 "interface='org.freedesktop.DBus.Local',"
1096 "member='Disconnected'",
1097 match_disconnected
, d
);
1107 static int bus_process_wait(sd_bus
*bus
) {
1111 r
= sd_bus_process(bus
, NULL
);
1117 r
= sd_bus_wait(bus
, (uint64_t) -1);
1123 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1124 _cleanup_free_
char *dbus_path
= NULL
;
1130 if (!endswith(d
->name
, ".service"))
1133 dbus_path
= unit_dbus_path_from_name(d
->name
);
1137 return sd_bus_get_property_string(d
->bus
,
1138 "org.freedesktop.systemd1",
1140 "org.freedesktop.systemd1.Service",
1146 static const struct {
1147 const char *result
, *explanation
;
1148 } explanations
[] = {
1149 { "resources", "of unavailable resources or another system error" },
1150 { "protocol", "the service did not take the steps required by its unit configuration" },
1151 { "timeout", "a timeout was exceeded" },
1152 { "exit-code", "the control process exited with error code" },
1153 { "signal", "a fatal signal was delivered to the control process" },
1154 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1155 { "watchdog", "the service failed to send watchdog ping" },
1156 { "start-limit", "start of the service was attempted too often" }
1159 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1160 _cleanup_free_
char *service_shell_quoted
= NULL
;
1161 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1165 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1168 _cleanup_free_
char *t
;
1170 t
= strv_join((char**) extra_args
, " ");
1171 systemctl
= strjoina("systemctl ", t
? : "<args>");
1172 journalctl
= strjoina("journalctl ", t
? : "<args>");
1175 if (!isempty(result
)) {
1178 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1179 if (streq(result
, explanations
[i
].result
))
1182 if (i
< ELEMENTSOF(explanations
)) {
1183 log_error("Job for %s failed because %s.\n"
1184 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1186 explanations
[i
].explanation
,
1188 service_shell_quoted
?: "<service>",
1194 log_error("Job for %s failed.\n"
1195 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1198 service_shell_quoted
?: "<service>",
1202 /* For some results maybe additional explanation is required */
1203 if (streq_ptr(result
, "start-limit"))
1204 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1205 "followed by \"%1$s start %2$s\" again.",
1207 service_shell_quoted
?: "<service>");
1210 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1216 if (streq(d
->result
, "canceled"))
1217 log_error("Job for %s canceled.", strna(d
->name
));
1218 else if (streq(d
->result
, "timeout"))
1219 log_error("Job for %s timed out.", strna(d
->name
));
1220 else if (streq(d
->result
, "dependency"))
1221 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1222 else if (streq(d
->result
, "invalid"))
1223 log_error("%s is not active, cannot reload.", strna(d
->name
));
1224 else if (streq(d
->result
, "assert"))
1225 log_error("Assertion failed on job for %s.", strna(d
->name
));
1226 else if (streq(d
->result
, "unsupported"))
1227 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1228 else if (streq(d
->result
, "collected"))
1229 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1230 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1232 _cleanup_free_
char *result
= NULL
;
1235 q
= bus_job_get_service_result(d
, &result
);
1237 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1239 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1241 log_error("Job failed. See \"journalctl -xe\" for details.");
1245 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1247 else if (streq(d
->result
, "timeout"))
1249 else if (streq(d
->result
, "dependency"))
1251 else if (streq(d
->result
, "invalid"))
1253 else if (streq(d
->result
, "assert"))
1255 else if (streq(d
->result
, "unsupported"))
1257 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1263 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1268 while (!set_isempty(d
->jobs
)) {
1271 q
= bus_process_wait(d
->bus
);
1273 return log_error_errno(q
, "Failed to wait for response: %m");
1276 q
= check_wait_response(d
, quiet
, extra_args
);
1277 /* Return the first error as it is most likely to be
1279 if (q
< 0 && r
== 0)
1282 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1285 d
->name
= mfree(d
->name
);
1286 d
->result
= mfree(d
->result
);
1292 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1297 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1301 return set_put_strdup(d
->jobs
, path
);
1304 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1307 r
= bus_wait_for_jobs_add(d
, path
);
1311 return bus_wait_for_jobs(d
, quiet
, NULL
);
1314 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1315 const char *type
, *path
, *source
;
1318 /* changes is dereferenced when calling unit_file_dump_changes() later,
1319 * so we have to make sure this is not NULL. */
1323 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1325 return bus_log_parse_error(r
);
1327 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1328 /* We expect only "success" changes to be sent over the bus.
1329 Hence, reject anything negative. */
1330 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1333 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1337 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1342 return bus_log_parse_error(r
);
1344 r
= sd_bus_message_exit_container(m
);
1346 return bus_log_parse_error(r
);
1348 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, false);
1354 bool is_const
; /* If false, cgroup_path should be free()'d */
1356 Hashmap
*pids
; /* PID → process name */
1359 struct CGroupInfo
*parent
;
1360 LIST_FIELDS(struct CGroupInfo
, siblings
);
1361 LIST_HEAD(struct CGroupInfo
, children
);
1365 static bool IS_ROOT(const char *p
) {
1366 return isempty(p
) || streq(p
, "/");
1369 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1370 struct CGroupInfo
*parent
= NULL
, *cg
;
1379 cg
= hashmap_get(cgroups
, path
);
1385 if (!IS_ROOT(path
)) {
1388 e
= strrchr(path
, '/');
1392 pp
= strndupa(path
, e
- path
);
1396 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1401 cg
= new0(struct CGroupInfo
, 1);
1406 cg
->cgroup_path
= (char*) path
;
1408 cg
->cgroup_path
= strdup(path
);
1409 if (!cg
->cgroup_path
) {
1415 cg
->is_const
= is_const
;
1416 cg
->parent
= parent
;
1418 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1421 free(cg
->cgroup_path
);
1427 LIST_PREPEND(siblings
, parent
->children
, cg
);
1428 parent
->n_children
++;
1435 static int add_process(
1441 struct CGroupInfo
*cg
;
1448 r
= add_cgroup(cgroups
, path
, true, &cg
);
1452 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1456 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1459 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1463 while (cg
->children
)
1464 remove_cgroup(cgroups
, cg
->children
);
1466 hashmap_remove(cgroups
, cg
->cgroup_path
);
1469 free(cg
->cgroup_path
);
1471 hashmap_free(cg
->pids
);
1474 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1479 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1480 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1485 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1488 static int dump_processes(
1490 const char *cgroup_path
,
1493 OutputFlags flags
) {
1495 struct CGroupInfo
*cg
;
1500 if (IS_ROOT(cgroup_path
))
1503 cg
= hashmap_get(cgroups
, cgroup_path
);
1507 if (!hashmap_isempty(cg
->pids
)) {
1515 /* Order processes by their PID */
1516 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1518 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1519 pids
[n
++] = PTR_TO_PID(pidp
);
1521 assert(n
== hashmap_size(cg
->pids
));
1522 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1524 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1526 for (i
= 0; i
< n
; i
++) {
1527 _cleanup_free_
char *e
= NULL
;
1528 const char *special
;
1531 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1534 if (n_columns
!= 0) {
1537 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1539 e
= ellipsize(name
, k
, 100);
1544 more
= i
+1 < n
|| cg
->children
;
1545 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1547 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1556 struct CGroupInfo
**children
, *child
;
1559 /* Order subcgroups by their name */
1560 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1561 LIST_FOREACH(siblings
, child
, cg
->children
)
1562 children
[n
++] = child
;
1563 assert(n
== cg
->n_children
);
1564 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1567 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1569 for (i
= 0; i
< n
; i
++) {
1570 _cleanup_free_
char *pp
= NULL
;
1571 const char *name
, *special
;
1574 child
= children
[i
];
1576 name
= strrchr(child
->cgroup_path
, '/');
1582 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1584 fputs(prefix
, stdout
);
1585 fputs(special
, stdout
);
1586 fputs(name
, stdout
);
1587 fputc('\n', stdout
);
1589 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1591 pp
= strappend(prefix
, special
);
1595 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1605 static int dump_extra_processes(
1609 OutputFlags flags
) {
1611 _cleanup_free_ pid_t
*pids
= NULL
;
1612 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1613 struct CGroupInfo
*cg
;
1614 size_t n_allocated
= 0, n
= 0, k
;
1618 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1619 * combined, sorted, linear list. */
1621 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1629 if (hashmap_isempty(cg
->pids
))
1632 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1636 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1639 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1640 pids
[n
++] = PTR_TO_PID(pidp
);
1642 r
= hashmap_put(names
, pidp
, (void*) name
);
1651 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1652 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1654 for (k
= 0; k
< n
; k
++) {
1655 _cleanup_free_
char *e
= NULL
;
1658 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1661 if (n_columns
!= 0) {
1664 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1666 e
= ellipsize(name
, z
, 100);
1671 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1673 special_glyph(TRIANGULAR_BULLET
),
1681 int unit_show_processes(
1684 const char *cgroup_path
,
1688 sd_bus_error
*error
) {
1690 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1691 Hashmap
*cgroups
= NULL
;
1692 struct CGroupInfo
*cg
;
1698 if (flags
& OUTPUT_FULL_WIDTH
)
1700 else if (n_columns
<= 0)
1701 n_columns
= columns();
1703 prefix
= strempty(prefix
);
1705 r
= sd_bus_call_method(
1707 "org.freedesktop.systemd1",
1708 "/org/freedesktop/systemd1",
1709 "org.freedesktop.systemd1.Manager",
1718 cgroups
= hashmap_new(&string_hash_ops
);
1722 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1727 const char *path
= NULL
, *name
= NULL
;
1730 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1736 r
= add_process(cgroups
, path
, pid
, name
);
1741 r
= sd_bus_message_exit_container(reply
);
1745 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1749 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
1752 while ((cg
= hashmap_first(cgroups
)))
1753 remove_cgroup(cgroups
, cg
);
1755 hashmap_free(cgroups
);