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 (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 offset
+= word
[offset
] == '+';
505 if (!path_is_absolute(word
+ offset
)) {
506 log_error("Failed to parse %s value %s", field
, eq
);
510 path_kill_slashes(word
+ offset
);
512 r
= sd_bus_message_append_basic(m
, 's', word
);
514 return bus_log_create_error(r
);
517 r
= sd_bus_message_close_container(m
);
519 return bus_log_create_error(r
);
521 r
= sd_bus_message_close_container(m
);
523 } else if (streq(field
, "RuntimeDirectory")) {
526 r
= sd_bus_message_open_container(m
, 'v', "as");
528 return bus_log_create_error(r
);
530 r
= sd_bus_message_open_container(m
, 'a', "s");
532 return bus_log_create_error(r
);
535 _cleanup_free_
char *word
= NULL
;
537 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
539 return log_error_errno(r
, "Failed to parse %s value %s", field
, eq
);
544 r
= sd_bus_message_append_basic(m
, 's', word
);
546 return bus_log_create_error(r
);
549 r
= sd_bus_message_close_container(m
);
551 return bus_log_create_error(r
);
553 r
= sd_bus_message_close_container(m
);
555 } else if (streq(field
, "RestrictNamespaces")) {
557 unsigned long flags
= 0;
564 r
= parse_boolean(eq
);
568 flags
= NAMESPACE_FLAGS_ALL
;
570 r
= namespace_flag_from_string_many(eq
, &flags
);
572 return log_error_errno(r
, "Failed to parse %s value %s.", field
, eq
);
576 flags
= (~flags
) & NAMESPACE_FLAGS_ALL
;
578 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) flags
);
579 } else if ((dep
= unit_dependency_from_string(field
)) >= 0)
580 r
= sd_bus_message_append(m
, "v", "as", 1, eq
);
581 else if (streq(field
, "MountFlags")) {
584 r
= mount_propagation_flags_from_string(eq
, &f
);
586 return log_error_errno(r
, "Failed to parse mount propagation flags: %s", eq
);
588 r
= sd_bus_message_append(m
, "v", "t", (uint64_t) f
);
589 } else if (STR_IN_SET(field
, "BindPaths", "BindReadOnlyPaths")) {
592 r
= sd_bus_message_open_container(m
, 'v', "a(ssbt)");
596 r
= sd_bus_message_open_container(m
, 'a', "(ssbt)");
601 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
602 char *s
= NULL
, *d
= NULL
;
603 bool ignore_enoent
= false;
604 uint64_t flags
= MS_REC
;
606 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
608 return log_error_errno(r
, "Failed to parse argument: %m");
614 ignore_enoent
= true;
618 if (p
&& p
[-1] == ':') {
619 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
621 return log_error_errno(r
, "Failed to parse argument: %m");
623 log_error("Missing argument after ':': %s", eq
);
629 if (p
&& p
[-1] == ':') {
630 _cleanup_free_
char *options
= NULL
;
632 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
634 return log_error_errno(r
, "Failed to parse argument: %m");
636 if (isempty(options
) || streq(options
, "rbind"))
638 else if (streq(options
, "norbind"))
641 log_error("Unknown options: %s", eq
);
649 r
= sd_bus_message_append(m
, "(ssbt)", s
, d
, ignore_enoent
, flags
);
654 r
= sd_bus_message_close_container(m
);
658 r
= sd_bus_message_close_container(m
);
660 log_error("Unknown assignment %s.", assignment
);
666 return bus_log_create_error(r
);
668 r
= sd_bus_message_close_container(m
);
670 return bus_log_create_error(r
);
675 int bus_append_unit_property_assignment_many(sd_bus_message
*m
, char **l
) {
682 r
= bus_append_unit_property_assignment(m
, *i
);
690 typedef struct BusWaitForJobs
{
697 sd_bus_slot
*slot_job_removed
;
698 sd_bus_slot
*slot_disconnected
;
701 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
704 log_error("Warning! D-Bus connection terminated.");
705 sd_bus_close(sd_bus_message_get_bus(m
));
710 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
711 const char *path
, *unit
, *result
;
712 BusWaitForJobs
*d
= userdata
;
720 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
722 bus_log_parse_error(r
);
726 found
= set_remove(d
->jobs
, (char*) path
);
732 if (!isempty(result
))
733 d
->result
= strdup(result
);
736 d
->name
= strdup(unit
);
741 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
745 set_free_free(d
->jobs
);
747 sd_bus_slot_unref(d
->slot_disconnected
);
748 sd_bus_slot_unref(d
->slot_job_removed
);
750 sd_bus_unref(d
->bus
);
758 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
759 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
765 d
= new0(BusWaitForJobs
, 1);
769 d
->bus
= sd_bus_ref(bus
);
771 /* When we are a bus client we match by sender. Direct
772 * connections OTOH have no initialized sender field, and
773 * hence we ignore the sender then */
774 r
= sd_bus_add_match(
776 &d
->slot_job_removed
,
779 "sender='org.freedesktop.systemd1',"
780 "interface='org.freedesktop.systemd1.Manager',"
781 "member='JobRemoved',"
782 "path='/org/freedesktop/systemd1'" :
784 "interface='org.freedesktop.systemd1.Manager',"
785 "member='JobRemoved',"
786 "path='/org/freedesktop/systemd1'",
787 match_job_removed
, d
);
791 r
= sd_bus_add_match(
793 &d
->slot_disconnected
,
795 "sender='org.freedesktop.DBus.Local',"
796 "interface='org.freedesktop.DBus.Local',"
797 "member='Disconnected'",
798 match_disconnected
, d
);
808 static int bus_process_wait(sd_bus
*bus
) {
812 r
= sd_bus_process(bus
, NULL
);
818 r
= sd_bus_wait(bus
, (uint64_t) -1);
824 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
825 _cleanup_free_
char *dbus_path
= NULL
;
831 dbus_path
= unit_dbus_path_from_name(d
->name
);
835 return sd_bus_get_property_string(d
->bus
,
836 "org.freedesktop.systemd1",
838 "org.freedesktop.systemd1.Service",
844 static const struct {
845 const char *result
, *explanation
;
846 } explanations
[] = {
847 { "resources", "of unavailable resources or another system error" },
848 { "protocol", "the service did not take the steps required by its unit configuration" },
849 { "timeout", "a timeout was exceeded" },
850 { "exit-code", "the control process exited with error code" },
851 { "signal", "a fatal signal was delivered to the control process" },
852 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
853 { "watchdog", "the service failed to send watchdog ping" },
854 { "start-limit", "start of the service was attempted too often" }
857 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
858 _cleanup_free_
char *service_shell_quoted
= NULL
;
859 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
863 service_shell_quoted
= shell_maybe_quote(service
);
865 if (extra_args
&& extra_args
[1]) {
866 _cleanup_free_
char *t
;
868 t
= strv_join((char**) extra_args
, " ");
869 systemctl
= strjoina("systemctl ", t
? : "<args>");
870 journalctl
= strjoina("journalctl ", t
? : "<args>");
873 if (!isempty(result
)) {
876 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
877 if (streq(result
, explanations
[i
].result
))
880 if (i
< ELEMENTSOF(explanations
)) {
881 log_error("Job for %s failed because %s.\n"
882 "See \"%s status %s\" and \"%s -xe\" for details.\n",
884 explanations
[i
].explanation
,
886 service_shell_quoted
?: "<service>",
892 log_error("Job for %s failed.\n"
893 "See \"%s status %s\" and \"%s -xe\" for details.\n",
896 service_shell_quoted
?: "<service>",
900 /* For some results maybe additional explanation is required */
901 if (streq_ptr(result
, "start-limit"))
902 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
903 "followed by \"%1$s start %2$s\" again.",
905 service_shell_quoted
?: "<service>");
908 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
914 if (streq(d
->result
, "canceled"))
915 log_error("Job for %s canceled.", strna(d
->name
));
916 else if (streq(d
->result
, "timeout"))
917 log_error("Job for %s timed out.", strna(d
->name
));
918 else if (streq(d
->result
, "dependency"))
919 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
920 else if (streq(d
->result
, "invalid"))
921 log_error("%s is not active, cannot reload.", strna(d
->name
));
922 else if (streq(d
->result
, "assert"))
923 log_error("Assertion failed on job for %s.", strna(d
->name
));
924 else if (streq(d
->result
, "unsupported"))
925 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
926 else if (streq(d
->result
, "collected"))
927 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
928 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped")) {
931 _cleanup_free_
char *result
= NULL
;
933 q
= bus_job_get_service_result(d
, &result
);
935 log_debug_errno(q
, "Failed to get Result property of service %s: %m", d
->name
);
937 log_job_error_with_service_result(d
->name
, result
, extra_args
);
939 log_error("Job failed. See \"journalctl -xe\" for details.");
943 if (STR_IN_SET(d
->result
, "canceled", "collected"))
945 else if (streq(d
->result
, "timeout"))
947 else if (streq(d
->result
, "dependency"))
949 else if (streq(d
->result
, "invalid"))
951 else if (streq(d
->result
, "assert"))
953 else if (streq(d
->result
, "unsupported"))
955 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped"))
961 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
966 while (!set_isempty(d
->jobs
)) {
969 q
= bus_process_wait(d
->bus
);
971 return log_error_errno(q
, "Failed to wait for response: %m");
974 q
= check_wait_response(d
, quiet
, extra_args
);
975 /* Return the first error as it is most likely to be
980 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
983 d
->name
= mfree(d
->name
);
984 d
->result
= mfree(d
->result
);
990 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
995 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
999 return set_put_strdup(d
->jobs
, path
);
1002 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
1005 r
= bus_wait_for_jobs_add(d
, path
);
1009 return bus_wait_for_jobs(d
, quiet
, NULL
);
1012 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
1013 const char *type
, *path
, *source
;
1016 /* changes is dereferenced when calling unit_file_dump_changes() later,
1017 * so we have to make sure this is not NULL. */
1021 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
1023 return bus_log_parse_error(r
);
1025 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
1026 /* We expect only "success" changes to be sent over the bus.
1027 Hence, reject anything negative. */
1028 UnitFileChangeType ch
= unit_file_change_type_from_string(type
);
1031 log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type
, path
);
1035 r
= unit_file_changes_add(changes
, n_changes
, ch
, path
, source
);
1040 return bus_log_parse_error(r
);
1042 r
= sd_bus_message_exit_container(m
);
1044 return bus_log_parse_error(r
);
1046 unit_file_dump_changes(0, NULL
, *changes
, *n_changes
, false);
1052 bool is_const
; /* If false, cgroup_path should be free()'d */
1054 Hashmap
*pids
; /* PID → process name */
1057 struct CGroupInfo
*parent
;
1058 LIST_FIELDS(struct CGroupInfo
, siblings
);
1059 LIST_HEAD(struct CGroupInfo
, children
);
1063 static bool IS_ROOT(const char *p
) {
1064 return isempty(p
) || streq(p
, "/");
1067 static int add_cgroup(Hashmap
*cgroups
, const char *path
, bool is_const
, struct CGroupInfo
**ret
) {
1068 struct CGroupInfo
*parent
= NULL
, *cg
;
1077 cg
= hashmap_get(cgroups
, path
);
1083 if (!IS_ROOT(path
)) {
1086 e
= strrchr(path
, '/');
1090 pp
= strndupa(path
, e
- path
);
1094 r
= add_cgroup(cgroups
, pp
, false, &parent
);
1099 cg
= new0(struct CGroupInfo
, 1);
1104 cg
->cgroup_path
= (char*) path
;
1106 cg
->cgroup_path
= strdup(path
);
1107 if (!cg
->cgroup_path
) {
1113 cg
->is_const
= is_const
;
1114 cg
->parent
= parent
;
1116 r
= hashmap_put(cgroups
, cg
->cgroup_path
, cg
);
1119 free(cg
->cgroup_path
);
1125 LIST_PREPEND(siblings
, parent
->children
, cg
);
1126 parent
->n_children
++;
1133 static int add_process(
1139 struct CGroupInfo
*cg
;
1146 r
= add_cgroup(cgroups
, path
, true, &cg
);
1150 r
= hashmap_ensure_allocated(&cg
->pids
, &trivial_hash_ops
);
1154 return hashmap_put(cg
->pids
, PID_TO_PTR(pid
), (void*) name
);
1157 static void remove_cgroup(Hashmap
*cgroups
, struct CGroupInfo
*cg
) {
1161 while (cg
->children
)
1162 remove_cgroup(cgroups
, cg
->children
);
1164 hashmap_remove(cgroups
, cg
->cgroup_path
);
1167 free(cg
->cgroup_path
);
1169 hashmap_free(cg
->pids
);
1172 LIST_REMOVE(siblings
, cg
->parent
->children
, cg
);
1177 static int cgroup_info_compare_func(const void *a
, const void *b
) {
1178 const struct CGroupInfo
*x
= *(const struct CGroupInfo
* const*) a
, *y
= *(const struct CGroupInfo
* const*) b
;
1183 return strcmp(x
->cgroup_path
, y
->cgroup_path
);
1186 static int dump_processes(
1188 const char *cgroup_path
,
1191 OutputFlags flags
) {
1193 struct CGroupInfo
*cg
;
1198 if (IS_ROOT(cgroup_path
))
1201 cg
= hashmap_get(cgroups
, cgroup_path
);
1205 if (!hashmap_isempty(cg
->pids
)) {
1213 /* Order processes by their PID */
1214 pids
= newa(pid_t
, hashmap_size(cg
->pids
));
1216 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
)
1217 pids
[n
++] = PTR_TO_PID(pidp
);
1219 assert(n
== hashmap_size(cg
->pids
));
1220 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1222 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1224 for (i
= 0; i
< n
; i
++) {
1225 _cleanup_free_
char *e
= NULL
;
1226 const char *special
;
1229 name
= hashmap_get(cg
->pids
, PID_TO_PTR(pids
[i
]));
1232 if (n_columns
!= 0) {
1235 k
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1237 e
= ellipsize(name
, k
, 100);
1242 more
= i
+1 < n
|| cg
->children
;
1243 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1245 fprintf(stdout
, "%s%s%*"PID_PRI
" %s\n",
1254 struct CGroupInfo
**children
, *child
;
1257 /* Order subcgroups by their name */
1258 children
= newa(struct CGroupInfo
*, cg
->n_children
);
1259 LIST_FOREACH(siblings
, child
, cg
->children
)
1260 children
[n
++] = child
;
1261 assert(n
== cg
->n_children
);
1262 qsort_safe(children
, n
, sizeof(struct CGroupInfo
*), cgroup_info_compare_func
);
1265 n_columns
= MAX(LESS_BY(n_columns
, 2U), 20U);
1267 for (i
= 0; i
< n
; i
++) {
1268 _cleanup_free_
char *pp
= NULL
;
1269 const char *name
, *special
;
1272 child
= children
[i
];
1274 name
= strrchr(child
->cgroup_path
, '/');
1280 special
= special_glyph(more
? TREE_BRANCH
: TREE_RIGHT
);
1282 fputs(prefix
, stdout
);
1283 fputs(special
, stdout
);
1284 fputs(name
, stdout
);
1285 fputc('\n', stdout
);
1287 special
= special_glyph(more
? TREE_VERTICAL
: TREE_SPACE
);
1289 pp
= strappend(prefix
, special
);
1293 r
= dump_processes(cgroups
, child
->cgroup_path
, pp
, n_columns
, flags
);
1303 static int dump_extra_processes(
1307 OutputFlags flags
) {
1309 _cleanup_free_ pid_t
*pids
= NULL
;
1310 _cleanup_hashmap_free_ Hashmap
*names
= NULL
;
1311 struct CGroupInfo
*cg
;
1312 size_t n_allocated
= 0, n
= 0, k
;
1316 /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as
1317 * combined, sorted, linear list. */
1319 HASHMAP_FOREACH(cg
, cgroups
, i
) {
1327 if (hashmap_isempty(cg
->pids
))
1330 r
= hashmap_ensure_allocated(&names
, &trivial_hash_ops
);
1334 if (!GREEDY_REALLOC(pids
, n_allocated
, n
+ hashmap_size(cg
->pids
)))
1337 HASHMAP_FOREACH_KEY(name
, pidp
, cg
->pids
, j
) {
1338 pids
[n
++] = PTR_TO_PID(pidp
);
1340 r
= hashmap_put(names
, pidp
, (void*) name
);
1349 qsort_safe(pids
, n
, sizeof(pid_t
), pid_compare_func
);
1350 width
= DECIMAL_STR_WIDTH(pids
[n
-1]);
1352 for (k
= 0; k
< n
; k
++) {
1353 _cleanup_free_
char *e
= NULL
;
1356 name
= hashmap_get(names
, PID_TO_PTR(pids
[k
]));
1359 if (n_columns
!= 0) {
1362 z
= MAX(LESS_BY(n_columns
, 2U + width
+ 1U), 20U);
1364 e
= ellipsize(name
, z
, 100);
1369 fprintf(stdout
, "%s%s %*" PID_PRI
" %s\n",
1371 special_glyph(TRIANGULAR_BULLET
),
1379 int unit_show_processes(
1382 const char *cgroup_path
,
1386 sd_bus_error
*error
) {
1388 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1389 Hashmap
*cgroups
= NULL
;
1390 struct CGroupInfo
*cg
;
1396 if (flags
& OUTPUT_FULL_WIDTH
)
1398 else if (n_columns
<= 0)
1399 n_columns
= columns();
1401 prefix
= strempty(prefix
);
1403 r
= sd_bus_call_method(
1405 "org.freedesktop.systemd1",
1406 "/org/freedesktop/systemd1",
1407 "org.freedesktop.systemd1.Manager",
1416 cgroups
= hashmap_new(&string_hash_ops
);
1420 r
= sd_bus_message_enter_container(reply
, 'a', "(sus)");
1425 const char *path
= NULL
, *name
= NULL
;
1428 r
= sd_bus_message_read(reply
, "(sus)", &path
, &pid
, &name
);
1434 r
= add_process(cgroups
, path
, pid
, name
);
1439 r
= sd_bus_message_exit_container(reply
);
1443 r
= dump_processes(cgroups
, cgroup_path
, prefix
, n_columns
, flags
);
1447 r
= dump_extra_processes(cgroups
, prefix
, n_columns
, flags
);
1450 while ((cg
= hashmap_first(cgroups
)))
1451 remove_cgroup(cgroups
, cg
);
1453 hashmap_free(cgroups
);