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")) {
582 r
= mount_propagation_flags_from_string(eq
, &f
);
584 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
586 r
= sd_bus_message_append(m
, "v", "t", f
);
587 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
590 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
594 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
599 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
600 char *s
= NULL
, *d
= NULL
;
601 bool ignore_enoent
= false;
602 uint64_t flags
= MS_REC
;
604 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
606 return log_error_errno(r
, "Failed to parse argument: %m");
612 ignore_enoent
= true;
616 if (p
&& p
[-1] == ':') {
617 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
619 return log_error_errno(r
, "Failed to parse argument: %m");
621 log_error("Missing argument after ':': %s", eq
);
627 if (p
&& p
[-1] == ':') {
628 _cleanup_free_
char *options
= NULL
;
630 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
632 return log_error_errno(r
, "Failed to parse argument: %m");
634 if (isempty(options
) || streq(options
, "rbind"))
636 else if (streq(options
, "norbind"))
639 log_error("Unknown options: %s", eq
);
647 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
652 r
= sd_bus_message_close_container(m
);
656 r
= sd_bus_message_close_container(m
);
658 log_error("Unknown assignment %s.", assignment
);
664 return bus_log_create_error(r
);
666 r
= sd_bus_message_close_container(m
);
668 return bus_log_create_error(r
);
673 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
680 r
= bus_append_unit_property_assignment(m
, *i
);
688 typedef struct BusWaitForJobs
{
695 sd_bus_slot
*slot_job_removed
;
696 sd_bus_slot
*slot_disconnected
;
699 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
702 log_error("Warning! D-Bus connection terminated.");
703 sd_bus_close(sd_bus_message_get_bus(m
));
708 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
709 const char *path
, *unit
, *result
;
710 BusWaitForJobs
*d
= userdata
;
718 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
720 bus_log_parse_error(r
);
724 found
= set_remove(d
->jobs
, (char*) path
);
730 if (!isempty(result
))
731 d
->result
= strdup(result
);
734 d
->name
= strdup(unit
);
739 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
743 set_free_free(d
->jobs
);
745 sd_bus_slot_unref(d
->slot_disconnected
);
746 sd_bus_slot_unref(d
->slot_job_removed
);
748 sd_bus_unref(d
->bus
);
756 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
757 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
763 d
= new0(BusWaitForJobs
, 1);
767 d
->bus
= sd_bus_ref(bus
);
769 /* When we are a bus client we match by sender. Direct
770 * connections OTOH have no initialized sender field, and
771 * hence we ignore the sender then */
772 r
= sd_bus_add_match(
774 &d
->slot_job_removed
,
777 "sender='org.freedesktop.systemd1',"
778 "interface='org.freedesktop.systemd1.Manager',"
779 "member='JobRemoved',"
780 "path='/org/freedesktop/systemd1'" :
782 "interface='org.freedesktop.systemd1.Manager',"
783 "member='JobRemoved',"
784 "path='/org/freedesktop/systemd1'",
785 match_job_removed
, d
);
789 r
= sd_bus_add_match(
791 &d
->slot_disconnected
,
793 "sender='org.freedesktop.DBus.Local',"
794 "interface='org.freedesktop.DBus.Local',"
795 "member='Disconnected'",
796 match_disconnected
, d
);
806 static int bus_process_wait(sd_bus
*bus
) {
810 r
= sd_bus_process(bus
, NULL
);
816 r
= sd_bus_wait(bus
, (uint64_t) -1);
822 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
823 _cleanup_free_
char *dbus_path
= NULL
;
829 dbus_path
= unit_dbus_path_from_name(d
->name
);
833 return sd_bus_get_property_string(d
->bus
,
834 "org.freedesktop.systemd1",
836 "org.freedesktop.systemd1.Service",
842 static const struct {
843 const char *result
, *explanation
;
844 } explanations
[] = {
845 { "resources", "of unavailable resources or another system error" },
846 { "protocol", "the service did not take the steps required by its unit configuration" },
847 { "timeout", "a timeout was exceeded" },
848 { "exit-code", "the control process exited with error code" },
849 { "signal", "a fatal signal was delivered to the control process" },
850 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
851 { "watchdog", "the service failed to send watchdog ping" },
852 { "start-limit", "start of the service was attempted too often" }
855 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
856 _cleanup_free_
char *service_shell_quoted
= NULL
;
857 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
861 service_shell_quoted
= shell_maybe_quote(service
);
863 if (extra_args
&& extra_args
[1]) {
864 _cleanup_free_
char *t
;
866 t
= strv_join((char**) extra_args
, " ");
867 systemctl
= strjoina("systemctl ", t
? : "<args>");
868 journalctl
= strjoina("journalctl ", t
? : "<args>");
871 if (!isempty(result
)) {
874 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
875 if (streq(result
, explanations
[i
].result
))
878 if (i
< ELEMENTSOF(explanations
)) {
879 log_error("Job for %s failed because %s.\n"
880 "See \"%s status %s\" and \"%s -xe\" for details.\n",
882 explanations
[i
].explanation
,
884 service_shell_quoted
?: "<service>",
890 log_error("Job for %s failed.\n"
891 "See \"%s status %s\" and \"%s -xe\" for details.\n",
894 service_shell_quoted
?: "<service>",
898 /* For some results maybe additional explanation is required */
899 if (streq_ptr(result
, "start-limit"))
900 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
901 "followed by \"%1$s start %2$s\" again.",
903 service_shell_quoted
?: "<service>");
906 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
912 if (streq(d
->result
, "canceled"))
913 log_error("Job for %s canceled.", strna(d
->name
));
914 else if (streq(d
->result
, "timeout"))
915 log_error("Job for %s timed out.", strna(d
->name
));
916 else if (streq(d
->result
, "dependency"))
917 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
918 else if (streq(d
->result
, "invalid"))
919 log_error("%s is not active, cannot reload.", strna(d
->name
));
920 else if (streq(d
->result
, "assert"))
921 log_error("Assertion failed on job for %s.", strna(d
->name
));
922 else if (streq(d
->result
, "unsupported"))
923 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
924 else if (streq(d
->result
, "collected"))
925 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
926 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped")) {
929 _cleanup_free_
char *result
= NULL
;
931 q
= bus_job_get_service_result(d
, &result
);
933 log_debug_errno(q
, "Failed to get Result property of service %s: %m", d
->name
);
935 log_job_error_with_service_result(d
->name
, result
, extra_args
);
937 log_error("Job failed. See \"journalctl -xe\" for details.");
941 if (STR_IN_SET(d
->result
, "canceled", "collected"))
943 else if (streq(d
->result
, "timeout"))
945 else if (streq(d
->result
, "dependency"))
947 else if (streq(d
->result
, "invalid"))
949 else if (streq(d
->result
, "assert"))
951 else if (streq(d
->result
, "unsupported"))
953 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped"))
959 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
964 while (!set_isempty(d
->jobs
)) {
967 q
= bus_process_wait(d
->bus
);
969 return log_error_errno(q
, "Failed to wait for response: %m");
972 q
= check_wait_response(d
, quiet
, extra_args
);
973 /* Return the first error as it is most likely to be
978 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
981 d
->name
= mfree(d
->name
);
982 d
->result
= mfree(d
->result
);
988 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
993 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
997 return set_put_strdup(d
->jobs
, path
);
1000 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1003 r
= bus_wait_for_jobs_add(d
, path
);
1007 return bus_wait_for_jobs(d
, quiet
, NULL
);
1010 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1011 const char *type
, *path
, *source
;
1014 /* changes is dereferenced when calling unit_file_dump_changes() later,
1015 * so we have to make sure this is not NULL. */
1019 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1021 return bus_log_parse_error(r
);
1023 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1024 /* We expect only "success" changes to be sent over the bus.
1025 Hence, reject anything negative. */
1026 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1029 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1033 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1038 return bus_log_parse_error(r
);
1040 r
= sd_bus_message_exit_container(m
);
1042 return bus_log_parse_error(r
);
1044 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, false);
1050 bool is_const
; /* If false, cgroup_path should be free()'d */
1052 Hashmap
*pids
; /* PID → process name */
1055 struct CGroupInfo
*parent
;
1056 LIST_FIELDS(struct CGroupInfo
, siblings
);
1057 LIST_HEAD(struct CGroupInfo
, children
);
1061 static bool IS_ROOT(const char *p
) {
1062 return isempty(p
) || streq(p
, "/");
1065 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1066 struct CGroupInfo
*parent
= NULL
, *cg
;
1075 cg
= hashmap_get(cgroups
, path
);
1081 if (!IS_ROOT(path
)) {
1084 e
= strrchr(path
, '/');
1088 pp
= strndupa(path
, e
- path
);
1092 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1097 cg
= new0(struct CGroupInfo
, 1);
1102 cg
->cgroup_path
= (char*) path
;
1104 cg
->cgroup_path
= strdup(path
);
1105 if (!cg
->cgroup_path
) {
1111 cg
->is_const
= is_const
;
1112 cg
->parent
= parent
;
1114 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1117 free(cg
->cgroup_path
);
1123 LIST_PREPEND(siblings
, parent
->children
, cg
);
1124 parent
->n_children
++;
1131 static int add_process(
1137 struct CGroupInfo
*cg
;
1144 r
= add_cgroup(cgroups
, path
, true, &cg
);
1148 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1152 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1155 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1159 while (cg
->children
)
1160 remove_cgroup(cgroups
, cg
->children
);
1162 hashmap_remove(cgroups
, cg
->cgroup_path
);
1165 free(cg
->cgroup_path
);
1167 hashmap_free(cg
->pids
);
1170 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1175 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1176 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1181 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1184 static int dump_processes(
1186 const char *cgroup_path
,
1189 OutputFlags flags
) {
1191 struct CGroupInfo
*cg
;
1196 if (IS_ROOT(cgroup_path
))
1199 cg
= hashmap_get(cgroups
, cgroup_path
);
1203 if (!hashmap_isempty(cg
->pids
)) {
1211 /* Order processes by their PID */
1212 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1214 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1215 pids
[n
++] = PTR_TO_PID(pidp
);
1217 assert(n
== hashmap_size(cg
->pids
));
1218 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1220 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1222 for (i
= 0; i
< n
; i
++) {
1223 _cleanup_free_
char *e
= NULL
;
1224 const char *special
;
1227 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1230 if (n_columns
!= 0) {
1233 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1235 e
= ellipsize(name
, k
, 100);
1240 more
= i
+1 < n
|| cg
->children
;
1241 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1243 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1252 struct CGroupInfo
**children
, *child
;
1255 /* Order subcgroups by their name */
1256 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1257 LIST_FOREACH(siblings
, child
, cg
->children
)
1258 children
[n
++] = child
;
1259 assert(n
== cg
->n_children
);
1260 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1263 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1265 for (i
= 0; i
< n
; i
++) {
1266 _cleanup_free_
char *pp
= NULL
;
1267 const char *name
, *special
;
1270 child
= children
[i
];
1272 name
= strrchr(child
->cgroup_path
, '/');
1278 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1280 fputs(prefix
, stdout
);
1281 fputs(special
, stdout
);
1282 fputs(name
, stdout
);
1283 fputc('\n', stdout
);
1285 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1287 pp
= strappend(prefix
, special
);
1291 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1301 static int dump_extra_processes(
1305 OutputFlags flags
) {
1307 _cleanup_free_ pid_t
*pids
= NULL
;
1308 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1309 struct CGroupInfo
*cg
;
1310 size_t n_allocated
= 0, n
= 0, k
;
1314 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1315 * combined, sorted, linear list. */
1317 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1325 if (hashmap_isempty(cg
->pids
))
1328 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1332 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1335 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1336 pids
[n
++] = PTR_TO_PID(pidp
);
1338 r
= hashmap_put(names
, pidp
, (void*) name
);
1347 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1348 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1350 for (k
= 0; k
< n
; k
++) {
1351 _cleanup_free_
char *e
= NULL
;
1354 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1357 if (n_columns
!= 0) {
1360 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1362 e
= ellipsize(name
, z
, 100);
1367 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1369 special_glyph(TRIANGULAR_BULLET
),
1377 int unit_show_processes(
1380 const char *cgroup_path
,
1384 sd_bus_error
*error
) {
1386 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1387 Hashmap
*cgroups
= NULL
;
1388 struct CGroupInfo
*cg
;
1394 if (flags
& OUTPUT_FULL_WIDTH
)
1396 else if (n_columns
<= 0)
1397 n_columns
= columns();
1399 prefix
= strempty(prefix
);
1401 r
= sd_bus_call_method(
1403 "org.freedesktop.systemd1",
1404 "/org/freedesktop/systemd1",
1405 "org.freedesktop.systemd1.Manager",
1414 cgroups
= hashmap_new(&string_hash_ops
);
1418 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1423 const char *path
= NULL
, *name
= NULL
;
1426 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1432 r
= add_process(cgroups
, path
, pid
, name
);
1437 r
= sd_bus_message_exit_container(reply
);
1441 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1445 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
1448 while ((cg
= hashmap_first(cgroups
)))
1449 remove_cgroup(cgroups
, cg
);
1451 hashmap_free(cgroups
);