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", "UnsetEnvironment", "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 if (streq(field
, "UnsetEnvironment")) {
672 if (!env_assignment_is_valid(word
) && !env_name_is_valid(word
)) {
673 log_error("Invalid environment name or assignment: %s", word
);
676 } else { /* PassEnvironment */
677 if (!env_name_is_valid(word
)) {
678 log_error("Invalid environment variable name: %s", word
);
683 r
= sd_bus_message_append_basic(m
, 's', word
);
685 return bus_log_create_error(r
);
688 r
= sd_bus_message_close_container(m
);
690 return bus_log_create_error(r
);
692 r
= sd_bus_message_close_container(m
);
694 } else if (streq(field
, "KillSignal")) {
697 sig
= signal_from_string_try_harder(eq
);
699 log_error("Failed to parse %s value %s.", field
, eq
);
703 r
= sd_bus_message_append(m
, "v", "i", sig
);
705 } else if (streq(field
, "TimerSlackNSec")) {
708 r
= parse_nsec(eq
, &n
);
710 log_error("Failed to parse %s value %s", field
, eq
);
714 r
= sd_bus_message_append(m
, "v", "t", n
);
715 } else if (streq(field
, "OOMScoreAdjust")) {
718 r
= safe_atoi(eq
, &oa
);
720 log_error("Failed to parse %s value %s", field
, eq
);
724 if (!oom_score_adjust_is_valid(oa
)) {
725 log_error("OOM score adjust value out of range");
729 r
= sd_bus_message_append(m
, "v", "i", oa
);
730 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
731 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
734 r
= sd_bus_message_open_container(m
, 'v', "as");
736 return bus_log_create_error(r
);
738 r
= sd_bus_message_open_container(m
, 'a', "s");
740 return bus_log_create_error(r
);
743 _cleanup_free_
char *word
= NULL
;
746 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
748 log_error("Failed to parse %s value %s", field
, eq
);
754 if (!utf8_is_valid(word
)) {
755 log_error("Failed to parse %s value %s", field
, eq
);
759 offset
= word
[0] == '-';
760 offset
+= word
[offset
] == '+';
762 if (!path_is_absolute(word
+ offset
)) {
763 log_error("Failed to parse %s value %s", field
, eq
);
767 path_kill_slashes(word
+ offset
);
769 r
= sd_bus_message_append_basic(m
, 's', word
);
771 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 } else if (streq(field
, "SupplementaryGroups")) {
783 r
= sd_bus_message_open_container(m
, 'v', "as");
785 return bus_log_create_error(r
);
787 r
= sd_bus_message_open_container(m
, 'a', "s");
789 return bus_log_create_error(r
);
792 _cleanup_free_
char *word
= NULL
;
794 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
796 log_error("Failed to parse %s value %s", field
, eq
);
802 if (!valid_user_group_name_or_id(word
)) {
803 log_error("Failed to parse %s value %s", field
, eq
);
807 r
= sd_bus_message_append_basic(m
, 's', word
);
809 return bus_log_create_error(r
);
812 r
= sd_bus_message_close_container(m
);
814 return bus_log_create_error(r
);
816 r
= sd_bus_message_close_container(m
);
818 } else if (STR_IN_SET(field
, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) {
821 r
= parse_mode(eq
, &mode
);
823 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
825 r
= sd_bus_message_append(m
, "v", "u", mode
);
827 } else if (STR_IN_SET(field
, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
830 r
= sd_bus_message_open_container(m
, 'v', "as");
832 return bus_log_create_error(r
);
834 r
= sd_bus_message_open_container(m
, 'a', "s");
836 return bus_log_create_error(r
);
839 _cleanup_free_
char *word
= NULL
;
841 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
843 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
848 r
= sd_bus_message_append_basic(m
, 's', word
);
850 return bus_log_create_error(r
);
853 r
= sd_bus_message_close_container(m
);
855 return bus_log_create_error(r
);
857 r
= sd_bus_message_close_container(m
);
859 } else if (streq(field
, "RestrictNamespaces")) {
861 unsigned long flags
= 0;
868 r
= parse_boolean(eq
);
872 flags
= NAMESPACE_FLAGS_ALL
;
874 r
= namespace_flag_from_string_many(eq
, &flags
);
876 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
880 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
882 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
883 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
884 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
885 else if (streq(field
, "MountFlags")) {
888 r
= mount_propagation_flags_from_string(eq
, &f
);
890 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
892 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
893 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
896 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
900 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
905 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
906 char *s
= NULL
, *d
= NULL
;
907 bool ignore_enoent
= false;
908 uint64_t flags
= MS_REC
;
910 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
912 return log_error_errno(r
, "Failed to parse argument: %m");
918 ignore_enoent
= true;
922 if (p
&& p
[-1] == ':') {
923 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
925 return log_error_errno(r
, "Failed to parse argument: %m");
927 log_error("Missing argument after ':': %s", eq
);
933 if (p
&& p
[-1] == ':') {
934 _cleanup_free_
char *options
= NULL
;
936 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
938 return log_error_errno(r
, "Failed to parse argument: %m");
940 if (isempty(options
) || streq(options
, "rbind"))
942 else if (streq(options
, "norbind"))
945 log_error("Unknown options: %s", eq
);
953 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
958 r
= sd_bus_message_close_container(m
);
962 r
= sd_bus_message_close_container(m
);
964 log_error("Unknown assignment %s.", assignment
);
970 return bus_log_create_error(r
);
972 r
= sd_bus_message_close_container(m
);
974 return bus_log_create_error(r
);
979 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
986 r
= bus_append_unit_property_assignment(m
, *i
);
994 typedef struct BusWaitForJobs
{
1001 sd_bus_slot
*slot_job_removed
;
1002 sd_bus_slot
*slot_disconnected
;
1005 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1008 log_error("Warning! D-Bus connection terminated.");
1009 sd_bus_close(sd_bus_message_get_bus(m
));
1014 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1015 const char *path
, *unit
, *result
;
1016 BusWaitForJobs
*d
= userdata
;
1024 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1026 bus_log_parse_error(r
);
1030 found
= set_remove(d
->jobs
, (char*) path
);
1036 if (!isempty(result
))
1037 d
->result
= strdup(result
);
1040 d
->name
= strdup(unit
);
1045 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1049 set_free_free(d
->jobs
);
1051 sd_bus_slot_unref(d
->slot_disconnected
);
1052 sd_bus_slot_unref(d
->slot_job_removed
);
1054 sd_bus_unref(d
->bus
);
1062 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1063 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1069 d
= new0(BusWaitForJobs
, 1);
1073 d
->bus
= sd_bus_ref(bus
);
1075 /* When we are a bus client we match by sender. Direct
1076 * connections OTOH have no initialized sender field, and
1077 * hence we ignore the sender then */
1078 r
= sd_bus_add_match(
1080 &d
->slot_job_removed
,
1083 "sender='org.freedesktop.systemd1',"
1084 "interface='org.freedesktop.systemd1.Manager',"
1085 "member='JobRemoved',"
1086 "path='/org/freedesktop/systemd1'" :
1088 "interface='org.freedesktop.systemd1.Manager',"
1089 "member='JobRemoved',"
1090 "path='/org/freedesktop/systemd1'",
1091 match_job_removed
, d
);
1095 r
= sd_bus_add_match(
1097 &d
->slot_disconnected
,
1099 "sender='org.freedesktop.DBus.Local',"
1100 "interface='org.freedesktop.DBus.Local',"
1101 "member='Disconnected'",
1102 match_disconnected
, d
);
1112 static int bus_process_wait(sd_bus
*bus
) {
1116 r
= sd_bus_process(bus
, NULL
);
1122 r
= sd_bus_wait(bus
, (uint64_t) -1);
1128 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1129 _cleanup_free_
char *dbus_path
= NULL
;
1135 if (!endswith(d
->name
, ".service"))
1138 dbus_path
= unit_dbus_path_from_name(d
->name
);
1142 return sd_bus_get_property_string(d
->bus
,
1143 "org.freedesktop.systemd1",
1145 "org.freedesktop.systemd1.Service",
1151 static const struct {
1152 const char *result
, *explanation
;
1153 } explanations
[] = {
1154 { "resources", "of unavailable resources or another system error" },
1155 { "protocol", "the service did not take the steps required by its unit configuration" },
1156 { "timeout", "a timeout was exceeded" },
1157 { "exit-code", "the control process exited with error code" },
1158 { "signal", "a fatal signal was delivered to the control process" },
1159 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1160 { "watchdog", "the service failed to send watchdog ping" },
1161 { "start-limit", "start of the service was attempted too often" }
1164 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1165 _cleanup_free_
char *service_shell_quoted
= NULL
;
1166 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1170 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1173 _cleanup_free_
char *t
;
1175 t
= strv_join((char**) extra_args
, " ");
1176 systemctl
= strjoina("systemctl ", t
? : "<args>");
1177 journalctl
= strjoina("journalctl ", t
? : "<args>");
1180 if (!isempty(result
)) {
1183 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1184 if (streq(result
, explanations
[i
].result
))
1187 if (i
< ELEMENTSOF(explanations
)) {
1188 log_error("Job for %s failed because %s.\n"
1189 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1191 explanations
[i
].explanation
,
1193 service_shell_quoted
?: "<service>",
1199 log_error("Job for %s failed.\n"
1200 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1203 service_shell_quoted
?: "<service>",
1207 /* For some results maybe additional explanation is required */
1208 if (streq_ptr(result
, "start-limit"))
1209 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1210 "followed by \"%1$s start %2$s\" again.",
1212 service_shell_quoted
?: "<service>");
1215 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1221 if (streq(d
->result
, "canceled"))
1222 log_error("Job for %s canceled.", strna(d
->name
));
1223 else if (streq(d
->result
, "timeout"))
1224 log_error("Job for %s timed out.", strna(d
->name
));
1225 else if (streq(d
->result
, "dependency"))
1226 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1227 else if (streq(d
->result
, "invalid"))
1228 log_error("%s is not active, cannot reload.", strna(d
->name
));
1229 else if (streq(d
->result
, "assert"))
1230 log_error("Assertion failed on job for %s.", strna(d
->name
));
1231 else if (streq(d
->result
, "unsupported"))
1232 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1233 else if (streq(d
->result
, "collected"))
1234 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1235 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1237 _cleanup_free_
char *result
= NULL
;
1240 q
= bus_job_get_service_result(d
, &result
);
1242 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1244 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1246 log_error("Job failed. See \"journalctl -xe\" for details.");
1250 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1252 else if (streq(d
->result
, "timeout"))
1254 else if (streq(d
->result
, "dependency"))
1256 else if (streq(d
->result
, "invalid"))
1258 else if (streq(d
->result
, "assert"))
1260 else if (streq(d
->result
, "unsupported"))
1262 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1268 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1273 while (!set_isempty(d
->jobs
)) {
1276 q
= bus_process_wait(d
->bus
);
1278 return log_error_errno(q
, "Failed to wait for response: %m");
1281 q
= check_wait_response(d
, quiet
, extra_args
);
1282 /* Return the first error as it is most likely to be
1284 if (q
< 0 && r
== 0)
1287 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1290 d
->name
= mfree(d
->name
);
1291 d
->result
= mfree(d
->result
);
1297 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1302 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1306 return set_put_strdup(d
->jobs
, path
);
1309 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1312 r
= bus_wait_for_jobs_add(d
, path
);
1316 return bus_wait_for_jobs(d
, quiet
, NULL
);
1319 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1320 const char *type
, *path
, *source
;
1323 /* changes is dereferenced when calling unit_file_dump_changes() later,
1324 * so we have to make sure this is not NULL. */
1328 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1330 return bus_log_parse_error(r
);
1332 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1333 /* We expect only "success" changes to be sent over the bus.
1334 Hence, reject anything negative. */
1335 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1338 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1342 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1347 return bus_log_parse_error(r
);
1349 r
= sd_bus_message_exit_container(m
);
1351 return bus_log_parse_error(r
);
1353 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, false);
1359 bool is_const
; /* If false, cgroup_path should be free()'d */
1361 Hashmap
*pids
; /* PID → process name */
1364 struct CGroupInfo
*parent
;
1365 LIST_FIELDS(struct CGroupInfo
, siblings
);
1366 LIST_HEAD(struct CGroupInfo
, children
);
1370 static bool IS_ROOT(const char *p
) {
1371 return isempty(p
) || streq(p
, "/");
1374 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1375 struct CGroupInfo
*parent
= NULL
, *cg
;
1384 cg
= hashmap_get(cgroups
, path
);
1390 if (!IS_ROOT(path
)) {
1393 e
= strrchr(path
, '/');
1397 pp
= strndupa(path
, e
- path
);
1401 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1406 cg
= new0(struct CGroupInfo
, 1);
1411 cg
->cgroup_path
= (char*) path
;
1413 cg
->cgroup_path
= strdup(path
);
1414 if (!cg
->cgroup_path
) {
1420 cg
->is_const
= is_const
;
1421 cg
->parent
= parent
;
1423 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1426 free(cg
->cgroup_path
);
1432 LIST_PREPEND(siblings
, parent
->children
, cg
);
1433 parent
->n_children
++;
1440 static int add_process(
1446 struct CGroupInfo
*cg
;
1453 r
= add_cgroup(cgroups
, path
, true, &cg
);
1457 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1461 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1464 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1468 while (cg
->children
)
1469 remove_cgroup(cgroups
, cg
->children
);
1471 hashmap_remove(cgroups
, cg
->cgroup_path
);
1474 free(cg
->cgroup_path
);
1476 hashmap_free(cg
->pids
);
1479 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1484 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1485 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1490 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1493 static int dump_processes(
1495 const char *cgroup_path
,
1498 OutputFlags flags
) {
1500 struct CGroupInfo
*cg
;
1505 if (IS_ROOT(cgroup_path
))
1508 cg
= hashmap_get(cgroups
, cgroup_path
);
1512 if (!hashmap_isempty(cg
->pids
)) {
1520 /* Order processes by their PID */
1521 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1523 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1524 pids
[n
++] = PTR_TO_PID(pidp
);
1526 assert(n
== hashmap_size(cg
->pids
));
1527 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1529 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1531 for (i
= 0; i
< n
; i
++) {
1532 _cleanup_free_
char *e
= NULL
;
1533 const char *special
;
1536 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1539 if (n_columns
!= 0) {
1542 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1544 e
= ellipsize(name
, k
, 100);
1549 more
= i
+1 < n
|| cg
->children
;
1550 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1552 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1561 struct CGroupInfo
**children
, *child
;
1564 /* Order subcgroups by their name */
1565 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1566 LIST_FOREACH(siblings
, child
, cg
->children
)
1567 children
[n
++] = child
;
1568 assert(n
== cg
->n_children
);
1569 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1572 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1574 for (i
= 0; i
< n
; i
++) {
1575 _cleanup_free_
char *pp
= NULL
;
1576 const char *name
, *special
;
1579 child
= children
[i
];
1581 name
= strrchr(child
->cgroup_path
, '/');
1587 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1589 fputs(prefix
, stdout
);
1590 fputs(special
, stdout
);
1591 fputs(name
, stdout
);
1592 fputc('\n', stdout
);
1594 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1596 pp
= strappend(prefix
, special
);
1600 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1610 static int dump_extra_processes(
1614 OutputFlags flags
) {
1616 _cleanup_free_ pid_t
*pids
= NULL
;
1617 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1618 struct CGroupInfo
*cg
;
1619 size_t n_allocated
= 0, n
= 0, k
;
1623 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1624 * combined, sorted, linear list. */
1626 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1634 if (hashmap_isempty(cg
->pids
))
1637 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1641 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1644 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1645 pids
[n
++] = PTR_TO_PID(pidp
);
1647 r
= hashmap_put(names
, pidp
, (void*) name
);
1656 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1657 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1659 for (k
= 0; k
< n
; k
++) {
1660 _cleanup_free_
char *e
= NULL
;
1663 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1666 if (n_columns
!= 0) {
1669 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1671 e
= ellipsize(name
, z
, 100);
1676 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1678 special_glyph(TRIANGULAR_BULLET
),
1686 int unit_show_processes(
1689 const char *cgroup_path
,
1693 sd_bus_error
*error
) {
1695 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1696 Hashmap
*cgroups
= NULL
;
1697 struct CGroupInfo
*cg
;
1703 if (flags
& OUTPUT_FULL_WIDTH
)
1705 else if (n_columns
<= 0)
1706 n_columns
= columns();
1708 prefix
= strempty(prefix
);
1710 r
= sd_bus_call_method(
1712 "org.freedesktop.systemd1",
1713 "/org/freedesktop/systemd1",
1714 "org.freedesktop.systemd1.Manager",
1723 cgroups
= hashmap_new(&string_hash_ops
);
1727 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1732 const char *path
= NULL
, *name
= NULL
;
1735 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1741 r
= add_process(cgroups
, path
, pid
, name
);
1746 r
= sd_bus_message_exit_container(reply
);
1750 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1754 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
1757 while ((cg
= hashmap_first(cgroups
)))
1758 remove_cgroup(cgroups
, cg
);
1760 hashmap_free(cgroups
);