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 r
= sd_bus_message_append(m
, "v", "s", eq
);
272 else if (streq(field
, "SyslogLevel")) {
275 level
= log_level_from_string(eq
);
277 log_error("Failed to parse %s value %s.", field
, eq
);
281 r
= sd_bus_message_append(m
, "v", "i", level
);
283 } else if (streq(field
, "SyslogFacility")) {
286 facility
= log_facility_unshifted_from_string(eq
);
288 log_error("Failed to parse %s value %s.", field
, eq
);
292 r
= sd_bus_message_append(m
, "v", "i", facility
);
294 } else if (streq(field
, "DeviceAllow")) {
297 r
= sd_bus_message_append(m
, "v", "a(ss)", 0);
299 const char *path
, *rwm
, *e
;
303 path
= strndupa(eq
, e
- eq
);
310 if (!is_deviceallow_pattern(path
)) {
311 log_error("%s is not a device file in /dev.", path
);
315 r
= sd_bus_message_append(m
, "v", "a(ss)", 1, path
, rwm
);
318 } else if (cgroup_io_limit_type_from_string(field
) >= 0 || STR_IN_SET(field
, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
321 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
323 const char *path
, *bandwidth
, *e
;
328 path
= strndupa(eq
, e
- eq
);
331 log_error("Failed to parse %s value %s.", field
, eq
);
335 if (!path_startswith(path
, "/dev")) {
336 log_error("%s is not a device file in /dev.", path
);
340 if (streq(bandwidth
, "infinity")) {
341 bytes
= CGROUP_LIMIT_MAX
;
343 r
= parse_size(bandwidth
, 1000, &bytes
);
345 log_error("Failed to parse byte value %s.", bandwidth
);
350 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, bytes
);
353 } else if (STR_IN_SET(field
, "IODeviceWeight", "BlockIODeviceWeight")) {
356 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
358 const char *path
, *weight
, *e
;
363 path
= strndupa(eq
, e
- eq
);
366 log_error("Failed to parse %s value %s.", field
, eq
);
370 if (!path_startswith(path
, "/dev")) {
371 log_error("%s is not a device file in /dev.", path
);
375 r
= safe_atou64(weight
, &u
);
377 log_error("Failed to parse %s value %s.", field
, weight
);
380 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, u
);
383 } else if (streq(field
, "Nice")) {
386 r
= parse_nice(eq
, &n
);
388 return log_error_errno(r
, "Failed to parse nice value: %s", eq
);
390 r
= sd_bus_message_append(m
, "v", "i", (int32_t) n
);
392 } else if (streq(field
, "IOSchedulingClass")) {
395 c
= ioprio_class_from_string(eq
);
397 return log_error_errno(r
, "Failed to parse IO scheduling class: %s", eq
);
399 r
= sd_bus_message_append(m
, "v", "i", (int32_t) c
);
401 } else if (streq(field
, "IOSchedulingPriority")) {
404 r
= ioprio_parse_priority(eq
, &q
);
406 return log_error_errno(r
, "Failed to parse IO scheduling priority: %s", eq
);
408 r
= sd_bus_message_append(m
, "v", "i", (int32_t) q
);
410 } else if (STR_IN_SET(field
, "Environment", "PassEnvironment")) {
413 r
= sd_bus_message_open_container(m
, 'v', "as");
415 return bus_log_create_error(r
);
417 r
= sd_bus_message_open_container(m
, 'a', "s");
419 return bus_log_create_error(r
);
422 _cleanup_free_
char *word
= NULL
;
424 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
426 log_error("Failed to parse Environment value %s", eq
);
432 if (streq(field
, "Environment")) {
433 if (!env_assignment_is_valid(word
)) {
434 log_error("Invalid environment assignment: %s", word
);
437 } else { /* PassEnvironment */
438 if (!env_name_is_valid(word
)) {
439 log_error("Invalid environment variable name: %s", word
);
444 r
= sd_bus_message_append_basic(m
, 's', word
);
446 return bus_log_create_error(r
);
449 r
= sd_bus_message_close_container(m
);
451 return bus_log_create_error(r
);
453 r
= sd_bus_message_close_container(m
);
455 } else if (streq(field
, "KillSignal")) {
458 sig
= signal_from_string_try_harder(eq
);
460 log_error("Failed to parse %s value %s.", field
, eq
);
464 r
= sd_bus_message_append(m
, "v", "i", sig
);
466 } else if (streq(field
, "TimerSlackNSec")) {
469 r
= parse_nsec(eq
, &n
);
471 log_error("Failed to parse %s value %s", field
, eq
);
475 r
= sd_bus_message_append(m
, "v", "t", n
);
476 } else if (streq(field
, "OOMScoreAdjust")) {
479 r
= safe_atoi(eq
, &oa
);
481 log_error("Failed to parse %s value %s", field
, eq
);
485 if (!oom_score_adjust_is_valid(oa
)) {
486 log_error("OOM score adjust value out of range");
490 r
= sd_bus_message_append(m
, "v", "i", oa
);
491 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
492 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
495 r
= sd_bus_message_open_container(m
, 'v', "as");
497 return bus_log_create_error(r
);
499 r
= sd_bus_message_open_container(m
, 'a', "s");
501 return bus_log_create_error(r
);
504 _cleanup_free_
char *word
= NULL
;
507 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
509 log_error("Failed to parse %s value %s", field
, eq
);
515 if (!utf8_is_valid(word
)) {
516 log_error("Failed to parse %s value %s", field
, eq
);
520 offset
= word
[0] == '-';
521 offset
+= word
[offset
] == '+';
523 if (!path_is_absolute(word
+ offset
)) {
524 log_error("Failed to parse %s value %s", field
, eq
);
528 path_kill_slashes(word
+ offset
);
530 r
= sd_bus_message_append_basic(m
, 's', word
);
532 return bus_log_create_error(r
);
535 r
= sd_bus_message_close_container(m
);
537 return bus_log_create_error(r
);
539 r
= sd_bus_message_close_container(m
);
541 } else if (streq(field
, "RuntimeDirectory")) {
544 r
= sd_bus_message_open_container(m
, 'v', "as");
546 return bus_log_create_error(r
);
548 r
= sd_bus_message_open_container(m
, 'a', "s");
550 return bus_log_create_error(r
);
553 _cleanup_free_
char *word
= NULL
;
555 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
557 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
562 r
= sd_bus_message_append_basic(m
, 's', word
);
564 return bus_log_create_error(r
);
567 r
= sd_bus_message_close_container(m
);
569 return bus_log_create_error(r
);
571 r
= sd_bus_message_close_container(m
);
573 } else if (streq(field
, "RestrictNamespaces")) {
575 unsigned long flags
= 0;
582 r
= parse_boolean(eq
);
586 flags
= NAMESPACE_FLAGS_ALL
;
588 r
= namespace_flag_from_string_many(eq
, &flags
);
590 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
594 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
596 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
597 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
598 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
599 else if (streq(field
, "MountFlags")) {
602 r
= mount_propagation_flags_from_string(eq
, &f
);
604 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
606 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
607 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
610 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
614 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
619 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
620 char *s
= NULL
, *d
= NULL
;
621 bool ignore_enoent
= false;
622 uint64_t flags
= MS_REC
;
624 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
626 return log_error_errno(r
, "Failed to parse argument: %m");
632 ignore_enoent
= true;
636 if (p
&& p
[-1] == ':') {
637 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
639 return log_error_errno(r
, "Failed to parse argument: %m");
641 log_error("Missing argument after ':': %s", eq
);
647 if (p
&& p
[-1] == ':') {
648 _cleanup_free_
char *options
= NULL
;
650 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
652 return log_error_errno(r
, "Failed to parse argument: %m");
654 if (isempty(options
) || streq(options
, "rbind"))
656 else if (streq(options
, "norbind"))
659 log_error("Unknown options: %s", eq
);
667 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
672 r
= sd_bus_message_close_container(m
);
676 r
= sd_bus_message_close_container(m
);
678 log_error("Unknown assignment %s.", assignment
);
684 return bus_log_create_error(r
);
686 r
= sd_bus_message_close_container(m
);
688 return bus_log_create_error(r
);
693 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
700 r
= bus_append_unit_property_assignment(m
, *i
);
708 typedef struct BusWaitForJobs
{
715 sd_bus_slot
*slot_job_removed
;
716 sd_bus_slot
*slot_disconnected
;
719 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
722 log_error("Warning! D-Bus connection terminated.");
723 sd_bus_close(sd_bus_message_get_bus(m
));
728 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
729 const char *path
, *unit
, *result
;
730 BusWaitForJobs
*d
= userdata
;
738 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
740 bus_log_parse_error(r
);
744 found
= set_remove(d
->jobs
, (char*) path
);
750 if (!isempty(result
))
751 d
->result
= strdup(result
);
754 d
->name
= strdup(unit
);
759 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
763 set_free_free(d
->jobs
);
765 sd_bus_slot_unref(d
->slot_disconnected
);
766 sd_bus_slot_unref(d
->slot_job_removed
);
768 sd_bus_unref(d
->bus
);
776 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
777 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
783 d
= new0(BusWaitForJobs
, 1);
787 d
->bus
= sd_bus_ref(bus
);
789 /* When we are a bus client we match by sender. Direct
790 * connections OTOH have no initialized sender field, and
791 * hence we ignore the sender then */
792 r
= sd_bus_add_match(
794 &d
->slot_job_removed
,
797 "sender='org.freedesktop.systemd1',"
798 "interface='org.freedesktop.systemd1.Manager',"
799 "member='JobRemoved',"
800 "path='/org/freedesktop/systemd1'" :
802 "interface='org.freedesktop.systemd1.Manager',"
803 "member='JobRemoved',"
804 "path='/org/freedesktop/systemd1'",
805 match_job_removed
, d
);
809 r
= sd_bus_add_match(
811 &d
->slot_disconnected
,
813 "sender='org.freedesktop.DBus.Local',"
814 "interface='org.freedesktop.DBus.Local',"
815 "member='Disconnected'",
816 match_disconnected
, d
);
826 static int bus_process_wait(sd_bus
*bus
) {
830 r
= sd_bus_process(bus
, NULL
);
836 r
= sd_bus_wait(bus
, (uint64_t) -1);
842 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
843 _cleanup_free_
char *dbus_path
= NULL
;
849 dbus_path
= unit_dbus_path_from_name(d
->name
);
853 return sd_bus_get_property_string(d
->bus
,
854 "org.freedesktop.systemd1",
856 "org.freedesktop.systemd1.Service",
862 static const struct {
863 const char *result
, *explanation
;
864 } explanations
[] = {
865 { "resources", "of unavailable resources or another system error" },
866 { "protocol", "the service did not take the steps required by its unit configuration" },
867 { "timeout", "a timeout was exceeded" },
868 { "exit-code", "the control process exited with error code" },
869 { "signal", "a fatal signal was delivered to the control process" },
870 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
871 { "watchdog", "the service failed to send watchdog ping" },
872 { "start-limit", "start of the service was attempted too often" }
875 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
876 _cleanup_free_
char *service_shell_quoted
= NULL
;
877 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
881 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
884 _cleanup_free_
char *t
;
886 t
= strv_join((char**) extra_args
, " ");
887 systemctl
= strjoina("systemctl ", t
? : "<args>");
888 journalctl
= strjoina("journalctl ", t
? : "<args>");
891 if (!isempty(result
)) {
894 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
895 if (streq(result
, explanations
[i
].result
))
898 if (i
< ELEMENTSOF(explanations
)) {
899 log_error("Job for %s failed because %s.\n"
900 "See \"%s status %s\" and \"%s -xe\" for details.\n",
902 explanations
[i
].explanation
,
904 service_shell_quoted
?: "<service>",
910 log_error("Job for %s failed.\n"
911 "See \"%s status %s\" and \"%s -xe\" for details.\n",
914 service_shell_quoted
?: "<service>",
918 /* For some results maybe additional explanation is required */
919 if (streq_ptr(result
, "start-limit"))
920 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
921 "followed by \"%1$s start %2$s\" again.",
923 service_shell_quoted
?: "<service>");
926 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
932 if (streq(d
->result
, "canceled"))
933 log_error("Job for %s canceled.", strna(d
->name
));
934 else if (streq(d
->result
, "timeout"))
935 log_error("Job for %s timed out.", strna(d
->name
));
936 else if (streq(d
->result
, "dependency"))
937 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
938 else if (streq(d
->result
, "invalid"))
939 log_error("%s is not active, cannot reload.", strna(d
->name
));
940 else if (streq(d
->result
, "assert"))
941 log_error("Assertion failed on job for %s.", strna(d
->name
));
942 else if (streq(d
->result
, "unsupported"))
943 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
944 else if (streq(d
->result
, "collected"))
945 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
946 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped")) {
949 _cleanup_free_
char *result
= NULL
;
951 q
= bus_job_get_service_result(d
, &result
);
953 log_debug_errno(q
, "Failed to get Result property of service %s: %m", d
->name
);
955 log_job_error_with_service_result(d
->name
, result
, extra_args
);
957 log_error("Job failed. See \"journalctl -xe\" for details.");
961 if (STR_IN_SET(d
->result
, "canceled", "collected"))
963 else if (streq(d
->result
, "timeout"))
965 else if (streq(d
->result
, "dependency"))
967 else if (streq(d
->result
, "invalid"))
969 else if (streq(d
->result
, "assert"))
971 else if (streq(d
->result
, "unsupported"))
973 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped"))
979 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
984 while (!set_isempty(d
->jobs
)) {
987 q
= bus_process_wait(d
->bus
);
989 return log_error_errno(q
, "Failed to wait for response: %m");
992 q
= check_wait_response(d
, quiet
, extra_args
);
993 /* Return the first error as it is most likely to be
998 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
1001 d
->name
= mfree(d
->name
);
1002 d
->result
= mfree(d
->result
);
1008 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1013 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1017 return set_put_strdup(d
->jobs
, path
);
1020 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1023 r
= bus_wait_for_jobs_add(d
, path
);
1027 return bus_wait_for_jobs(d
, quiet
, NULL
);
1030 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1031 const char *type
, *path
, *source
;
1034 /* changes is dereferenced when calling unit_file_dump_changes() later,
1035 * so we have to make sure this is not NULL. */
1039 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1041 return bus_log_parse_error(r
);
1043 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1044 /* We expect only "success" changes to be sent over the bus.
1045 Hence, reject anything negative. */
1046 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1049 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1053 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1058 return bus_log_parse_error(r
);
1060 r
= sd_bus_message_exit_container(m
);
1062 return bus_log_parse_error(r
);
1064 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, false);
1070 bool is_const
; /* If false, cgroup_path should be free()'d */
1072 Hashmap
*pids
; /* PID → process name */
1075 struct CGroupInfo
*parent
;
1076 LIST_FIELDS(struct CGroupInfo
, siblings
);
1077 LIST_HEAD(struct CGroupInfo
, children
);
1081 static bool IS_ROOT(const char *p
) {
1082 return isempty(p
) || streq(p
, "/");
1085 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1086 struct CGroupInfo
*parent
= NULL
, *cg
;
1095 cg
= hashmap_get(cgroups
, path
);
1101 if (!IS_ROOT(path
)) {
1104 e
= strrchr(path
, '/');
1108 pp
= strndupa(path
, e
- path
);
1112 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1117 cg
= new0(struct CGroupInfo
, 1);
1122 cg
->cgroup_path
= (char*) path
;
1124 cg
->cgroup_path
= strdup(path
);
1125 if (!cg
->cgroup_path
) {
1131 cg
->is_const
= is_const
;
1132 cg
->parent
= parent
;
1134 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1137 free(cg
->cgroup_path
);
1143 LIST_PREPEND(siblings
, parent
->children
, cg
);
1144 parent
->n_children
++;
1151 static int add_process(
1157 struct CGroupInfo
*cg
;
1164 r
= add_cgroup(cgroups
, path
, true, &cg
);
1168 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1172 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1175 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1179 while (cg
->children
)
1180 remove_cgroup(cgroups
, cg
->children
);
1182 hashmap_remove(cgroups
, cg
->cgroup_path
);
1185 free(cg
->cgroup_path
);
1187 hashmap_free(cg
->pids
);
1190 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1195 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1196 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1201 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1204 static int dump_processes(
1206 const char *cgroup_path
,
1209 OutputFlags flags
) {
1211 struct CGroupInfo
*cg
;
1216 if (IS_ROOT(cgroup_path
))
1219 cg
= hashmap_get(cgroups
, cgroup_path
);
1223 if (!hashmap_isempty(cg
->pids
)) {
1231 /* Order processes by their PID */
1232 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1234 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1235 pids
[n
++] = PTR_TO_PID(pidp
);
1237 assert(n
== hashmap_size(cg
->pids
));
1238 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1240 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1242 for (i
= 0; i
< n
; i
++) {
1243 _cleanup_free_
char *e
= NULL
;
1244 const char *special
;
1247 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1250 if (n_columns
!= 0) {
1253 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1255 e
= ellipsize(name
, k
, 100);
1260 more
= i
+1 < n
|| cg
->children
;
1261 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1263 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1272 struct CGroupInfo
**children
, *child
;
1275 /* Order subcgroups by their name */
1276 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1277 LIST_FOREACH(siblings
, child
, cg
->children
)
1278 children
[n
++] = child
;
1279 assert(n
== cg
->n_children
);
1280 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1283 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1285 for (i
= 0; i
< n
; i
++) {
1286 _cleanup_free_
char *pp
= NULL
;
1287 const char *name
, *special
;
1290 child
= children
[i
];
1292 name
= strrchr(child
->cgroup_path
, '/');
1298 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1300 fputs(prefix
, stdout
);
1301 fputs(special
, stdout
);
1302 fputs(name
, stdout
);
1303 fputc('\n', stdout
);
1305 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1307 pp
= strappend(prefix
, special
);
1311 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1321 static int dump_extra_processes(
1325 OutputFlags flags
) {
1327 _cleanup_free_ pid_t
*pids
= NULL
;
1328 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1329 struct CGroupInfo
*cg
;
1330 size_t n_allocated
= 0, n
= 0, k
;
1334 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1335 * combined, sorted, linear list. */
1337 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1345 if (hashmap_isempty(cg
->pids
))
1348 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1352 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1355 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1356 pids
[n
++] = PTR_TO_PID(pidp
);
1358 r
= hashmap_put(names
, pidp
, (void*) name
);
1367 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1368 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1370 for (k
= 0; k
< n
; k
++) {
1371 _cleanup_free_
char *e
= NULL
;
1374 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1377 if (n_columns
!= 0) {
1380 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1382 e
= ellipsize(name
, z
, 100);
1387 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1389 special_glyph(TRIANGULAR_BULLET
),
1397 int unit_show_processes(
1400 const char *cgroup_path
,
1404 sd_bus_error
*error
) {
1406 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1407 Hashmap
*cgroups
= NULL
;
1408 struct CGroupInfo
*cg
;
1414 if (flags
& OUTPUT_FULL_WIDTH
)
1416 else if (n_columns
<= 0)
1417 n_columns
= columns();
1419 prefix
= strempty(prefix
);
1421 r
= sd_bus_call_method(
1423 "org.freedesktop.systemd1",
1424 "/org/freedesktop/systemd1",
1425 "org.freedesktop.systemd1.Manager",
1434 cgroups
= hashmap_new(&string_hash_ops
);
1438 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1443 const char *path
= NULL
, *name
= NULL
;
1446 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1452 r
= add_process(cgroups
, path
, pid
, name
);
1457 r
= sd_bus_message_exit_container(reply
);
1461 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1465 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
1468 while ((cg
= hashmap_first(cgroups
)))
1469 remove_cgroup(cgroups
, cg
);
1471 hashmap_free(cgroups
);