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")) {
607 r
= sd_bus_message_open_container(m
, 'v', "bas");
609 return bus_log_create_error(r
);
618 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
620 return bus_log_create_error(r
);
622 r
= sd_bus_message_open_container(m
, 'a', "s");
624 return bus_log_create_error(r
);
626 if (whitelist
!= 0) {
627 r
= sd_bus_message_append_basic(m
, 's', "@default");
629 return bus_log_create_error(r
);
633 _cleanup_free_
char *word
= NULL
;
635 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
637 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
641 r
= sd_bus_message_append_basic(m
, 's', word
);
643 return bus_log_create_error(r
);
646 r
= sd_bus_message_close_container(m
);
648 return bus_log_create_error(r
);
650 r
= sd_bus_message_close_container(m
);
652 } else if (streq(field
, "SystemCallArchitectures")) {
655 r
= sd_bus_message_open_container(m
, 'v', "as");
657 return bus_log_create_error(r
);
659 r
= sd_bus_message_open_container(m
, 'a', "s");
661 return bus_log_create_error(r
);
664 _cleanup_free_
char *word
= NULL
;
666 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
668 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
672 r
= sd_bus_message_append_basic(m
, 's', word
);
674 return bus_log_create_error(r
);
677 r
= sd_bus_message_close_container(m
);
679 return bus_log_create_error(r
);
681 r
= sd_bus_message_close_container(m
);
683 } else if (streq(field
, "SystemCallErrorNumber")) {
686 n
= errno_from_name(eq
);
688 return log_error_errno(r
, "Failed to parse %s value: %s", field
, eq
);
690 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
692 } else if (streq(field
, "RestrictAddressFamilies")) {
696 r
= sd_bus_message_open_container(m
, 'v', "bas");
698 return bus_log_create_error(r
);
707 r
= sd_bus_message_append_basic(m
, 'b', &whitelist
);
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
);
737 } else if (streq(field
, "FileDescriptorStoreMax")) {
740 r
= safe_atou(eq
, &u
);
742 return log_error_errno(r
, "Failed to parse file descriptor store limit: %s", eq
);
744 r
= sd_bus_message_append(m
, "v", "u", (uint32_t) u
);
746 } else if (streq(field
, "IOSchedulingClass")) {
749 c
= ioprio_class_from_string(eq
);
751 return log_error_errno(r
, "Failed to parse IO scheduling class: %s", eq
);
753 r
= sd_bus_message_append(m
, "v", "i", (int32_t) c
);
755 } else if (streq(field
, "IOSchedulingPriority")) {
758 r
= ioprio_parse_priority(eq
, &q
);
760 return log_error_errno(r
, "Failed to parse IO scheduling priority: %s", eq
);
762 r
= sd_bus_message_append(m
, "v", "i", (int32_t) q
);
764 } else if (STR_IN_SET(field
, "Environment", "UnsetEnvironment", "PassEnvironment")) {
767 r
= sd_bus_message_open_container(m
, 'v', "as");
769 return bus_log_create_error(r
);
771 r
= sd_bus_message_open_container(m
, 'a', "s");
773 return bus_log_create_error(r
);
776 _cleanup_free_
char *word
= NULL
;
778 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
780 log_error("Failed to parse Environment value %s", eq
);
786 if (streq(field
, "Environment")) {
787 if (!env_assignment_is_valid(word
)) {
788 log_error("Invalid environment assignment: %s", word
);
791 } else if (streq(field
, "UnsetEnvironment")) {
792 if (!env_assignment_is_valid(word
) && !env_name_is_valid(word
)) {
793 log_error("Invalid environment name or assignment: %s", word
);
796 } else { /* PassEnvironment */
797 if (!env_name_is_valid(word
)) {
798 log_error("Invalid environment variable name: %s", word
);
803 r
= sd_bus_message_append_basic(m
, 's', word
);
805 return bus_log_create_error(r
);
808 r
= sd_bus_message_close_container(m
);
810 return bus_log_create_error(r
);
812 r
= sd_bus_message_close_container(m
);
814 } else if (streq(field
, "KillSignal")) {
817 sig
= signal_from_string_try_harder(eq
);
819 log_error("Failed to parse %s value %s.", field
, eq
);
823 r
= sd_bus_message_append(m
, "v", "i", sig
);
825 } else if (streq(field
, "TimerSlackNSec")) {
828 r
= parse_nsec(eq
, &n
);
830 log_error("Failed to parse %s value %s", field
, eq
);
834 r
= sd_bus_message_append(m
, "v", "t", n
);
835 } else if (streq(field
, "OOMScoreAdjust")) {
838 r
= safe_atoi(eq
, &oa
);
840 log_error("Failed to parse %s value %s", field
, eq
);
844 if (!oom_score_adjust_is_valid(oa
)) {
845 log_error("OOM score adjust value out of range");
849 r
= sd_bus_message_append(m
, "v", "i", oa
);
850 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
851 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
854 r
= sd_bus_message_open_container(m
, 'v', "as");
856 return bus_log_create_error(r
);
858 r
= sd_bus_message_open_container(m
, 'a', "s");
860 return bus_log_create_error(r
);
863 _cleanup_free_
char *word
= NULL
;
866 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
868 log_error("Failed to parse %s value %s", field
, eq
);
874 if (!utf8_is_valid(word
)) {
875 log_error("Failed to parse %s value %s", field
, eq
);
879 offset
= word
[0] == '-';
880 offset
+= word
[offset
] == '+';
882 if (!path_is_absolute(word
+ offset
)) {
883 log_error("Failed to parse %s value %s", field
, eq
);
887 path_kill_slashes(word
+ offset
);
889 r
= sd_bus_message_append_basic(m
, 's', word
);
891 return bus_log_create_error(r
);
894 r
= sd_bus_message_close_container(m
);
896 return bus_log_create_error(r
);
898 r
= sd_bus_message_close_container(m
);
900 } else if (streq(field
, "SupplementaryGroups")) {
903 r
= sd_bus_message_open_container(m
, 'v', "as");
905 return bus_log_create_error(r
);
907 r
= sd_bus_message_open_container(m
, 'a', "s");
909 return bus_log_create_error(r
);
912 _cleanup_free_
char *word
= NULL
;
914 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
916 log_error("Failed to parse %s value %s", field
, eq
);
922 if (!valid_user_group_name_or_id(word
)) {
923 log_error("Failed to parse %s value %s", field
, eq
);
927 r
= sd_bus_message_append_basic(m
, 's', word
);
929 return bus_log_create_error(r
);
932 r
= sd_bus_message_close_container(m
);
934 return bus_log_create_error(r
);
936 r
= sd_bus_message_close_container(m
);
938 } else if (STR_IN_SET(field
, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) {
941 r
= parse_mode(eq
, &mode
);
943 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
945 r
= sd_bus_message_append(m
, "v", "u", mode
);
947 } else if (STR_IN_SET(field
, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
950 r
= sd_bus_message_open_container(m
, 'v', "as");
952 return bus_log_create_error(r
);
954 r
= sd_bus_message_open_container(m
, 'a', "s");
956 return bus_log_create_error(r
);
959 _cleanup_free_
char *word
= NULL
;
961 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
963 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
968 r
= sd_bus_message_append_basic(m
, 's', word
);
970 return bus_log_create_error(r
);
973 r
= sd_bus_message_close_container(m
);
975 return bus_log_create_error(r
);
977 r
= sd_bus_message_close_container(m
);
979 } else if (streq(field
, "RestrictNamespaces")) {
981 unsigned long flags
= 0;
988 r
= parse_boolean(eq
);
992 flags
= NAMESPACE_FLAGS_ALL
;
994 r
= namespace_flag_from_string_many(eq
, &flags
);
996 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
1000 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
1002 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
1003 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
1004 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
1005 else if (streq(field
, "MountFlags")) {
1008 r
= mount_propagation_flags_from_string(eq
, &f
);
1010 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
1012 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
1013 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
1016 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
1020 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
1025 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
1026 char *s
= NULL
, *d
= NULL
;
1027 bool ignore_enoent
= false;
1028 uint64_t flags
= MS_REC
;
1030 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1032 return log_error_errno(r
, "Failed to parse argument: %m");
1038 ignore_enoent
= true;
1042 if (p
&& p
[-1] == ':') {
1043 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1045 return log_error_errno(r
, "Failed to parse argument: %m");
1047 log_error("Missing argument after ':': %s", eq
);
1053 if (p
&& p
[-1] == ':') {
1054 _cleanup_free_
char *options
= NULL
;
1056 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
1058 return log_error_errno(r
, "Failed to parse argument: %m");
1060 if (isempty(options
) || streq(options
, "rbind"))
1062 else if (streq(options
, "norbind"))
1065 log_error("Unknown options: %s", eq
);
1073 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
1078 r
= sd_bus_message_close_container(m
);
1082 r
= sd_bus_message_close_container(m
);
1084 log_error("Unknown assignment %s.", assignment
);
1090 return bus_log_create_error(r
);
1092 r
= sd_bus_message_close_container(m
);
1094 return bus_log_create_error(r
);
1099 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
1105 STRV_FOREACH(i
, l
) {
1106 r
= bus_append_unit_property_assignment(m
, *i
);
1114 typedef struct BusWaitForJobs
{
1121 sd_bus_slot
*slot_job_removed
;
1122 sd_bus_slot
*slot_disconnected
;
1125 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1128 log_error("Warning! D-Bus connection terminated.");
1129 sd_bus_close(sd_bus_message_get_bus(m
));
1134 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1135 const char *path
, *unit
, *result
;
1136 BusWaitForJobs
*d
= userdata
;
1144 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1146 bus_log_parse_error(r
);
1150 found
= set_remove(d
->jobs
, (char*) path
);
1156 if (!isempty(result
))
1157 d
->result
= strdup(result
);
1160 d
->name
= strdup(unit
);
1165 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1169 set_free_free(d
->jobs
);
1171 sd_bus_slot_unref(d
->slot_disconnected
);
1172 sd_bus_slot_unref(d
->slot_job_removed
);
1174 sd_bus_unref(d
->bus
);
1182 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1183 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1189 d
= new0(BusWaitForJobs
, 1);
1193 d
->bus
= sd_bus_ref(bus
);
1195 /* When we are a bus client we match by sender. Direct
1196 * connections OTOH have no initialized sender field, and
1197 * hence we ignore the sender then */
1198 r
= sd_bus_add_match(
1200 &d
->slot_job_removed
,
1203 "sender='org.freedesktop.systemd1',"
1204 "interface='org.freedesktop.systemd1.Manager',"
1205 "member='JobRemoved',"
1206 "path='/org/freedesktop/systemd1'" :
1208 "interface='org.freedesktop.systemd1.Manager',"
1209 "member='JobRemoved',"
1210 "path='/org/freedesktop/systemd1'",
1211 match_job_removed
, d
);
1215 r
= sd_bus_add_match(
1217 &d
->slot_disconnected
,
1219 "sender='org.freedesktop.DBus.Local',"
1220 "interface='org.freedesktop.DBus.Local',"
1221 "member='Disconnected'",
1222 match_disconnected
, d
);
1232 static int bus_process_wait(sd_bus
*bus
) {
1236 r
= sd_bus_process(bus
, NULL
);
1242 r
= sd_bus_wait(bus
, (uint64_t) -1);
1248 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1249 _cleanup_free_
char *dbus_path
= NULL
;
1255 if (!endswith(d
->name
, ".service"))
1258 dbus_path
= unit_dbus_path_from_name(d
->name
);
1262 return sd_bus_get_property_string(d
->bus
,
1263 "org.freedesktop.systemd1",
1265 "org.freedesktop.systemd1.Service",
1271 static const struct {
1272 const char *result
, *explanation
;
1273 } explanations
[] = {
1274 { "resources", "of unavailable resources or another system error" },
1275 { "protocol", "the service did not take the steps required by its unit configuration" },
1276 { "timeout", "a timeout was exceeded" },
1277 { "exit-code", "the control process exited with error code" },
1278 { "signal", "a fatal signal was delivered to the control process" },
1279 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1280 { "watchdog", "the service failed to send watchdog ping" },
1281 { "start-limit", "start of the service was attempted too often" }
1284 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1285 _cleanup_free_
char *service_shell_quoted
= NULL
;
1286 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1290 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1293 _cleanup_free_
char *t
;
1295 t
= strv_join((char**) extra_args
, " ");
1296 systemctl
= strjoina("systemctl ", t
? : "<args>");
1297 journalctl
= strjoina("journalctl ", t
? : "<args>");
1300 if (!isempty(result
)) {
1303 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1304 if (streq(result
, explanations
[i
].result
))
1307 if (i
< ELEMENTSOF(explanations
)) {
1308 log_error("Job for %s failed because %s.\n"
1309 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1311 explanations
[i
].explanation
,
1313 service_shell_quoted
?: "<service>",
1319 log_error("Job for %s failed.\n"
1320 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1323 service_shell_quoted
?: "<service>",
1327 /* For some results maybe additional explanation is required */
1328 if (streq_ptr(result
, "start-limit"))
1329 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1330 "followed by \"%1$s start %2$s\" again.",
1332 service_shell_quoted
?: "<service>");
1335 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1341 if (streq(d
->result
, "canceled"))
1342 log_error("Job for %s canceled.", strna(d
->name
));
1343 else if (streq(d
->result
, "timeout"))
1344 log_error("Job for %s timed out.", strna(d
->name
));
1345 else if (streq(d
->result
, "dependency"))
1346 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1347 else if (streq(d
->result
, "invalid"))
1348 log_error("%s is not active, cannot reload.", strna(d
->name
));
1349 else if (streq(d
->result
, "assert"))
1350 log_error("Assertion failed on job for %s.", strna(d
->name
));
1351 else if (streq(d
->result
, "unsupported"))
1352 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1353 else if (streq(d
->result
, "collected"))
1354 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1355 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1357 _cleanup_free_
char *result
= NULL
;
1360 q
= bus_job_get_service_result(d
, &result
);
1362 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1364 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1366 log_error("Job failed. See \"journalctl -xe\" for details.");
1370 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1372 else if (streq(d
->result
, "timeout"))
1374 else if (streq(d
->result
, "dependency"))
1376 else if (streq(d
->result
, "invalid"))
1378 else if (streq(d
->result
, "assert"))
1380 else if (streq(d
->result
, "unsupported"))
1382 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1388 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1393 while (!set_isempty(d
->jobs
)) {
1396 q
= bus_process_wait(d
->bus
);
1398 return log_error_errno(q
, "Failed to wait for response: %m");
1401 q
= check_wait_response(d
, quiet
, extra_args
);
1402 /* Return the first error as it is most likely to be
1404 if (q
< 0 && r
== 0)
1407 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1410 d
->name
= mfree(d
->name
);
1411 d
->result
= mfree(d
->result
);
1417 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1422 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1426 return set_put_strdup(d
->jobs
, path
);
1429 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1432 r
= bus_wait_for_jobs_add(d
, path
);
1436 return bus_wait_for_jobs(d
, quiet
, NULL
);
1439 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1440 const char *type
, *path
, *source
;
1443 /* changes is dereferenced when calling unit_file_dump_changes() later,
1444 * so we have to make sure this is not NULL. */
1448 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1450 return bus_log_parse_error(r
);
1452 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1453 /* We expect only "success" changes to be sent over the bus.
1454 Hence, reject anything negative. */
1455 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1458 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1462 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1467 return bus_log_parse_error(r
);
1469 r
= sd_bus_message_exit_container(m
);
1471 return bus_log_parse_error(r
);
1473 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, false);
1479 bool is_const
; /* If false, cgroup_path should be free()'d */
1481 Hashmap
*pids
; /* PID → process name */
1484 struct CGroupInfo
*parent
;
1485 LIST_FIELDS(struct CGroupInfo
, siblings
);
1486 LIST_HEAD(struct CGroupInfo
, children
);
1490 static bool IS_ROOT(const char *p
) {
1491 return isempty(p
) || streq(p
, "/");
1494 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1495 struct CGroupInfo
*parent
= NULL
, *cg
;
1504 cg
= hashmap_get(cgroups
, path
);
1510 if (!IS_ROOT(path
)) {
1513 e
= strrchr(path
, '/');
1517 pp
= strndupa(path
, e
- path
);
1521 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1526 cg
= new0(struct CGroupInfo
, 1);
1531 cg
->cgroup_path
= (char*) path
;
1533 cg
->cgroup_path
= strdup(path
);
1534 if (!cg
->cgroup_path
) {
1540 cg
->is_const
= is_const
;
1541 cg
->parent
= parent
;
1543 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1546 free(cg
->cgroup_path
);
1552 LIST_PREPEND(siblings
, parent
->children
, cg
);
1553 parent
->n_children
++;
1560 static int add_process(
1566 struct CGroupInfo
*cg
;
1573 r
= add_cgroup(cgroups
, path
, true, &cg
);
1577 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1581 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1584 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1588 while (cg
->children
)
1589 remove_cgroup(cgroups
, cg
->children
);
1591 hashmap_remove(cgroups
, cg
->cgroup_path
);
1594 free(cg
->cgroup_path
);
1596 hashmap_free(cg
->pids
);
1599 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1604 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1605 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1610 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1613 static int dump_processes(
1615 const char *cgroup_path
,
1618 OutputFlags flags
) {
1620 struct CGroupInfo
*cg
;
1625 if (IS_ROOT(cgroup_path
))
1628 cg
= hashmap_get(cgroups
, cgroup_path
);
1632 if (!hashmap_isempty(cg
->pids
)) {
1640 /* Order processes by their PID */
1641 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1643 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1644 pids
[n
++] = PTR_TO_PID(pidp
);
1646 assert(n
== hashmap_size(cg
->pids
));
1647 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1649 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1651 for (i
= 0; i
< n
; i
++) {
1652 _cleanup_free_
char *e
= NULL
;
1653 const char *special
;
1656 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1659 if (n_columns
!= 0) {
1662 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1664 e
= ellipsize(name
, k
, 100);
1669 more
= i
+1 < n
|| cg
->children
;
1670 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1672 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1681 struct CGroupInfo
**children
, *child
;
1684 /* Order subcgroups by their name */
1685 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1686 LIST_FOREACH(siblings
, child
, cg
->children
)
1687 children
[n
++] = child
;
1688 assert(n
== cg
->n_children
);
1689 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1692 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1694 for (i
= 0; i
< n
; i
++) {
1695 _cleanup_free_
char *pp
= NULL
;
1696 const char *name
, *special
;
1699 child
= children
[i
];
1701 name
= strrchr(child
->cgroup_path
, '/');
1707 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1709 fputs(prefix
, stdout
);
1710 fputs(special
, stdout
);
1711 fputs(name
, stdout
);
1712 fputc('\n', stdout
);
1714 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1716 pp
= strappend(prefix
, special
);
1720 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1730 static int dump_extra_processes(
1734 OutputFlags flags
) {
1736 _cleanup_free_ pid_t
*pids
= NULL
;
1737 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1738 struct CGroupInfo
*cg
;
1739 size_t n_allocated
= 0, n
= 0, k
;
1743 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1744 * combined, sorted, linear list. */
1746 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1754 if (hashmap_isempty(cg
->pids
))
1757 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1761 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1764 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1765 pids
[n
++] = PTR_TO_PID(pidp
);
1767 r
= hashmap_put(names
, pidp
, (void*) name
);
1776 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1777 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1779 for (k
= 0; k
< n
; k
++) {
1780 _cleanup_free_
char *e
= NULL
;
1783 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1786 if (n_columns
!= 0) {
1789 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1791 e
= ellipsize(name
, z
, 100);
1796 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1798 special_glyph(TRIANGULAR_BULLET
),
1806 int unit_show_processes(
1809 const char *cgroup_path
,
1813 sd_bus_error
*error
) {
1815 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1816 Hashmap
*cgroups
= NULL
;
1817 struct CGroupInfo
*cg
;
1823 if (flags
& OUTPUT_FULL_WIDTH
)
1825 else if (n_columns
<= 0)
1826 n_columns
= columns();
1828 prefix
= strempty(prefix
);
1830 r
= sd_bus_call_method(
1832 "org.freedesktop.systemd1",
1833 "/org/freedesktop/systemd1",
1834 "org.freedesktop.systemd1.Manager",
1843 cgroups
= hashmap_new(&string_hash_ops
);
1847 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1852 const char *path
= NULL
, *name
= NULL
;
1855 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1861 r
= add_process(cgroups
, path
, pid
, name
);
1866 r
= sd_bus_message_exit_container(reply
);
1870 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1874 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
1877 while ((cg
= hashmap_first(cgroups
)))
1878 remove_cgroup(cgroups
, cg
);
1880 hashmap_free(cgroups
);