]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bus-wait-for-jobs.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "alloc-util.h"
4 #include "bus-wait-for-jobs.h"
7 #include "bus-internal.h"
12 typedef struct BusWaitForJobs
{
15 /* The set of jobs to wait for, as bus object paths */
18 /* The unit name and job result of the last Job message */
22 sd_bus_slot
*slot_job_removed
;
23 sd_bus_slot
*slot_disconnected
;
26 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
29 log_error("Warning! D-Bus connection terminated.");
30 sd_bus_close(sd_bus_message_get_bus(m
));
35 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
36 const char *path
, *unit
, *result
;
37 BusWaitForJobs
*d
= ASSERT_PTR(userdata
);
44 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
46 bus_log_parse_error(r
);
50 found
= set_remove(d
->jobs
, (char*) path
);
56 (void) free_and_strdup(&d
->result
, empty_to_null(result
));
58 (void) free_and_strdup(&d
->name
, empty_to_null(unit
));
63 BusWaitForJobs
* bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
69 sd_bus_slot_unref(d
->slot_disconnected
);
70 sd_bus_slot_unref(d
->slot_job_removed
);
80 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
81 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
87 d
= new(BusWaitForJobs
, 1);
91 *d
= (BusWaitForJobs
) {
92 .bus
= sd_bus_ref(bus
),
95 /* When we are a bus client we match by sender. Direct
96 * connections OTOH have no initialized sender field, and
97 * hence we ignore the sender then */
98 r
= sd_bus_match_signal_async(
100 &d
->slot_job_removed
,
101 bus
->bus_client
? "org.freedesktop.systemd1" : NULL
,
102 "/org/freedesktop/systemd1",
103 "org.freedesktop.systemd1.Manager",
105 match_job_removed
, NULL
, d
);
109 r
= sd_bus_match_signal_async(
111 &d
->slot_disconnected
,
112 "org.freedesktop.DBus.Local",
114 "org.freedesktop.DBus.Local",
116 match_disconnected
, NULL
, d
);
125 static int bus_process_wait(sd_bus
*bus
) {
129 r
= sd_bus_process(bus
, NULL
);
135 r
= sd_bus_wait(bus
, UINT64_MAX
);
141 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
142 _cleanup_free_
char *dbus_path
= NULL
;
148 if (!endswith(d
->name
, ".service"))
151 dbus_path
= unit_dbus_path_from_name(d
->name
);
155 return sd_bus_get_property_string(d
->bus
,
156 "org.freedesktop.systemd1",
158 "org.freedesktop.systemd1.Service",
164 static void log_job_error_with_service_result(const char* service
, const char *result
, const char* const* extra_args
) {
165 _cleanup_free_
char *service_shell_quoted
= NULL
;
166 const char *systemctl
= "systemctl", *journalctl
= "journalctl";
168 static const struct {
169 const char *result
, *explanation
;
171 { "resources", "of unavailable resources or another system error" },
172 { "protocol", "the service did not take the steps required by its unit configuration" },
173 { "timeout", "a timeout was exceeded" },
174 { "exit-code", "the control process exited with error code" },
175 { "signal", "a fatal signal was delivered to the control process" },
176 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
177 { "watchdog", "the service failed to send watchdog ping" },
178 { "start-limit", "start of the service was attempted too often" }
183 service_shell_quoted
= shell_maybe_quote(service
, 0);
185 if (!strv_isempty((char**) extra_args
)) {
186 _cleanup_free_
char *t
= NULL
;
188 t
= strv_join((char**) extra_args
, " ");
189 systemctl
= strjoina("systemctl ", t
? : "<args>");
190 journalctl
= strjoina("journalctl ", t
? : "<args>");
193 if (!isempty(result
)) {
196 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
197 if (streq(result
, explanations
[i
].result
))
200 if (i
< ELEMENTSOF(explanations
)) {
201 log_error("Job for %s failed because %s.\n"
202 "See \"%s status %s\" and \"%s -xeu %s\" for details.\n",
204 explanations
[i
].explanation
,
206 service_shell_quoted
?: "<service>",
208 service_shell_quoted
?: "<service>");
213 log_error("Job for %s failed.\n"
214 "See \"%s status %s\" and \"%s -xeu %s\" for details.\n",
217 service_shell_quoted
?: "<service>",
219 service_shell_quoted
?: "<service>");
222 /* For some results maybe additional explanation is required */
223 if (streq_ptr(result
, "start-limit"))
224 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
225 "followed by \"%1$s start %2$s\" again.",
227 service_shell_quoted
?: "<service>");
230 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
236 if (streq(d
->result
, "canceled"))
237 log_error("Job for %s canceled.", strna(d
->name
));
238 else if (streq(d
->result
, "timeout"))
239 log_error("Job for %s timed out.", strna(d
->name
));
240 else if (streq(d
->result
, "dependency"))
241 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
242 else if (streq(d
->result
, "invalid"))
243 log_error("%s is not active, cannot reload.", strna(d
->name
));
244 else if (streq(d
->result
, "assert"))
245 log_error("Assertion failed on job for %s.", strna(d
->name
));
246 else if (streq(d
->result
, "unsupported"))
247 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
248 else if (streq(d
->result
, "collected"))
249 log_error("Queued job for %s was garbage collected.", strna(d
->name
));
250 else if (streq(d
->result
, "once"))
251 log_error("Unit %s was started already once and can't be started again.", strna(d
->name
));
252 else if (!STR_IN_SET(d
->result
, "done", "skipped")) {
254 if (d
->name
&& endswith(d
->name
, ".service")) {
255 _cleanup_free_
char *result
= NULL
;
258 q
= bus_job_get_service_result(d
, &result
);
260 log_debug_errno(q
, "Failed to get Result property of unit %s: %m", d
->name
);
262 log_job_error_with_service_result(d
->name
, result
, extra_args
);
264 log_error("Job failed. See \"journalctl -xe\" for details.");
268 if (STR_IN_SET(d
->result
, "canceled", "collected"))
270 else if (streq(d
->result
, "timeout"))
272 else if (streq(d
->result
, "dependency"))
274 else if (streq(d
->result
, "invalid"))
276 else if (streq(d
->result
, "assert"))
278 else if (streq(d
->result
, "unsupported"))
280 else if (streq(d
->result
, "once"))
282 else if (STR_IN_SET(d
->result
, "done", "skipped"))
285 return log_debug_errno(SYNTHETIC_ERRNO(EIO
),
286 "Unexpected job result, assuming server side newer than us: %s", d
->result
);
289 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
, const char* const* extra_args
) {
294 while (!set_isempty(d
->jobs
)) {
297 q
= bus_process_wait(d
->bus
);
299 return log_error_errno(q
, "Failed to wait for response: %m");
301 if (d
->name
&& d
->result
) {
302 q
= check_wait_response(d
, quiet
, extra_args
);
303 /* Return the first error as it is most likely to be
308 log_full_errno_zerook(LOG_DEBUG
, q
,
309 "Got result %s/%m for job %s", d
->result
, d
->name
);
312 d
->name
= mfree(d
->name
);
313 d
->result
= mfree(d
->result
);
319 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
322 return set_put_strdup(&d
->jobs
, path
);
325 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
, const char* const* extra_args
) {
328 r
= bus_wait_for_jobs_add(d
, path
);
332 return bus_wait_for_jobs(d
, quiet
, extra_args
);