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")) {
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"))
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 (STR_IN_SET(field
, "Environment", "PassEnvironment")) {
395 r
= sd_bus_message_open_container(m
, 'v', "as");
397 return bus_log_create_error(r
);
399 r
= sd_bus_message_open_container(m
, 'a', "s");
401 return bus_log_create_error(r
);
404 _cleanup_free_
char *word
= NULL
;
406 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
408 log_error("Failed to parse Environment value %s", eq
);
414 if (streq(field
, "Environment")) {
415 if (!env_assignment_is_valid(word
)) {
416 log_error("Invalid environment assignment: %s", word
);
419 } else { /* PassEnvironment */
420 if (!env_name_is_valid(word
)) {
421 log_error("Invalid environment variable name: %s", word
);
426 r
= sd_bus_message_append_basic(m
, 's', word
);
428 return bus_log_create_error(r
);
431 r
= sd_bus_message_close_container(m
);
433 return bus_log_create_error(r
);
435 r
= sd_bus_message_close_container(m
);
437 } else if (streq(field
, "KillSignal")) {
440 sig
= signal_from_string_try_harder(eq
);
442 log_error("Failed to parse %s value %s.", field
, eq
);
446 r
= sd_bus_message_append(m
, "v", "i", sig
);
448 } else if (streq(field
, "TimerSlackNSec")) {
451 r
= parse_nsec(eq
, &n
);
453 log_error("Failed to parse %s value %s", field
, eq
);
457 r
= sd_bus_message_append(m
, "v", "t", n
);
458 } else if (streq(field
, "OOMScoreAdjust")) {
461 r
= safe_atoi(eq
, &oa
);
463 log_error("Failed to parse %s value %s", field
, eq
);
467 if (!oom_score_adjust_is_valid(oa
)) {
468 log_error("OOM score adjust value out of range");
472 r
= sd_bus_message_append(m
, "v", "i", oa
);
473 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
474 "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
477 r
= sd_bus_message_open_container(m
, 'v', "as");
479 return bus_log_create_error(r
);
481 r
= sd_bus_message_open_container(m
, 'a', "s");
483 return bus_log_create_error(r
);
486 _cleanup_free_
char *word
= NULL
;
489 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
491 log_error("Failed to parse %s value %s", field
, eq
);
497 if (!utf8_is_valid(word
)) {
498 log_error("Failed to parse %s value %s", field
, eq
);
502 offset
= word
[0] == '-';
503 if (!path_is_absolute(word
+ offset
)) {
504 log_error("Failed to parse %s value %s", field
, eq
);
508 path_kill_slashes(word
+ offset
);
510 r
= sd_bus_message_append_basic(m
, 's', word
);
512 return bus_log_create_error(r
);
515 r
= sd_bus_message_close_container(m
);
517 return bus_log_create_error(r
);
519 r
= sd_bus_message_close_container(m
);
521 } else if (streq(field
, "RuntimeDirectory")) {
524 r
= sd_bus_message_open_container(m
, 'v', "as");
526 return bus_log_create_error(r
);
528 r
= sd_bus_message_open_container(m
, 'a', "s");
530 return bus_log_create_error(r
);
533 _cleanup_free_
char *word
= NULL
;
535 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
537 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
542 r
= sd_bus_message_append_basic(m
, 's', word
);
544 return bus_log_create_error(r
);
547 r
= sd_bus_message_close_container(m
);
549 return bus_log_create_error(r
);
551 r
= sd_bus_message_close_container(m
);
553 } else if (streq(field
, "RestrictNamespaces")) {
562 r
= parse_boolean(eq
);
566 flags
= NAMESPACE_FLAGS_ALL
;
568 r
= namespace_flag_from_string_many(eq
, &flags
);
570 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
574 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
576 r
= sd_bus_message_append(m
, "v", "t", flags
);
577 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
578 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
579 else if (streq(field
, "MountFlags")) {
585 f
= mount_propagation_flags_from_string(eq
);
587 log_error("Failed to parse mount propagation type: %s", eq
);
592 r
= sd_bus_message_append(m
, "v", "t", f
);
593 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
596 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
600 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
605 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
606 char *s
= NULL
, *d
= NULL
;
607 bool ignore_enoent
= false;
608 uint64_t flags
= MS_REC
;
610 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
612 return log_error_errno(r
, "Failed to parse argument: %m");
618 ignore_enoent
= true;
622 if (p
&& p
[-1] == ':') {
623 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
625 return log_error_errno(r
, "Failed to parse argument: %m");
627 log_error("Missing argument after ':': %s", eq
);
633 if (p
&& p
[-1] == ':') {
634 _cleanup_free_
char *options
= NULL
;
636 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
638 return log_error_errno(r
, "Failed to parse argument: %m");
640 if (isempty(options
) || streq(options
, "rbind"))
642 else if (streq(options
, "norbind"))
645 log_error("Unknown options: %s", eq
);
653 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
658 r
= sd_bus_message_close_container(m
);
662 r
= sd_bus_message_close_container(m
);
664 log_error("Unknown assignment %s.", assignment
);
670 return bus_log_create_error(r
);
672 r
= sd_bus_message_close_container(m
);
674 return bus_log_create_error(r
);
679 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
686 r
= bus_append_unit_property_assignment(m
, *i
);
694 typedef struct BusWaitForJobs
{
701 sd_bus_slot
*slot_job_removed
;
702 sd_bus_slot
*slot_disconnected
;
705 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
708 log_error("Warning! D-Bus connection terminated.");
709 sd_bus_close(sd_bus_message_get_bus(m
));
714 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
715 const char *path
, *unit
, *result
;
716 BusWaitForJobs
*d
= userdata
;
724 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
726 bus_log_parse_error(r
);
730 found
= set_remove(d
->jobs
, (char*) path
);
736 if (!isempty(result
))
737 d
->result
= strdup(result
);
740 d
->name
= strdup(unit
);
745 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
749 set_free_free(d
->jobs
);
751 sd_bus_slot_unref(d
->slot_disconnected
);
752 sd_bus_slot_unref(d
->slot_job_removed
);
754 sd_bus_unref(d
->bus
);
762 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
763 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
769 d
= new0(BusWaitForJobs
, 1);
773 d
->bus
= sd_bus_ref(bus
);
775 /* When we are a bus client we match by sender. Direct
776 * connections OTOH have no initialized sender field, and
777 * hence we ignore the sender then */
778 r
= sd_bus_add_match(
780 &d
->slot_job_removed
,
783 "sender='org.freedesktop.systemd1',"
784 "interface='org.freedesktop.systemd1.Manager',"
785 "member='JobRemoved',"
786 "path='/org/freedesktop/systemd1'" :
788 "interface='org.freedesktop.systemd1.Manager',"
789 "member='JobRemoved',"
790 "path='/org/freedesktop/systemd1'",
791 match_job_removed
, d
);
795 r
= sd_bus_add_match(
797 &d
->slot_disconnected
,
799 "sender='org.freedesktop.DBus.Local',"
800 "interface='org.freedesktop.DBus.Local',"
801 "member='Disconnected'",
802 match_disconnected
, d
);
812 static int bus_process_wait(sd_bus
*bus
) {
816 r
= sd_bus_process(bus
, NULL
);
822 r
= sd_bus_wait(bus
, (uint64_t) -1);
828 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
829 _cleanup_free_
char *dbus_path
= NULL
;
835 dbus_path
= unit_dbus_path_from_name(d
->name
);
839 return sd_bus_get_property_string(d
->bus
,
840 "org.freedesktop.systemd1",
842 "org.freedesktop.systemd1.Service",
848 static const struct {
849 const char *result
, *explanation
;
850 } explanations
[] = {
851 { "resources", "of unavailable resources or another system error" },
852 { "protocol", "the service did not take the steps required by its unit configuration" },
853 { "timeout", "a timeout was exceeded" },
854 { "exit-code", "the control process exited with error code" },
855 { "signal", "a fatal signal was delivered to the control process" },
856 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
857 { "watchdog", "the service failed to send watchdog ping" },
858 { "start-limit", "start of the service was attempted too often" }
861 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
862 _cleanup_free_
char *service_shell_quoted
= NULL
;
863 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
867 service_shell_quoted
= shell_maybe_quote(service
);
869 if (extra_args
&& extra_args
[1]) {
870 _cleanup_free_
char *t
;
872 t
= strv_join((char**) extra_args
, " ");
873 systemctl
= strjoina("systemctl ", t
? : "<args>");
874 journalctl
= strjoina("journalctl ", t
? : "<args>");
877 if (!isempty(result
)) {
880 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
881 if (streq(result
, explanations
[i
].result
))
884 if (i
< ELEMENTSOF(explanations
)) {
885 log_error("Job for %s failed because %s.\n"
886 "See \"%s status %s\" and \"%s -xe\" for details.\n",
888 explanations
[i
].explanation
,
890 service_shell_quoted
?: "<service>",
896 log_error("Job for %s failed.\n"
897 "See \"%s status %s\" and \"%s -xe\" for details.\n",
900 service_shell_quoted
?: "<service>",
904 /* For some results maybe additional explanation is required */
905 if (streq_ptr(result
, "start-limit"))
906 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
907 "followed by \"%1$s start %2$s\" again.",
909 service_shell_quoted
?: "<service>");
912 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
918 if (streq(d
->result
, "canceled"))
919 log_error("Job for %s canceled.", strna(d
->name
));
920 else if (streq(d
->result
, "timeout"))
921 log_error("Job for %s timed out.", strna(d
->name
));
922 else if (streq(d
->result
, "dependency"))
923 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
924 else if (streq(d
->result
, "invalid"))
925 log_error("%s is not active, cannot reload.", strna(d
->name
));
926 else if (streq(d
->result
, "assert"))
927 log_error("Assertion failed on job for %s.", strna(d
->name
));
928 else if (streq(d
->result
, "unsupported"))
929 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
930 else if (streq(d
->result
, "collected"))
931 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
932 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped")) {
935 _cleanup_free_
char *result
= NULL
;
937 q
= bus_job_get_service_result(d
, &result
);
939 log_debug_errno(q
, "Failed to get Result property of service %s: %m", d
->name
);
941 log_job_error_with_service_result(d
->name
, result
, extra_args
);
943 log_error("Job failed. See \"journalctl -xe\" for details.");
947 if (STR_IN_SET(d
->result
, "canceled", "collected"))
949 else if (streq(d
->result
, "timeout"))
951 else if (streq(d
->result
, "dependency"))
953 else if (streq(d
->result
, "invalid"))
955 else if (streq(d
->result
, "assert"))
957 else if (streq(d
->result
, "unsupported"))
959 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped"))
965 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
970 while (!set_isempty(d
->jobs
)) {
973 q
= bus_process_wait(d
->bus
);
975 return log_error_errno(q
, "Failed to wait for response: %m");
978 q
= check_wait_response(d
, quiet
, extra_args
);
979 /* Return the first error as it is most likely to be
984 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
987 d
->name
= mfree(d
->name
);
988 d
->result
= mfree(d
->result
);
994 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
999 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
1003 return set_put_strdup(d
->jobs
, path
);
1006 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1009 r
= bus_wait_for_jobs_add(d
, path
);
1013 return bus_wait_for_jobs(d
, quiet
, NULL
);
1016 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1017 const char *type
, *path
, *source
;
1020 /* changes is dereferenced when calling unit_file_dump_changes() later,
1021 * so we have to make sure this is not NULL. */
1025 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1027 return bus_log_parse_error(r
);
1029 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1030 /* We expect only "success" changes to be sent over the bus.
1031 Hence, reject anything negative. */
1032 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1035 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1039 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1044 return bus_log_parse_error(r
);
1046 r
= sd_bus_message_exit_container(m
);
1048 return bus_log_parse_error(r
);
1050 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, false);
1056 bool is_const
; /* If false, cgroup_path should be free()'d */
1058 Hashmap
*pids
; /* PID → process name */
1061 struct CGroupInfo
*parent
;
1062 LIST_FIELDS(struct CGroupInfo
, siblings
);
1063 LIST_HEAD(struct CGroupInfo
, children
);
1067 static bool IS_ROOT(const char *p
) {
1068 return isempty(p
) || streq(p
, "/");
1071 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1072 struct CGroupInfo
*parent
= NULL
, *cg
;
1081 cg
= hashmap_get(cgroups
, path
);
1087 if (!IS_ROOT(path
)) {
1090 e
= strrchr(path
, '/');
1094 pp
= strndupa(path
, e
- path
);
1098 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1103 cg
= new0(struct CGroupInfo
, 1);
1108 cg
->cgroup_path
= (char*) path
;
1110 cg
->cgroup_path
= strdup(path
);
1111 if (!cg
->cgroup_path
) {
1117 cg
->is_const
= is_const
;
1118 cg
->parent
= parent
;
1120 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1123 free(cg
->cgroup_path
);
1129 LIST_PREPEND(siblings
, parent
->children
, cg
);
1130 parent
->n_children
++;
1137 static int add_process(
1143 struct CGroupInfo
*cg
;
1150 r
= add_cgroup(cgroups
, path
, true, &cg
);
1154 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1158 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1161 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1165 while (cg
->children
)
1166 remove_cgroup(cgroups
, cg
->children
);
1168 hashmap_remove(cgroups
, cg
->cgroup_path
);
1171 free(cg
->cgroup_path
);
1173 hashmap_free(cg
->pids
);
1176 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1181 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1182 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1187 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1190 static int dump_processes(
1192 const char *cgroup_path
,
1195 OutputFlags flags
) {
1197 struct CGroupInfo
*cg
;
1202 if (IS_ROOT(cgroup_path
))
1205 cg
= hashmap_get(cgroups
, cgroup_path
);
1209 if (!hashmap_isempty(cg
->pids
)) {
1217 /* Order processes by their PID */
1218 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1220 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1221 pids
[n
++] = PTR_TO_PID(pidp
);
1223 assert(n
== hashmap_size(cg
->pids
));
1224 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1226 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1228 for (i
= 0; i
< n
; i
++) {
1229 _cleanup_free_
char *e
= NULL
;
1230 const char *special
;
1233 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1236 if (n_columns
!= 0) {
1239 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1241 e
= ellipsize(name
, k
, 100);
1246 more
= i
+1 < n
|| cg
->children
;
1247 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1249 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1258 struct CGroupInfo
**children
, *child
;
1261 /* Order subcgroups by their name */
1262 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1263 LIST_FOREACH(siblings
, child
, cg
->children
)
1264 children
[n
++] = child
;
1265 assert(n
== cg
->n_children
);
1266 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1269 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1271 for (i
= 0; i
< n
; i
++) {
1272 _cleanup_free_
char *pp
= NULL
;
1273 const char *name
, *special
;
1276 child
= children
[i
];
1278 name
= strrchr(child
->cgroup_path
, '/');
1284 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1286 fputs(prefix
, stdout
);
1287 fputs(special
, stdout
);
1288 fputs(name
, stdout
);
1289 fputc('\n', stdout
);
1291 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1293 pp
= strappend(prefix
, special
);
1297 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1307 static int dump_extra_processes(
1311 OutputFlags flags
) {
1313 _cleanup_free_ pid_t
*pids
= NULL
;
1314 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1315 struct CGroupInfo
*cg
;
1316 size_t n_allocated
= 0, n
= 0, k
;
1320 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1321 * combined, sorted, linear list. */
1323 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1331 if (hashmap_isempty(cg
->pids
))
1334 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1338 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1341 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1342 pids
[n
++] = PTR_TO_PID(pidp
);
1344 r
= hashmap_put(names
, pidp
, (void*) name
);
1353 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1354 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1356 for (k
= 0; k
< n
; k
++) {
1357 _cleanup_free_
char *e
= NULL
;
1360 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1363 if (n_columns
!= 0) {
1366 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1368 e
= ellipsize(name
, z
, 100);
1373 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1375 special_glyph(TRIANGULAR_BULLET
),
1383 int unit_show_processes(
1386 const char *cgroup_path
,
1390 sd_bus_error
*error
) {
1392 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1393 Hashmap
*cgroups
= NULL
;
1394 struct CGroupInfo
*cg
;
1400 if (flags
& OUTPUT_FULL_WIDTH
)
1402 else if (n_columns
<= 0)
1403 n_columns
= columns();
1405 prefix
= strempty(prefix
);
1407 r
= sd_bus_call_method(
1409 "org.freedesktop.systemd1",
1410 "/org/freedesktop/systemd1",
1411 "org.freedesktop.systemd1.Manager",
1420 cgroups
= hashmap_new(&string_hash_ops
);
1424 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1429 const char *path
= NULL
, *name
= NULL
;
1432 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1438 r
= add_process(cgroups
, path
, pid
, name
);
1443 r
= sd_bus_message_exit_container(reply
);
1447 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1451 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
1454 while ((cg
= hashmap_first(cgroups
)))
1455 remove_cgroup(cgroups
, cg
);
1457 hashmap_free(cgroups
);