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",
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
, "RuntimeDirectory")) {
554 r
= sd_bus_message_open_container(m
, 'v', "as");
556 return bus_log_create_error(r
);
558 r
= sd_bus_message_open_container(m
, 'a', "s");
560 return bus_log_create_error(r
);
563 _cleanup_free_
char *word
= NULL
;
565 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
567 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
572 r
= sd_bus_message_append_basic(m
, 's', word
);
574 return bus_log_create_error(r
);
577 r
= sd_bus_message_close_container(m
);
579 return bus_log_create_error(r
);
581 r
= sd_bus_message_close_container(m
);
583 } else if (streq(field
, "RestrictNamespaces")) {
585 unsigned long flags
= 0;
592 r
= parse_boolean(eq
);
596 flags
= NAMESPACE_FLAGS_ALL
;
598 r
= namespace_flag_from_string_many(eq
, &flags
);
600 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
604 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
606 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
607 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
608 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
609 else if (streq(field
, "MountFlags")) {
612 r
= mount_propagation_flags_from_string(eq
, &f
);
614 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
616 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
617 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
620 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
624 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
629 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
630 char *s
= NULL
, *d
= NULL
;
631 bool ignore_enoent
= false;
632 uint64_t flags
= MS_REC
;
634 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
636 return log_error_errno(r
, "Failed to parse argument: %m");
642 ignore_enoent
= true;
646 if (p
&& p
[-1] == ':') {
647 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
649 return log_error_errno(r
, "Failed to parse argument: %m");
651 log_error("Missing argument after ':': %s", eq
);
657 if (p
&& p
[-1] == ':') {
658 _cleanup_free_
char *options
= NULL
;
660 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
662 return log_error_errno(r
, "Failed to parse argument: %m");
664 if (isempty(options
) || streq(options
, "rbind"))
666 else if (streq(options
, "norbind"))
669 log_error("Unknown options: %s", eq
);
677 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
682 r
= sd_bus_message_close_container(m
);
686 r
= sd_bus_message_close_container(m
);
688 log_error("Unknown assignment %s.", assignment
);
694 return bus_log_create_error(r
);
696 r
= sd_bus_message_close_container(m
);
698 return bus_log_create_error(r
);
703 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
710 r
= bus_append_unit_property_assignment(m
, *i
);
718 typedef struct BusWaitForJobs
{
725 sd_bus_slot
*slot_job_removed
;
726 sd_bus_slot
*slot_disconnected
;
729 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
732 log_error("Warning! D-Bus connection terminated.");
733 sd_bus_close(sd_bus_message_get_bus(m
));
738 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
739 const char *path
, *unit
, *result
;
740 BusWaitForJobs
*d
= userdata
;
748 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
750 bus_log_parse_error(r
);
754 found
= set_remove(d
->jobs
, (char*) path
);
760 if (!isempty(result
))
761 d
->result
= strdup(result
);
764 d
->name
= strdup(unit
);
769 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
773 set_free_free(d
->jobs
);
775 sd_bus_slot_unref(d
->slot_disconnected
);
776 sd_bus_slot_unref(d
->slot_job_removed
);
778 sd_bus_unref(d
->bus
);
786 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
787 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
793 d
= new0(BusWaitForJobs
, 1);
797 d
->bus
= sd_bus_ref(bus
);
799 /* When we are a bus client we match by sender. Direct
800 * connections OTOH have no initialized sender field, and
801 * hence we ignore the sender then */
802 r
= sd_bus_add_match(
804 &d
->slot_job_removed
,
807 "sender='org.freedesktop.systemd1',"
808 "interface='org.freedesktop.systemd1.Manager',"
809 "member='JobRemoved',"
810 "path='/org/freedesktop/systemd1'" :
812 "interface='org.freedesktop.systemd1.Manager',"
813 "member='JobRemoved',"
814 "path='/org/freedesktop/systemd1'",
815 match_job_removed
, d
);
819 r
= sd_bus_add_match(
821 &d
->slot_disconnected
,
823 "sender='org.freedesktop.DBus.Local',"
824 "interface='org.freedesktop.DBus.Local',"
825 "member='Disconnected'",
826 match_disconnected
, d
);
836 static int bus_process_wait(sd_bus
*bus
) {
840 r
= sd_bus_process(bus
, NULL
);
846 r
= sd_bus_wait(bus
, (uint64_t) -1);
852 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
853 _cleanup_free_
char *dbus_path
= NULL
;
859 dbus_path
= unit_dbus_path_from_name(d
->name
);
863 return sd_bus_get_property_string(d
->bus
,
864 "org.freedesktop.systemd1",
866 "org.freedesktop.systemd1.Service",
872 static const struct {
873 const char *result
, *explanation
;
874 } explanations
[] = {
875 { "resources", "of unavailable resources or another system error" },
876 { "protocol", "the service did not take the steps required by its unit configuration" },
877 { "timeout", "a timeout was exceeded" },
878 { "exit-code", "the control process exited with error code" },
879 { "signal", "a fatal signal was delivered to the control process" },
880 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
881 { "watchdog", "the service failed to send watchdog ping" },
882 { "start-limit", "start of the service was attempted too often" }
885 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
886 _cleanup_free_
char *service_shell_quoted
= NULL
;
887 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
891 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
894 _cleanup_free_
char *t
;
896 t
= strv_join((char**) extra_args
, " ");
897 systemctl
= strjoina("systemctl ", t
? : "<args>");
898 journalctl
= strjoina("journalctl ", t
? : "<args>");
901 if (!isempty(result
)) {
904 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
905 if (streq(result
, explanations
[i
].result
))
908 if (i
< ELEMENTSOF(explanations
)) {
909 log_error("Job for %s failed because %s.\n"
910 "See \"%s status %s\" and \"%s -xe\" for details.\n",
912 explanations
[i
].explanation
,
914 service_shell_quoted
?: "<service>",
920 log_error("Job for %s failed.\n"
921 "See \"%s status %s\" and \"%s -xe\" for details.\n",
924 service_shell_quoted
?: "<service>",
928 /* For some results maybe additional explanation is required */
929 if (streq_ptr(result
, "start-limit"))
930 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
931 "followed by \"%1$s start %2$s\" again.",
933 service_shell_quoted
?: "<service>");
936 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
942 if (streq(d
->result
, "canceled"))
943 log_error("Job for %s canceled.", strna(d
->name
));
944 else if (streq(d
->result
, "timeout"))
945 log_error("Job for %s timed out.", strna(d
->name
));
946 else if (streq(d
->result
, "dependency"))
947 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
948 else if (streq(d
->result
, "invalid"))
949 log_error("%s is not active, cannot reload.", strna(d
->name
));
950 else if (streq(d
->result
, "assert"))
951 log_error("Assertion failed on job for %s.", strna(d
->name
));
952 else if (streq(d
->result
, "unsupported"))
953 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
954 else if (streq(d
->result
, "collected"))
955 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
956 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped")) {
959 _cleanup_free_
char *result
= NULL
;
961 q
= bus_job_get_service_result(d
, &result
);
963 log_debug_errno(q
, "Failed to get Result property of service %s: %m", d
->name
);
965 log_job_error_with_service_result(d
->name
, result
, extra_args
);
967 log_error("Job failed. See \"journalctl -xe\" for details.");
971 if (STR_IN_SET(d
->result
, "canceled", "collected"))
973 else if (streq(d
->result
, "timeout"))
975 else if (streq(d
->result
, "dependency"))
977 else if (streq(d
->result
, "invalid"))
979 else if (streq(d
->result
, "assert"))
981 else if (streq(d
->result
, "unsupported"))
983 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped"))
989 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
994 while (!set_isempty(d
->jobs
)) {
997 q
= bus_process_wait(d
->bus
);
999 return log_error_errno(q
, "Failed to wait for response: %m");
1002 q
= check_wait_response(d
, quiet
, extra_args
);
1003 /* Return the first error as it is most likely to be
1005 if (q
< 0 && r
== 0)
1008 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1011 d
->name
= mfree(d
->name
);
1012 d
->result
= mfree(d
->result
);
1018 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1023 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1027 return set_put_strdup(d
->jobs
, path
);
1030 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1033 r
= bus_wait_for_jobs_add(d
, path
);
1037 return bus_wait_for_jobs(d
, quiet
, NULL
);
1040 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1041 const char *type
, *path
, *source
;
1044 /* changes is dereferenced when calling unit_file_dump_changes() later,
1045 * so we have to make sure this is not NULL. */
1049 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1051 return bus_log_parse_error(r
);
1053 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1054 /* We expect only "success" changes to be sent over the bus.
1055 Hence, reject anything negative. */
1056 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1059 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1063 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1068 return bus_log_parse_error(r
);
1070 r
= sd_bus_message_exit_container(m
);
1072 return bus_log_parse_error(r
);
1074 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, false);
1080 bool is_const
; /* If false, cgroup_path should be free()'d */
1082 Hashmap
*pids
; /* PID → process name */
1085 struct CGroupInfo
*parent
;
1086 LIST_FIELDS(struct CGroupInfo
, siblings
);
1087 LIST_HEAD(struct CGroupInfo
, children
);
1091 static bool IS_ROOT(const char *p
) {
1092 return isempty(p
) || streq(p
, "/");
1095 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1096 struct CGroupInfo
*parent
= NULL
, *cg
;
1105 cg
= hashmap_get(cgroups
, path
);
1111 if (!IS_ROOT(path
)) {
1114 e
= strrchr(path
, '/');
1118 pp
= strndupa(path
, e
- path
);
1122 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1127 cg
= new0(struct CGroupInfo
, 1);
1132 cg
->cgroup_path
= (char*) path
;
1134 cg
->cgroup_path
= strdup(path
);
1135 if (!cg
->cgroup_path
) {
1141 cg
->is_const
= is_const
;
1142 cg
->parent
= parent
;
1144 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1147 free(cg
->cgroup_path
);
1153 LIST_PREPEND(siblings
, parent
->children
, cg
);
1154 parent
->n_children
++;
1161 static int add_process(
1167 struct CGroupInfo
*cg
;
1174 r
= add_cgroup(cgroups
, path
, true, &cg
);
1178 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1182 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1185 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1189 while (cg
->children
)
1190 remove_cgroup(cgroups
, cg
->children
);
1192 hashmap_remove(cgroups
, cg
->cgroup_path
);
1195 free(cg
->cgroup_path
);
1197 hashmap_free(cg
->pids
);
1200 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1205 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1206 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1211 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1214 static int dump_processes(
1216 const char *cgroup_path
,
1219 OutputFlags flags
) {
1221 struct CGroupInfo
*cg
;
1226 if (IS_ROOT(cgroup_path
))
1229 cg
= hashmap_get(cgroups
, cgroup_path
);
1233 if (!hashmap_isempty(cg
->pids
)) {
1241 /* Order processes by their PID */
1242 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1244 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1245 pids
[n
++] = PTR_TO_PID(pidp
);
1247 assert(n
== hashmap_size(cg
->pids
));
1248 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1250 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1252 for (i
= 0; i
< n
; i
++) {
1253 _cleanup_free_
char *e
= NULL
;
1254 const char *special
;
1257 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1260 if (n_columns
!= 0) {
1263 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1265 e
= ellipsize(name
, k
, 100);
1270 more
= i
+1 < n
|| cg
->children
;
1271 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1273 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1282 struct CGroupInfo
**children
, *child
;
1285 /* Order subcgroups by their name */
1286 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1287 LIST_FOREACH(siblings
, child
, cg
->children
)
1288 children
[n
++] = child
;
1289 assert(n
== cg
->n_children
);
1290 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1293 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1295 for (i
= 0; i
< n
; i
++) {
1296 _cleanup_free_
char *pp
= NULL
;
1297 const char *name
, *special
;
1300 child
= children
[i
];
1302 name
= strrchr(child
->cgroup_path
, '/');
1308 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1310 fputs(prefix
, stdout
);
1311 fputs(special
, stdout
);
1312 fputs(name
, stdout
);
1313 fputc('\n', stdout
);
1315 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1317 pp
= strappend(prefix
, special
);
1321 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1331 static int dump_extra_processes(
1335 OutputFlags flags
) {
1337 _cleanup_free_ pid_t
*pids
= NULL
;
1338 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1339 struct CGroupInfo
*cg
;
1340 size_t n_allocated
= 0, n
= 0, k
;
1344 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1345 * combined, sorted, linear list. */
1347 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1355 if (hashmap_isempty(cg
->pids
))
1358 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1362 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1365 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1366 pids
[n
++] = PTR_TO_PID(pidp
);
1368 r
= hashmap_put(names
, pidp
, (void*) name
);
1377 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1378 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1380 for (k
= 0; k
< n
; k
++) {
1381 _cleanup_free_
char *e
= NULL
;
1384 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1387 if (n_columns
!= 0) {
1390 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1392 e
= ellipsize(name
, z
, 100);
1397 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1399 special_glyph(TRIANGULAR_BULLET
),
1407 int unit_show_processes(
1410 const char *cgroup_path
,
1414 sd_bus_error
*error
) {
1416 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1417 Hashmap
*cgroups
= NULL
;
1418 struct CGroupInfo
*cg
;
1424 if (flags
& OUTPUT_FULL_WIDTH
)
1426 else if (n_columns
<= 0)
1427 n_columns
= columns();
1429 prefix
= strempty(prefix
);
1431 r
= sd_bus_call_method(
1433 "org.freedesktop.systemd1",
1434 "/org/freedesktop/systemd1",
1435 "org.freedesktop.systemd1.Manager",
1444 cgroups
= hashmap_new(&string_hash_ops
);
1448 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1453 const char *path
= NULL
, *name
= NULL
;
1456 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1462 r
= add_process(cgroups
, path
, pid
, name
);
1467 r
= sd_bus_message_exit_container(reply
);
1471 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1475 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
1478 while ((cg
= hashmap_first(cgroups
)))
1479 remove_cgroup(cgroups
, cg
);
1481 hashmap_free(cgroups
);