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 dbus_path
= unit_dbus_path_from_name(d
->name
);
1134 return sd_bus_get_property_string(d
->bus
,
1135 "org.freedesktop.systemd1",
1137 "org.freedesktop.systemd1.Service",
1143 static const struct {
1144 const char *result
, *explanation
;
1145 } explanations
[] = {
1146 { "resources", "of unavailable resources or another system error" },
1147 { "protocol", "the service did not take the steps required by its unit configuration" },
1148 { "timeout", "a timeout was exceeded" },
1149 { "exit-code", "the control process exited with error code" },
1150 { "signal", "a fatal signal was delivered to the control process" },
1151 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1152 { "watchdog", "the service failed to send watchdog ping" },
1153 { "start-limit", "start of the service was attempted too often" }
1156 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1157 _cleanup_free_
char *service_shell_quoted
= NULL
;
1158 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1162 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1165 _cleanup_free_
char *t
;
1167 t
= strv_join((char**) extra_args
, " ");
1168 systemctl
= strjoina("systemctl ", t
? : "<args>");
1169 journalctl
= strjoina("journalctl ", t
? : "<args>");
1172 if (!isempty(result
)) {
1175 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1176 if (streq(result
, explanations
[i
].result
))
1179 if (i
< ELEMENTSOF(explanations
)) {
1180 log_error("Job for %s failed because %s.\n"
1181 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1183 explanations
[i
].explanation
,
1185 service_shell_quoted
?: "<service>",
1191 log_error("Job for %s failed.\n"
1192 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1195 service_shell_quoted
?: "<service>",
1199 /* For some results maybe additional explanation is required */
1200 if (streq_ptr(result
, "start-limit"))
1201 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1202 "followed by \"%1$s start %2$s\" again.",
1204 service_shell_quoted
?: "<service>");
1207 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1213 if (streq(d
->result
, "canceled"))
1214 log_error("Job for %s canceled.", strna(d
->name
));
1215 else if (streq(d
->result
, "timeout"))
1216 log_error("Job for %s timed out.", strna(d
->name
));
1217 else if (streq(d
->result
, "dependency"))
1218 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1219 else if (streq(d
->result
, "invalid"))
1220 log_error("%s is not active, cannot reload.", strna(d
->name
));
1221 else if (streq(d
->result
, "assert"))
1222 log_error("Assertion failed on job for %s.", strna(d
->name
));
1223 else if (streq(d
->result
, "unsupported"))
1224 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1225 else if (streq(d
->result
, "collected"))
1226 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1227 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped")) {
1230 _cleanup_free_
char *result
= NULL
;
1232 q
= bus_job_get_service_result(d
, &result
);
1234 log_debug_errno(q
, "Failed to get Result property of service %s: %m", d
->name
);
1236 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1238 log_error("Job failed. See \"journalctl -xe\" for details.");
1242 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1244 else if (streq(d
->result
, "timeout"))
1246 else if (streq(d
->result
, "dependency"))
1248 else if (streq(d
->result
, "invalid"))
1250 else if (streq(d
->result
, "assert"))
1252 else if (streq(d
->result
, "unsupported"))
1254 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped"))
1260 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1265 while (!set_isempty(d
->jobs
)) {
1268 q
= bus_process_wait(d
->bus
);
1270 return log_error_errno(q
, "Failed to wait for response: %m");
1273 q
= check_wait_response(d
, quiet
, extra_args
);
1274 /* Return the first error as it is most likely to be
1276 if (q
< 0 && r
== 0)
1279 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1282 d
->name
= mfree(d
->name
);
1283 d
->result
= mfree(d
->result
);
1289 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1294 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1298 return set_put_strdup(d
->jobs
, path
);
1301 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1304 r
= bus_wait_for_jobs_add(d
, path
);
1308 return bus_wait_for_jobs(d
, quiet
, NULL
);
1311 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1312 const char *type
, *path
, *source
;
1315 /* changes is dereferenced when calling unit_file_dump_changes() later,
1316 * so we have to make sure this is not NULL. */
1320 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1322 return bus_log_parse_error(r
);
1324 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1325 /* We expect only "success" changes to be sent over the bus.
1326 Hence, reject anything negative. */
1327 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1330 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1334 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1339 return bus_log_parse_error(r
);
1341 r
= sd_bus_message_exit_container(m
);
1343 return bus_log_parse_error(r
);
1345 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, false);
1351 bool is_const
; /* If false, cgroup_path should be free()'d */
1353 Hashmap
*pids
; /* PID → process name */
1356 struct CGroupInfo
*parent
;
1357 LIST_FIELDS(struct CGroupInfo
, siblings
);
1358 LIST_HEAD(struct CGroupInfo
, children
);
1362 static bool IS_ROOT(const char *p
) {
1363 return isempty(p
) || streq(p
, "/");
1366 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1367 struct CGroupInfo
*parent
= NULL
, *cg
;
1376 cg
= hashmap_get(cgroups
, path
);
1382 if (!IS_ROOT(path
)) {
1385 e
= strrchr(path
, '/');
1389 pp
= strndupa(path
, e
- path
);
1393 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1398 cg
= new0(struct CGroupInfo
, 1);
1403 cg
->cgroup_path
= (char*) path
;
1405 cg
->cgroup_path
= strdup(path
);
1406 if (!cg
->cgroup_path
) {
1412 cg
->is_const
= is_const
;
1413 cg
->parent
= parent
;
1415 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1418 free(cg
->cgroup_path
);
1424 LIST_PREPEND(siblings
, parent
->children
, cg
);
1425 parent
->n_children
++;
1432 static int add_process(
1438 struct CGroupInfo
*cg
;
1445 r
= add_cgroup(cgroups
, path
, true, &cg
);
1449 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1453 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1456 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1460 while (cg
->children
)
1461 remove_cgroup(cgroups
, cg
->children
);
1463 hashmap_remove(cgroups
, cg
->cgroup_path
);
1466 free(cg
->cgroup_path
);
1468 hashmap_free(cg
->pids
);
1471 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1476 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1477 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1482 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1485 static int dump_processes(
1487 const char *cgroup_path
,
1490 OutputFlags flags
) {
1492 struct CGroupInfo
*cg
;
1497 if (IS_ROOT(cgroup_path
))
1500 cg
= hashmap_get(cgroups
, cgroup_path
);
1504 if (!hashmap_isempty(cg
->pids
)) {
1512 /* Order processes by their PID */
1513 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1515 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1516 pids
[n
++] = PTR_TO_PID(pidp
);
1518 assert(n
== hashmap_size(cg
->pids
));
1519 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1521 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1523 for (i
= 0; i
< n
; i
++) {
1524 _cleanup_free_
char *e
= NULL
;
1525 const char *special
;
1528 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1531 if (n_columns
!= 0) {
1534 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1536 e
= ellipsize(name
, k
, 100);
1541 more
= i
+1 < n
|| cg
->children
;
1542 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1544 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1553 struct CGroupInfo
**children
, *child
;
1556 /* Order subcgroups by their name */
1557 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1558 LIST_FOREACH(siblings
, child
, cg
->children
)
1559 children
[n
++] = child
;
1560 assert(n
== cg
->n_children
);
1561 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1564 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1566 for (i
= 0; i
< n
; i
++) {
1567 _cleanup_free_
char *pp
= NULL
;
1568 const char *name
, *special
;
1571 child
= children
[i
];
1573 name
= strrchr(child
->cgroup_path
, '/');
1579 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1581 fputs(prefix
, stdout
);
1582 fputs(special
, stdout
);
1583 fputs(name
, stdout
);
1584 fputc('\n', stdout
);
1586 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1588 pp
= strappend(prefix
, special
);
1592 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1602 static int dump_extra_processes(
1606 OutputFlags flags
) {
1608 _cleanup_free_ pid_t
*pids
= NULL
;
1609 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1610 struct CGroupInfo
*cg
;
1611 size_t n_allocated
= 0, n
= 0, k
;
1615 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1616 * combined, sorted, linear list. */
1618 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1626 if (hashmap_isempty(cg
->pids
))
1629 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1633 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1636 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1637 pids
[n
++] = PTR_TO_PID(pidp
);
1639 r
= hashmap_put(names
, pidp
, (void*) name
);
1648 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1649 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1651 for (k
= 0; k
< n
; k
++) {
1652 _cleanup_free_
char *e
= NULL
;
1655 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1658 if (n_columns
!= 0) {
1661 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1663 e
= ellipsize(name
, z
, 100);
1668 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1670 special_glyph(TRIANGULAR_BULLET
),
1678 int unit_show_processes(
1681 const char *cgroup_path
,
1685 sd_bus_error
*error
) {
1687 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1688 Hashmap
*cgroups
= NULL
;
1689 struct CGroupInfo
*cg
;
1695 if (flags
& OUTPUT_FULL_WIDTH
)
1697 else if (n_columns
<= 0)
1698 n_columns
= columns();
1700 prefix
= strempty(prefix
);
1702 r
= sd_bus_call_method(
1704 "org.freedesktop.systemd1",
1705 "/org/freedesktop/systemd1",
1706 "org.freedesktop.systemd1.Manager",
1715 cgroups
= hashmap_new(&string_hash_ops
);
1719 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1724 const char *path
= NULL
, *name
= NULL
;
1727 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1733 r
= add_process(cgroups
, path
, pid
, name
);
1738 r
= sd_bus_message_exit_container(reply
);
1742 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1746 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
1749 while ((cg
= hashmap_first(cgroups
)))
1750 remove_cgroup(cgroups
, cg
);
1752 hashmap_free(cgroups
);