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
);
187 } else if (streq(field
, "Delegate")) {
189 r
= parse_boolean(eq
);
193 r
= sd_bus_message_append(m
, "s", "DelegateControllers");
197 r
= sd_bus_message_open_container(m
, 'v', "as");
201 r
= sd_bus_message_open_container(m
, 'a', "s");
206 _cleanup_free_
char *word
= NULL
;
208 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
214 return log_error_errno(r
, "Invalid syntax: %s", eq
);
216 r
= sd_bus_message_append(m
, "s", word
);
221 r
= sd_bus_message_close_container(m
);
225 r
= sd_bus_message_close_container(m
);
227 r
= sd_bus_message_append(m
, "sv", "Delegate", "b", r
);
231 } else if (streq(field
, "TasksMax")) {
234 if (isempty(eq
) || streq(eq
, "infinity"))
237 r
= parse_percent(eq
);
239 r
= sd_bus_message_append(m
, "sv", "TasksMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
242 r
= safe_atou64(eq
, &t
);
244 return log_error_errno(r
, "Failed to parse maximum tasks specification %s", assignment
);
249 r
= sd_bus_message_append(m
, "sv", "TasksMax", "t", t
);
253 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
255 return bus_log_create_error(r
);
257 rl
= rlimit_from_string(field
);
262 r
= rlimit_parse(rl
, eq
, &l
);
264 return log_error_errno(r
, "Failed to parse resource limit: %s", eq
);
266 r
= sd_bus_message_append(m
, "v", "t", l
.rlim_max
);
268 return bus_log_create_error(r
);
270 r
= sd_bus_message_close_container(m
);
272 return bus_log_create_error(r
);
274 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
276 return bus_log_create_error(r
);
278 sn
= strjoina(field
, "Soft");
279 r
= sd_bus_message_append(m
, "sv", sn
, "t", l
.rlim_cur
);
281 } else if (STR_IN_SET(field
,
282 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
283 "TasksAccounting", "IPAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem",
284 "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
285 "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
286 "NoNewPrivileges", "SyslogLevelPrefix", "RemainAfterElapse",
287 "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
288 "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS",
289 "CPUSchedulingResetOnFork", "LockPersonality")) {
291 r
= parse_boolean(eq
);
293 return log_error_errno(r
, "Failed to parse boolean assignment %s.", assignment
);
295 r
= sd_bus_message_append(m
, "v", "b", r
);
297 } else if (STR_IN_SET(field
, "CPUWeight", "StartupCPUWeight")) {
300 r
= cg_weight_parse(eq
, &u
);
302 log_error("Failed to parse %s value %s.", field
, eq
);
306 r
= sd_bus_message_append(m
, "v", "t", u
);
308 } else if (STR_IN_SET(field
, "CPUShares", "StartupCPUShares")) {
311 r
= cg_cpu_shares_parse(eq
, &u
);
313 log_error("Failed to parse %s value %s.", field
, eq
);
317 r
= sd_bus_message_append(m
, "v", "t", u
);
319 } else if (STR_IN_SET(field
, "IOWeight", "StartupIOWeight")) {
322 r
= cg_weight_parse(eq
, &u
);
324 log_error("Failed to parse %s value %s.", field
, eq
);
328 r
= sd_bus_message_append(m
, "v", "t", u
);
330 } else if (STR_IN_SET(field
, "BlockIOWeight", "StartupBlockIOWeight")) {
333 r
= cg_blkio_weight_parse(eq
, &u
);
335 log_error("Failed to parse %s value %s.", field
, eq
);
339 r
= sd_bus_message_append(m
, "v", "t", u
);
341 } else if (STR_IN_SET(field
,
342 "User", "Group", "DevicePolicy", "KillMode",
343 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
344 "StandardInput", "StandardOutput", "StandardError",
345 "Description", "Slice", "Type", "WorkingDirectory",
346 "RootDirectory", "SyslogIdentifier", "ProtectSystem",
347 "ProtectHome", "SELinuxContext", "Restart", "RootImage",
348 "NotifyAccess", "RuntimeDirectoryPreserve", "Personality",
350 r
= sd_bus_message_append(m
, "v", "s", eq
);
352 else if (STR_IN_SET(field
, "AppArmorProfile", "SmackProcessLabel")) {
364 r
= sd_bus_message_append(m
, "v", "(bs)", ignore
, s
);
366 } else if (streq(field
, "SyslogLevel")) {
369 level
= log_level_from_string(eq
);
371 log_error("Failed to parse %s value %s.", field
, eq
);
375 r
= sd_bus_message_append(m
, "v", "i", level
);
377 } else if (streq(field
, "SyslogFacility")) {
380 facility
= log_facility_unshifted_from_string(eq
);
382 log_error("Failed to parse %s value %s.", field
, eq
);
386 r
= sd_bus_message_append(m
, "v", "i", facility
);
388 } else if (streq(field
, "SecureBits")) {
390 r
= secure_bits_from_string(eq
);
392 log_error("Failed to parse %s value %s.", field
, eq
);
396 r
= sd_bus_message_append(m
, "v", "i", r
);
398 } else if (STR_IN_SET(field
, "CapabilityBoundingSet", "AmbientCapabilities")) {
409 r
= capability_set_from_string(p
, &sum
);
411 log_error("Failed to parse %s value %s.", field
, eq
);
415 sum
= invert
? ~sum
: sum
;
417 r
= sd_bus_message_append(m
, "v", "t", sum
);
419 } else if (streq(field
, "DeviceAllow")) {
422 r
= sd_bus_message_append(m
, "v", "a(ss)", 0);
424 const char *path
, *rwm
, *e
;
428 path
= strndupa(eq
, e
- eq
);
435 if (!is_deviceallow_pattern(path
)) {
436 log_error("%s is not a device file in /dev.", path
);
440 r
= sd_bus_message_append(m
, "v", "a(ss)", 1, path
, rwm
);
443 } else if (cgroup_io_limit_type_from_string(field
) >= 0 || STR_IN_SET(field
, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
446 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
448 const char *path
, *bandwidth
, *e
;
453 path
= strndupa(eq
, e
- eq
);
456 log_error("Failed to parse %s value %s.", field
, eq
);
460 if (!path_startswith(path
, "/dev")) {
461 log_error("%s is not a device file in /dev.", path
);
465 if (streq(bandwidth
, "infinity")) {
466 bytes
= CGROUP_LIMIT_MAX
;
468 r
= parse_size(bandwidth
, 1000, &bytes
);
470 log_error("Failed to parse byte value %s.", bandwidth
);
475 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, bytes
);
478 } else if (STR_IN_SET(field
, "IODeviceWeight", "BlockIODeviceWeight")) {
481 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
483 const char *path
, *weight
, *e
;
488 path
= strndupa(eq
, e
- eq
);
491 log_error("Failed to parse %s value %s.", field
, eq
);
495 if (!path_startswith(path
, "/dev")) {
496 log_error("%s is not a device file in /dev.", path
);
500 r
= safe_atou64(weight
, &u
);
502 log_error("Failed to parse %s value %s.", field
, weight
);
505 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, u
);
508 } else if (STR_IN_SET(field
, "IPAddressAllow", "IPAddressDeny")) {
511 r
= sd_bus_message_append(m
, "v", "a(iayu)", 0);
513 unsigned char prefixlen
;
514 union in_addr_union prefix
= {};
517 r
= sd_bus_message_open_container(m
, 'v', "a(iayu)");
519 return bus_log_create_error(r
);
521 r
= sd_bus_message_open_container(m
, 'a', "(iayu)");
523 return bus_log_create_error(r
);
525 if (streq(eq
, "any")) {
526 /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */
528 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 0);
530 return bus_log_create_error(r
);
532 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 0);
534 return bus_log_create_error(r
);
536 } else if (is_localhost(eq
)) {
537 /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */
539 prefix
.in
.s_addr
= htobe32(0x7f000000);
540 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 8);
542 return bus_log_create_error(r
);
544 prefix
.in6
= (struct in6_addr
) IN6ADDR_LOOPBACK_INIT
;
545 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 128);
549 } else if (streq(eq
, "link-local")) {
551 /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */
553 prefix
.in
.s_addr
= htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16));
554 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 16);
556 return bus_log_create_error(r
);
558 prefix
.in6
= (struct in6_addr
) {
559 .s6_addr32
[0] = htobe32(0xfe800000)
561 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 64);
563 return bus_log_create_error(r
);
565 } else if (streq(eq
, "multicast")) {
567 /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */
569 prefix
.in
.s_addr
= htobe32((UINT32_C(224) << 24));
570 r
= bus_append_ip_address_access(m
, AF_INET
, &prefix
, 4);
572 return bus_log_create_error(r
);
574 prefix
.in6
= (struct in6_addr
) {
575 .s6_addr32
[0] = htobe32(0xff000000)
577 r
= bus_append_ip_address_access(m
, AF_INET6
, &prefix
, 8);
579 return bus_log_create_error(r
);
582 r
= in_addr_prefix_from_string_auto(eq
, &family
, &prefix
, &prefixlen
);
584 return log_error_errno(r
, "Failed to parse IP address prefix: %s", eq
);
586 r
= bus_append_ip_address_access(m
, family
, &prefix
, prefixlen
);
588 return bus_log_create_error(r
);
591 r
= sd_bus_message_close_container(m
);
593 return bus_log_create_error(r
);
595 r
= sd_bus_message_close_container(m
);
597 return bus_log_create_error(r
);
600 } else if (streq(field
, "CPUSchedulingPolicy")) {
603 n
= sched_policy_from_string(eq
);
605 return log_error_errno(r
, "Failed to parse CPUSchedulingPolicy: %s", eq
);
607 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
609 } else if (streq(field
, "CPUSchedulingPriority")) {
612 r
= safe_atoi(eq
, &n
);
614 return log_error_errno(r
, "Failed to parse CPUSchedulingPriority: %s", eq
);
615 if (!sched_priority_is_valid(n
))
616 return log_error_errno(r
, "Invalid CPUSchedulingPriority: %s", eq
);
618 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
620 } else if (streq(field
, "CPUAffinity")) {
621 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
624 ncpus
= parse_cpu_set(eq
, &cpuset
);
626 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
628 r
= sd_bus_message_open_container(m
, 'v', "ay");
630 return bus_log_create_error(r
);
633 sd_bus_message_append_array(m
, 'y', cpuset
, CPU_ALLOC_SIZE(ncpus
));
635 r
= sd_bus_message_close_container(m
);
637 } else if (streq(field
, "Nice")) {
640 r
= parse_nice(eq
, &n
);
642 return log_error_errno(r
, "Failed to parse nice value: %s", eq
);
644 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
648 } else if (streq(field
, "SystemCallFilter")) {
650 _cleanup_strv_free_
char **l
= NULL
;
660 if (whitelist
!= 0) {
661 r
= strv_extend(&l
, "@default");
667 _cleanup_free_
char *word
= NULL
;
669 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
671 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
675 r
= strv_extend(&l
, word
);
680 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
682 return bus_log_create_error(r
);
684 r
= sd_bus_message_open_container(m
, 'r', "bas");
686 return bus_log_create_error(r
);
688 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
690 return bus_log_create_error(r
);
692 r
= sd_bus_message_append_strv(m
, l
);
694 return bus_log_create_error(r
);
696 r
= sd_bus_message_close_container(m
);
698 return bus_log_create_error(r
);
700 r
= sd_bus_message_close_container(m
);
702 return bus_log_create_error(r
);
704 } else if (streq(field
, "SystemCallArchitectures")) {
707 r
= sd_bus_message_open_container(m
, 'v', "as");
709 return bus_log_create_error(r
);
711 r
= sd_bus_message_open_container(m
, 'a', "s");
713 return bus_log_create_error(r
);
716 _cleanup_free_
char *word
= NULL
;
718 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
720 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
724 r
= sd_bus_message_append_basic(m
, 's', word
);
726 return bus_log_create_error(r
);
729 r
= sd_bus_message_close_container(m
);
731 return bus_log_create_error(r
);
733 r
= sd_bus_message_close_container(m
);
735 } else if (streq(field
, "SystemCallErrorNumber")) {
740 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
742 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
744 } else if (streq(field
, "RestrictAddressFamilies")) {
746 _cleanup_strv_free_
char **l
= NULL
;
756 _cleanup_free_
char *word
= NULL
;
758 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
760 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
764 r
= strv_extend(&l
, word
);
769 r
= sd_bus_message_open_container(m
, 'v', "(bas)");
771 return bus_log_create_error(r
);
773 r
= sd_bus_message_open_container(m
, 'r', "bas");
775 return bus_log_create_error(r
);
777 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
779 return bus_log_create_error(r
);
781 r
= sd_bus_message_append_strv(m
, l
);
783 return bus_log_create_error(r
);
785 r
= sd_bus_message_close_container(m
);
787 return bus_log_create_error(r
);
789 r
= sd_bus_message_close_container(m
);
791 return bus_log_create_error(r
);
793 } else if (streq(field
, "FileDescriptorStoreMax")) {
796 r
= safe_atou(eq
, &u
);
798 return log_error_errno(r
, "Failed to parse file descriptor store limit: %s", eq
);
800 r
= sd_bus_message_append(m
, "v", "u", (uint32_t) u
);
802 } else if (streq(field
, "IOSchedulingClass")) {
805 c
= ioprio_class_from_string(eq
);
807 return log_error_errno(r
, "Failed to parse IO scheduling class: %s", eq
);
809 r
= sd_bus_message_append(m
, "v", "i", (int32_t) c
);
811 } else if (streq(field
, "IOSchedulingPriority")) {
814 r
= ioprio_parse_priority(eq
, &q
);
816 return log_error_errno(r
, "Failed to parse IO scheduling priority: %s", eq
);
818 r
= sd_bus_message_append(m
, "v", "i", (int32_t) q
);
820 } else if (STR_IN_SET(field
, "Environment", "UnsetEnvironment", "PassEnvironment")) {
823 r
= sd_bus_message_open_container(m
, 'v', "as");
825 return bus_log_create_error(r
);
827 r
= sd_bus_message_open_container(m
, 'a', "s");
829 return bus_log_create_error(r
);
832 _cleanup_free_
char *word
= NULL
;
834 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
836 log_error("Failed to parse Environment value %s", eq
);
842 if (streq(field
, "Environment")) {
843 if (!env_assignment_is_valid(word
)) {
844 log_error("Invalid environment assignment: %s", word
);
847 } else if (streq(field
, "UnsetEnvironment")) {
848 if (!env_assignment_is_valid(word
) && !env_name_is_valid(word
)) {
849 log_error("Invalid environment name or assignment: %s", word
);
852 } else { /* PassEnvironment */
853 if (!env_name_is_valid(word
)) {
854 log_error("Invalid environment variable name: %s", word
);
859 r
= sd_bus_message_append_basic(m
, 's', word
);
861 return bus_log_create_error(r
);
864 r
= sd_bus_message_close_container(m
);
866 return bus_log_create_error(r
);
868 r
= sd_bus_message_close_container(m
);
870 } else if (streq(field
, "KillSignal")) {
873 sig
= signal_from_string_try_harder(eq
);
875 log_error("Failed to parse %s value %s.", field
, eq
);
879 r
= sd_bus_message_append(m
, "v", "i", sig
);
881 } else if (streq(field
, "TimerSlackNSec")) {
884 r
= parse_nsec(eq
, &n
);
886 log_error("Failed to parse %s value %s", field
, eq
);
890 r
= sd_bus_message_append(m
, "v", "t", n
);
891 } else if (streq(field
, "OOMScoreAdjust")) {
894 r
= safe_atoi(eq
, &oa
);
896 log_error("Failed to parse %s value %s", field
, eq
);
900 if (!oom_score_adjust_is_valid(oa
)) {
901 log_error("OOM score adjust value out of range");
905 r
= sd_bus_message_append(m
, "v", "i", oa
);
906 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
907 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
910 r
= sd_bus_message_open_container(m
, 'v', "as");
912 return bus_log_create_error(r
);
914 r
= sd_bus_message_open_container(m
, 'a', "s");
916 return bus_log_create_error(r
);
919 _cleanup_free_
char *word
= NULL
;
922 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
924 log_error("Failed to parse %s value %s", field
, eq
);
930 if (!utf8_is_valid(word
)) {
931 log_error("Failed to parse %s value %s", field
, eq
);
935 offset
= word
[0] == '-';
936 offset
+= word
[offset
] == '+';
938 if (!path_is_absolute(word
+ offset
)) {
939 log_error("Failed to parse %s value %s", field
, eq
);
943 path_kill_slashes(word
+ offset
);
945 r
= sd_bus_message_append_basic(m
, 's', word
);
947 return bus_log_create_error(r
);
950 r
= sd_bus_message_close_container(m
);
952 return bus_log_create_error(r
);
954 r
= sd_bus_message_close_container(m
);
956 } else if (streq(field
, "SupplementaryGroups")) {
959 r
= sd_bus_message_open_container(m
, 'v', "as");
961 return bus_log_create_error(r
);
963 r
= sd_bus_message_open_container(m
, 'a', "s");
965 return bus_log_create_error(r
);
968 _cleanup_free_
char *word
= NULL
;
970 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
972 log_error("Failed to parse %s value %s", field
, eq
);
978 if (!valid_user_group_name_or_id(word
)) {
979 log_error("Failed to parse %s value %s", field
, eq
);
983 r
= sd_bus_message_append_basic(m
, 's', word
);
985 return bus_log_create_error(r
);
988 r
= sd_bus_message_close_container(m
);
990 return bus_log_create_error(r
);
992 r
= sd_bus_message_close_container(m
);
994 } else if (STR_IN_SET(field
, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) {
997 r
= parse_mode(eq
, &mode
);
999 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
1001 r
= sd_bus_message_append(m
, "v", "u", mode
);
1003 } else if (STR_IN_SET(field
, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
1006 r
= sd_bus_message_open_container(m
, 'v', "as");
1008 return bus_log_create_error(r
);
1010 r
= sd_bus_message_open_container(m
, 'a', "s");
1012 return bus_log_create_error(r
);
1015 _cleanup_free_
char *word
= NULL
;
1017 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1021 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
1025 r
= sd_bus_message_append_basic(m
, 's', word
);
1027 return bus_log_create_error(r
);
1030 r
= sd_bus_message_close_container(m
);
1032 return bus_log_create_error(r
);
1034 r
= sd_bus_message_close_container(m
);
1036 } else if (streq(field
, "RestrictNamespaces")) {
1037 bool invert
= false;
1038 unsigned long flags
= 0;
1045 r
= parse_boolean(eq
);
1049 flags
= NAMESPACE_FLAGS_ALL
;
1051 r
= namespace_flag_from_string_many(eq
, &flags
);
1053 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
1057 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
1059 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
1060 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
1061 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
1062 else if (streq(field
, "MountFlags")) {
1065 r
= mount_propagation_flags_from_string(eq
, &f
);
1067 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
1069 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
1070 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
1073 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
1077 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
1082 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
1083 char *s
= NULL
, *d
= NULL
;
1084 bool ignore_enoent
= false;
1085 uint64_t flags
= MS_REC
;
1087 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1089 return log_error_errno(r
, "Failed to parse argument: %m");
1095 ignore_enoent
= true;
1099 if (p
&& p
[-1] == ':') {
1100 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1102 return log_error_errno(r
, "Failed to parse argument: %m");
1104 log_error("Missing argument after ':': %s", eq
);
1110 if (p
&& p
[-1] == ':') {
1111 _cleanup_free_
char *options
= NULL
;
1113 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
1115 return log_error_errno(r
, "Failed to parse argument: %m");
1117 if (isempty(options
) || streq(options
, "rbind"))
1119 else if (streq(options
, "norbind"))
1122 log_error("Unknown options: %s", eq
);
1130 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
1135 r
= sd_bus_message_close_container(m
);
1139 r
= sd_bus_message_close_container(m
);
1141 log_error("Unknown assignment %s.", assignment
);
1147 return bus_log_create_error(r
);
1149 r
= sd_bus_message_close_container(m
);
1151 return bus_log_create_error(r
);
1156 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
1162 STRV_FOREACH(i
, l
) {
1163 r
= bus_append_unit_property_assignment(m
, *i
);
1171 typedef struct BusWaitForJobs
{
1178 sd_bus_slot
*slot_job_removed
;
1179 sd_bus_slot
*slot_disconnected
;
1182 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1185 log_error("Warning! D-Bus connection terminated.");
1186 sd_bus_close(sd_bus_message_get_bus(m
));
1191 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1192 const char *path
, *unit
, *result
;
1193 BusWaitForJobs
*d
= userdata
;
1201 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1203 bus_log_parse_error(r
);
1207 found
= set_remove(d
->jobs
, (char*) path
);
1213 if (!isempty(result
))
1214 d
->result
= strdup(result
);
1217 d
->name
= strdup(unit
);
1222 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1226 set_free_free(d
->jobs
);
1228 sd_bus_slot_unref(d
->slot_disconnected
);
1229 sd_bus_slot_unref(d
->slot_job_removed
);
1231 sd_bus_unref(d
->bus
);
1239 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1240 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1246 d
= new0(BusWaitForJobs
, 1);
1250 d
->bus
= sd_bus_ref(bus
);
1252 /* When we are a bus client we match by sender. Direct
1253 * connections OTOH have no initialized sender field, and
1254 * hence we ignore the sender then */
1255 r
= sd_bus_add_match(
1257 &d
->slot_job_removed
,
1260 "sender='org.freedesktop.systemd1',"
1261 "interface='org.freedesktop.systemd1.Manager',"
1262 "member='JobRemoved',"
1263 "path='/org/freedesktop/systemd1'" :
1265 "interface='org.freedesktop.systemd1.Manager',"
1266 "member='JobRemoved',"
1267 "path='/org/freedesktop/systemd1'",
1268 match_job_removed
, d
);
1272 r
= sd_bus_add_match(
1274 &d
->slot_disconnected
,
1276 "sender='org.freedesktop.DBus.Local',"
1277 "interface='org.freedesktop.DBus.Local',"
1278 "member='Disconnected'",
1279 match_disconnected
, d
);
1289 static int bus_process_wait(sd_bus
*bus
) {
1293 r
= sd_bus_process(bus
, NULL
);
1299 r
= sd_bus_wait(bus
, (uint64_t) -1);
1305 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1306 _cleanup_free_
char *dbus_path
= NULL
;
1312 if (!endswith(d
->name
, ".service"))
1315 dbus_path
= unit_dbus_path_from_name(d
->name
);
1319 return sd_bus_get_property_string(d
->bus
,
1320 "org.freedesktop.systemd1",
1322 "org.freedesktop.systemd1.Service",
1328 static const struct {
1329 const char *result
, *explanation
;
1330 } explanations
[] = {
1331 { "resources", "of unavailable resources or another system error" },
1332 { "protocol", "the service did not take the steps required by its unit configuration" },
1333 { "timeout", "a timeout was exceeded" },
1334 { "exit-code", "the control process exited with error code" },
1335 { "signal", "a fatal signal was delivered to the control process" },
1336 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1337 { "watchdog", "the service failed to send watchdog ping" },
1338 { "start-limit", "start of the service was attempted too often" }
1341 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1342 _cleanup_free_
char *service_shell_quoted
= NULL
;
1343 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1347 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1349 if (!strv_isempty((char**) extra_args
)) {
1350 _cleanup_free_
char *t
;
1352 t
= strv_join((char**) extra_args
, " ");
1353 systemctl
= strjoina("systemctl ", t
? : "<args>");
1354 journalctl
= strjoina("journalctl ", t
? : "<args>");
1357 if (!isempty(result
)) {
1360 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1361 if (streq(result
, explanations
[i
].result
))
1364 if (i
< ELEMENTSOF(explanations
)) {
1365 log_error("Job for %s failed because %s.\n"
1366 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1368 explanations
[i
].explanation
,
1370 service_shell_quoted
?: "<service>",
1376 log_error("Job for %s failed.\n"
1377 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1380 service_shell_quoted
?: "<service>",
1384 /* For some results maybe additional explanation is required */
1385 if (streq_ptr(result
, "start-limit"))
1386 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1387 "followed by \"%1$s start %2$s\" again.",
1389 service_shell_quoted
?: "<service>");
1392 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1398 if (streq(d
->result
, "canceled"))
1399 log_error("Job for %s canceled.", strna(d
->name
));
1400 else if (streq(d
->result
, "timeout"))
1401 log_error("Job for %s timed out.", strna(d
->name
));
1402 else if (streq(d
->result
, "dependency"))
1403 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1404 else if (streq(d
->result
, "invalid"))
1405 log_error("%s is not active, cannot reload.", strna(d
->name
));
1406 else if (streq(d
->result
, "assert"))
1407 log_error("Assertion failed on job for %s.", strna(d
->name
));
1408 else if (streq(d
->result
, "unsupported"))
1409 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1410 else if (streq(d
->result
, "collected"))
1411 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1412 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1414 _cleanup_free_
char *result
= NULL
;
1417 q
= bus_job_get_service_result(d
, &result
);
1419 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1421 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1423 log_error("Job failed. See \"journalctl -xe\" for details.");
1427 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1429 else if (streq(d
->result
, "timeout"))
1431 else if (streq(d
->result
, "dependency"))
1433 else if (streq(d
->result
, "invalid"))
1435 else if (streq(d
->result
, "assert"))
1437 else if (streq(d
->result
, "unsupported"))
1439 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1445 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1450 while (!set_isempty(d
->jobs
)) {
1453 q
= bus_process_wait(d
->bus
);
1455 return log_error_errno(q
, "Failed to wait for response: %m");
1458 q
= check_wait_response(d
, quiet
, extra_args
);
1459 /* Return the first error as it is most likely to be
1461 if (q
< 0 && r
== 0)
1464 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1467 d
->name
= mfree(d
->name
);
1468 d
->result
= mfree(d
->result
);
1474 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1479 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1483 return set_put_strdup(d
->jobs
, path
);
1486 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1489 r
= bus_wait_for_jobs_add(d
, path
);
1493 return bus_wait_for_jobs(d
, quiet
, NULL
);
1496 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1497 const char *type
, *path
, *source
;
1500 /* changes is dereferenced when calling unit_file_dump_changes() later,
1501 * so we have to make sure this is not NULL. */
1505 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1507 return bus_log_parse_error(r
);
1509 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1510 /* We expect only "success" changes to be sent over the bus.
1511 Hence, reject anything negative. */
1512 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1515 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1519 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1524 return bus_log_parse_error(r
);
1526 r
= sd_bus_message_exit_container(m
);
1528 return bus_log_parse_error(r
);
1530 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, quiet
);
1536 bool is_const
; /* If false, cgroup_path should be free()'d */
1538 Hashmap
*pids
; /* PID → process name */
1541 struct CGroupInfo
*parent
;
1542 LIST_FIELDS(struct CGroupInfo
, siblings
);
1543 LIST_HEAD(struct CGroupInfo
, children
);
1547 static bool IS_ROOT(const char *p
) {
1548 return isempty(p
) || streq(p
, "/");
1551 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1552 struct CGroupInfo
*parent
= NULL
, *cg
;
1561 cg
= hashmap_get(cgroups
, path
);
1567 if (!IS_ROOT(path
)) {
1570 e
= strrchr(path
, '/');
1574 pp
= strndupa(path
, e
- path
);
1578 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1583 cg
= new0(struct CGroupInfo
, 1);
1588 cg
->cgroup_path
= (char*) path
;
1590 cg
->cgroup_path
= strdup(path
);
1591 if (!cg
->cgroup_path
) {
1597 cg
->is_const
= is_const
;
1598 cg
->parent
= parent
;
1600 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1603 free(cg
->cgroup_path
);
1609 LIST_PREPEND(siblings
, parent
->children
, cg
);
1610 parent
->n_children
++;
1617 static int add_process(
1623 struct CGroupInfo
*cg
;
1630 r
= add_cgroup(cgroups
, path
, true, &cg
);
1634 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1638 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1641 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1645 while (cg
->children
)
1646 remove_cgroup(cgroups
, cg
->children
);
1648 hashmap_remove(cgroups
, cg
->cgroup_path
);
1651 free(cg
->cgroup_path
);
1653 hashmap_free(cg
->pids
);
1656 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1661 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1662 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1667 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1670 static int dump_processes(
1672 const char *cgroup_path
,
1675 OutputFlags flags
) {
1677 struct CGroupInfo
*cg
;
1682 if (IS_ROOT(cgroup_path
))
1685 cg
= hashmap_get(cgroups
, cgroup_path
);
1689 if (!hashmap_isempty(cg
->pids
)) {
1697 /* Order processes by their PID */
1698 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1700 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1701 pids
[n
++] = PTR_TO_PID(pidp
);
1703 assert(n
== hashmap_size(cg
->pids
));
1704 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1706 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1708 for (i
= 0; i
< n
; i
++) {
1709 _cleanup_free_
char *e
= NULL
;
1710 const char *special
;
1713 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1716 if (n_columns
!= 0) {
1719 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1721 e
= ellipsize(name
, k
, 100);
1726 more
= i
+1 < n
|| cg
->children
;
1727 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1729 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1738 struct CGroupInfo
**children
, *child
;
1741 /* Order subcgroups by their name */
1742 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1743 LIST_FOREACH(siblings
, child
, cg
->children
)
1744 children
[n
++] = child
;
1745 assert(n
== cg
->n_children
);
1746 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1749 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1751 for (i
= 0; i
< n
; i
++) {
1752 _cleanup_free_
char *pp
= NULL
;
1753 const char *name
, *special
;
1756 child
= children
[i
];
1758 name
= strrchr(child
->cgroup_path
, '/');
1764 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1766 fputs(prefix
, stdout
);
1767 fputs(special
, stdout
);
1768 fputs(name
, stdout
);
1769 fputc('\n', stdout
);
1771 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1773 pp
= strappend(prefix
, special
);
1777 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1787 static int dump_extra_processes(
1791 OutputFlags flags
) {
1793 _cleanup_free_ pid_t
*pids
= NULL
;
1794 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1795 struct CGroupInfo
*cg
;
1796 size_t n_allocated
= 0, n
= 0, k
;
1800 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1801 * combined, sorted, linear list. */
1803 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1811 if (hashmap_isempty(cg
->pids
))
1814 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1818 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1821 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1822 pids
[n
++] = PTR_TO_PID(pidp
);
1824 r
= hashmap_put(names
, pidp
, (void*) name
);
1833 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1834 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1836 for (k
= 0; k
< n
; k
++) {
1837 _cleanup_free_
char *e
= NULL
;
1840 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1843 if (n_columns
!= 0) {
1846 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1848 e
= ellipsize(name
, z
, 100);
1853 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1855 special_glyph(TRIANGULAR_BULLET
),
1863 int unit_show_processes(
1866 const char *cgroup_path
,
1870 sd_bus_error
*error
) {
1872 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1873 Hashmap
*cgroups
= NULL
;
1874 struct CGroupInfo
*cg
;
1880 if (flags
& OUTPUT_FULL_WIDTH
)
1882 else if (n_columns
<= 0)
1883 n_columns
= columns();
1885 prefix
= strempty(prefix
);
1887 r
= sd_bus_call_method(
1889 "org.freedesktop.systemd1",
1890 "/org/freedesktop/systemd1",
1891 "org.freedesktop.systemd1.Manager",
1900 cgroups
= hashmap_new(&string_hash_ops
);
1904 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1909 const char *path
= NULL
, *name
= NULL
;
1912 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1918 r
= add_process(cgroups
, path
, pid
, name
);
1923 r
= sd_bus_message_exit_container(reply
);
1927 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1931 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
1934 while ((cg
= hashmap_first(cgroups
)))
1935 remove_cgroup(cgroups
, cg
);
1937 hashmap_free(cgroups
);