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"
24 #include "cgroup-util.h"
29 #include "locale-util.h"
30 #include "mount-util.h"
32 #include "parse-util.h"
33 #include "path-util.h"
34 #include "process-util.h"
35 #include "rlimit-util.h"
36 #include "signal-util.h"
37 #include "string-util.h"
38 #include "syslog-util.h"
39 #include "terminal-util.h"
43 int bus_parse_unit_info(sd_bus_message
*message
, UnitInfo
*u
) {
49 return sd_bus_message_read(
64 int bus_append_unit_property_assignment(sd_bus_message
*m
, const char *assignment
) {
65 const char *eq
, *field
;
72 eq
= strchr(assignment
, '=');
74 log_error("Not an assignment: %s", assignment
);
78 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
80 return bus_log_create_error(r
);
82 field
= strndupa(assignment
, eq
- assignment
);
85 if (streq(field
, "CPUQuota")) {
88 r
= sd_bus_message_append(m
, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY
);
90 r
= parse_percent_unbounded(eq
);
92 log_error_errno(r
, "CPU quota '%s' invalid.", eq
);
96 r
= sd_bus_message_append(m
, "sv", "CPUQuotaPerSecUSec", "t", (usec_t
) r
* USEC_PER_SEC
/ 100U);
101 } else if (streq(field
, "EnvironmentFile")) {
103 r
= sd_bus_message_append(m
, "sv", "EnvironmentFiles", "a(sb)", 1,
104 eq
[0] == '-' ? eq
+ 1 : eq
,
108 } else if (STR_IN_SET(field
, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) {
113 r
= parse_sec(eq
, &t
);
115 return log_error_errno(r
, "Failed to parse %s= parameter: %s", field
, eq
);
118 n
= newa(char, l
+ 2);
122 /* Change suffix Sec → USec */
123 strcpy(mempcpy(n
, field
, l
- 3), "USec");
124 r
= sd_bus_message_append(m
, "sv", n
, "t", t
);
127 } else if (STR_IN_SET(field
, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) {
130 if (isempty(eq
) || streq(eq
, "infinity"))
131 bytes
= CGROUP_LIMIT_MAX
;
133 r
= parse_percent(eq
);
137 /* When this is a percentage we'll convert this into a relative value in the range
138 * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related
139 * ones). This way the physical memory size can be determined server-side */
141 n
= strjoina(field
, "Scale");
142 r
= sd_bus_message_append(m
, "sv", n
, "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
146 r
= parse_size(eq
, 1024, &bytes
);
148 return log_error_errno(r
, "Failed to parse bytes specification %s", assignment
);
152 r
= sd_bus_message_append(m
, "sv", field
, "t", bytes
);
154 } else if (streq(field
, "TasksMax")) {
157 if (isempty(eq
) || streq(eq
, "infinity"))
160 r
= parse_percent(eq
);
162 r
= sd_bus_message_append(m
, "sv", "TasksMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX
* r
) / 100U));
165 r
= safe_atou64(eq
, &t
);
167 return log_error_errno(r
, "Failed to parse maximum tasks specification %s", assignment
);
172 r
= sd_bus_message_append(m
, "sv", "TasksMax", "t", t
);
176 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
178 return bus_log_create_error(r
);
180 rl
= rlimit_from_string(field
);
185 r
= rlimit_parse(rl
, eq
, &l
);
187 return log_error_errno(r
, "Failed to parse resource limit: %s", eq
);
189 r
= sd_bus_message_append(m
, "v", "t", l
.rlim_max
);
191 return bus_log_create_error(r
);
193 r
= sd_bus_message_close_container(m
);
195 return bus_log_create_error(r
);
197 r
= sd_bus_message_open_container(m
, SD_BUS_TYPE_STRUCT
, "sv");
199 return bus_log_create_error(r
);
201 sn
= strjoina(field
, "Soft");
202 r
= sd_bus_message_append(m
, "sv", sn
, "t", l
.rlim_cur
);
204 } else if (STR_IN_SET(field
,
205 "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting", "TasksAccounting",
206 "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
207 "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
208 "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges",
209 "SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute",
210 "RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables",
211 "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS")) {
213 r
= parse_boolean(eq
);
215 return log_error_errno(r
, "Failed to parse boolean assignment %s.", assignment
);
217 r
= sd_bus_message_append(m
, "v", "b", r
);
219 } else if (STR_IN_SET(field
, "CPUWeight", "StartupCPUWeight")) {
222 r
= cg_weight_parse(eq
, &u
);
224 log_error("Failed to parse %s value %s.", field
, eq
);
228 r
= sd_bus_message_append(m
, "v", "t", u
);
230 } else if (STR_IN_SET(field
, "CPUShares", "StartupCPUShares")) {
233 r
= cg_cpu_shares_parse(eq
, &u
);
235 log_error("Failed to parse %s value %s.", field
, eq
);
239 r
= sd_bus_message_append(m
, "v", "t", u
);
241 } else if (STR_IN_SET(field
, "IOWeight", "StartupIOWeight")) {
244 r
= cg_weight_parse(eq
, &u
);
246 log_error("Failed to parse %s value %s.", field
, eq
);
250 r
= sd_bus_message_append(m
, "v", "t", u
);
252 } else if (STR_IN_SET(field
, "BlockIOWeight", "StartupBlockIOWeight")) {
255 r
= cg_blkio_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
,
264 "User", "Group", "DevicePolicy", "KillMode",
265 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
266 "StandardInput", "StandardOutput", "StandardError",
267 "Description", "Slice", "Type", "WorkingDirectory",
268 "RootDirectory", "SyslogIdentifier", "ProtectSystem",
269 "ProtectHome", "SELinuxContext", "Restart", "RootImage",
270 "NotifyAccess", "RuntimeDirectoryPreserve"))
271 r
= sd_bus_message_append(m
, "v", "s", eq
);
273 else if (streq(field
, "SyslogLevel")) {
276 level
= log_level_from_string(eq
);
278 log_error("Failed to parse %s value %s.", field
, eq
);
282 r
= sd_bus_message_append(m
, "v", "i", level
);
284 } else if (streq(field
, "SyslogFacility")) {
287 facility
= log_facility_unshifted_from_string(eq
);
289 log_error("Failed to parse %s value %s.", field
, eq
);
293 r
= sd_bus_message_append(m
, "v", "i", facility
);
295 } else if (streq(field
, "DeviceAllow")) {
298 r
= sd_bus_message_append(m
, "v", "a(ss)", 0);
300 const char *path
, *rwm
, *e
;
304 path
= strndupa(eq
, e
- eq
);
311 if (!is_deviceallow_pattern(path
)) {
312 log_error("%s is not a device file in /dev.", path
);
316 r
= sd_bus_message_append(m
, "v", "a(ss)", 1, path
, rwm
);
319 } else if (cgroup_io_limit_type_from_string(field
) >= 0 || STR_IN_SET(field
, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
322 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
324 const char *path
, *bandwidth
, *e
;
329 path
= strndupa(eq
, e
- eq
);
332 log_error("Failed to parse %s value %s.", field
, eq
);
336 if (!path_startswith(path
, "/dev")) {
337 log_error("%s is not a device file in /dev.", path
);
341 if (streq(bandwidth
, "infinity")) {
342 bytes
= CGROUP_LIMIT_MAX
;
344 r
= parse_size(bandwidth
, 1000, &bytes
);
346 log_error("Failed to parse byte value %s.", bandwidth
);
351 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, bytes
);
354 } else if (STR_IN_SET(field
, "IODeviceWeight", "BlockIODeviceWeight")) {
357 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
359 const char *path
, *weight
, *e
;
364 path
= strndupa(eq
, e
- eq
);
367 log_error("Failed to parse %s value %s.", field
, eq
);
371 if (!path_startswith(path
, "/dev")) {
372 log_error("%s is not a device file in /dev.", path
);
376 r
= safe_atou64(weight
, &u
);
378 log_error("Failed to parse %s value %s.", field
, weight
);
381 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, u
);
384 } else if (streq(field
, "Nice")) {
387 r
= parse_nice(eq
, &n
);
389 return log_error_errno(r
, "Failed to parse nice value: %s", eq
);
391 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
393 } else if (streq(field
, "FileDescriptorStoreMax")) {
396 r
= safe_atou(eq
, &u
);
398 return log_error_errno(r
, "Failed to parse file descriptor store limit: %s", eq
);
400 r
= sd_bus_message_append(m
, "v", "u", (uint32_t) u
);
402 } else if (streq(field
, "IOSchedulingClass")) {
405 c
= ioprio_class_from_string(eq
);
407 return log_error_errno(r
, "Failed to parse IO scheduling class: %s", eq
);
409 r
= sd_bus_message_append(m
, "v", "i", (int32_t) c
);
411 } else if (streq(field
, "IOSchedulingPriority")) {
414 r
= ioprio_parse_priority(eq
, &q
);
416 return log_error_errno(r
, "Failed to parse IO scheduling priority: %s", eq
);
418 r
= sd_bus_message_append(m
, "v", "i", (int32_t) q
);
420 } else if (STR_IN_SET(field
, "Environment", "PassEnvironment")) {
423 r
= sd_bus_message_open_container(m
, 'v', "as");
425 return bus_log_create_error(r
);
427 r
= sd_bus_message_open_container(m
, 'a', "s");
429 return bus_log_create_error(r
);
432 _cleanup_free_
char *word
= NULL
;
434 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
436 log_error("Failed to parse Environment value %s", eq
);
442 if (streq(field
, "Environment")) {
443 if (!env_assignment_is_valid(word
)) {
444 log_error("Invalid environment assignment: %s", word
);
447 } else { /* PassEnvironment */
448 if (!env_name_is_valid(word
)) {
449 log_error("Invalid environment variable name: %s", word
);
454 r
= sd_bus_message_append_basic(m
, 's', word
);
456 return bus_log_create_error(r
);
459 r
= sd_bus_message_close_container(m
);
461 return bus_log_create_error(r
);
463 r
= sd_bus_message_close_container(m
);
465 } else if (streq(field
, "KillSignal")) {
468 sig
= signal_from_string_try_harder(eq
);
470 log_error("Failed to parse %s value %s.", field
, eq
);
474 r
= sd_bus_message_append(m
, "v", "i", sig
);
476 } else if (streq(field
, "TimerSlackNSec")) {
479 r
= parse_nsec(eq
, &n
);
481 log_error("Failed to parse %s value %s", field
, eq
);
485 r
= sd_bus_message_append(m
, "v", "t", n
);
486 } else if (streq(field
, "OOMScoreAdjust")) {
489 r
= safe_atoi(eq
, &oa
);
491 log_error("Failed to parse %s value %s", field
, eq
);
495 if (!oom_score_adjust_is_valid(oa
)) {
496 log_error("OOM score adjust value out of range");
500 r
= sd_bus_message_append(m
, "v", "i", oa
);
501 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
502 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
505 r
= sd_bus_message_open_container(m
, 'v', "as");
507 return bus_log_create_error(r
);
509 r
= sd_bus_message_open_container(m
, 'a', "s");
511 return bus_log_create_error(r
);
514 _cleanup_free_
char *word
= NULL
;
517 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
519 log_error("Failed to parse %s value %s", field
, eq
);
525 if (!utf8_is_valid(word
)) {
526 log_error("Failed to parse %s value %s", field
, eq
);
530 offset
= word
[0] == '-';
531 offset
+= word
[offset
] == '+';
533 if (!path_is_absolute(word
+ offset
)) {
534 log_error("Failed to parse %s value %s", field
, eq
);
538 path_kill_slashes(word
+ offset
);
540 r
= sd_bus_message_append_basic(m
, 's', word
);
542 return bus_log_create_error(r
);
545 r
= sd_bus_message_close_container(m
);
547 return bus_log_create_error(r
);
549 r
= sd_bus_message_close_container(m
);
551 } else if (streq(field
, "RuntimeDirectoryMode")) {
554 r
= parse_mode(eq
, &mode
);
556 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
558 r
= sd_bus_message_append(m
, "v", "u", mode
);
560 } else if (streq(field
, "RuntimeDirectory")) {
563 r
= sd_bus_message_open_container(m
, 'v', "as");
565 return bus_log_create_error(r
);
567 r
= sd_bus_message_open_container(m
, 'a', "s");
569 return bus_log_create_error(r
);
572 _cleanup_free_
char *word
= NULL
;
574 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
576 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
581 r
= sd_bus_message_append_basic(m
, 's', word
);
583 return bus_log_create_error(r
);
586 r
= sd_bus_message_close_container(m
);
588 return bus_log_create_error(r
);
590 r
= sd_bus_message_close_container(m
);
592 } else if (streq(field
, "RestrictNamespaces")) {
594 unsigned long flags
= 0;
601 r
= parse_boolean(eq
);
605 flags
= NAMESPACE_FLAGS_ALL
;
607 r
= namespace_flag_from_string_many(eq
, &flags
);
609 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
613 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
615 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
616 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
617 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
618 else if (streq(field
, "MountFlags")) {
621 r
= mount_propagation_flags_from_string(eq
, &f
);
623 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
625 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
626 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
629 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
633 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
638 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
639 char *s
= NULL
, *d
= NULL
;
640 bool ignore_enoent
= false;
641 uint64_t flags
= MS_REC
;
643 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
645 return log_error_errno(r
, "Failed to parse argument: %m");
651 ignore_enoent
= true;
655 if (p
&& p
[-1] == ':') {
656 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
658 return log_error_errno(r
, "Failed to parse argument: %m");
660 log_error("Missing argument after ':': %s", eq
);
666 if (p
&& p
[-1] == ':') {
667 _cleanup_free_
char *options
= NULL
;
669 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
671 return log_error_errno(r
, "Failed to parse argument: %m");
673 if (isempty(options
) || streq(options
, "rbind"))
675 else if (streq(options
, "norbind"))
678 log_error("Unknown options: %s", eq
);
686 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
691 r
= sd_bus_message_close_container(m
);
695 r
= sd_bus_message_close_container(m
);
697 log_error("Unknown assignment %s.", assignment
);
703 return bus_log_create_error(r
);
705 r
= sd_bus_message_close_container(m
);
707 return bus_log_create_error(r
);
712 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
719 r
= bus_append_unit_property_assignment(m
, *i
);
727 typedef struct BusWaitForJobs
{
734 sd_bus_slot
*slot_job_removed
;
735 sd_bus_slot
*slot_disconnected
;
738 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
741 log_error("Warning! D-Bus connection terminated.");
742 sd_bus_close(sd_bus_message_get_bus(m
));
747 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
748 const char *path
, *unit
, *result
;
749 BusWaitForJobs
*d
= userdata
;
757 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
759 bus_log_parse_error(r
);
763 found
= set_remove(d
->jobs
, (char*) path
);
769 if (!isempty(result
))
770 d
->result
= strdup(result
);
773 d
->name
= strdup(unit
);
778 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
782 set_free_free(d
->jobs
);
784 sd_bus_slot_unref(d
->slot_disconnected
);
785 sd_bus_slot_unref(d
->slot_job_removed
);
787 sd_bus_unref(d
->bus
);
795 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
796 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
802 d
= new0(BusWaitForJobs
, 1);
806 d
->bus
= sd_bus_ref(bus
);
808 /* When we are a bus client we match by sender. Direct
809 * connections OTOH have no initialized sender field, and
810 * hence we ignore the sender then */
811 r
= sd_bus_add_match(
813 &d
->slot_job_removed
,
816 "sender='org.freedesktop.systemd1',"
817 "interface='org.freedesktop.systemd1.Manager',"
818 "member='JobRemoved',"
819 "path='/org/freedesktop/systemd1'" :
821 "interface='org.freedesktop.systemd1.Manager',"
822 "member='JobRemoved',"
823 "path='/org/freedesktop/systemd1'",
824 match_job_removed
, d
);
828 r
= sd_bus_add_match(
830 &d
->slot_disconnected
,
832 "sender='org.freedesktop.DBus.Local',"
833 "interface='org.freedesktop.DBus.Local',"
834 "member='Disconnected'",
835 match_disconnected
, d
);
845 static int bus_process_wait(sd_bus
*bus
) {
849 r
= sd_bus_process(bus
, NULL
);
855 r
= sd_bus_wait(bus
, (uint64_t) -1);
861 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
862 _cleanup_free_
char *dbus_path
= NULL
;
868 dbus_path
= unit_dbus_path_from_name(d
->name
);
872 return sd_bus_get_property_string(d
->bus
,
873 "org.freedesktop.systemd1",
875 "org.freedesktop.systemd1.Service",
881 static const struct {
882 const char *result
, *explanation
;
883 } explanations
[] = {
884 { "resources", "of unavailable resources or another system error" },
885 { "protocol", "the service did not take the steps required by its unit configuration" },
886 { "timeout", "a timeout was exceeded" },
887 { "exit-code", "the control process exited with error code" },
888 { "signal", "a fatal signal was delivered to the control process" },
889 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
890 { "watchdog", "the service failed to send watchdog ping" },
891 { "start-limit", "start of the service was attempted too often" }
894 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
895 _cleanup_free_
char *service_shell_quoted
= NULL
;
896 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
900 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
903 _cleanup_free_
char *t
;
905 t
= strv_join((char**) extra_args
, " ");
906 systemctl
= strjoina("systemctl ", t
? : "<args>");
907 journalctl
= strjoina("journalctl ", t
? : "<args>");
910 if (!isempty(result
)) {
913 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
914 if (streq(result
, explanations
[i
].result
))
917 if (i
< ELEMENTSOF(explanations
)) {
918 log_error("Job for %s failed because %s.\n"
919 "See \"%s status %s\" and \"%s -xe\" for details.\n",
921 explanations
[i
].explanation
,
923 service_shell_quoted
?: "<service>",
929 log_error("Job for %s failed.\n"
930 "See \"%s status %s\" and \"%s -xe\" for details.\n",
933 service_shell_quoted
?: "<service>",
937 /* For some results maybe additional explanation is required */
938 if (streq_ptr(result
, "start-limit"))
939 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
940 "followed by \"%1$s start %2$s\" again.",
942 service_shell_quoted
?: "<service>");
945 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
951 if (streq(d
->result
, "canceled"))
952 log_error("Job for %s canceled.", strna(d
->name
));
953 else if (streq(d
->result
, "timeout"))
954 log_error("Job for %s timed out.", strna(d
->name
));
955 else if (streq(d
->result
, "dependency"))
956 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
957 else if (streq(d
->result
, "invalid"))
958 log_error("%s is not active, cannot reload.", strna(d
->name
));
959 else if (streq(d
->result
, "assert"))
960 log_error("Assertion failed on job for %s.", strna(d
->name
));
961 else if (streq(d
->result
, "unsupported"))
962 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
963 else if (streq(d
->result
, "collected"))
964 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
965 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped")) {
968 _cleanup_free_
char *result
= NULL
;
970 q
= bus_job_get_service_result(d
, &result
);
972 log_debug_errno(q
, "Failed to get Result property of service %s: %m", d
->name
);
974 log_job_error_with_service_result(d
->name
, result
, extra_args
);
976 log_error("Job failed. See \"journalctl -xe\" for details.");
980 if (STR_IN_SET(d
->result
, "canceled", "collected"))
982 else if (streq(d
->result
, "timeout"))
984 else if (streq(d
->result
, "dependency"))
986 else if (streq(d
->result
, "invalid"))
988 else if (streq(d
->result
, "assert"))
990 else if (streq(d
->result
, "unsupported"))
992 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped"))
998 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
1003 while (!set_isempty(d
->jobs
)) {
1006 q
= bus_process_wait(d
->bus
);
1008 return log_error_errno(q
, "Failed to wait for response: %m");
1011 q
= check_wait_response(d
, quiet
, extra_args
);
1012 /* Return the first error as it is most likely to be
1014 if (q
< 0 && r
== 0)
1017 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1020 d
->name
= mfree(d
->name
);
1021 d
->result
= mfree(d
->result
);
1027 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1032 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1036 return set_put_strdup(d
->jobs
, path
);
1039 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1042 r
= bus_wait_for_jobs_add(d
, path
);
1046 return bus_wait_for_jobs(d
, quiet
, NULL
);
1049 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1050 const char *type
, *path
, *source
;
1053 /* changes is dereferenced when calling unit_file_dump_changes() later,
1054 * so we have to make sure this is not NULL. */
1058 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1060 return bus_log_parse_error(r
);
1062 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1063 /* We expect only "success" changes to be sent over the bus.
1064 Hence, reject anything negative. */
1065 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1068 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1072 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1077 return bus_log_parse_error(r
);
1079 r
= sd_bus_message_exit_container(m
);
1081 return bus_log_parse_error(r
);
1083 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, false);
1089 bool is_const
; /* If false, cgroup_path should be free()'d */
1091 Hashmap
*pids
; /* PID → process name */
1094 struct CGroupInfo
*parent
;
1095 LIST_FIELDS(struct CGroupInfo
, siblings
);
1096 LIST_HEAD(struct CGroupInfo
, children
);
1100 static bool IS_ROOT(const char *p
) {
1101 return isempty(p
) || streq(p
, "/");
1104 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1105 struct CGroupInfo
*parent
= NULL
, *cg
;
1114 cg
= hashmap_get(cgroups
, path
);
1120 if (!IS_ROOT(path
)) {
1123 e
= strrchr(path
, '/');
1127 pp
= strndupa(path
, e
- path
);
1131 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1136 cg
= new0(struct CGroupInfo
, 1);
1141 cg
->cgroup_path
= (char*) path
;
1143 cg
->cgroup_path
= strdup(path
);
1144 if (!cg
->cgroup_path
) {
1150 cg
->is_const
= is_const
;
1151 cg
->parent
= parent
;
1153 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1156 free(cg
->cgroup_path
);
1162 LIST_PREPEND(siblings
, parent
->children
, cg
);
1163 parent
->n_children
++;
1170 static int add_process(
1176 struct CGroupInfo
*cg
;
1183 r
= add_cgroup(cgroups
, path
, true, &cg
);
1187 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1191 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1194 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1198 while (cg
->children
)
1199 remove_cgroup(cgroups
, cg
->children
);
1201 hashmap_remove(cgroups
, cg
->cgroup_path
);
1204 free(cg
->cgroup_path
);
1206 hashmap_free(cg
->pids
);
1209 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1214 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1215 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1220 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1223 static int dump_processes(
1225 const char *cgroup_path
,
1228 OutputFlags flags
) {
1230 struct CGroupInfo
*cg
;
1235 if (IS_ROOT(cgroup_path
))
1238 cg
= hashmap_get(cgroups
, cgroup_path
);
1242 if (!hashmap_isempty(cg
->pids
)) {
1250 /* Order processes by their PID */
1251 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1253 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1254 pids
[n
++] = PTR_TO_PID(pidp
);
1256 assert(n
== hashmap_size(cg
->pids
));
1257 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1259 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1261 for (i
= 0; i
< n
; i
++) {
1262 _cleanup_free_
char *e
= NULL
;
1263 const char *special
;
1266 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1269 if (n_columns
!= 0) {
1272 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1274 e
= ellipsize(name
, k
, 100);
1279 more
= i
+1 < n
|| cg
->children
;
1280 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1282 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1291 struct CGroupInfo
**children
, *child
;
1294 /* Order subcgroups by their name */
1295 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1296 LIST_FOREACH(siblings
, child
, cg
->children
)
1297 children
[n
++] = child
;
1298 assert(n
== cg
->n_children
);
1299 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1302 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1304 for (i
= 0; i
< n
; i
++) {
1305 _cleanup_free_
char *pp
= NULL
;
1306 const char *name
, *special
;
1309 child
= children
[i
];
1311 name
= strrchr(child
->cgroup_path
, '/');
1317 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1319 fputs(prefix
, stdout
);
1320 fputs(special
, stdout
);
1321 fputs(name
, stdout
);
1322 fputc('\n', stdout
);
1324 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1326 pp
= strappend(prefix
, special
);
1330 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1340 static int dump_extra_processes(
1344 OutputFlags flags
) {
1346 _cleanup_free_ pid_t
*pids
= NULL
;
1347 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1348 struct CGroupInfo
*cg
;
1349 size_t n_allocated
= 0, n
= 0, k
;
1353 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1354 * combined, sorted, linear list. */
1356 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1364 if (hashmap_isempty(cg
->pids
))
1367 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1371 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1374 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1375 pids
[n
++] = PTR_TO_PID(pidp
);
1377 r
= hashmap_put(names
, pidp
, (void*) name
);
1386 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1387 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1389 for (k
= 0; k
< n
; k
++) {
1390 _cleanup_free_
char *e
= NULL
;
1393 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1396 if (n_columns
!= 0) {
1399 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1401 e
= ellipsize(name
, z
, 100);
1406 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1408 special_glyph(TRIANGULAR_BULLET
),
1416 int unit_show_processes(
1419 const char *cgroup_path
,
1423 sd_bus_error
*error
) {
1425 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1426 Hashmap
*cgroups
= NULL
;
1427 struct CGroupInfo
*cg
;
1433 if (flags
& OUTPUT_FULL_WIDTH
)
1435 else if (n_columns
<= 0)
1436 n_columns
= columns();
1438 prefix
= strempty(prefix
);
1440 r
= sd_bus_call_method(
1442 "org.freedesktop.systemd1",
1443 "/org/freedesktop/systemd1",
1444 "org.freedesktop.systemd1.Manager",
1453 cgroups
= hashmap_new(&string_hash_ops
);
1457 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1462 const char *path
= NULL
, *name
= NULL
;
1465 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1471 r
= add_process(cgroups
, path
, pid
, name
);
1476 r
= sd_bus_message_exit_container(reply
);
1480 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1484 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
1487 while ((cg
= hashmap_first(cgroups
)))
1488 remove_cgroup(cgroups
, cg
);
1490 hashmap_free(cgroups
);