From: Luca Boccassi Date: Tue, 19 May 2026 18:01:32 +0000 (+0100) Subject: portablectl: use new EnqueueUnitJobMany() when available X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e2e0d610baa2c58975dca2693496c422b04ed08a;p=thirdparty%2Fsystemd.git portablectl: use new EnqueueUnitJobMany() when available --- diff --git a/src/portable/portablectl.c b/src/portable/portablectl.c index 9286a6fbb36..831b4543722 100644 --- a/src/portable/portablectl.c +++ b/src/portable/portablectl.c @@ -678,22 +678,18 @@ static int maybe_enable_disable(sd_bus *bus, const char *path, bool enable) { return 0; } -static int maybe_start_stop_restart(sd_bus *bus, const char *path, const char *method, BusWaitForJobs *wait) { +static int maybe_start_stop_restart(sd_bus *bus, const char *name, const char *method, BusWaitForJobs *wait) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_free_ char *name = NULL; const char *job = NULL; int r; + assert(name); assert(STR_IN_SET(method, "StartUnit", "StopUnit", "RestartUnit")); if (!arg_now) return 0; - r = path_extract_filename(path, &name); - if (r < 0) - return log_error_errno(r, "Failed to extract file name from '%s': %m", path); - r = bus_call_method( bus, bus_systemd_mgr, @@ -704,7 +700,7 @@ static int maybe_start_stop_restart(sd_bus *bus, const char *path, const char *m if (r < 0) return log_error_errno(r, "Failed to call %s on the portable service %s: %s", method, - path, + name, bus_error_message(&error, r)); r = sd_bus_message_read(reply, "o", &job); @@ -724,8 +720,92 @@ static int maybe_start_stop_restart(sd_bus *bus, const char *path, const char *m return 0; } +static int maybe_start_stop_restart_units( + sd_bus *bus, + char * const *names, + const char *job_type, + BusWaitForJobs *wait) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + const char *method; + int r; + + assert(bus); + assert(STR_IN_SET(job_type, "start", "stop", "restart")); + + if (!arg_now || strv_isempty(names)) + return 0; + + method = streq(job_type, "start") ? "StartUnit" : + streq(job_type, "stop") ? "StopUnit" : + "RestartUnit"; + + /* Prefer the new EnqueueUnitJobMany() method which submits all units in a single transaction. + * Falls back to per-unit calls on older managers (UnknownMethod) or when the new method rejects + * something about the request (InvalidArgs). */ + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "EnqueueUnitJobMany"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, (char**) names); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "sst", job_type, "replace", UINT64_C(0)); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r >= 0) { + r = sd_bus_message_enter_container(reply, 'a', "(uosos)"); + if (r < 0) + return bus_log_parse_error(r); + + for (;;) { + const char *path, *unit_id; + uint32_t id; + + r = sd_bus_message_read(reply, "(uosos)", &id, &path, &unit_id, NULL, NULL); + if (r < 0) + return bus_log_parse_error(r); + if (r == 0) + break; + + if (!arg_quiet) + log_info("Queued %s to call %s on portable service %s.", path, method, unit_id); + + if (wait) { + r = bus_wait_for_jobs_add(wait, path); + if (r < 0) + return log_error_errno(r, "Failed to watch %s job to call %s on %s: %m", + path, method, unit_id); + } + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + return 0; + } + + if (!sd_bus_error_has_names(&error, SD_BUS_ERROR_UNKNOWN_METHOD, SD_BUS_ERROR_INVALID_ARGS)) + return log_error_errno(r, "Failed to enqueue jobs for portable services: %s", + bus_error_message(&error, r)); + + log_debug_errno(r, "EnqueueUnitJobMany() not supported (%s), falling back to per-unit calls.", + bus_error_message(&error, r)); + + STRV_FOREACH(name, names) + (void) maybe_start_stop_restart(bus, *name, method, wait); + + return 0; +} + static int maybe_enable_start(sd_bus *bus, sd_bus_message *reply) { _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *wait = NULL; + _cleanup_strv_free_ char **start_names = NULL; int r; if (!arg_enable && !arg_now) @@ -755,7 +835,15 @@ static int maybe_enable_start(sd_bus *bus, sd_bus_message *reply) { if (STR_IN_SET(type, "symlink", "copy") && is_portable_managed(path)) { (void) maybe_enable_disable(bus, path, true); - (void) maybe_start_stop_restart(bus, path, "StartUnit", wait); + + _cleanup_free_ char *name = NULL; + r = path_extract_filename(path, &name); + if (r < 0) + return log_error_errno(r, "Failed to extract file name from '%s': %m", path); + + r = strv_consume(&start_names, TAKE_PTR(name)); + if (r < 0) + return log_oom(); } } @@ -763,6 +851,8 @@ static int maybe_enable_start(sd_bus *bus, sd_bus_message *reply) { if (r < 0) return r; + (void) maybe_start_stop_restart_units(bus, start_names, "start", wait); + if (!arg_no_block) { r = bus_wait_for_jobs(wait, arg_quiet, NULL); if (r < 0) @@ -774,6 +864,7 @@ static int maybe_enable_start(sd_bus *bus, sd_bus_message *reply) { static int maybe_stop_enable_restart(sd_bus *bus, sd_bus_message *reply) { _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *wait = NULL; + _cleanup_strv_free_ char **stop_names = NULL, **restart_names = NULL; int r; if (!arg_enable && !arg_now) @@ -804,14 +895,25 @@ static int maybe_stop_enable_restart(sd_bus *bus, sd_bus_message *reply) { if (r == 0) break; - if (streq(type, "unlink") && is_portable_managed(path)) - (void) maybe_start_stop_restart(bus, path, "StopUnit", wait); + if (streq(type, "unlink") && is_portable_managed(path) && arg_now) { + _cleanup_free_ char *name = NULL; + + r = path_extract_filename(path, &name); + if (r < 0) + return log_error_errno(r, "Failed to extract file name from '%s': %m", path); + + r = strv_consume(&stop_names, TAKE_PTR(name)); + if (r < 0) + return log_oom(); + } } r = sd_bus_message_exit_container(reply); if (r < 0) return r; + (void) maybe_start_stop_restart_units(bus, stop_names, "stop", wait); + /* Then we get a list of units that were either added or changed, so that we can * enable them and/or restart them if the user asked us to. */ r = sd_bus_message_enter_container(reply, 'a', "(sss)"); @@ -829,7 +931,15 @@ static int maybe_stop_enable_restart(sd_bus *bus, sd_bus_message *reply) { if (STR_IN_SET(type, "symlink", "copy") && is_portable_managed(path)) { (void) maybe_enable_disable(bus, path, true); - (void) maybe_start_stop_restart(bus, path, "RestartUnit", wait); + + _cleanup_free_ char *name = NULL; + r = path_extract_filename(path, &name); + if (r < 0) + return log_error_errno(r, "Failed to extract file name from '%s': %m", path); + + r = strv_consume(&restart_names, TAKE_PTR(name)); + if (r < 0) + return log_oom(); } } @@ -837,6 +947,8 @@ static int maybe_stop_enable_restart(sd_bus *bus, sd_bus_message *reply) { if (r < 0) return r; + (void) maybe_start_stop_restart_units(bus, restart_names, "restart", wait); + if (!arg_no_block) { r = bus_wait_for_jobs(wait, arg_quiet, NULL); if (r < 0) @@ -940,9 +1052,6 @@ static int maybe_stop_disable_clean(sd_bus *bus, char *image, char *argv[]) { if (r < 0) return bus_log_parse_error(r); - (void) maybe_start_stop_restart(bus, name, "StopUnit", wait); - (void) maybe_enable_disable(bus, name, false); - r = strv_extend(&units, name); if (r < 0) return log_oom(); @@ -952,6 +1061,12 @@ static int maybe_stop_disable_clean(sd_bus *bus, char *image, char *argv[]) { if (r < 0) return bus_log_parse_error(r); + (void) maybe_start_stop_restart_units(bus, units, "stop", wait); + + /* Disable after stopping to match the idiomatic stop-then-disable lifecycle order. */ + STRV_FOREACH(name, units) + (void) maybe_enable_disable(bus, *name, false); + /* Stopping must always block or the detach will fail if the unit is still running */ r = bus_wait_for_jobs(wait, arg_quiet, NULL); if (r < 0)