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
);
965 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
969 r
= sd_bus_message_append_basic(m
, 's', word
);
971 return bus_log_create_error(r
);
974 r
= sd_bus_message_close_container(m
);
976 return bus_log_create_error(r
);
978 r
= sd_bus_message_close_container(m
);
980 } else if (streq(field
, "RestrictNamespaces")) {
982 unsigned long flags
= 0;
989 r
= parse_boolean(eq
);
993 flags
= NAMESPACE_FLAGS_ALL
;
995 r
= namespace_flag_from_string_many(eq
, &flags
);
997 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
1001 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
1003 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
1004 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
1005 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
1006 else if (streq(field
, "MountFlags")) {
1009 r
= mount_propagation_flags_from_string(eq
, &f
);
1011 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
1013 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
1014 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
1017 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
1021 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
1026 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
1027 char *s
= NULL
, *d
= NULL
;
1028 bool ignore_enoent
= false;
1029 uint64_t flags
= MS_REC
;
1031 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1033 return log_error_errno(r
, "Failed to parse argument: %m");
1039 ignore_enoent
= true;
1043 if (p
&& p
[-1] == ':') {
1044 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
1046 return log_error_errno(r
, "Failed to parse argument: %m");
1048 log_error("Missing argument after ':': %s", eq
);
1054 if (p
&& p
[-1] == ':') {
1055 _cleanup_free_
char *options
= NULL
;
1057 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
1059 return log_error_errno(r
, "Failed to parse argument: %m");
1061 if (isempty(options
) || streq(options
, "rbind"))
1063 else if (streq(options
, "norbind"))
1066 log_error("Unknown options: %s", eq
);
1074 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
1079 r
= sd_bus_message_close_container(m
);
1083 r
= sd_bus_message_close_container(m
);
1085 log_error("Unknown assignment %s.", assignment
);
1091 return bus_log_create_error(r
);
1093 r
= sd_bus_message_close_container(m
);
1095 return bus_log_create_error(r
);
1100 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
1106 STRV_FOREACH(i
, l
) {
1107 r
= bus_append_unit_property_assignment(m
, *i
);
1115 typedef struct BusWaitForJobs
{
1122 sd_bus_slot
*slot_job_removed
;
1123 sd_bus_slot
*slot_disconnected
;
1126 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1129 log_error("Warning! D-Bus connection terminated.");
1130 sd_bus_close(sd_bus_message_get_bus(m
));
1135 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1136 const char *path
, *unit
, *result
;
1137 BusWaitForJobs
*d
= userdata
;
1145 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1147 bus_log_parse_error(r
);
1151 found
= set_remove(d
->jobs
, (char*) path
);
1157 if (!isempty(result
))
1158 d
->result
= strdup(result
);
1161 d
->name
= strdup(unit
);
1166 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1170 set_free_free(d
->jobs
);
1172 sd_bus_slot_unref(d
->slot_disconnected
);
1173 sd_bus_slot_unref(d
->slot_job_removed
);
1175 sd_bus_unref(d
->bus
);
1183 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1184 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1190 d
= new0(BusWaitForJobs
, 1);
1194 d
->bus
= sd_bus_ref(bus
);
1196 /* When we are a bus client we match by sender. Direct
1197 * connections OTOH have no initialized sender field, and
1198 * hence we ignore the sender then */
1199 r
= sd_bus_add_match(
1201 &d
->slot_job_removed
,
1204 "sender='org.freedesktop.systemd1',"
1205 "interface='org.freedesktop.systemd1.Manager',"
1206 "member='JobRemoved',"
1207 "path='/org/freedesktop/systemd1'" :
1209 "interface='org.freedesktop.systemd1.Manager',"
1210 "member='JobRemoved',"
1211 "path='/org/freedesktop/systemd1'",
1212 match_job_removed
, d
);
1216 r
= sd_bus_add_match(
1218 &d
->slot_disconnected
,
1220 "sender='org.freedesktop.DBus.Local',"
1221 "interface='org.freedesktop.DBus.Local',"
1222 "member='Disconnected'",
1223 match_disconnected
, d
);
1233 static int bus_process_wait(sd_bus
*bus
) {
1237 r
= sd_bus_process(bus
, NULL
);
1243 r
= sd_bus_wait(bus
, (uint64_t) -1);
1249 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1250 _cleanup_free_
char *dbus_path
= NULL
;
1256 if (!endswith(d
->name
, ".service"))
1259 dbus_path
= unit_dbus_path_from_name(d
->name
);
1263 return sd_bus_get_property_string(d
->bus
,
1264 "org.freedesktop.systemd1",
1266 "org.freedesktop.systemd1.Service",
1272 static const struct {
1273 const char *result
, *explanation
;
1274 } explanations
[] = {
1275 { "resources", "of unavailable resources or another system error" },
1276 { "protocol", "the service did not take the steps required by its unit configuration" },
1277 { "timeout", "a timeout was exceeded" },
1278 { "exit-code", "the control process exited with error code" },
1279 { "signal", "a fatal signal was delivered to the control process" },
1280 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1281 { "watchdog", "the service failed to send watchdog ping" },
1282 { "start-limit", "start of the service was attempted too often" }
1285 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
1286 _cleanup_free_
char *service_shell_quoted
= NULL
;
1287 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
1291 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
1294 _cleanup_free_
char *t
;
1296 t
= strv_join((char**) extra_args
, " ");
1297 systemctl
= strjoina("systemctl ", t
? : "<args>");
1298 journalctl
= strjoina("journalctl ", t
? : "<args>");
1301 if (!isempty(result
)) {
1304 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1305 if (streq(result
, explanations
[i
].result
))
1308 if (i
< ELEMENTSOF(explanations
)) {
1309 log_error("Job for %s failed because %s.\n"
1310 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1312 explanations
[i
].explanation
,
1314 service_shell_quoted
?: "<service>",
1320 log_error("Job for %s failed.\n"
1321 "See \"%s status %s\" and \"%s -xe\" for details.\n",
1324 service_shell_quoted
?: "<service>",
1328 /* For some results maybe additional explanation is required */
1329 if (streq_ptr(result
, "start-limit"))
1330 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
1331 "followed by \"%1$s start %2$s\" again.",
1333 service_shell_quoted
?: "<service>");
1336 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1342 if (streq(d
->result
, "canceled"))
1343 log_error("Job for %s canceled.", strna(d
->name
));
1344 else if (streq(d
->result
, "timeout"))
1345 log_error("Job for %s timed out.", strna(d
->name
));
1346 else if (streq(d
->result
, "dependency"))
1347 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
1348 else if (streq(d
->result
, "invalid"))
1349 log_error("%s is not active, cannot reload.", strna(d
->name
));
1350 else if (streq(d
->result
, "assert"))
1351 log_error("Assertion failed on job for %s.", strna(d
->name
));
1352 else if (streq(d
->result
, "unsupported"))
1353 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
1354 else if (streq(d
->result
, "collected"))
1355 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
1356 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
1358 _cleanup_free_
char *result
= NULL
;
1361 q
= bus_job_get_service_result(d
, &result
);
1363 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
1365 log_job_error_with_service_result(d
->name
, result
, extra_args
);
1367 log_error("Job failed. See \"journalctl -xe\" for details.");
1371 if (STR_IN_SET(d
->result
, "canceled", "collected"))
1373 else if (streq(d
->result
, "timeout"))
1375 else if (streq(d
->result
, "dependency"))
1377 else if (streq(d
->result
, "invalid"))
1379 else if (streq(d
->result
, "assert"))
1381 else if (streq(d
->result
, "unsupported"))
1383 else if (!STR_IN_SET(d
->result
, "done", "skipped"))
1389 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1394 while (!set_isempty(d
->jobs
)) {
1397 q
= bus_process_wait(d
->bus
);
1399 return log_error_errno(q
, "Failed to wait for response: %m");
1402 q
= check_wait_response(d
, quiet
, extra_args
);
1403 /* Return the first error as it is most likely to be
1405 if (q
< 0 && r
== 0)
1408 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1411 d
->name
= mfree(d
->name
);
1412 d
->result
= mfree(d
->result
);
1418 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1423 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1427 return set_put_strdup(d
->jobs
, path
);
1430 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1433 r
= bus_wait_for_jobs_add(d
, path
);
1437 return bus_wait_for_jobs(d
, quiet
, NULL
);
1440 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1441 const char *type
, *path
, *source
;
1444 /* changes is dereferenced when calling unit_file_dump_changes() later,
1445 * so we have to make sure this is not NULL. */
1449 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1451 return bus_log_parse_error(r
);
1453 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1454 /* We expect only "success" changes to be sent over the bus.
1455 Hence, reject anything negative. */
1456 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1459 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1463 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1468 return bus_log_parse_error(r
);
1470 r
= sd_bus_message_exit_container(m
);
1472 return bus_log_parse_error(r
);
1474 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, false);
1480 bool is_const
; /* If false, cgroup_path should be free()'d */
1482 Hashmap
*pids
; /* PID → process name */
1485 struct CGroupInfo
*parent
;
1486 LIST_FIELDS(struct CGroupInfo
, siblings
);
1487 LIST_HEAD(struct CGroupInfo
, children
);
1491 static bool IS_ROOT(const char *p
) {
1492 return isempty(p
) || streq(p
, "/");
1495 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1496 struct CGroupInfo
*parent
= NULL
, *cg
;
1505 cg
= hashmap_get(cgroups
, path
);
1511 if (!IS_ROOT(path
)) {
1514 e
= strrchr(path
, '/');
1518 pp
= strndupa(path
, e
- path
);
1522 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1527 cg
= new0(struct CGroupInfo
, 1);
1532 cg
->cgroup_path
= (char*) path
;
1534 cg
->cgroup_path
= strdup(path
);
1535 if (!cg
->cgroup_path
) {
1541 cg
->is_const
= is_const
;
1542 cg
->parent
= parent
;
1544 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1547 free(cg
->cgroup_path
);
1553 LIST_PREPEND(siblings
, parent
->children
, cg
);
1554 parent
->n_children
++;
1561 static int add_process(
1567 struct CGroupInfo
*cg
;
1574 r
= add_cgroup(cgroups
, path
, true, &cg
);
1578 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1582 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1585 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1589 while (cg
->children
)
1590 remove_cgroup(cgroups
, cg
->children
);
1592 hashmap_remove(cgroups
, cg
->cgroup_path
);
1595 free(cg
->cgroup_path
);
1597 hashmap_free(cg
->pids
);
1600 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1605 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1606 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1611 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1614 static int dump_processes(
1616 const char *cgroup_path
,
1619 OutputFlags flags
) {
1621 struct CGroupInfo
*cg
;
1626 if (IS_ROOT(cgroup_path
))
1629 cg
= hashmap_get(cgroups
, cgroup_path
);
1633 if (!hashmap_isempty(cg
->pids
)) {
1641 /* Order processes by their PID */
1642 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1644 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1645 pids
[n
++] = PTR_TO_PID(pidp
);
1647 assert(n
== hashmap_size(cg
->pids
));
1648 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1650 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1652 for (i
= 0; i
< n
; i
++) {
1653 _cleanup_free_
char *e
= NULL
;
1654 const char *special
;
1657 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1660 if (n_columns
!= 0) {
1663 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1665 e
= ellipsize(name
, k
, 100);
1670 more
= i
+1 < n
|| cg
->children
;
1671 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1673 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1682 struct CGroupInfo
**children
, *child
;
1685 /* Order subcgroups by their name */
1686 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1687 LIST_FOREACH(siblings
, child
, cg
->children
)
1688 children
[n
++] = child
;
1689 assert(n
== cg
->n_children
);
1690 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1693 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1695 for (i
= 0; i
< n
; i
++) {
1696 _cleanup_free_
char *pp
= NULL
;
1697 const char *name
, *special
;
1700 child
= children
[i
];
1702 name
= strrchr(child
->cgroup_path
, '/');
1708 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1710 fputs(prefix
, stdout
);
1711 fputs(special
, stdout
);
1712 fputs(name
, stdout
);
1713 fputc('\n', stdout
);
1715 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1717 pp
= strappend(prefix
, special
);
1721 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1731 static int dump_extra_processes(
1735 OutputFlags flags
) {
1737 _cleanup_free_ pid_t
*pids
= NULL
;
1738 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1739 struct CGroupInfo
*cg
;
1740 size_t n_allocated
= 0, n
= 0, k
;
1744 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1745 * combined, sorted, linear list. */
1747 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1755 if (hashmap_isempty(cg
->pids
))
1758 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1762 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1765 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1766 pids
[n
++] = PTR_TO_PID(pidp
);
1768 r
= hashmap_put(names
, pidp
, (void*) name
);
1777 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1778 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1780 for (k
= 0; k
< n
; k
++) {
1781 _cleanup_free_
char *e
= NULL
;
1784 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1787 if (n_columns
!= 0) {
1790 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1792 e
= ellipsize(name
, z
, 100);
1797 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1799 special_glyph(TRIANGULAR_BULLET
),
1807 int unit_show_processes(
1810 const char *cgroup_path
,
1814 sd_bus_error
*error
) {
1816 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1817 Hashmap
*cgroups
= NULL
;
1818 struct CGroupInfo
*cg
;
1824 if (flags
& OUTPUT_FULL_WIDTH
)
1826 else if (n_columns
<= 0)
1827 n_columns
= columns();
1829 prefix
= strempty(prefix
);
1831 r
= sd_bus_call_method(
1833 "org.freedesktop.systemd1",
1834 "/org/freedesktop/systemd1",
1835 "org.freedesktop.systemd1.Manager",
1844 cgroups
= hashmap_new(&string_hash_ops
);
1848 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1853 const char *path
= NULL
, *name
= NULL
;
1856 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1862 r
= add_process(cgroups
, path
, pid
, name
);
1867 r
= sd_bus_message_exit_container(reply
);
1871 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1875 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
1878 while ((cg
= hashmap_first(cgroups
)))
1879 remove_cgroup(cgroups
, cg
);
1881 hashmap_free(cgroups
);