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"
31 #include "hostname-util.h"
32 #include "in-addr-util.h"
34 #include "locale-util.h"
35 #include "mount-util.h"
37 #include "parse-util.h"
38 #include "path-util.h"
39 #include "process-util.h"
40 #include "rlimit-util.h"
41 #include "securebits-util.h"
42 #include "signal-util.h"
43 #include "string-util.h"
44 #include "syslog-util.h"
45 #include "terminal-util.h"
46 #include "user-util.h"
50 int bus_parse_unit_info(sd_bus_message
*message
, UnitInfo
*u
) {
56 return sd_bus_message_read(
71 static int bus_append_ip_address_access(sd_bus_message
*m
, int family
, const union in_addr_union
*prefix
, unsigned char prefixlen
) {
77 r
= sd_bus_message_open_container(m
, 'r', "iayu");
81 r
= sd_bus_message_append(m
, "i", family
);
85 r
= sd_bus_message_append_array(m
, 'y', prefix
, FAMILY_ADDRESS_SIZE(family
));
89 r
= sd_bus_message_append(m
, "u", prefixlen
);
93 return sd_bus_message_close_container(m
);
96 int bus_append_unit_property_assignment(sd_bus_message
*m
, const char *assignment
) {
97 const char *eq
, *field
;
104 eq
= strchr(assignment
, '=');
106 log_error("Not an assignment: %s", assignment
);
110 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
112 return bus_log_create_error(r
);
114 field
= strndupa(assignment
, eq
- assignment
);
117 if (streq(field
, "CPUQuota")) {
120 r
= sd_bus_message_append(m
, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY
);
122 r
= parse_percent_unbounded(eq
);
124 log_error_errno(r
, "CPU quota '%s' invalid.", eq
);
128 r
= sd_bus_message_append(m
, "sv", "CPUQuotaPerSecUSec", "t", (usec_t
) r
* USEC_PER_SEC
/ 100U);
133 } else if (streq(field
, "EnvironmentFile")) {
135 r
= sd_bus_message_append(m
, "sv", "EnvironmentFiles", "a(sb)", 1,
136 eq
[0] == '-' ? eq
+ 1 : eq
,
140 } else if (STR_IN_SET(field
, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) {
145 r
= parse_sec(eq
, &t
);
147 return log_error_errno(r
, "Failed to parse %s= parameter: %s", field
, eq
);
150 n
= newa(char, l
+ 2);
154 /* Change suffix Sec → USec */
155 strcpy(mempcpy(n
, field
, l
- 3), "USec");
156 r
= sd_bus_message_append(m
, "sv", n
, "t", t
);
159 } else if (STR_IN_SET(field
, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) {
162 if (isempty(eq
) || streq(eq
, "infinity"))
163 bytes
= CGROUP_LIMIT_MAX
;
165 r
= parse_percent(eq
);
169 /* When this is a percentage we'll convert this into a relative value in the range
170 * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
171 * ones). This way the physical memory size can be determined server-side */
173 n
= strjoina(field
, "Scale");
174 r
= sd_bus_message_append(m
, "sv", n
, "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
178 r
= parse_size(eq
, 1024, &bytes
);
180 return log_error_errno(r
, "Failed to parse bytes specification %s", assignment
);
184 r
= sd_bus_message_append(m
, "sv", field
, "t", bytes
);
186 } else if (streq(field
, "TasksMax")) {
189 if (isempty(eq
) || streq(eq
, "infinity"))
192 r
= parse_percent(eq
);
194 r
= sd_bus_message_append(m
, "sv", "TasksMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
197 r
= safe_atou64(eq
, &t
);
199 return log_error_errno(r
, "Failed to parse maximum tasks specification %s", assignment
);
204 r
= sd_bus_message_append(m
, "sv", "TasksMax", "t", t
);
208 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
210 return bus_log_create_error(r
);
212 rl
= rlimit_from_string(field
);
217 r
= rlimit_parse(rl
, eq
, &l
);
219 return log_error_errno(r
, "Failed to parse resource limit: %s", eq
);
221 r
= sd_bus_message_append(m
, "v", "t", l
.rlim_max
);
223 return bus_log_create_error(r
);
225 r
= sd_bus_message_close_container(m
);
227 return bus_log_create_error(r
);
229 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
231 return bus_log_create_error(r
);
233 sn
= strjoina(field
, "Soft");
234 r
= sd_bus_message_append(m
, "sv", sn
, "t", l
.rlim_cur
);
236 } else if (STR_IN_SET(field
,
237 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
238 "TasksAccounting", "IPAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem",
239 "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
240 "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
241 "NoNewPrivileges", "SyslogLevelPrefix", "Delegate", "RemainAfterElapse",
242 "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
243 "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS",
244 "CPUSchedulingResetOnFork", "LockPersonality")) {
246 r
= parse_boolean(eq
);
248 return log_error_errno(r
, "Failed to parse boolean assignment %s.", assignment
);
250 r
= sd_bus_message_append(m
, "v", "b", r
);
252 } else if (STR_IN_SET(field
, "CPUWeight", "StartupCPUWeight")) {
255 r
= cg_weight_parse(eq
, &u
);
257 log_error("Failed to parse %s value %s.", field
, eq
);
261 r
= sd_bus_message_append(m
, "v", "t", u
);
263 } else if (STR_IN_SET(field
, "CPUShares", "StartupCPUShares")) {
266 r
= cg_cpu_shares_parse(eq
, &u
);
268 log_error("Failed to parse %s value %s.", field
, eq
);
272 r
= sd_bus_message_append(m
, "v", "t", u
);
274 } else if (STR_IN_SET(field
, "IOWeight", "StartupIOWeight")) {
277 r
= cg_weight_parse(eq
, &u
);
279 log_error("Failed to parse %s value %s.", field
, eq
);
283 r
= sd_bus_message_append(m
, "v", "t", u
);
285 } else if (STR_IN_SET(field
, "BlockIOWeight", "StartupBlockIOWeight")) {
288 r
= cg_blkio_weight_parse(eq
, &u
);
290 log_error("Failed to parse %s value %s.", field
, eq
);
294 r
= sd_bus_message_append(m
, "v", "t", u
);
296 } else if (STR_IN_SET(field
,
297 "User", "Group", "DevicePolicy", "KillMode",
298 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
299 "StandardInput", "StandardOutput", "StandardError",
300 "Description", "Slice", "Type", "WorkingDirectory",
301 "RootDirectory", "SyslogIdentifier", "ProtectSystem",
302 "ProtectHome", "SELinuxContext", "Restart", "RootImage",
303 "NotifyAccess", "RuntimeDirectoryPreserve", "Personality",
305 r
= sd_bus_message_append(m
, "v", "s", eq
);
307 else if (STR_IN_SET(field
, "AppArmorProfile", "SmackProcessLabel")) {
319 r
= sd_bus_message_append(m
, "v", "(bs)", ignore
, s
);
321 } else if (streq(field
, "SyslogLevel")) {
324 level
= log_level_from_string(eq
);
326 log_error("Failed to parse %s value %s.", field
, eq
);
330 r
= sd_bus_message_append(m
, "v", "i", level
);
332 } else if (streq(field
, "SyslogFacility")) {
335 facility
= log_facility_unshifted_from_string(eq
);
337 log_error("Failed to parse %s value %s.", field
, eq
);
341 r
= sd_bus_message_append(m
, "v", "i", facility
);
343 } else if (streq(field
, "SecureBits")) {
345 r
= secure_bits_from_string(eq
);
347 log_error("Failed to parse %s value %s.", field
, eq
);
351 r
= sd_bus_message_append(m
, "v", "i", r
);
353 } else if (STR_IN_SET(field
, "CapabilityBoundingSet", "AmbientCapabilities")) {
364 r
= capability_set_from_string(p
, &sum
);
366 log_error("Failed to parse %s value %s.", field
, eq
);
370 sum
= invert
? ~sum
: sum
;
372 r
= sd_bus_message_append(m
, "v", "t", sum
);
374 } else if (streq(field
, "DeviceAllow")) {
377 r
= sd_bus_message_append(m
, "v", "a(ss)", 0);
379 const char *path
, *rwm
, *e
;
383 path
= strndupa(eq
, e
- eq
);
390 if (!is_deviceallow_pattern(path
)) {
391 log_error("%s is not a device file in /dev.", path
);
395 r
= sd_bus_message_append(m
, "v", "a(ss)", 1, path
, rwm
);
398 } else if (cgroup_io_limit_type_from_string(field
) >= 0 || STR_IN_SET(field
, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
401 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
403 const char *path
, *bandwidth
, *e
;
408 path
= strndupa(eq
, e
- eq
);
411 log_error("Failed to parse %s value %s.", field
, eq
);
415 if (!path_startswith(path
, "/dev")) {
416 log_error("%s is not a device file in /dev.", path
);
420 if (streq(bandwidth
, "infinity")) {
421 bytes
= CGROUP_LIMIT_MAX
;
423 r
= parse_size(bandwidth
, 1000, &bytes
);
425 log_error("Failed to parse byte value %s.", bandwidth
);
430 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, bytes
);
433 } else if (STR_IN_SET(field
, "IODeviceWeight", "BlockIODeviceWeight")) {
436 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
438 const char *path
, *weight
, *e
;
443 path
= strndupa(eq
, e
- eq
);
446 log_error("Failed to parse %s value %s.", field
, eq
);
450 if (!path_startswith(path
, "/dev")) {
451 log_error("%s is not a device file in /dev.", path
);
455 r
= safe_atou64(weight
, &u
);
457 log_error("Failed to parse %s value %s.", field
, weight
);
460 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, u
);
463 } else if (STR_IN_SET(field
, "IPAddressAllow", "IPAddressDeny")) {
466 r
= sd_bus_message_append(m
, "v", "a(iayu)", 0);
468 unsigned char prefixlen
;
469 union in_addr_union prefix
= {};
472 r
= sd_bus_message_open_container(m
, 'v', "a(iayu)");
474 return bus_log_create_error(r
);
476 r
= sd_bus_message_open_container(m
, 'a', "(iayu)");
478 return bus_log_create_error(r
);
480 if (streq(eq
, "any")) {
481 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
483 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 0);
485 return bus_log_create_error(r
);
487 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 0);
489 return bus_log_create_error(r
);
491 } else if (is_localhost(eq
)) {
492 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
494 prefix
.in
.s_addr
= htobe32(0x7f000000);
495 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 8);
497 return bus_log_create_error(r
);
499 prefix
.in6
= (struct in6_addr
) IN6ADDR_LOOPBACK_INIT
;
500 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 128);
504 } else if (streq(eq
, "link-local")) {
506 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
508 prefix
.in
.s_addr
= htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
509 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 16);
511 return bus_log_create_error(r
);
513 prefix
.in6
= (struct in6_addr
) {
514 .__in6_u
.__u6_addr32
[0] = htobe32(0xfe800000)
516 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 64);
518 return bus_log_create_error(r
);
520 } else if (streq(eq
, "multicast")) {
522 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
524 prefix
.in
.s_addr
= htobe32((UINT32_C(224) << 24));
525 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 4);
527 return bus_log_create_error(r
);
529 prefix
.in6
= (struct in6_addr
) {
530 .__in6_u
.__u6_addr32
[0] = htobe32(0xff000000)
532 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 8);
534 return bus_log_create_error(r
);
537 r
= in_addr_prefix_from_string_auto(eq
, &family
, &prefix
, &prefixlen
);
539 return log_error_errno(r
, "Failed to parse IP address prefix: %s", eq
);
541 r
= bus_append_ip_address_access(m
, family
, &prefix
, prefixlen
);
543 return bus_log_create_error(r
);
546 r
= sd_bus_message_close_container(m
);
548 return bus_log_create_error(r
);
550 r
= sd_bus_message_close_container(m
);
552 return bus_log_create_error(r
);
555 } else if (streq(field
, "CPUSchedulingPolicy")) {
558 n
= sched_policy_from_string(eq
);
560 return log_error_errno(r
, "Failed to parse CPUSchedulingPolicy: %s", eq
);
562 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
564 } else if (streq(field
, "CPUSchedulingPriority")) {
567 r
= safe_atoi(eq
, &n
);
569 return log_error_errno(r
, "Failed to parse CPUSchedulingPriority: %s", eq
);
570 if (!sched_priority_is_valid(n
))
571 return log_error_errno(r
, "Invalid CPUSchedulingPriority: %s", eq
);
573 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
575 } else if (streq(field
, "CPUAffinity")) {
576 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
579 ncpus
= parse_cpu_set(eq
, &cpuset
);
581 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
583 r
= sd_bus_message_open_container(m
, 'v', "ay");
585 return bus_log_create_error(r
);
588 sd_bus_message_append_array(m
, 'y', cpuset
, CPU_ALLOC_SIZE(ncpus
));
590 r
= sd_bus_message_close_container(m
);
592 } else if (streq(field
, "Nice")) {
595 r
= parse_nice(eq
, &n
);
597 return log_error_errno(r
, "Failed to parse nice value: %s", eq
);
599 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
603 } else if (streq(field
, "SystemCallFilter")) {
605 _cleanup_strv_free_
char **l
= NULL
;
615 if (whitelist
!= 0) {
616 r
= strv_extend(&l
, "@default");
622 _cleanup_free_
char *word
= NULL
;
624 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
626 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
630 r
= strv_extend(&l
, word
);
635 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
637 return bus_log_create_error(r
);
639 r
= sd_bus_message_open_container(m
, 'r', "bas");
641 return bus_log_create_error(r
);
643 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
645 return bus_log_create_error(r
);
647 r
= sd_bus_message_append_strv(m
, l
);
649 return bus_log_create_error(r
);
651 r
= sd_bus_message_close_container(m
);
653 return bus_log_create_error(r
);
655 r
= sd_bus_message_close_container(m
);
657 return bus_log_create_error(r
);
659 } else if (streq(field
, "SystemCallArchitectures")) {
662 r
= sd_bus_message_open_container(m
, 'v', "as");
664 return bus_log_create_error(r
);
666 r
= sd_bus_message_open_container(m
, 'a', "s");
668 return bus_log_create_error(r
);
671 _cleanup_free_
char *word
= NULL
;
673 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
675 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
679 r
= sd_bus_message_append_basic(m
, 's', word
);
681 return bus_log_create_error(r
);
684 r
= sd_bus_message_close_container(m
);
686 return bus_log_create_error(r
);
688 r
= sd_bus_message_close_container(m
);
690 } else if (streq(field
, "SystemCallErrorNumber")) {
693 n
= errno_from_name(eq
);
695 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
697 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
699 } else if (streq(field
, "RestrictAddressFamilies")) {
701 _cleanup_strv_free_
char **l
= NULL
;
711 _cleanup_free_
char *word
= NULL
;
713 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
715 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
719 r
= strv_extend(&l
, word
);
724 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
726 return bus_log_create_error(r
);
728 r
= sd_bus_message_open_container(m
, 'r', "bas");
730 return bus_log_create_error(r
);
732 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
734 return bus_log_create_error(r
);
736 r
= sd_bus_message_append_strv(m
, l
);
738 return bus_log_create_error(r
);
740 r
= sd_bus_message_close_container(m
);
742 return bus_log_create_error(r
);
744 r
= sd_bus_message_close_container(m
);
746 return bus_log_create_error(r
);
748 } else if (streq(field
, "FileDescriptorStoreMax")) {
751 r
= safe_atou(eq
, &u
);
753 return log_error_errno(r
, "Failed to parse file descriptor store limit: %s", eq
);
755 r
= sd_bus_message_append(m
, "v", "u", (uint32_t) u
);
757 } else if (streq(field
, "IOSchedulingClass")) {
760 c
= ioprio_class_from_string(eq
);
762 return log_error_errno(r
, "Failed to parse IO scheduling class: %s", eq
);
764 r
= sd_bus_message_append(m
, "v", "i", (int32_t) c
);
766 } else if (streq(field
, "IOSchedulingPriority")) {
769 r
= ioprio_parse_priority(eq
, &q
);
771 return log_error_errno(r
, "Failed to parse IO scheduling priority: %s", eq
);
773 r
= sd_bus_message_append(m
, "v", "i", (int32_t) q
);
775 } else if (STR_IN_SET(field
, "Environment", "UnsetEnvironment", "PassEnvironment")) {
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
|EXTRACT_CUNESCAPE
);
791 log_error("Failed to parse Environment value %s", eq
);
797 if (streq(field
, "Environment")) {
798 if (!env_assignment_is_valid(word
)) {
799 log_error("Invalid environment assignment: %s", word
);
802 } else if (streq(field
, "UnsetEnvironment")) {
803 if (!env_assignment_is_valid(word
) && !env_name_is_valid(word
)) {
804 log_error("Invalid environment name or assignment: %s", word
);
807 } else { /* PassEnvironment */
808 if (!env_name_is_valid(word
)) {
809 log_error("Invalid environment variable name: %s", word
);
814 r
= sd_bus_message_append_basic(m
, 's', word
);
816 return bus_log_create_error(r
);
819 r
= sd_bus_message_close_container(m
);
821 return bus_log_create_error(r
);
823 r
= sd_bus_message_close_container(m
);
825 } else if (streq(field
, "KillSignal")) {
828 sig
= signal_from_string_try_harder(eq
);
830 log_error("Failed to parse %s value %s.", field
, eq
);
834 r
= sd_bus_message_append(m
, "v", "i", sig
);
836 } else if (streq(field
, "TimerSlackNSec")) {
839 r
= parse_nsec(eq
, &n
);
841 log_error("Failed to parse %s value %s", field
, eq
);
845 r
= sd_bus_message_append(m
, "v", "t", n
);
846 } else if (streq(field
, "OOMScoreAdjust")) {
849 r
= safe_atoi(eq
, &oa
);
851 log_error("Failed to parse %s value %s", field
, eq
);
855 if (!oom_score_adjust_is_valid(oa
)) {
856 log_error("OOM score adjust value out of range");
860 r
= sd_bus_message_append(m
, "v", "i", oa
);
861 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
862 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
865 r
= sd_bus_message_open_container(m
, 'v', "as");
867 return bus_log_create_error(r
);
869 r
= sd_bus_message_open_container(m
, 'a', "s");
871 return bus_log_create_error(r
);
874 _cleanup_free_
char *word
= NULL
;
877 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
879 log_error("Failed to parse %s value %s", field
, eq
);
885 if (!utf8_is_valid(word
)) {
886 log_error("Failed to parse %s value %s", field
, eq
);
890 offset
= word
[0] == '-';
891 offset
+= word
[offset
] == '+';
893 if (!path_is_absolute(word
+ offset
)) {
894 log_error("Failed to parse %s value %s", field
, eq
);
898 path_kill_slashes(word
+ offset
);
900 r
= sd_bus_message_append_basic(m
, 's', word
);
902 return bus_log_create_error(r
);
905 r
= sd_bus_message_close_container(m
);
907 return bus_log_create_error(r
);
909 r
= sd_bus_message_close_container(m
);
911 } else if (streq(field
, "SupplementaryGroups")) {
914 r
= sd_bus_message_open_container(m
, 'v', "as");
916 return bus_log_create_error(r
);
918 r
= sd_bus_message_open_container(m
, 'a', "s");
920 return bus_log_create_error(r
);
923 _cleanup_free_
char *word
= NULL
;
925 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
927 log_error("Failed to parse %s value %s", field
, eq
);
933 if (!valid_user_group_name_or_id(word
)) {
934 log_error("Failed to parse %s value %s", field
, eq
);
938 r
= sd_bus_message_append_basic(m
, 's', word
);
940 return bus_log_create_error(r
);
943 r
= sd_bus_message_close_container(m
);
945 return bus_log_create_error(r
);
947 r
= sd_bus_message_close_container(m
);
949 } else if (STR_IN_SET(field
, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) {
952 r
= parse_mode(eq
, &mode
);
954 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
956 r
= sd_bus_message_append(m
, "v", "u", mode
);
958 } else if (STR_IN_SET(field
, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
961 r
= sd_bus_message_open_container(m
, 'v', "as");
963 return bus_log_create_error(r
);
965 r
= sd_bus_message_open_container(m
, 'a', "s");
967 return bus_log_create_error(r
);
970 _cleanup_free_
char *word
= NULL
;
972 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
976 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
980 r
= sd_bus_message_append_basic(m
, 's', word
);
982 return bus_log_create_error(r
);
985 r
= sd_bus_message_close_container(m
);
987 return bus_log_create_error(r
);
989 r
= sd_bus_message_close_container(m
);
991 } else if (streq(field
, "RestrictNamespaces")) {
993 unsigned long flags
= 0;
1000 r
= parse_boolean(eq
);
1004 flags
= NAMESPACE_FLAGS_ALL
;
1006 r
= namespace_flag_from_string_many(eq
, &flags
);
1008 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
1012 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
1014 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
1015 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
1016 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
1017 else if (streq(field
, "MountFlags")) {
1020 r
= mount_propagation_flags_from_string(eq
, &f
);
1022 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
1024 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
1025 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
1028 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
1032 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
1037 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
1038 char *s
= NULL
, *d
= NULL
;
1039 bool ignore_enoent
= false;
1040 uint64_t flags
= MS_REC
;
1042 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1044 return log_error_errno(r
, "Failed to parse argument: %m");
1050 ignore_enoent
= true;
1054 if (p
&& p
[-1] == ':') {
1055 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1057 return log_error_errno(r
, "Failed to parse argument: %m");
1059 log_error("Missing argument after ':': %s", eq
);
1065 if (p
&& p
[-1] == ':') {
1066 _cleanup_free_
char *options
= NULL
;
1068 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
1070 return log_error_errno(r
, "Failed to parse argument: %m");
1072 if (isempty(options
) || streq(options
, "rbind"))
1074 else if (streq(options
, "norbind"))
1077 log_error("Unknown options: %s", eq
);
1085 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
1090 r
= sd_bus_message_close_container(m
);
1094 r
= sd_bus_message_close_container(m
);
1096 log_error("Unknown assignment %s.", assignment
);
1102 return bus_log_create_error(r
);
1104 r
= sd_bus_message_close_container(m
);
1106 return bus_log_create_error(r
);
1111 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
1117 STRV_FOREACH(i
, l
) {
1118 r
= bus_append_unit_property_assignment(m
, *i
);
1126 typedef struct BusWaitForJobs
{
1133 sd_bus_slot
*slot_job_removed
;
1134 sd_bus_slot
*slot_disconnected
;
1137 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1140 log_error("Warning! D-Bus connection terminated.");
1141 sd_bus_close(sd_bus_message_get_bus(m
));
1146 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1147 const char *path
, *unit
, *result
;
1148 BusWaitForJobs
*d
= userdata
;
1156 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1158 bus_log_parse_error(r
);
1162 found
= set_remove(d
->jobs
, (char*) path
);
1168 if (!isempty(result
))
1169 d
->result
= strdup(result
);
1172 d
->name
= strdup(unit
);
1177 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1181 set_free_free(d
->jobs
);
1183 sd_bus_slot_unref(d
->slot_disconnected
);
1184 sd_bus_slot_unref(d
->slot_job_removed
);
1186 sd_bus_unref(d
->bus
);
1194 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1195 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1201 d
= new0(BusWaitForJobs
, 1);
1205 d
->bus
= sd_bus_ref(bus
);
1207 /* When we are a bus client we match by sender. Direct
1208 * connections OTOH have no initialized sender field, and
1209 * hence we ignore the sender then */
1210 r
= sd_bus_add_match(
1212 &d
->slot_job_removed
,
1215 "sender='org.freedesktop.systemd1',"
1216 "interface='org.freedesktop.systemd1.Manager',"
1217 "member='JobRemoved',"
1218 "path='/org/freedesktop/systemd1'" :
1220 "interface='org.freedesktop.systemd1.Manager',"
1221 "member='JobRemoved',"
1222 "path='/org/freedesktop/systemd1'",
1223 match_job_removed
, d
);
1227 r
= sd_bus_add_match(
1229 &d
->slot_disconnected
,
1231 "sender='org.freedesktop.DBus.Local',"
1232 "interface='org.freedesktop.DBus.Local',"
1233 "member='Disconnected'",
1234 match_disconnected
, d
);
1244 static int bus_process_wait(sd_bus
*bus
) {
1248 r
= sd_bus_process(bus
, NULL
);
1254 r
= sd_bus_wait(bus
, (uint64_t) -1);
1260 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1261 _cleanup_free_
char *dbus_path
= NULL
;
1267 if (!endswith(d
->name
, ".service"))
1270 dbus_path
= unit_dbus_path_from_name(d
->name
);
1274 return sd_bus_get_property_string(d
->bus
,
1275 "org.freedesktop.systemd1",
1277 "org.freedesktop.systemd1.Service",
1283 static const struct {
1284 const char *result
, *explanation
;
1285 } explanations
[] = {
1286 { "resources", "of unavailable resources or another system error" },
1287 { "protocol", "the service did not take the steps required by its unit configuration" },
1288 { "timeout", "a timeout was exceeded" },
1289 { "exit-code", "the control process exited with error code" },
1290 { "signal", "a fatal signal was delivered to the control process" },
1291 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1292 { "watchdog", "the service failed to send watchdog ping" },
1293 { "start-limit", "start of the service was attempted too often" }
1296 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1297 _cleanup_free_
char *service_shell_quoted
= NULL
;
1298 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1302 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1305 _cleanup_free_
char *t
;
1307 t
= strv_join((char**) extra_args
, " ");
1308 systemctl
= strjoina("systemctl ", t
? : "<args>");
1309 journalctl
= strjoina("journalctl ", t
? : "<args>");
1312 if (!isempty(result
)) {
1315 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1316 if (streq(result
, explanations
[i
].result
))
1319 if (i
< ELEMENTSOF(explanations
)) {
1320 log_error("Job for %s failed because %s.\n"
1321 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1323 explanations
[i
].explanation
,
1325 service_shell_quoted
?: "<service>",
1331 log_error("Job for %s failed.\n"
1332 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1335 service_shell_quoted
?: "<service>",
1339 /* For some results maybe additional explanation is required */
1340 if (streq_ptr(result
, "start-limit"))
1341 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1342 "followed by \"%1$s start %2$s\" again.",
1344 service_shell_quoted
?: "<service>");
1347 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1353 if (streq(d
->result
, "canceled"))
1354 log_error("Job for %s canceled.", strna(d
->name
));
1355 else if (streq(d
->result
, "timeout"))
1356 log_error("Job for %s timed out.", strna(d
->name
));
1357 else if (streq(d
->result
, "dependency"))
1358 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1359 else if (streq(d
->result
, "invalid"))
1360 log_error("%s is not active, cannot reload.", strna(d
->name
));
1361 else if (streq(d
->result
, "assert"))
1362 log_error("Assertion failed on job for %s.", strna(d
->name
));
1363 else if (streq(d
->result
, "unsupported"))
1364 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1365 else if (streq(d
->result
, "collected"))
1366 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1367 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1369 _cleanup_free_
char *result
= NULL
;
1372 q
= bus_job_get_service_result(d
, &result
);
1374 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1376 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1378 log_error("Job failed. See \"journalctl -xe\" for details.");
1382 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1384 else if (streq(d
->result
, "timeout"))
1386 else if (streq(d
->result
, "dependency"))
1388 else if (streq(d
->result
, "invalid"))
1390 else if (streq(d
->result
, "assert"))
1392 else if (streq(d
->result
, "unsupported"))
1394 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1400 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1405 while (!set_isempty(d
->jobs
)) {
1408 q
= bus_process_wait(d
->bus
);
1410 return log_error_errno(q
, "Failed to wait for response: %m");
1413 q
= check_wait_response(d
, quiet
, extra_args
);
1414 /* Return the first error as it is most likely to be
1416 if (q
< 0 && r
== 0)
1419 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1422 d
->name
= mfree(d
->name
);
1423 d
->result
= mfree(d
->result
);
1429 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1434 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1438 return set_put_strdup(d
->jobs
, path
);
1441 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1444 r
= bus_wait_for_jobs_add(d
, path
);
1448 return bus_wait_for_jobs(d
, quiet
, NULL
);
1451 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1452 const char *type
, *path
, *source
;
1455 /* changes is dereferenced when calling unit_file_dump_changes() later,
1456 * so we have to make sure this is not NULL. */
1460 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1462 return bus_log_parse_error(r
);
1464 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1465 /* We expect only "success" changes to be sent over the bus.
1466 Hence, reject anything negative. */
1467 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1470 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1474 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1479 return bus_log_parse_error(r
);
1481 r
= sd_bus_message_exit_container(m
);
1483 return bus_log_parse_error(r
);
1485 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, quiet
);
1491 bool is_const
; /* If false, cgroup_path should be free()'d */
1493 Hashmap
*pids
; /* PID → process name */
1496 struct CGroupInfo
*parent
;
1497 LIST_FIELDS(struct CGroupInfo
, siblings
);
1498 LIST_HEAD(struct CGroupInfo
, children
);
1502 static bool IS_ROOT(const char *p
) {
1503 return isempty(p
) || streq(p
, "/");
1506 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1507 struct CGroupInfo
*parent
= NULL
, *cg
;
1516 cg
= hashmap_get(cgroups
, path
);
1522 if (!IS_ROOT(path
)) {
1525 e
= strrchr(path
, '/');
1529 pp
= strndupa(path
, e
- path
);
1533 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1538 cg
= new0(struct CGroupInfo
, 1);
1543 cg
->cgroup_path
= (char*) path
;
1545 cg
->cgroup_path
= strdup(path
);
1546 if (!cg
->cgroup_path
) {
1552 cg
->is_const
= is_const
;
1553 cg
->parent
= parent
;
1555 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1558 free(cg
->cgroup_path
);
1564 LIST_PREPEND(siblings
, parent
->children
, cg
);
1565 parent
->n_children
++;
1572 static int add_process(
1578 struct CGroupInfo
*cg
;
1585 r
= add_cgroup(cgroups
, path
, true, &cg
);
1589 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1593 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1596 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1600 while (cg
->children
)
1601 remove_cgroup(cgroups
, cg
->children
);
1603 hashmap_remove(cgroups
, cg
->cgroup_path
);
1606 free(cg
->cgroup_path
);
1608 hashmap_free(cg
->pids
);
1611 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1616 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1617 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1622 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1625 static int dump_processes(
1627 const char *cgroup_path
,
1630 OutputFlags flags
) {
1632 struct CGroupInfo
*cg
;
1637 if (IS_ROOT(cgroup_path
))
1640 cg
= hashmap_get(cgroups
, cgroup_path
);
1644 if (!hashmap_isempty(cg
->pids
)) {
1652 /* Order processes by their PID */
1653 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1655 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1656 pids
[n
++] = PTR_TO_PID(pidp
);
1658 assert(n
== hashmap_size(cg
->pids
));
1659 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1661 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1663 for (i
= 0; i
< n
; i
++) {
1664 _cleanup_free_
char *e
= NULL
;
1665 const char *special
;
1668 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1671 if (n_columns
!= 0) {
1674 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1676 e
= ellipsize(name
, k
, 100);
1681 more
= i
+1 < n
|| cg
->children
;
1682 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1684 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1693 struct CGroupInfo
**children
, *child
;
1696 /* Order subcgroups by their name */
1697 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1698 LIST_FOREACH(siblings
, child
, cg
->children
)
1699 children
[n
++] = child
;
1700 assert(n
== cg
->n_children
);
1701 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1704 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1706 for (i
= 0; i
< n
; i
++) {
1707 _cleanup_free_
char *pp
= NULL
;
1708 const char *name
, *special
;
1711 child
= children
[i
];
1713 name
= strrchr(child
->cgroup_path
, '/');
1719 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1721 fputs(prefix
, stdout
);
1722 fputs(special
, stdout
);
1723 fputs(name
, stdout
);
1724 fputc('\n', stdout
);
1726 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1728 pp
= strappend(prefix
, special
);
1732 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1742 static int dump_extra_processes(
1746 OutputFlags flags
) {
1748 _cleanup_free_ pid_t
*pids
= NULL
;
1749 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1750 struct CGroupInfo
*cg
;
1751 size_t n_allocated
= 0, n
= 0, k
;
1755 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1756 * combined, sorted, linear list. */
1758 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1766 if (hashmap_isempty(cg
->pids
))
1769 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1773 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1776 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1777 pids
[n
++] = PTR_TO_PID(pidp
);
1779 r
= hashmap_put(names
, pidp
, (void*) name
);
1788 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1789 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1791 for (k
= 0; k
< n
; k
++) {
1792 _cleanup_free_
char *e
= NULL
;
1795 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1798 if (n_columns
!= 0) {
1801 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1803 e
= ellipsize(name
, z
, 100);
1808 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1810 special_glyph(TRIANGULAR_BULLET
),
1818 int unit_show_processes(
1821 const char *cgroup_path
,
1825 sd_bus_error
*error
) {
1827 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1828 Hashmap
*cgroups
= NULL
;
1829 struct CGroupInfo
*cg
;
1835 if (flags
& OUTPUT_FULL_WIDTH
)
1837 else if (n_columns
<= 0)
1838 n_columns
= columns();
1840 prefix
= strempty(prefix
);
1842 r
= sd_bus_call_method(
1844 "org.freedesktop.systemd1",
1845 "/org/freedesktop/systemd1",
1846 "org.freedesktop.systemd1.Manager",
1855 cgroups
= hashmap_new(&string_hash_ops
);
1859 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1864 const char *path
= NULL
, *name
= NULL
;
1867 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1873 r
= add_process(cgroups
, path
, pid
, name
);
1878 r
= sd_bus_message_exit_container(reply
);
1882 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1886 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
1889 while ((cg
= hashmap_first(cgroups
)))
1890 remove_cgroup(cgroups
, cg
);
1892 hashmap_free(cgroups
);