mirror of
https://github.com/systemd/systemd.git
synced 2024-11-01 09:21:26 +03:00
systemctl: port "systemctl start --wait" logic to new bus-wait-for-units.c API
This commit is contained in:
parent
d81bc51fe3
commit
a7056cde56
@ -2793,158 +2793,6 @@ static const char *verb_to_job_type(const char *verb) {
|
||||
return "start";
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
sd_bus_slot *match;
|
||||
sd_event *event;
|
||||
Set *unit_paths;
|
||||
bool any_failed;
|
||||
} WaitContext;
|
||||
|
||||
static void wait_context_free(WaitContext *c) {
|
||||
c->match = sd_bus_slot_unref(c->match);
|
||||
c->event = sd_event_unref(c->event);
|
||||
c->unit_paths = set_free_free(c->unit_paths);
|
||||
}
|
||||
|
||||
static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
|
||||
const char *path, *interface, *active_state = NULL, *job_path = NULL;
|
||||
WaitContext *c = userdata;
|
||||
bool is_failed;
|
||||
int r;
|
||||
|
||||
/* Called whenever we get a PropertiesChanged signal. Checks if ActiveState changed to inactive/failed.
|
||||
*
|
||||
* Signal parameters: (s interface, a{sv} changed_properties, as invalidated_properties) */
|
||||
|
||||
path = sd_bus_message_get_path(m);
|
||||
if (!set_contains(c->unit_paths, path))
|
||||
return 0;
|
||||
|
||||
r = sd_bus_message_read(m, "s", &interface);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
if (!streq(interface, "org.freedesktop.systemd1.Unit")) /* ActiveState is on the Unit interface */
|
||||
return 0;
|
||||
|
||||
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
for (;;) {
|
||||
const char *s;
|
||||
|
||||
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv");
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
if (r == 0) /* end of array */
|
||||
break;
|
||||
|
||||
r = sd_bus_message_read(m, "s", &s); /* Property name */
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
if (streq(s, "ActiveState")) {
|
||||
r = sd_bus_message_read(m, "v", "s", &active_state);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
if (job_path) /* Found everything we need */
|
||||
break;
|
||||
|
||||
} else if (streq(s, "Job")) {
|
||||
uint32_t job_id;
|
||||
|
||||
r = sd_bus_message_read(m, "v", "(uo)", &job_id, &job_path);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
/* There's still a job pending for this unit, let's ignore this for now, and return right-away. */
|
||||
if (job_id != 0)
|
||||
return 0;
|
||||
|
||||
if (active_state) /* Found everything we need */
|
||||
break;
|
||||
|
||||
} else {
|
||||
r = sd_bus_message_skip(m, "v"); /* Other property */
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
}
|
||||
|
||||
r = sd_bus_message_exit_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
}
|
||||
|
||||
/* If this didn't contain the ActiveState property we can't do anything */
|
||||
if (!active_state)
|
||||
return 0;
|
||||
|
||||
is_failed = streq(active_state, "failed");
|
||||
if (streq(active_state, "inactive") || is_failed) {
|
||||
log_debug("%s became %s, dropping from --wait tracking", path, active_state);
|
||||
free(set_remove(c->unit_paths, path));
|
||||
c->any_failed = c->any_failed || is_failed;
|
||||
} else
|
||||
log_debug("ActiveState on %s changed to %s", path, active_state);
|
||||
|
||||
if (set_isempty(c->unit_paths))
|
||||
sd_event_exit(c->event, EXIT_SUCCESS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wait_context_watch(
|
||||
WaitContext *wait_context,
|
||||
sd_bus *bus,
|
||||
const char *name) {
|
||||
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_free_ char *unit_path = NULL;
|
||||
int r;
|
||||
|
||||
assert(wait_context);
|
||||
assert(name);
|
||||
|
||||
log_debug("Watching for property changes of %s", name);
|
||||
r = sd_bus_call_method(
|
||||
bus,
|
||||
"org.freedesktop.systemd1",
|
||||
"/org/freedesktop/systemd1",
|
||||
"org.freedesktop.systemd1.Manager",
|
||||
"RefUnit",
|
||||
&error,
|
||||
NULL,
|
||||
"s", name);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add reference to unit %s: %s", name, bus_error_message(&error, r));
|
||||
|
||||
unit_path = unit_dbus_path_from_name(name);
|
||||
if (!unit_path)
|
||||
return log_oom();
|
||||
|
||||
r = set_ensure_allocated(&wait_context->unit_paths, &string_hash_ops);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = set_put_strdup(wait_context->unit_paths, unit_path);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add unit path %s to set: %m", unit_path);
|
||||
|
||||
r = sd_bus_match_signal_async(bus,
|
||||
&wait_context->match,
|
||||
NULL,
|
||||
unit_path,
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"PropertiesChanged",
|
||||
on_properties_changed, NULL, wait_context);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to request match for PropertiesChanged signal: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int start_unit_one(
|
||||
sd_bus *bus,
|
||||
const char *method, /* When using classic per-job bus methods */
|
||||
@ -2953,7 +2801,7 @@ static int start_unit_one(
|
||||
const char *mode,
|
||||
sd_bus_error *error,
|
||||
BusWaitForJobs *w,
|
||||
WaitContext *wait_context) {
|
||||
BusWaitForUnits *wu) {
|
||||
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
const char *path;
|
||||
@ -2965,12 +2813,6 @@ static int start_unit_one(
|
||||
assert(mode);
|
||||
assert(error);
|
||||
|
||||
if (wait_context) {
|
||||
r = wait_context_watch(wait_context, bus, name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
log_debug("%s dbus call org.freedesktop.systemd1.Manager %s(%s, %s)",
|
||||
arg_dry_run ? "Would execute" : "Executing",
|
||||
method, name, mode);
|
||||
@ -3059,6 +2901,12 @@ static int start_unit_one(
|
||||
return log_error_errno(r, "Failed to watch job for %s: %m", name);
|
||||
}
|
||||
|
||||
if (wu) {
|
||||
r = bus_wait_for_units_add_unit(wu, name, BUS_WAIT_FOR_INACTIVE|BUS_WAIT_NO_JOB, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to watch unit %s: %m", name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
@ -3190,8 +3038,8 @@ static const char** make_extra_args(const char *extra_args[static 4]) {
|
||||
}
|
||||
|
||||
static int start_unit(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(bus_wait_for_units_freep) BusWaitForUnits *wu = NULL;
|
||||
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
|
||||
_cleanup_(wait_context_free) WaitContext wait_context = {};
|
||||
const char *method, *job_type, *mode, *one_name, *suffix = NULL;
|
||||
_cleanup_free_ char **stopped_units = NULL; /* Do not use _cleanup_strv_free_ */
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
@ -3279,19 +3127,15 @@ static int start_unit(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to enable subscription: %m");
|
||||
|
||||
r = sd_event_default(&wait_context.event);
|
||||
r = bus_wait_for_units_new(bus, &wu);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate event loop: %m");
|
||||
|
||||
r = sd_bus_attach_event(bus, wait_context.event, 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to attach bus to event loop: %m");
|
||||
return log_error_errno(r, "Failed to allocate unit watch context: %m");
|
||||
}
|
||||
|
||||
STRV_FOREACH(name, names) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
r = start_unit_one(bus, method, job_type, *name, mode, &error, w, arg_wait ? &wait_context : NULL);
|
||||
r = start_unit_one(bus, method, job_type, *name, mode, &error, w, wu);
|
||||
if (ret == EXIT_SUCCESS && r < 0)
|
||||
ret = translate_bus_error_to_exit_status(r, &error);
|
||||
|
||||
@ -3316,11 +3160,11 @@ static int start_unit(int argc, char *argv[], void *userdata) {
|
||||
(void) check_triggering_units(bus, *name);
|
||||
}
|
||||
|
||||
if (ret == EXIT_SUCCESS && arg_wait && !set_isempty(wait_context.unit_paths)) {
|
||||
r = sd_event_loop(wait_context.event);
|
||||
if (arg_wait) {
|
||||
r = bus_wait_for_units_run(wu);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to run event loop: %m");
|
||||
if (wait_context.any_failed)
|
||||
return log_error_errno(r, "Failed to wait for units: %m");
|
||||
if (r == BUS_WAIT_FAILURE && ret == EXIT_SUCCESS)
|
||||
ret = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user