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 (STR_IN_SET(field
, "Environment", "PassEnvironment")) {
405 r
= sd_bus_message_open_container(m
, 'v', "as");
407 return bus_log_create_error(r
);
409 r
= sd_bus_message_open_container(m
, 'a', "s");
411 return bus_log_create_error(r
);
414 _cleanup_free_
char *word
= NULL
;
416 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
418 log_error("Failed to parse Environment value %s", eq
);
424 if (streq(field
, "Environment")) {
425 if (!env_assignment_is_valid(word
)) {
426 log_error("Invalid environment assignment: %s", word
);
429 } else { /* PassEnvironment */
430 if (!env_name_is_valid(word
)) {
431 log_error("Invalid environment variable name: %s", word
);
436 r
= sd_bus_message_append_basic(m
, 's', word
);
438 return bus_log_create_error(r
);
441 r
= sd_bus_message_close_container(m
);
443 return bus_log_create_error(r
);
445 r
= sd_bus_message_close_container(m
);
447 } else if (streq(field
, "KillSignal")) {
450 sig
= signal_from_string_try_harder(eq
);
452 log_error("Failed to parse %s value %s.", field
, eq
);
456 r
= sd_bus_message_append(m
, "v", "i", sig
);
458 } else if (streq(field
, "TimerSlackNSec")) {
461 r
= parse_nsec(eq
, &n
);
463 log_error("Failed to parse %s value %s", field
, eq
);
467 r
= sd_bus_message_append(m
, "v", "t", n
);
468 } else if (streq(field
, "OOMScoreAdjust")) {
471 r
= safe_atoi(eq
, &oa
);
473 log_error("Failed to parse %s value %s", field
, eq
);
477 if (!oom_score_adjust_is_valid(oa
)) {
478 log_error("OOM score adjust value out of range");
482 r
= sd_bus_message_append(m
, "v", "i", oa
);
483 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
484 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
487 r
= sd_bus_message_open_container(m
, 'v', "as");
489 return bus_log_create_error(r
);
491 r
= sd_bus_message_open_container(m
, 'a', "s");
493 return bus_log_create_error(r
);
496 _cleanup_free_
char *word
= NULL
;
499 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
501 log_error("Failed to parse %s value %s", field
, eq
);
507 if (!utf8_is_valid(word
)) {
508 log_error("Failed to parse %s value %s", field
, eq
);
512 offset
= word
[0] == '-';
513 offset
+= word
[offset
] == '+';
515 if (!path_is_absolute(word
+ offset
)) {
516 log_error("Failed to parse %s value %s", field
, eq
);
520 path_kill_slashes(word
+ offset
);
522 r
= sd_bus_message_append_basic(m
, 's', word
);
524 return bus_log_create_error(r
);
527 r
= sd_bus_message_close_container(m
);
529 return bus_log_create_error(r
);
531 r
= sd_bus_message_close_container(m
);
533 } else if (streq(field
, "RuntimeDirectory")) {
536 r
= sd_bus_message_open_container(m
, 'v', "as");
538 return bus_log_create_error(r
);
540 r
= sd_bus_message_open_container(m
, 'a', "s");
542 return bus_log_create_error(r
);
545 _cleanup_free_
char *word
= NULL
;
547 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
549 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
554 r
= sd_bus_message_append_basic(m
, 's', word
);
556 return bus_log_create_error(r
);
559 r
= sd_bus_message_close_container(m
);
561 return bus_log_create_error(r
);
563 r
= sd_bus_message_close_container(m
);
565 } else if (streq(field
, "RestrictNamespaces")) {
567 unsigned long flags
= 0;
574 r
= parse_boolean(eq
);
578 flags
= NAMESPACE_FLAGS_ALL
;
580 r
= namespace_flag_from_string_many(eq
, &flags
);
582 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
586 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
588 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
589 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
590 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
591 else if (streq(field
, "MountFlags")) {
594 r
= mount_propagation_flags_from_string(eq
, &f
);
596 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
598 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
599 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
602 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
606 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
611 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
612 char *s
= NULL
, *d
= NULL
;
613 bool ignore_enoent
= false;
614 uint64_t flags
= MS_REC
;
616 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
618 return log_error_errno(r
, "Failed to parse argument: %m");
624 ignore_enoent
= true;
628 if (p
&& p
[-1] == ':') {
629 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
631 return log_error_errno(r
, "Failed to parse argument: %m");
633 log_error("Missing argument after ':': %s", eq
);
639 if (p
&& p
[-1] == ':') {
640 _cleanup_free_
char *options
= NULL
;
642 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
644 return log_error_errno(r
, "Failed to parse argument: %m");
646 if (isempty(options
) || streq(options
, "rbind"))
648 else if (streq(options
, "norbind"))
651 log_error("Unknown options: %s", eq
);
659 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
664 r
= sd_bus_message_close_container(m
);
668 r
= sd_bus_message_close_container(m
);
670 log_error("Unknown assignment %s.", assignment
);
676 return bus_log_create_error(r
);
678 r
= sd_bus_message_close_container(m
);
680 return bus_log_create_error(r
);
685 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
692 r
= bus_append_unit_property_assignment(m
, *i
);
700 typedef struct BusWaitForJobs
{
707 sd_bus_slot
*slot_job_removed
;
708 sd_bus_slot
*slot_disconnected
;
711 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
714 log_error("Warning! D-Bus connection terminated.");
715 sd_bus_close(sd_bus_message_get_bus(m
));
720 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
721 const char *path
, *unit
, *result
;
722 BusWaitForJobs
*d
= userdata
;
730 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
732 bus_log_parse_error(r
);
736 found
= set_remove(d
->jobs
, (char*) path
);
742 if (!isempty(result
))
743 d
->result
= strdup(result
);
746 d
->name
= strdup(unit
);
751 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
755 set_free_free(d
->jobs
);
757 sd_bus_slot_unref(d
->slot_disconnected
);
758 sd_bus_slot_unref(d
->slot_job_removed
);
760 sd_bus_unref(d
->bus
);
768 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
769 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
775 d
= new0(BusWaitForJobs
, 1);
779 d
->bus
= sd_bus_ref(bus
);
781 /* When we are a bus client we match by sender. Direct
782 * connections OTOH have no initialized sender field, and
783 * hence we ignore the sender then */
784 r
= sd_bus_add_match(
786 &d
->slot_job_removed
,
789 "sender='org.freedesktop.systemd1',"
790 "interface='org.freedesktop.systemd1.Manager',"
791 "member='JobRemoved',"
792 "path='/org/freedesktop/systemd1'" :
794 "interface='org.freedesktop.systemd1.Manager',"
795 "member='JobRemoved',"
796 "path='/org/freedesktop/systemd1'",
797 match_job_removed
, d
);
801 r
= sd_bus_add_match(
803 &d
->slot_disconnected
,
805 "sender='org.freedesktop.DBus.Local',"
806 "interface='org.freedesktop.DBus.Local',"
807 "member='Disconnected'",
808 match_disconnected
, d
);
818 static int bus_process_wait(sd_bus
*bus
) {
822 r
= sd_bus_process(bus
, NULL
);
828 r
= sd_bus_wait(bus
, (uint64_t) -1);
834 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
835 _cleanup_free_
char *dbus_path
= NULL
;
841 dbus_path
= unit_dbus_path_from_name(d
->name
);
845 return sd_bus_get_property_string(d
->bus
,
846 "org.freedesktop.systemd1",
848 "org.freedesktop.systemd1.Service",
854 static const struct {
855 const char *result
, *explanation
;
856 } explanations
[] = {
857 { "resources", "of unavailable resources or another system error" },
858 { "protocol", "the service did not take the steps required by its unit configuration" },
859 { "timeout", "a timeout was exceeded" },
860 { "exit-code", "the control process exited with error code" },
861 { "signal", "a fatal signal was delivered to the control process" },
862 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
863 { "watchdog", "the service failed to send watchdog ping" },
864 { "start-limit", "start of the service was attempted too often" }
867 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
868 _cleanup_free_
char *service_shell_quoted
= NULL
;
869 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
873 service_shell_quoted
= shell_maybe_quote(service
, ESCAPE_BACKSLASH
);
876 _cleanup_free_
char *t
;
878 t
= strv_join((char**) extra_args
, " ");
879 systemctl
= strjoina("systemctl ", t
? : "<args>");
880 journalctl
= strjoina("journalctl ", t
? : "<args>");
883 if (!isempty(result
)) {
886 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
887 if (streq(result
, explanations
[i
].result
))
890 if (i
< ELEMENTSOF(explanations
)) {
891 log_error("Job for %s failed because %s.\n"
892 "See \"%s status %s\" and \"%s -xe\" for details.\n",
894 explanations
[i
].explanation
,
896 service_shell_quoted
?: "<service>",
902 log_error("Job for %s failed.\n"
903 "See \"%s status %s\" and \"%s -xe\" for details.\n",
906 service_shell_quoted
?: "<service>",
910 /* For some results maybe additional explanation is required */
911 if (streq_ptr(result
, "start-limit"))
912 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
913 "followed by \"%1$s start %2$s\" again.",
915 service_shell_quoted
?: "<service>");
918 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
924 if (streq(d
->result
, "canceled"))
925 log_error("Job for %s canceled.", strna(d
->name
));
926 else if (streq(d
->result
, "timeout"))
927 log_error("Job for %s timed out.", strna(d
->name
));
928 else if (streq(d
->result
, "dependency"))
929 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
930 else if (streq(d
->result
, "invalid"))
931 log_error("%s is not active, cannot reload.", strna(d
->name
));
932 else if (streq(d
->result
, "assert"))
933 log_error("Assertion failed on job for %s.", strna(d
->name
));
934 else if (streq(d
->result
, "unsupported"))
935 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
936 else if (streq(d
->result
, "collected"))
937 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
938 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped")) {
941 _cleanup_free_
char *result
= NULL
;
943 q
= bus_job_get_service_result(d
, &result
);
945 log_debug_errno(q
, "Failed to get Result property of service %s: %m", d
->name
);
947 log_job_error_with_service_result(d
->name
, result
, extra_args
);
949 log_error("Job failed. See \"journalctl -xe\" for details.");
953 if (STR_IN_SET(d
->result
, "canceled", "collected"))
955 else if (streq(d
->result
, "timeout"))
957 else if (streq(d
->result
, "dependency"))
959 else if (streq(d
->result
, "invalid"))
961 else if (streq(d
->result
, "assert"))
963 else if (streq(d
->result
, "unsupported"))
965 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped"))
971 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
976 while (!set_isempty(d
->jobs
)) {
979 q
= bus_process_wait(d
->bus
);
981 return log_error_errno(q
, "Failed to wait for response: %m");
984 q
= check_wait_response(d
, quiet
, extra_args
);
985 /* Return the first error as it is most likely to be
990 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
993 d
->name
= mfree(d
->name
);
994 d
->result
= mfree(d
->result
);
1000 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
1005 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1009 return set_put_strdup(d
->jobs
, path
);
1012 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1015 r
= bus_wait_for_jobs_add(d
, path
);
1019 return bus_wait_for_jobs(d
, quiet
, NULL
);
1022 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1023 const char *type
, *path
, *source
;
1026 /* changes is dereferenced when calling unit_file_dump_changes() later,
1027 * so we have to make sure this is not NULL. */
1031 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1033 return bus_log_parse_error(r
);
1035 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1036 /* We expect only "success" changes to be sent over the bus.
1037 Hence, reject anything negative. */
1038 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1041 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1045 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1050 return bus_log_parse_error(r
);
1052 r
= sd_bus_message_exit_container(m
);
1054 return bus_log_parse_error(r
);
1056 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, false);
1062 bool is_const
; /* If false, cgroup_path should be free()'d */
1064 Hashmap
*pids
; /* PID → process name */
1067 struct CGroupInfo
*parent
;
1068 LIST_FIELDS(struct CGroupInfo
, siblings
);
1069 LIST_HEAD(struct CGroupInfo
, children
);
1073 static bool IS_ROOT(const char *p
) {
1074 return isempty(p
) || streq(p
, "/");
1077 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1078 struct CGroupInfo
*parent
= NULL
, *cg
;
1087 cg
= hashmap_get(cgroups
, path
);
1093 if (!IS_ROOT(path
)) {
1096 e
= strrchr(path
, '/');
1100 pp
= strndupa(path
, e
- path
);
1104 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1109 cg
= new0(struct CGroupInfo
, 1);
1114 cg
->cgroup_path
= (char*) path
;
1116 cg
->cgroup_path
= strdup(path
);
1117 if (!cg
->cgroup_path
) {
1123 cg
->is_const
= is_const
;
1124 cg
->parent
= parent
;
1126 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1129 free(cg
->cgroup_path
);
1135 LIST_PREPEND(siblings
, parent
->children
, cg
);
1136 parent
->n_children
++;
1143 static int add_process(
1149 struct CGroupInfo
*cg
;
1156 r
= add_cgroup(cgroups
, path
, true, &cg
);
1160 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1164 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1167 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1171 while (cg
->children
)
1172 remove_cgroup(cgroups
, cg
->children
);
1174 hashmap_remove(cgroups
, cg
->cgroup_path
);
1177 free(cg
->cgroup_path
);
1179 hashmap_free(cg
->pids
);
1182 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1187 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1188 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1193 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1196 static int dump_processes(
1198 const char *cgroup_path
,
1201 OutputFlags flags
) {
1203 struct CGroupInfo
*cg
;
1208 if (IS_ROOT(cgroup_path
))
1211 cg
= hashmap_get(cgroups
, cgroup_path
);
1215 if (!hashmap_isempty(cg
->pids
)) {
1223 /* Order processes by their PID */
1224 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1226 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1227 pids
[n
++] = PTR_TO_PID(pidp
);
1229 assert(n
== hashmap_size(cg
->pids
));
1230 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1232 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1234 for (i
= 0; i
< n
; i
++) {
1235 _cleanup_free_
char *e
= NULL
;
1236 const char *special
;
1239 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1242 if (n_columns
!= 0) {
1245 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1247 e
= ellipsize(name
, k
, 100);
1252 more
= i
+1 < n
|| cg
->children
;
1253 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1255 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1264 struct CGroupInfo
**children
, *child
;
1267 /* Order subcgroups by their name */
1268 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1269 LIST_FOREACH(siblings
, child
, cg
->children
)
1270 children
[n
++] = child
;
1271 assert(n
== cg
->n_children
);
1272 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1275 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1277 for (i
= 0; i
< n
; i
++) {
1278 _cleanup_free_
char *pp
= NULL
;
1279 const char *name
, *special
;
1282 child
= children
[i
];
1284 name
= strrchr(child
->cgroup_path
, '/');
1290 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1292 fputs(prefix
, stdout
);
1293 fputs(special
, stdout
);
1294 fputs(name
, stdout
);
1295 fputc('\n', stdout
);
1297 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1299 pp
= strappend(prefix
, special
);
1303 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1313 static int dump_extra_processes(
1317 OutputFlags flags
) {
1319 _cleanup_free_ pid_t
*pids
= NULL
;
1320 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1321 struct CGroupInfo
*cg
;
1322 size_t n_allocated
= 0, n
= 0, k
;
1326 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1327 * combined, sorted, linear list. */
1329 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1337 if (hashmap_isempty(cg
->pids
))
1340 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1344 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1347 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1348 pids
[n
++] = PTR_TO_PID(pidp
);
1350 r
= hashmap_put(names
, pidp
, (void*) name
);
1359 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1360 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1362 for (k
= 0; k
< n
; k
++) {
1363 _cleanup_free_
char *e
= NULL
;
1366 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1369 if (n_columns
!= 0) {
1372 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1374 e
= ellipsize(name
, z
, 100);
1379 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1381 special_glyph(TRIANGULAR_BULLET
),
1389 int unit_show_processes(
1392 const char *cgroup_path
,
1396 sd_bus_error
*error
) {
1398 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1399 Hashmap
*cgroups
= NULL
;
1400 struct CGroupInfo
*cg
;
1406 if (flags
& OUTPUT_FULL_WIDTH
)
1408 else if (n_columns
<= 0)
1409 n_columns
= columns();
1411 prefix
= strempty(prefix
);
1413 r
= sd_bus_call_method(
1415 "org.freedesktop.systemd1",
1416 "/org/freedesktop/systemd1",
1417 "org.freedesktop.systemd1.Manager",
1426 cgroups
= hashmap_new(&string_hash_ops
);
1430 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1435 const char *path
= NULL
, *name
= NULL
;
1438 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1444 r
= add_process(cgroups
, path
, pid
, name
);
1449 r
= sd_bus_message_exit_container(reply
);
1453 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1457 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
1460 while ((cg
= hashmap_first(cgroups
)))
1461 remove_cgroup(cgroups
, cg
);
1463 hashmap_free(cgroups
);