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",
278 r
= sd_bus_message_append(m
, "v", "s", eq
);
280 else if (STR_IN_SET(field
, "AppArmorProfile", "SmackProcessLabel")) {
292 r
= sd_bus_message_append(m
, "v", "(bs)", ignore
, s
);
294 } else if (streq(field
, "SyslogLevel")) {
297 level
= log_level_from_string(eq
);
299 log_error("Failed to parse %s value %s.", field
, eq
);
303 r
= sd_bus_message_append(m
, "v", "i", level
);
305 } else if (streq(field
, "SyslogFacility")) {
308 facility
= log_facility_unshifted_from_string(eq
);
310 log_error("Failed to parse %s value %s.", field
, eq
);
314 r
= sd_bus_message_append(m
, "v", "i", facility
);
316 } else if (streq(field
, "SecureBits")) {
318 r
= secure_bits_from_string(eq
);
320 log_error("Failed to parse %s value %s.", field
, eq
);
324 r
= sd_bus_message_append(m
, "v", "i", r
);
326 } else if (STR_IN_SET(field
, "CapabilityBoundingSet", "AmbientCapabilities")) {
337 r
= capability_set_from_string(p
, &sum
);
339 log_error("Failed to parse %s value %s.", field
, eq
);
343 sum
= invert
? ~sum
: sum
;
345 r
= sd_bus_message_append(m
, "v", "t", sum
);
347 } else if (streq(field
, "DeviceAllow")) {
350 r
= sd_bus_message_append(m
, "v", "a(ss)", 0);
352 const char *path
, *rwm
, *e
;
356 path
= strndupa(eq
, e
- eq
);
363 if (!is_deviceallow_pattern(path
)) {
364 log_error("%s is not a device file in /dev.", path
);
368 r
= sd_bus_message_append(m
, "v", "a(ss)", 1, path
, rwm
);
371 } else if (cgroup_io_limit_type_from_string(field
) >= 0 || STR_IN_SET(field
, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
374 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
376 const char *path
, *bandwidth
, *e
;
381 path
= strndupa(eq
, e
- eq
);
384 log_error("Failed to parse %s value %s.", field
, eq
);
388 if (!path_startswith(path
, "/dev")) {
389 log_error("%s is not a device file in /dev.", path
);
393 if (streq(bandwidth
, "infinity")) {
394 bytes
= CGROUP_LIMIT_MAX
;
396 r
= parse_size(bandwidth
, 1000, &bytes
);
398 log_error("Failed to parse byte value %s.", bandwidth
);
403 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, bytes
);
406 } else if (STR_IN_SET(field
, "IODeviceWeight", "BlockIODeviceWeight")) {
409 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
411 const char *path
, *weight
, *e
;
416 path
= strndupa(eq
, e
- eq
);
419 log_error("Failed to parse %s value %s.", field
, eq
);
423 if (!path_startswith(path
, "/dev")) {
424 log_error("%s is not a device file in /dev.", path
);
428 r
= safe_atou64(weight
, &u
);
430 log_error("Failed to parse %s value %s.", field
, weight
);
433 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, u
);
436 } else if (streq(field
, "CPUSchedulingPolicy")) {
439 n
= sched_policy_from_string(eq
);
441 return log_error_errno(r
, "Failed to parse CPUSchedulingPolicy: %s", eq
);
443 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
445 } else if (streq(field
, "CPUSchedulingPriority")) {
448 r
= safe_atoi(eq
, &n
);
450 return log_error_errno(r
, "Failed to parse CPUSchedulingPriority: %s", eq
);
451 if (!sched_priority_is_valid(n
))
452 return log_error_errno(r
, "Invalid CPUSchedulingPriority: %s", eq
);
454 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
456 } else if (streq(field
, "CPUAffinity")) {
457 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
460 ncpus
= parse_cpu_set(eq
, &cpuset
);
462 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
464 r
= sd_bus_message_open_container(m
, 'v', "ay");
466 return bus_log_create_error(r
);
469 sd_bus_message_append_array(m
, 'y', cpuset
, CPU_ALLOC_SIZE(ncpus
));
471 r
= sd_bus_message_close_container(m
);
473 } else if (streq(field
, "Nice")) {
476 r
= parse_nice(eq
, &n
);
478 return log_error_errno(r
, "Failed to parse nice value: %s", eq
);
480 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
484 } else if (streq(field
, "SystemCallFilter")) {
488 r
= sd_bus_message_open_container(m
, 'v', "bas");
490 return bus_log_create_error(r
);
499 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
501 return bus_log_create_error(r
);
503 r
= sd_bus_message_open_container(m
, 'a', "s");
505 return bus_log_create_error(r
);
507 if (whitelist
!= 0) {
508 r
= sd_bus_message_append_basic(m
, 's', "@default");
510 return bus_log_create_error(r
);
514 _cleanup_free_
char *word
= NULL
;
516 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
518 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
522 r
= sd_bus_message_append_basic(m
, 's', word
);
524 return bus_log_create_error(r
);
527 r
= sd_bus_message_close_container(m
);
529 return bus_log_create_error(r
);
531 r
= sd_bus_message_close_container(m
);
533 } else if (streq(field
, "SystemCallArchitectures")) {
536 r
= sd_bus_message_open_container(m
, 'v', "as");
538 return bus_log_create_error(r
);
540 r
= sd_bus_message_open_container(m
, 'a', "s");
542 return bus_log_create_error(r
);
545 _cleanup_free_
char *word
= NULL
;
547 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
549 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
553 r
= sd_bus_message_append_basic(m
, 's', word
);
555 return bus_log_create_error(r
);
558 r
= sd_bus_message_close_container(m
);
560 return bus_log_create_error(r
);
562 r
= sd_bus_message_close_container(m
);
564 } else if (streq(field
, "SystemCallErrorNumber")) {
567 n
= errno_from_name(eq
);
569 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
571 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
573 } else if (streq(field
, "RestrictAddressFamilies")) {
577 r
= sd_bus_message_open_container(m
, 'v', "bas");
579 return bus_log_create_error(r
);
588 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
590 return bus_log_create_error(r
);
592 r
= sd_bus_message_open_container(m
, 'a', "s");
594 return bus_log_create_error(r
);
597 _cleanup_free_
char *word
= NULL
;
599 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
601 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
605 r
= sd_bus_message_append_basic(m
, 's', word
);
607 return bus_log_create_error(r
);
610 r
= sd_bus_message_close_container(m
);
612 return bus_log_create_error(r
);
614 r
= sd_bus_message_close_container(m
);
618 } else if (streq(field
, "FileDescriptorStoreMax")) {
621 r
= safe_atou(eq
, &u
);
623 return log_error_errno(r
, "Failed to parse file descriptor store limit: %s", eq
);
625 r
= sd_bus_message_append(m
, "v", "u", (uint32_t) u
);
627 } else if (streq(field
, "IOSchedulingClass")) {
630 c
= ioprio_class_from_string(eq
);
632 return log_error_errno(r
, "Failed to parse IO scheduling class: %s", eq
);
634 r
= sd_bus_message_append(m
, "v", "i", (int32_t) c
);
636 } else if (streq(field
, "IOSchedulingPriority")) {
639 r
= ioprio_parse_priority(eq
, &q
);
641 return log_error_errno(r
, "Failed to parse IO scheduling priority: %s", eq
);
643 r
= sd_bus_message_append(m
, "v", "i", (int32_t) q
);
645 } else if (STR_IN_SET(field
, "Environment", "UnsetEnvironment", "PassEnvironment")) {
648 r
= sd_bus_message_open_container(m
, 'v', "as");
650 return bus_log_create_error(r
);
652 r
= sd_bus_message_open_container(m
, 'a', "s");
654 return bus_log_create_error(r
);
657 _cleanup_free_
char *word
= NULL
;
659 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
661 log_error("Failed to parse Environment value %s", eq
);
667 if (streq(field
, "Environment")) {
668 if (!env_assignment_is_valid(word
)) {
669 log_error("Invalid environment assignment: %s", word
);
672 } else if (streq(field
, "UnsetEnvironment")) {
673 if (!env_assignment_is_valid(word
) && !env_name_is_valid(word
)) {
674 log_error("Invalid environment name or assignment: %s", word
);
677 } else { /* PassEnvironment */
678 if (!env_name_is_valid(word
)) {
679 log_error("Invalid environment variable name: %s", word
);
684 r
= sd_bus_message_append_basic(m
, 's', word
);
686 return bus_log_create_error(r
);
689 r
= sd_bus_message_close_container(m
);
691 return bus_log_create_error(r
);
693 r
= sd_bus_message_close_container(m
);
695 } else if (streq(field
, "KillSignal")) {
698 sig
= signal_from_string_try_harder(eq
);
700 log_error("Failed to parse %s value %s.", field
, eq
);
704 r
= sd_bus_message_append(m
, "v", "i", sig
);
706 } else if (streq(field
, "TimerSlackNSec")) {
709 r
= parse_nsec(eq
, &n
);
711 log_error("Failed to parse %s value %s", field
, eq
);
715 r
= sd_bus_message_append(m
, "v", "t", n
);
716 } else if (streq(field
, "OOMScoreAdjust")) {
719 r
= safe_atoi(eq
, &oa
);
721 log_error("Failed to parse %s value %s", field
, eq
);
725 if (!oom_score_adjust_is_valid(oa
)) {
726 log_error("OOM score adjust value out of range");
730 r
= sd_bus_message_append(m
, "v", "i", oa
);
731 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
732 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
735 r
= sd_bus_message_open_container(m
, 'v', "as");
737 return bus_log_create_error(r
);
739 r
= sd_bus_message_open_container(m
, 'a', "s");
741 return bus_log_create_error(r
);
744 _cleanup_free_
char *word
= NULL
;
747 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
749 log_error("Failed to parse %s value %s", field
, eq
);
755 if (!utf8_is_valid(word
)) {
756 log_error("Failed to parse %s value %s", field
, eq
);
760 offset
= word
[0] == '-';
761 offset
+= word
[offset
] == '+';
763 if (!path_is_absolute(word
+ offset
)) {
764 log_error("Failed to parse %s value %s", field
, eq
);
768 path_kill_slashes(word
+ offset
);
770 r
= sd_bus_message_append_basic(m
, 's', word
);
772 return bus_log_create_error(r
);
775 r
= sd_bus_message_close_container(m
);
777 return bus_log_create_error(r
);
779 r
= sd_bus_message_close_container(m
);
781 } else if (streq(field
, "SupplementaryGroups")) {
784 r
= sd_bus_message_open_container(m
, 'v', "as");
786 return bus_log_create_error(r
);
788 r
= sd_bus_message_open_container(m
, 'a', "s");
790 return bus_log_create_error(r
);
793 _cleanup_free_
char *word
= NULL
;
795 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
797 log_error("Failed to parse %s value %s", field
, eq
);
803 if (!valid_user_group_name_or_id(word
)) {
804 log_error("Failed to parse %s value %s", field
, eq
);
808 r
= sd_bus_message_append_basic(m
, 's', word
);
810 return bus_log_create_error(r
);
813 r
= sd_bus_message_close_container(m
);
815 return bus_log_create_error(r
);
817 r
= sd_bus_message_close_container(m
);
819 } else if (STR_IN_SET(field
, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) {
822 r
= parse_mode(eq
, &mode
);
824 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
826 r
= sd_bus_message_append(m
, "v", "u", mode
);
828 } else if (STR_IN_SET(field
, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
831 r
= sd_bus_message_open_container(m
, 'v', "as");
833 return bus_log_create_error(r
);
835 r
= sd_bus_message_open_container(m
, 'a', "s");
837 return bus_log_create_error(r
);
840 _cleanup_free_
char *word
= NULL
;
842 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
844 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
849 r
= sd_bus_message_append_basic(m
, 's', word
);
851 return bus_log_create_error(r
);
854 r
= sd_bus_message_close_container(m
);
856 return bus_log_create_error(r
);
858 r
= sd_bus_message_close_container(m
);
860 } else if (streq(field
, "RestrictNamespaces")) {
862 unsigned long flags
= 0;
869 r
= parse_boolean(eq
);
873 flags
= NAMESPACE_FLAGS_ALL
;
875 r
= namespace_flag_from_string_many(eq
, &flags
);
877 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
881 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
883 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
884 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
885 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
886 else if (streq(field
, "MountFlags")) {
889 r
= mount_propagation_flags_from_string(eq
, &f
);
891 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
893 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
894 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
897 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
901 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
906 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
907 char *s
= NULL
, *d
= NULL
;
908 bool ignore_enoent
= false;
909 uint64_t flags
= MS_REC
;
911 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
913 return log_error_errno(r
, "Failed to parse argument: %m");
919 ignore_enoent
= true;
923 if (p
&& p
[-1] == ':') {
924 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
926 return log_error_errno(r
, "Failed to parse argument: %m");
928 log_error("Missing argument after ':': %s", eq
);
934 if (p
&& p
[-1] == ':') {
935 _cleanup_free_
char *options
= NULL
;
937 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
939 return log_error_errno(r
, "Failed to parse argument: %m");
941 if (isempty(options
) || streq(options
, "rbind"))
943 else if (streq(options
, "norbind"))
946 log_error("Unknown options: %s", eq
);
954 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
959 r
= sd_bus_message_close_container(m
);
963 r
= sd_bus_message_close_container(m
);
965 log_error("Unknown assignment %s.", assignment
);
971 return bus_log_create_error(r
);
973 r
= sd_bus_message_close_container(m
);
975 return bus_log_create_error(r
);
980 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
987 r
= bus_append_unit_property_assignment(m
, *i
);
995 typedef struct BusWaitForJobs
{
1002 sd_bus_slot
*slot_job_removed
;
1003 sd_bus_slot
*slot_disconnected
;
1006 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1009 log_error("Warning! D-Bus connection terminated.");
1010 sd_bus_close(sd_bus_message_get_bus(m
));
1015 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1016 const char *path
, *unit
, *result
;
1017 BusWaitForJobs
*d
= userdata
;
1025 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1027 bus_log_parse_error(r
);
1031 found
= set_remove(d
->jobs
, (char*) path
);
1037 if (!isempty(result
))
1038 d
->result
= strdup(result
);
1041 d
->name
= strdup(unit
);
1046 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1050 set_free_free(d
->jobs
);
1052 sd_bus_slot_unref(d
->slot_disconnected
);
1053 sd_bus_slot_unref(d
->slot_job_removed
);
1055 sd_bus_unref(d
->bus
);
1063 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1064 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1070 d
= new0(BusWaitForJobs
, 1);
1074 d
->bus
= sd_bus_ref(bus
);
1076 /* When we are a bus client we match by sender. Direct
1077 * connections OTOH have no initialized sender field, and
1078 * hence we ignore the sender then */
1079 r
= sd_bus_add_match(
1081 &d
->slot_job_removed
,
1084 "sender='org.freedesktop.systemd1',"
1085 "interface='org.freedesktop.systemd1.Manager',"
1086 "member='JobRemoved',"
1087 "path='/org/freedesktop/systemd1'" :
1089 "interface='org.freedesktop.systemd1.Manager',"
1090 "member='JobRemoved',"
1091 "path='/org/freedesktop/systemd1'",
1092 match_job_removed
, d
);
1096 r
= sd_bus_add_match(
1098 &d
->slot_disconnected
,
1100 "sender='org.freedesktop.DBus.Local',"
1101 "interface='org.freedesktop.DBus.Local',"
1102 "member='Disconnected'",
1103 match_disconnected
, d
);
1113 static int bus_process_wait(sd_bus
*bus
) {
1117 r
= sd_bus_process(bus
, NULL
);
1123 r
= sd_bus_wait(bus
, (uint64_t) -1);
1129 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1130 _cleanup_free_
char *dbus_path
= NULL
;
1136 if (!endswith(d
->name
, ".service"))
1139 dbus_path
= unit_dbus_path_from_name(d
->name
);
1143 return sd_bus_get_property_string(d
->bus
,
1144 "org.freedesktop.systemd1",
1146 "org.freedesktop.systemd1.Service",
1152 static const struct {
1153 const char *result
, *explanation
;
1154 } explanations
[] = {
1155 { "resources", "of unavailable resources or another system error" },
1156 { "protocol", "the service did not take the steps required by its unit configuration" },
1157 { "timeout", "a timeout was exceeded" },
1158 { "exit-code", "the control process exited with error code" },
1159 { "signal", "a fatal signal was delivered to the control process" },
1160 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1161 { "watchdog", "the service failed to send watchdog ping" },
1162 { "start-limit", "start of the service was attempted too often" }
1165 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1166 _cleanup_free_
char *service_shell_quoted
= NULL
;
1167 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1171 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1174 _cleanup_free_
char *t
;
1176 t
= strv_join((char**) extra_args
, " ");
1177 systemctl
= strjoina("systemctl ", t
? : "<args>");
1178 journalctl
= strjoina("journalctl ", t
? : "<args>");
1181 if (!isempty(result
)) {
1184 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1185 if (streq(result
, explanations
[i
].result
))
1188 if (i
< ELEMENTSOF(explanations
)) {
1189 log_error("Job for %s failed because %s.\n"
1190 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1192 explanations
[i
].explanation
,
1194 service_shell_quoted
?: "<service>",
1200 log_error("Job for %s failed.\n"
1201 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1204 service_shell_quoted
?: "<service>",
1208 /* For some results maybe additional explanation is required */
1209 if (streq_ptr(result
, "start-limit"))
1210 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1211 "followed by \"%1$s start %2$s\" again.",
1213 service_shell_quoted
?: "<service>");
1216 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1222 if (streq(d
->result
, "canceled"))
1223 log_error("Job for %s canceled.", strna(d
->name
));
1224 else if (streq(d
->result
, "timeout"))
1225 log_error("Job for %s timed out.", strna(d
->name
));
1226 else if (streq(d
->result
, "dependency"))
1227 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1228 else if (streq(d
->result
, "invalid"))
1229 log_error("%s is not active, cannot reload.", strna(d
->name
));
1230 else if (streq(d
->result
, "assert"))
1231 log_error("Assertion failed on job for %s.", strna(d
->name
));
1232 else if (streq(d
->result
, "unsupported"))
1233 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1234 else if (streq(d
->result
, "collected"))
1235 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1236 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1238 _cleanup_free_
char *result
= NULL
;
1241 q
= bus_job_get_service_result(d
, &result
);
1243 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1245 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1247 log_error("Job failed. See \"journalctl -xe\" for details.");
1251 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1253 else if (streq(d
->result
, "timeout"))
1255 else if (streq(d
->result
, "dependency"))
1257 else if (streq(d
->result
, "invalid"))
1259 else if (streq(d
->result
, "assert"))
1261 else if (streq(d
->result
, "unsupported"))
1263 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1269 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1274 while (!set_isempty(d
->jobs
)) {
1277 q
= bus_process_wait(d
->bus
);
1279 return log_error_errno(q
, "Failed to wait for response: %m");
1282 q
= check_wait_response(d
, quiet
, extra_args
);
1283 /* Return the first error as it is most likely to be
1285 if (q
< 0 && r
== 0)
1288 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1291 d
->name
= mfree(d
->name
);
1292 d
->result
= mfree(d
->result
);
1298 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1303 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1307 return set_put_strdup(d
->jobs
, path
);
1310 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1313 r
= bus_wait_for_jobs_add(d
, path
);
1317 return bus_wait_for_jobs(d
, quiet
, NULL
);
1320 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1321 const char *type
, *path
, *source
;
1324 /* changes is dereferenced when calling unit_file_dump_changes() later,
1325 * so we have to make sure this is not NULL. */
1329 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1331 return bus_log_parse_error(r
);
1333 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1334 /* We expect only "success" changes to be sent over the bus.
1335 Hence, reject anything negative. */
1336 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1339 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1343 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1348 return bus_log_parse_error(r
);
1350 r
= sd_bus_message_exit_container(m
);
1352 return bus_log_parse_error(r
);
1354 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, false);
1360 bool is_const
; /* If false, cgroup_path should be free()'d */
1362 Hashmap
*pids
; /* PID → process name */
1365 struct CGroupInfo
*parent
;
1366 LIST_FIELDS(struct CGroupInfo
, siblings
);
1367 LIST_HEAD(struct CGroupInfo
, children
);
1371 static bool IS_ROOT(const char *p
) {
1372 return isempty(p
) || streq(p
, "/");
1375 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1376 struct CGroupInfo
*parent
= NULL
, *cg
;
1385 cg
= hashmap_get(cgroups
, path
);
1391 if (!IS_ROOT(path
)) {
1394 e
= strrchr(path
, '/');
1398 pp
= strndupa(path
, e
- path
);
1402 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1407 cg
= new0(struct CGroupInfo
, 1);
1412 cg
->cgroup_path
= (char*) path
;
1414 cg
->cgroup_path
= strdup(path
);
1415 if (!cg
->cgroup_path
) {
1421 cg
->is_const
= is_const
;
1422 cg
->parent
= parent
;
1424 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1427 free(cg
->cgroup_path
);
1433 LIST_PREPEND(siblings
, parent
->children
, cg
);
1434 parent
->n_children
++;
1441 static int add_process(
1447 struct CGroupInfo
*cg
;
1454 r
= add_cgroup(cgroups
, path
, true, &cg
);
1458 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1462 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1465 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1469 while (cg
->children
)
1470 remove_cgroup(cgroups
, cg
->children
);
1472 hashmap_remove(cgroups
, cg
->cgroup_path
);
1475 free(cg
->cgroup_path
);
1477 hashmap_free(cg
->pids
);
1480 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1485 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1486 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1491 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1494 static int dump_processes(
1496 const char *cgroup_path
,
1499 OutputFlags flags
) {
1501 struct CGroupInfo
*cg
;
1506 if (IS_ROOT(cgroup_path
))
1509 cg
= hashmap_get(cgroups
, cgroup_path
);
1513 if (!hashmap_isempty(cg
->pids
)) {
1521 /* Order processes by their PID */
1522 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1524 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1525 pids
[n
++] = PTR_TO_PID(pidp
);
1527 assert(n
== hashmap_size(cg
->pids
));
1528 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1530 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1532 for (i
= 0; i
< n
; i
++) {
1533 _cleanup_free_
char *e
= NULL
;
1534 const char *special
;
1537 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1540 if (n_columns
!= 0) {
1543 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1545 e
= ellipsize(name
, k
, 100);
1550 more
= i
+1 < n
|| cg
->children
;
1551 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1553 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1562 struct CGroupInfo
**children
, *child
;
1565 /* Order subcgroups by their name */
1566 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1567 LIST_FOREACH(siblings
, child
, cg
->children
)
1568 children
[n
++] = child
;
1569 assert(n
== cg
->n_children
);
1570 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1573 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1575 for (i
= 0; i
< n
; i
++) {
1576 _cleanup_free_
char *pp
= NULL
;
1577 const char *name
, *special
;
1580 child
= children
[i
];
1582 name
= strrchr(child
->cgroup_path
, '/');
1588 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1590 fputs(prefix
, stdout
);
1591 fputs(special
, stdout
);
1592 fputs(name
, stdout
);
1593 fputc('\n', stdout
);
1595 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1597 pp
= strappend(prefix
, special
);
1601 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1611 static int dump_extra_processes(
1615 OutputFlags flags
) {
1617 _cleanup_free_ pid_t
*pids
= NULL
;
1618 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1619 struct CGroupInfo
*cg
;
1620 size_t n_allocated
= 0, n
= 0, k
;
1624 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1625 * combined, sorted, linear list. */
1627 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1635 if (hashmap_isempty(cg
->pids
))
1638 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1642 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1645 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1646 pids
[n
++] = PTR_TO_PID(pidp
);
1648 r
= hashmap_put(names
, pidp
, (void*) name
);
1657 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1658 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1660 for (k
= 0; k
< n
; k
++) {
1661 _cleanup_free_
char *e
= NULL
;
1664 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1667 if (n_columns
!= 0) {
1670 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1672 e
= ellipsize(name
, z
, 100);
1677 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1679 special_glyph(TRIANGULAR_BULLET
),
1687 int unit_show_processes(
1690 const char *cgroup_path
,
1694 sd_bus_error
*error
) {
1696 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1697 Hashmap
*cgroups
= NULL
;
1698 struct CGroupInfo
*cg
;
1704 if (flags
& OUTPUT_FULL_WIDTH
)
1706 else if (n_columns
<= 0)
1707 n_columns
= columns();
1709 prefix
= strempty(prefix
);
1711 r
= sd_bus_call_method(
1713 "org.freedesktop.systemd1",
1714 "/org/freedesktop/systemd1",
1715 "org.freedesktop.systemd1.Manager",
1724 cgroups
= hashmap_new(&string_hash_ops
);
1728 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1733 const char *path
= NULL
, *name
= NULL
;
1736 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1742 r
= add_process(cgroups
, path
, pid
, name
);
1747 r
= sd_bus_message_exit_container(reply
);
1751 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1755 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
1758 while ((cg
= hashmap_first(cgroups
)))
1759 remove_cgroup(cgroups
, cg
);
1761 hashmap_free(cgroups
);