1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2016 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include "alloc-util.h"
22 #include "bus-internal.h"
23 #include "bus-unit-util.h"
26 #include "cgroup-util.h"
27 #include "cpu-set-util.h"
29 #include "errno-list.h"
32 #include "hostname-util.h"
33 #include "in-addr-util.h"
35 #include "locale-util.h"
36 #include "mount-util.h"
38 #include "parse-util.h"
39 #include "path-util.h"
40 #include "process-util.h"
41 #include "rlimit-util.h"
42 #include "securebits-util.h"
43 #include "signal-util.h"
44 #include "string-util.h"
45 #include "syslog-util.h"
46 #include "terminal-util.h"
47 #include "user-util.h"
51 int bus_parse_unit_info(sd_bus_message
*message
, UnitInfo
*u
) {
57 return sd_bus_message_read(
72 static int bus_append_ip_address_access(sd_bus_message
*m
, int family
, const union in_addr_union
*prefix
, unsigned char prefixlen
) {
78 r
= sd_bus_message_open_container(m
, 'r', "iayu");
82 r
= sd_bus_message_append(m
, "i", family
);
86 r
= sd_bus_message_append_array(m
, 'y', prefix
, FAMILY_ADDRESS_SIZE(family
));
90 r
= sd_bus_message_append(m
, "u", prefixlen
);
94 return sd_bus_message_close_container(m
);
97 int bus_append_unit_property_assignment(sd_bus_message
*m
, const char *assignment
) {
98 const char *eq
, *field
;
105 eq
= strchr(assignment
, '=');
107 log_error("Not an assignment: %s", assignment
);
111 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
113 return bus_log_create_error(r
);
115 field
= strndupa(assignment
, eq
- assignment
);
118 if (streq(field
, "CPUQuota")) {
121 r
= sd_bus_message_append(m
, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY
);
123 r
= parse_percent_unbounded(eq
);
125 log_error_errno(r
, "CPU quota '%s' invalid.", eq
);
129 r
= sd_bus_message_append(m
, "sv", "CPUQuotaPerSecUSec", "t", (usec_t
) r
* USEC_PER_SEC
/ 100U);
134 } else if (streq(field
, "EnvironmentFile")) {
136 r
= sd_bus_message_append(m
, "sv", "EnvironmentFiles", "a(sb)", 1,
137 eq
[0] == '-' ? eq
+ 1 : eq
,
141 } else if (STR_IN_SET(field
, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) {
146 r
= parse_sec(eq
, &t
);
148 return log_error_errno(r
, "Failed to parse %s= parameter: %s", field
, eq
);
151 n
= newa(char, l
+ 2);
155 /* Change suffix Sec → USec */
156 strcpy(mempcpy(n
, field
, l
- 3), "USec");
157 r
= sd_bus_message_append(m
, "sv", n
, "t", t
);
160 } else if (streq(field
, "LogExtraFields")) {
162 r
= sd_bus_message_append(m
, "s", "LogExtraFields");
166 r
= sd_bus_message_open_container(m
, 'v', "aay");
170 r
= sd_bus_message_open_container(m
, 'a', "ay");
174 r
= sd_bus_message_append_array(m
, 'y', eq
, strlen(eq
));
178 r
= sd_bus_message_close_container(m
);
182 r
= sd_bus_message_close_container(m
);
185 } else if (STR_IN_SET(field
, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) {
188 if (isempty(eq
) || streq(eq
, "infinity"))
189 bytes
= CGROUP_LIMIT_MAX
;
191 r
= parse_percent(eq
);
195 /* When this is a percentage we'll convert this into a relative value in the range
196 * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
197 * ones). This way the physical memory size can be determined server-side */
199 n
= strjoina(field
, "Scale");
200 r
= sd_bus_message_append(m
, "sv", n
, "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
204 r
= parse_size(eq
, 1024, &bytes
);
206 return log_error_errno(r
, "Failed to parse bytes specification %s", assignment
);
210 r
= sd_bus_message_append(m
, "sv", field
, "t", bytes
);
213 } else if (streq(field
, "Delegate")) {
215 r
= parse_boolean(eq
);
219 r
= sd_bus_message_append(m
, "s", "DelegateControllers");
223 r
= sd_bus_message_open_container(m
, 'v', "as");
227 r
= sd_bus_message_open_container(m
, 'a', "s");
232 _cleanup_free_
char *word
= NULL
;
234 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
240 return log_error_errno(r
, "Invalid syntax: %s", eq
);
242 r
= sd_bus_message_append(m
, "s", word
);
247 r
= sd_bus_message_close_container(m
);
251 r
= sd_bus_message_close_container(m
);
253 r
= sd_bus_message_append(m
, "sv", "Delegate", "b", r
);
257 } else if (streq(field
, "TasksMax")) {
260 if (isempty(eq
) || streq(eq
, "infinity"))
263 r
= parse_percent(eq
);
265 r
= sd_bus_message_append(m
, "sv", "TasksMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
268 r
= safe_atou64(eq
, &t
);
270 return log_error_errno(r
, "Failed to parse maximum tasks specification %s", assignment
);
275 r
= sd_bus_message_append(m
, "sv", "TasksMax", "t", t
);
279 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
281 return bus_log_create_error(r
);
283 rl
= rlimit_from_string(field
);
288 r
= rlimit_parse(rl
, eq
, &l
);
290 return log_error_errno(r
, "Failed to parse resource limit: %s", eq
);
292 r
= sd_bus_message_append(m
, "v", "t", l
.rlim_max
);
294 return bus_log_create_error(r
);
296 r
= sd_bus_message_close_container(m
);
298 return bus_log_create_error(r
);
300 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
302 return bus_log_create_error(r
);
304 sn
= strjoina(field
, "Soft");
305 r
= sd_bus_message_append(m
, "sv", sn
, "t", l
.rlim_cur
);
307 } else if (STR_IN_SET(field
,
308 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
309 "TasksAccounting", "IPAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem",
310 "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
311 "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
312 "NoNewPrivileges", "SyslogLevelPrefix", "RemainAfterElapse",
313 "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
314 "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS",
315 "CPUSchedulingResetOnFork", "LockPersonality")) {
317 r
= parse_boolean(eq
);
319 return log_error_errno(r
, "Failed to parse boolean assignment %s.", assignment
);
321 r
= sd_bus_message_append(m
, "v", "b", r
);
323 } else if (STR_IN_SET(field
, "CPUWeight", "StartupCPUWeight")) {
326 r
= cg_weight_parse(eq
, &u
);
328 log_error("Failed to parse %s value %s.", field
, eq
);
332 r
= sd_bus_message_append(m
, "v", "t", u
);
334 } else if (STR_IN_SET(field
, "CPUShares", "StartupCPUShares")) {
337 r
= cg_cpu_shares_parse(eq
, &u
);
339 log_error("Failed to parse %s value %s.", field
, eq
);
343 r
= sd_bus_message_append(m
, "v", "t", u
);
345 } else if (STR_IN_SET(field
, "IOWeight", "StartupIOWeight")) {
348 r
= cg_weight_parse(eq
, &u
);
350 log_error("Failed to parse %s value %s.", field
, eq
);
354 r
= sd_bus_message_append(m
, "v", "t", u
);
356 } else if (STR_IN_SET(field
, "BlockIOWeight", "StartupBlockIOWeight")) {
359 r
= cg_blkio_weight_parse(eq
, &u
);
361 log_error("Failed to parse %s value %s.", field
, eq
);
365 r
= sd_bus_message_append(m
, "v", "t", u
);
367 } else if (STR_IN_SET(field
,
368 "User", "Group", "DevicePolicy", "KillMode",
369 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
370 "StandardInput", "StandardOutput", "StandardError",
371 "Description", "Slice", "Type", "WorkingDirectory",
372 "RootDirectory", "SyslogIdentifier", "ProtectSystem",
373 "ProtectHome", "SELinuxContext", "Restart", "RootImage",
374 "NotifyAccess", "RuntimeDirectoryPreserve", "Personality",
375 "KeyringMode", "CollectMode"))
376 r
= sd_bus_message_append(m
, "v", "s", eq
);
378 else if (STR_IN_SET(field
, "AppArmorProfile", "SmackProcessLabel")) {
390 r
= sd_bus_message_append(m
, "v", "(bs)", ignore
, s
);
392 } else if (STR_IN_SET(field
, "SyslogLevel", "LogLevelMax")) {
395 level
= log_level_from_string(eq
);
397 log_error("Failed to parse %s value %s.", field
, eq
);
401 r
= sd_bus_message_append(m
, "v", "i", level
);
403 } else if (streq(field
, "SyslogFacility")) {
406 facility
= log_facility_unshifted_from_string(eq
);
408 log_error("Failed to parse %s value %s.", field
, eq
);
412 r
= sd_bus_message_append(m
, "v", "i", facility
);
414 } else if (streq(field
, "SecureBits")) {
416 r
= secure_bits_from_string(eq
);
418 log_error("Failed to parse %s value %s.", field
, eq
);
422 r
= sd_bus_message_append(m
, "v", "i", r
);
424 } else if (STR_IN_SET(field
, "CapabilityBoundingSet", "AmbientCapabilities")) {
435 r
= capability_set_from_string(p
, &sum
);
437 log_error("Failed to parse %s value %s.", field
, eq
);
441 sum
= invert
? ~sum
: sum
;
443 r
= sd_bus_message_append(m
, "v", "t", sum
);
445 } else if (streq(field
, "DeviceAllow")) {
448 r
= sd_bus_message_append(m
, "v", "a(ss)", 0);
450 const char *path
, *rwm
, *e
;
454 path
= strndupa(eq
, e
- eq
);
461 if (!is_deviceallow_pattern(path
)) {
462 log_error("%s is not a device file in /dev.", path
);
466 r
= sd_bus_message_append(m
, "v", "a(ss)", 1, path
, rwm
);
469 } else if (cgroup_io_limit_type_from_string(field
) >= 0 || STR_IN_SET(field
, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
472 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
474 const char *path
, *bandwidth
, *e
;
479 path
= strndupa(eq
, e
- eq
);
482 log_error("Failed to parse %s value %s.", field
, eq
);
486 if (!path_startswith(path
, "/dev")) {
487 log_error("%s is not a device file in /dev.", path
);
491 if (streq(bandwidth
, "infinity")) {
492 bytes
= CGROUP_LIMIT_MAX
;
494 r
= parse_size(bandwidth
, 1000, &bytes
);
496 log_error("Failed to parse byte value %s.", bandwidth
);
501 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, bytes
);
504 } else if (STR_IN_SET(field
, "IODeviceWeight", "BlockIODeviceWeight")) {
507 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
509 const char *path
, *weight
, *e
;
514 path
= strndupa(eq
, e
- eq
);
517 log_error("Failed to parse %s value %s.", field
, eq
);
521 if (!path_startswith(path
, "/dev")) {
522 log_error("%s is not a device file in /dev.", path
);
526 r
= safe_atou64(weight
, &u
);
528 log_error("Failed to parse %s value %s.", field
, weight
);
531 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, u
);
534 } else if (STR_IN_SET(field
, "IPAddressAllow", "IPAddressDeny")) {
537 r
= sd_bus_message_append(m
, "v", "a(iayu)", 0);
539 unsigned char prefixlen
;
540 union in_addr_union prefix
= {};
543 r
= sd_bus_message_open_container(m
, 'v', "a(iayu)");
545 return bus_log_create_error(r
);
547 r
= sd_bus_message_open_container(m
, 'a', "(iayu)");
549 return bus_log_create_error(r
);
551 if (streq(eq
, "any")) {
552 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
554 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 0);
556 return bus_log_create_error(r
);
558 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 0);
560 return bus_log_create_error(r
);
562 } else if (is_localhost(eq
)) {
563 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
565 prefix
.in
.s_addr
= htobe32(0x7f000000);
566 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 8);
568 return bus_log_create_error(r
);
570 prefix
.in6
= (struct in6_addr
) IN6ADDR_LOOPBACK_INIT
;
571 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 128);
575 } else if (streq(eq
, "link-local")) {
577 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
579 prefix
.in
.s_addr
= htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
580 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 16);
582 return bus_log_create_error(r
);
584 prefix
.in6
= (struct in6_addr
) {
585 .s6_addr32
[0] = htobe32(0xfe800000)
587 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 64);
589 return bus_log_create_error(r
);
591 } else if (streq(eq
, "multicast")) {
593 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
595 prefix
.in
.s_addr
= htobe32((UINT32_C(224) << 24));
596 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 4);
598 return bus_log_create_error(r
);
600 prefix
.in6
= (struct in6_addr
) {
601 .s6_addr32
[0] = htobe32(0xff000000)
603 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 8);
605 return bus_log_create_error(r
);
608 r
= in_addr_prefix_from_string_auto(eq
, &family
, &prefix
, &prefixlen
);
610 return log_error_errno(r
, "Failed to parse IP address prefix: %s", eq
);
612 r
= bus_append_ip_address_access(m
, family
, &prefix
, prefixlen
);
614 return bus_log_create_error(r
);
617 r
= sd_bus_message_close_container(m
);
619 return bus_log_create_error(r
);
621 r
= sd_bus_message_close_container(m
);
623 return bus_log_create_error(r
);
626 } else if (streq(field
, "CPUSchedulingPolicy")) {
629 n
= sched_policy_from_string(eq
);
631 return log_error_errno(r
, "Failed to parse CPUSchedulingPolicy: %s", eq
);
633 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
635 } else if (streq(field
, "CPUSchedulingPriority")) {
638 r
= safe_atoi(eq
, &n
);
640 return log_error_errno(r
, "Failed to parse CPUSchedulingPriority: %s", eq
);
641 if (!sched_priority_is_valid(n
))
642 return log_error_errno(r
, "Invalid CPUSchedulingPriority: %s", eq
);
644 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
646 } else if (streq(field
, "CPUAffinity")) {
647 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
650 ncpus
= parse_cpu_set(eq
, &cpuset
);
652 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
654 r
= sd_bus_message_open_container(m
, 'v', "ay");
656 return bus_log_create_error(r
);
659 sd_bus_message_append_array(m
, 'y', cpuset
, CPU_ALLOC_SIZE(ncpus
));
661 r
= sd_bus_message_close_container(m
);
663 } else if (streq(field
, "Nice")) {
666 r
= parse_nice(eq
, &n
);
668 return log_error_errno(r
, "Failed to parse nice value: %s", eq
);
670 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
674 } else if (streq(field
, "SystemCallFilter")) {
676 _cleanup_strv_free_
char **l
= NULL
;
686 if (whitelist
!= 0) {
687 r
= strv_extend(&l
, "@default");
693 _cleanup_free_
char *word
= NULL
;
695 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
697 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
701 r
= strv_extend(&l
, word
);
706 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
708 return bus_log_create_error(r
);
710 r
= sd_bus_message_open_container(m
, 'r', "bas");
712 return bus_log_create_error(r
);
714 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
716 return bus_log_create_error(r
);
718 r
= sd_bus_message_append_strv(m
, l
);
720 return bus_log_create_error(r
);
722 r
= sd_bus_message_close_container(m
);
724 return bus_log_create_error(r
);
726 r
= sd_bus_message_close_container(m
);
728 return bus_log_create_error(r
);
730 } else if (streq(field
, "SystemCallArchitectures")) {
733 r
= sd_bus_message_open_container(m
, 'v', "as");
735 return bus_log_create_error(r
);
737 r
= sd_bus_message_open_container(m
, 'a', "s");
739 return bus_log_create_error(r
);
742 _cleanup_free_
char *word
= NULL
;
744 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
746 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
750 r
= sd_bus_message_append_basic(m
, 's', word
);
752 return bus_log_create_error(r
);
755 r
= sd_bus_message_close_container(m
);
757 return bus_log_create_error(r
);
759 r
= sd_bus_message_close_container(m
);
761 } else if (streq(field
, "SystemCallErrorNumber")) {
766 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
768 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
770 } else if (streq(field
, "RestrictAddressFamilies")) {
772 _cleanup_strv_free_
char **l
= NULL
;
782 _cleanup_free_
char *word
= NULL
;
784 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
786 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
790 r
= strv_extend(&l
, word
);
795 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
797 return bus_log_create_error(r
);
799 r
= sd_bus_message_open_container(m
, 'r', "bas");
801 return bus_log_create_error(r
);
803 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
805 return bus_log_create_error(r
);
807 r
= sd_bus_message_append_strv(m
, l
);
809 return bus_log_create_error(r
);
811 r
= sd_bus_message_close_container(m
);
813 return bus_log_create_error(r
);
815 r
= sd_bus_message_close_container(m
);
817 return bus_log_create_error(r
);
819 } else if (streq(field
, "FileDescriptorStoreMax")) {
822 r
= safe_atou(eq
, &u
);
824 return log_error_errno(r
, "Failed to parse file descriptor store limit: %s", eq
);
826 r
= sd_bus_message_append(m
, "v", "u", (uint32_t) u
);
828 } else if (streq(field
, "IOSchedulingClass")) {
831 c
= ioprio_class_from_string(eq
);
833 return log_error_errno(r
, "Failed to parse IO scheduling class: %s", eq
);
835 r
= sd_bus_message_append(m
, "v", "i", (int32_t) c
);
837 } else if (streq(field
, "IOSchedulingPriority")) {
840 r
= ioprio_parse_priority(eq
, &q
);
842 return log_error_errno(r
, "Failed to parse IO scheduling priority: %s", eq
);
844 r
= sd_bus_message_append(m
, "v", "i", (int32_t) q
);
846 } else if (STR_IN_SET(field
, "Environment", "UnsetEnvironment", "PassEnvironment")) {
849 r
= sd_bus_message_open_container(m
, 'v', "as");
851 return bus_log_create_error(r
);
853 r
= sd_bus_message_open_container(m
, 'a', "s");
855 return bus_log_create_error(r
);
858 _cleanup_free_
char *word
= NULL
;
860 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
862 log_error("Failed to parse Environment value %s", eq
);
868 if (streq(field
, "Environment")) {
869 if (!env_assignment_is_valid(word
)) {
870 log_error("Invalid environment assignment: %s", word
);
873 } else if (streq(field
, "UnsetEnvironment")) {
874 if (!env_assignment_is_valid(word
) && !env_name_is_valid(word
)) {
875 log_error("Invalid environment name or assignment: %s", word
);
878 } else { /* PassEnvironment */
879 if (!env_name_is_valid(word
)) {
880 log_error("Invalid environment variable name: %s", word
);
885 r
= sd_bus_message_append_basic(m
, 's', word
);
887 return bus_log_create_error(r
);
890 r
= sd_bus_message_close_container(m
);
892 return bus_log_create_error(r
);
894 r
= sd_bus_message_close_container(m
);
896 } else if (streq(field
, "KillSignal")) {
899 sig
= signal_from_string_try_harder(eq
);
901 log_error("Failed to parse %s value %s.", field
, eq
);
905 r
= sd_bus_message_append(m
, "v", "i", sig
);
907 } else if (streq(field
, "TimerSlackNSec")) {
910 r
= parse_nsec(eq
, &n
);
912 log_error("Failed to parse %s value %s", field
, eq
);
916 r
= sd_bus_message_append(m
, "v", "t", n
);
917 } else if (streq(field
, "OOMScoreAdjust")) {
920 r
= safe_atoi(eq
, &oa
);
922 log_error("Failed to parse %s value %s", field
, eq
);
926 if (!oom_score_adjust_is_valid(oa
)) {
927 log_error("OOM score adjust value out of range");
931 r
= sd_bus_message_append(m
, "v", "i", oa
);
932 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
933 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
936 r
= sd_bus_message_open_container(m
, 'v', "as");
938 return bus_log_create_error(r
);
940 r
= sd_bus_message_open_container(m
, 'a', "s");
942 return bus_log_create_error(r
);
945 _cleanup_free_
char *word
= NULL
;
948 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
950 log_error("Failed to parse %s value %s", field
, eq
);
956 if (!utf8_is_valid(word
)) {
957 log_error("Failed to parse %s value %s", field
, eq
);
961 offset
= word
[0] == '-';
962 offset
+= word
[offset
] == '+';
964 if (!path_is_absolute(word
+ offset
)) {
965 log_error("Failed to parse %s value %s", field
, eq
);
969 path_kill_slashes(word
+ offset
);
971 r
= sd_bus_message_append_basic(m
, 's', word
);
973 return bus_log_create_error(r
);
976 r
= sd_bus_message_close_container(m
);
978 return bus_log_create_error(r
);
980 r
= sd_bus_message_close_container(m
);
982 } else if (streq(field
, "SupplementaryGroups")) {
985 r
= sd_bus_message_open_container(m
, 'v', "as");
987 return bus_log_create_error(r
);
989 r
= sd_bus_message_open_container(m
, 'a', "s");
991 return bus_log_create_error(r
);
994 _cleanup_free_
char *word
= NULL
;
996 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
998 log_error("Failed to parse %s value %s", field
, eq
);
1004 if (!valid_user_group_name_or_id(word
)) {
1005 log_error("Failed to parse %s value %s", field
, eq
);
1009 r
= sd_bus_message_append_basic(m
, 's', word
);
1011 return bus_log_create_error(r
);
1014 r
= sd_bus_message_close_container(m
);
1016 return bus_log_create_error(r
);
1018 r
= sd_bus_message_close_container(m
);
1020 } else if (STR_IN_SET(field
, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) {
1023 r
= parse_mode(eq
, &mode
);
1025 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
1027 r
= sd_bus_message_append(m
, "v", "u", mode
);
1029 } else if (STR_IN_SET(field
, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
1032 r
= sd_bus_message_open_container(m
, 'v', "as");
1034 return bus_log_create_error(r
);
1036 r
= sd_bus_message_open_container(m
, 'a', "s");
1038 return bus_log_create_error(r
);
1041 _cleanup_free_
char *word
= NULL
;
1043 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1047 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
1051 r
= sd_bus_message_append_basic(m
, 's', word
);
1053 return bus_log_create_error(r
);
1056 r
= sd_bus_message_close_container(m
);
1058 return bus_log_create_error(r
);
1060 r
= sd_bus_message_close_container(m
);
1062 } else if (streq(field
, "RestrictNamespaces")) {
1063 bool invert
= false;
1064 unsigned long flags
= 0;
1071 r
= parse_boolean(eq
);
1075 flags
= NAMESPACE_FLAGS_ALL
;
1077 r
= namespace_flag_from_string_many(eq
, &flags
);
1079 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
1083 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
1085 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
1086 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
1087 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
1088 else if (streq(field
, "MountFlags")) {
1091 r
= mount_propagation_flags_from_string(eq
, &f
);
1093 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
1095 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
1096 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
1099 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
1103 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
1108 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
1109 char *s
= NULL
, *d
= NULL
;
1110 bool ignore_enoent
= false;
1111 uint64_t flags
= MS_REC
;
1113 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1115 return log_error_errno(r
, "Failed to parse argument: %m");
1121 ignore_enoent
= true;
1125 if (p
&& p
[-1] == ':') {
1126 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1128 return log_error_errno(r
, "Failed to parse argument: %m");
1130 log_error("Missing argument after ':': %s", eq
);
1136 if (p
&& p
[-1] == ':') {
1137 _cleanup_free_
char *options
= NULL
;
1139 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
1141 return log_error_errno(r
, "Failed to parse argument: %m");
1143 if (isempty(options
) || streq(options
, "rbind"))
1145 else if (streq(options
, "norbind"))
1148 log_error("Unknown options: %s", eq
);
1156 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
1161 r
= sd_bus_message_close_container(m
);
1165 r
= sd_bus_message_close_container(m
);
1167 log_error("Unknown assignment %s.", assignment
);
1173 return bus_log_create_error(r
);
1175 r
= sd_bus_message_close_container(m
);
1177 return bus_log_create_error(r
);
1182 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
1188 STRV_FOREACH(i
, l
) {
1189 r
= bus_append_unit_property_assignment(m
, *i
);
1197 typedef struct BusWaitForJobs
{
1204 sd_bus_slot
*slot_job_removed
;
1205 sd_bus_slot
*slot_disconnected
;
1208 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1211 log_error("Warning! D-Bus connection terminated.");
1212 sd_bus_close(sd_bus_message_get_bus(m
));
1217 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1218 const char *path
, *unit
, *result
;
1219 BusWaitForJobs
*d
= userdata
;
1227 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1229 bus_log_parse_error(r
);
1233 found
= set_remove(d
->jobs
, (char*) path
);
1239 if (!isempty(result
))
1240 d
->result
= strdup(result
);
1243 d
->name
= strdup(unit
);
1248 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1252 set_free_free(d
->jobs
);
1254 sd_bus_slot_unref(d
->slot_disconnected
);
1255 sd_bus_slot_unref(d
->slot_job_removed
);
1257 sd_bus_unref(d
->bus
);
1265 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1266 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1272 d
= new0(BusWaitForJobs
, 1);
1276 d
->bus
= sd_bus_ref(bus
);
1278 /* When we are a bus client we match by sender. Direct
1279 * connections OTOH have no initialized sender field, and
1280 * hence we ignore the sender then */
1281 r
= sd_bus_add_match(
1283 &d
->slot_job_removed
,
1286 "sender='org.freedesktop.systemd1',"
1287 "interface='org.freedesktop.systemd1.Manager',"
1288 "member='JobRemoved',"
1289 "path='/org/freedesktop/systemd1'" :
1291 "interface='org.freedesktop.systemd1.Manager',"
1292 "member='JobRemoved',"
1293 "path='/org/freedesktop/systemd1'",
1294 match_job_removed
, d
);
1298 r
= sd_bus_add_match(
1300 &d
->slot_disconnected
,
1302 "sender='org.freedesktop.DBus.Local',"
1303 "interface='org.freedesktop.DBus.Local',"
1304 "member='Disconnected'",
1305 match_disconnected
, d
);
1315 static int bus_process_wait(sd_bus
*bus
) {
1319 r
= sd_bus_process(bus
, NULL
);
1325 r
= sd_bus_wait(bus
, (uint64_t) -1);
1331 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1332 _cleanup_free_
char *dbus_path
= NULL
;
1338 if (!endswith(d
->name
, ".service"))
1341 dbus_path
= unit_dbus_path_from_name(d
->name
);
1345 return sd_bus_get_property_string(d
->bus
,
1346 "org.freedesktop.systemd1",
1348 "org.freedesktop.systemd1.Service",
1354 static const struct {
1355 const char *result
, *explanation
;
1356 } explanations
[] = {
1357 { "resources", "of unavailable resources or another system error" },
1358 { "protocol", "the service did not take the steps required by its unit configuration" },
1359 { "timeout", "a timeout was exceeded" },
1360 { "exit-code", "the control process exited with error code" },
1361 { "signal", "a fatal signal was delivered to the control process" },
1362 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1363 { "watchdog", "the service failed to send watchdog ping" },
1364 { "start-limit", "start of the service was attempted too often" }
1367 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1368 _cleanup_free_
char *service_shell_quoted
= NULL
;
1369 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1373 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1375 if (!strv_isempty((char**) extra_args
)) {
1376 _cleanup_free_
char *t
;
1378 t
= strv_join((char**) extra_args
, " ");
1379 systemctl
= strjoina("systemctl ", t
? : "<args>");
1380 journalctl
= strjoina("journalctl ", t
? : "<args>");
1383 if (!isempty(result
)) {
1386 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1387 if (streq(result
, explanations
[i
].result
))
1390 if (i
< ELEMENTSOF(explanations
)) {
1391 log_error("Job for %s failed because %s.\n"
1392 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1394 explanations
[i
].explanation
,
1396 service_shell_quoted
?: "<service>",
1402 log_error("Job for %s failed.\n"
1403 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1406 service_shell_quoted
?: "<service>",
1410 /* For some results maybe additional explanation is required */
1411 if (streq_ptr(result
, "start-limit"))
1412 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1413 "followed by \"%1$s start %2$s\" again.",
1415 service_shell_quoted
?: "<service>");
1418 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1424 if (streq(d
->result
, "canceled"))
1425 log_error("Job for %s canceled.", strna(d
->name
));
1426 else if (streq(d
->result
, "timeout"))
1427 log_error("Job for %s timed out.", strna(d
->name
));
1428 else if (streq(d
->result
, "dependency"))
1429 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1430 else if (streq(d
->result
, "invalid"))
1431 log_error("%s is not active, cannot reload.", strna(d
->name
));
1432 else if (streq(d
->result
, "assert"))
1433 log_error("Assertion failed on job for %s.", strna(d
->name
));
1434 else if (streq(d
->result
, "unsupported"))
1435 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1436 else if (streq(d
->result
, "collected"))
1437 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1438 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1440 _cleanup_free_
char *result
= NULL
;
1443 q
= bus_job_get_service_result(d
, &result
);
1445 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1447 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1449 log_error("Job failed. See \"journalctl -xe\" for details.");
1453 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1455 else if (streq(d
->result
, "timeout"))
1457 else if (streq(d
->result
, "dependency"))
1459 else if (streq(d
->result
, "invalid"))
1461 else if (streq(d
->result
, "assert"))
1463 else if (streq(d
->result
, "unsupported"))
1465 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1471 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1476 while (!set_isempty(d
->jobs
)) {
1479 q
= bus_process_wait(d
->bus
);
1481 return log_error_errno(q
, "Failed to wait for response: %m");
1484 q
= check_wait_response(d
, quiet
, extra_args
);
1485 /* Return the first error as it is most likely to be
1487 if (q
< 0 && r
== 0)
1490 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1493 d
->name
= mfree(d
->name
);
1494 d
->result
= mfree(d
->result
);
1500 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1505 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1509 return set_put_strdup(d
->jobs
, path
);
1512 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1515 r
= bus_wait_for_jobs_add(d
, path
);
1519 return bus_wait_for_jobs(d
, quiet
, NULL
);
1522 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1523 const char *type
, *path
, *source
;
1526 /* changes is dereferenced when calling unit_file_dump_changes() later,
1527 * so we have to make sure this is not NULL. */
1531 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1533 return bus_log_parse_error(r
);
1535 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1536 /* We expect only "success" changes to be sent over the bus.
1537 Hence, reject anything negative. */
1538 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1541 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1545 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1550 return bus_log_parse_error(r
);
1552 r
= sd_bus_message_exit_container(m
);
1554 return bus_log_parse_error(r
);
1556 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, quiet
);
1562 bool is_const
; /* If false, cgroup_path should be free()'d */
1564 Hashmap
*pids
; /* PID → process name */
1567 struct CGroupInfo
*parent
;
1568 LIST_FIELDS(struct CGroupInfo
, siblings
);
1569 LIST_HEAD(struct CGroupInfo
, children
);
1573 static bool IS_ROOT(const char *p
) {
1574 return isempty(p
) || streq(p
, "/");
1577 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1578 struct CGroupInfo
*parent
= NULL
, *cg
;
1587 cg
= hashmap_get(cgroups
, path
);
1593 if (!IS_ROOT(path
)) {
1596 e
= strrchr(path
, '/');
1600 pp
= strndupa(path
, e
- path
);
1604 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1609 cg
= new0(struct CGroupInfo
, 1);
1614 cg
->cgroup_path
= (char*) path
;
1616 cg
->cgroup_path
= strdup(path
);
1617 if (!cg
->cgroup_path
) {
1623 cg
->is_const
= is_const
;
1624 cg
->parent
= parent
;
1626 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1629 free(cg
->cgroup_path
);
1635 LIST_PREPEND(siblings
, parent
->children
, cg
);
1636 parent
->n_children
++;
1643 static int add_process(
1649 struct CGroupInfo
*cg
;
1656 r
= add_cgroup(cgroups
, path
, true, &cg
);
1660 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1664 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1667 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1671 while (cg
->children
)
1672 remove_cgroup(cgroups
, cg
->children
);
1674 hashmap_remove(cgroups
, cg
->cgroup_path
);
1677 free(cg
->cgroup_path
);
1679 hashmap_free(cg
->pids
);
1682 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1687 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1688 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1693 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1696 static int dump_processes(
1698 const char *cgroup_path
,
1701 OutputFlags flags
) {
1703 struct CGroupInfo
*cg
;
1708 if (IS_ROOT(cgroup_path
))
1711 cg
= hashmap_get(cgroups
, cgroup_path
);
1715 if (!hashmap_isempty(cg
->pids
)) {
1723 /* Order processes by their PID */
1724 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1726 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1727 pids
[n
++] = PTR_TO_PID(pidp
);
1729 assert(n
== hashmap_size(cg
->pids
));
1730 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1732 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1734 for (i
= 0; i
< n
; i
++) {
1735 _cleanup_free_
char *e
= NULL
;
1736 const char *special
;
1739 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1742 if (n_columns
!= 0) {
1745 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1747 e
= ellipsize(name
, k
, 100);
1752 more
= i
+1 < n
|| cg
->children
;
1753 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1755 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1764 struct CGroupInfo
**children
, *child
;
1767 /* Order subcgroups by their name */
1768 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1769 LIST_FOREACH(siblings
, child
, cg
->children
)
1770 children
[n
++] = child
;
1771 assert(n
== cg
->n_children
);
1772 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1775 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1777 for (i
= 0; i
< n
; i
++) {
1778 _cleanup_free_
char *pp
= NULL
;
1779 const char *name
, *special
;
1782 child
= children
[i
];
1784 name
= strrchr(child
->cgroup_path
, '/');
1790 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1792 fputs(prefix
, stdout
);
1793 fputs(special
, stdout
);
1794 fputs(name
, stdout
);
1795 fputc('\n', stdout
);
1797 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1799 pp
= strappend(prefix
, special
);
1803 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1813 static int dump_extra_processes(
1817 OutputFlags flags
) {
1819 _cleanup_free_ pid_t
*pids
= NULL
;
1820 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1821 struct CGroupInfo
*cg
;
1822 size_t n_allocated
= 0, n
= 0, k
;
1826 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1827 * combined, sorted, linear list. */
1829 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1837 if (hashmap_isempty(cg
->pids
))
1840 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1844 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1847 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1848 pids
[n
++] = PTR_TO_PID(pidp
);
1850 r
= hashmap_put(names
, pidp
, (void*) name
);
1859 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1860 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1862 for (k
= 0; k
< n
; k
++) {
1863 _cleanup_free_
char *e
= NULL
;
1866 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1869 if (n_columns
!= 0) {
1872 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1874 e
= ellipsize(name
, z
, 100);
1879 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1881 special_glyph(TRIANGULAR_BULLET
),
1889 int unit_show_processes(
1892 const char *cgroup_path
,
1896 sd_bus_error
*error
) {
1898 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1899 Hashmap
*cgroups
= NULL
;
1900 struct CGroupInfo
*cg
;
1906 if (flags
& OUTPUT_FULL_WIDTH
)
1908 else if (n_columns
<= 0)
1909 n_columns
= columns();
1911 prefix
= strempty(prefix
);
1913 r
= sd_bus_call_method(
1915 "org.freedesktop.systemd1",
1916 "/org/freedesktop/systemd1",
1917 "org.freedesktop.systemd1.Manager",
1926 cgroups
= hashmap_new(&string_hash_ops
);
1930 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1935 const char *path
= NULL
, *name
= NULL
;
1938 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1944 r
= add_process(cgroups
, path
, pid
, name
);
1949 r
= sd_bus_message_exit_container(reply
);
1953 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1957 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
1960 while ((cg
= hashmap_first(cgroups
)))
1961 remove_cgroup(cgroups
, cg
);
1963 hashmap_free(cgroups
);