diff options
author | Lennart Poettering <lennart@poettering.net> | 2025-01-14 11:48:52 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2025-01-15 17:03:21 +0100 |
commit | db320d97ca3821fe32b9d0ab2771cf886a866b06 (patch) | |
tree | f33a6680de2286598d32ecf3bc7cb977c4e30824 | |
parent | logind: introduce "user-light" session class (diff) | |
download | systemd-db320d97ca3821fe32b9d0ab2771cf886a866b06.tar.xz systemd-db320d97ca3821fe32b9d0ab2771cf886a866b06.zip |
run: fire sd_notify("READY=1") when in service mode and the unit is properly started
Let's make sure systemd-run itself works nicely as a service that tells
the caller when it is ready.
Note that we don't fire the same message in scope mode, since in that
case want to leave sd_notify() handling to the invoked process.
-rw-r--r-- | src/run/run.c | 95 |
1 files changed, 79 insertions, 16 deletions
diff --git a/src/run/run.c b/src/run/run.c index 3f7e0a6360..1f2507c10e 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -1901,6 +1901,46 @@ static int print_unit_invocation(const char *unit, sd_id128_t invocation_id) { return sd_json_variant_dump(v, arg_json_format_flags, stdout, NULL); } +typedef struct JobDoneContext { + char *unit; + char *start_job; + sd_bus_slot *match; +} JobDoneContext; + +static void job_done_context_done(JobDoneContext *c) { + assert(c); + + c->unit = mfree(c->unit); + c->start_job = mfree(c->start_job); + c->match = sd_bus_slot_unref(c->match); +} + +static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { + JobDoneContext *c = ASSERT_PTR(userdata); + const char *path; + int r; + + assert(m); + + r = sd_bus_message_read(m, "uoss", /* id = */ NULL, &path, /* unit= */ NULL, /* result= */ NULL); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + if (!streq_ptr(path, c->start_job)) + return 0; + + /* Notify our caller that the service is now running, just in case. */ + (void) sd_notifyf(/* unset_environment= */ false, + "READY=1\n" + "RUN_UNIT=%s", + c->unit); + + job_done_context_done(c); + return 0; +} + static int start_transient_service(sd_bus *bus) { _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; @@ -1974,16 +2014,6 @@ static int start_transient_service(sd_bus *bus) { assert_not_reached(); } - /* Optionally, wait for the start job to complete. If we are supposed to read the service's stdin - * lets skip this however, because we should start that already when the start job is running, and - * there's little point in waiting for the start job to complete in that case anyway, as we'll wait - * for EOF anyway, which is going to be much later. */ - if (!arg_no_block && arg_stdio == ARG_STDIO_NONE) { - r = bus_wait_for_jobs_new(bus, &w); - if (r < 0) - return log_error_errno(r, "Could not watch jobs: %m"); - } - if (arg_unit) { r = unit_name_mangle_with_suffix(arg_unit, "as unit", arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, @@ -1996,6 +2026,36 @@ static int start_transient_service(sd_bus *bus) { return r; } + /* Optionally, wait for the start job to complete. If we are supposed to read the service's stdin + * lets skip this however, because we should start that already when the start job is running, and + * there's little point in waiting for the start job to complete in that case anyway, as we'll wait + * for EOF anyway, which is going to be much later. */ + _cleanup_(job_done_context_done) JobDoneContext job_done_context = {}; + if (!arg_no_block) { + if (arg_stdio == ARG_STDIO_NONE) { + r = bus_wait_for_jobs_new(bus, &w); + if (r < 0) + return log_error_errno(r, "Could not watch jobs: %m"); + } else { + job_done_context.unit = strdup(service); + if (!job_done_context.unit) + return log_oom(); + + /* When we are a bus client we match by sender. Direct connections OTOH have no + * initialized sender field, and hence we ignore the sender then */ + r = sd_bus_match_signal_async( + bus, + &job_done_context.match, + sd_bus_is_bus_client(bus) ? "org.freedesktop.systemd1" : NULL, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "JobRemoved", + match_job_removed, NULL, &job_done_context); + if (r < 0) + return log_error_errno(r, "Failed to install JobRemove match: %m"); + } + } + r = make_transient_service_unit(bus, &m, service, pty_path, peer_fd); if (r < 0) return r; @@ -2005,19 +2065,22 @@ static int start_transient_service(sd_bus *bus) { if (r < 0) return r; - if (w) { - const char *object; - - r = sd_bus_message_read(reply, "o", &object); - if (r < 0) - return bus_log_parse_error(r); + const char *object; + r = sd_bus_message_read(reply, "o", &object); + if (r < 0) + return bus_log_parse_error(r); + if (w) { r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR, arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL); if (r < 0) return r; + } else if (job_done_context.match) { + job_done_context.start_job = strdup(object); + if (!job_done_context.start_job) + return log_oom(); } if (!arg_quiet) { |