]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
updatectl: Use sysupdated’s Acquire() and Install() methods
authorPhilip Withnall <pwithnall@gnome.org>
Mon, 19 Jan 2026 17:19:39 +0000 (17:19 +0000)
committerPhilip Withnall <pwithnall@gnome.org>
Mon, 23 Feb 2026 16:35:05 +0000 (16:35 +0000)
Rather than calling `Update()`. This should allow us to eventually drop
`Update()`.

This should just be an internal change, and should not result in any
user-visible changes to the behaviour of `updatectl update`.

Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Helps: https://github.com/systemd/systemd/issues/34814

src/sysupdate/updatectl.c

index 5eae86e4ea47d72f35319d9838ddf9d8dbf9f93d..7638bbff795774b8554bbb1c359bbd9701cb85bc 100644 (file)
@@ -72,6 +72,9 @@ typedef struct Operation {
         sd_event_source *job_interrupt_source;
         sd_bus_slot *job_properties_slot;
         sd_bus_slot *job_finished_slot;
+
+        /* Only used for Acquire()/Install() operations: */
+        char *acquired_version;
 } Operation;
 
 static Operation* operation_free(Operation *p) {
@@ -86,6 +89,7 @@ static Operation* operation_free(Operation *p) {
                 assert_se(sd_event_exit(p->event, 0) >= 0);
 
         free(p->job_path);
+        free(p->acquired_version);
 
         sd_event_source_disable_unref(p->job_interrupt_source);
         sd_bus_slot_unref(p->job_properties_slot);
@@ -804,6 +808,7 @@ static int verb_check(int argc, char **argv, void *userdata) {
 }
 
 #define UPDATE_PROGRESS_FAILED INT_MIN
+#define UPDATE_PROGRESS_ACQUIRED (INT_MAX - 1)
 #define UPDATE_PROGRESS_DONE INT_MAX
 /* Make sure it doesn't overlap w/ errno values */
 assert_cc(UPDATE_PROGRESS_FAILED < -ERRNO_MAX);
@@ -853,6 +858,10 @@ static int update_render_progress(sd_event_source *source, void *userdata) {
                         clear_progress_bar_unbuffered(target);
                         fprintf(stderr, "%s: %s %s\n", target, RED_CROSS_MARK(), STRERROR(progress));
                         total += 100;
+                } else if (progress == UPDATE_PROGRESS_ACQUIRED) {
+                        clear_progress_bar_unbuffered(target);
+                        fprintf(stderr, "%s: %s Installing\n", target, glyph(GLYPH_DOWNLOAD));
+                        total += 100;
                 } else if (progress == UPDATE_PROGRESS_DONE) {
                         clear_progress_bar_unbuffered(target);
                         fprintf(stderr, "%s: %s Done\n", target, GREEN_CHECK_MARK());
@@ -920,7 +929,60 @@ static int update_properties_changed(sd_bus_message *m, void *userdata, sd_bus_e
         return 0;
 }
 
-static int update_finished(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+static int update_install_started(sd_bus_message *reply, void *userdata, sd_bus_error *ret_error) {
+        _cleanup_(operation_freep) Operation *op = ASSERT_PTR(userdata);
+        OrderedHashmap *map = ASSERT_PTR(op->userdata);
+        const sd_bus_error *e;
+        const char *job_path;
+        int r;
+
+        assert(reply);
+
+        e = sd_bus_message_get_error(reply);
+        if (e) {
+                r = -sd_bus_error_get_errno(e);
+
+                r = ordered_hashmap_replace(map, op->target_id, INT_TO_PTR(r));
+                if (r < 0)
+                        log_debug_errno(r, "Failed to update hashmap: %m");
+
+                return 0;
+        }
+
+        r = sd_bus_message_read(reply, "sto", NULL, &op->job_id, &job_path);
+        if (r < 0)
+                return bus_log_parse_error(r);
+        r = free_and_strdup_warn(&op->job_path, job_path);
+        if (r < 0)
+                return r;
+
+        /* Update this job in the hashmap. */
+        r = ordered_hashmap_replace(map, op->target_id, INT_TO_PTR(UPDATE_PROGRESS_ACQUIRED));
+        if (r < 0)
+                log_debug_errno(r, "Failed to update hashmap: %m");
+
+        /* Register for progress notifications for this Install() D-Bus call; previously
+         * op->job_properties_slot was registered for progress notifications for the Acquire() D-Bus call. */
+        sd_bus_slot_unref(TAKE_PTR(op->job_properties_slot));
+        r = sd_bus_match_signal_async(
+                        op->bus,
+                        &op->job_properties_slot,
+                        bus_sysupdate_mgr->destination,
+                        job_path,
+                        "org.freedesktop.DBus.Properties",
+                        "PropertiesChanged",
+                        update_properties_changed,
+                        NULL,
+                        op);
+        if (r < 0)
+                return log_bus_error(r, NULL, op->target_id, "listen for PropertiesChanged");
+
+        TAKE_PTR(op); /* update_install_finished/update_interrupted take ownership of the data */
+
+        return 0;
+}
+
+static int update_install_finished(sd_bus_message *m, void *userdata, sd_bus_error *error) {
         _cleanup_(operation_freep) Operation *op = ASSERT_PTR(userdata);
         OrderedHashmap *map = ASSERT_PTR(op->userdata);
         uint64_t id;
@@ -951,6 +1013,64 @@ static int update_finished(sd_bus_message *m, void *userdata, sd_bus_error *erro
         return 0;
 }
 
+static int update_acquire_finished(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+        _cleanup_(operation_freep) Operation *op = ASSERT_PTR(userdata);
+        OrderedHashmap *map = ASSERT_PTR(op->userdata);
+        uint64_t id;
+        int r, status;
+
+        assert(m);
+
+        r = sd_bus_message_read(m, "toi", &id, NULL, &status);
+        if (r < 0) {
+                bus_log_parse_error_debug(r);
+                return 0;
+        }
+
+        if (id != op->job_id) {
+                TAKE_PTR(op);
+                return 0;
+        }
+
+        if (status == 0) /* success */
+                status = UPDATE_PROGRESS_ACQUIRED;
+        else if (status > 0) /* exit status without errno */
+                status = UPDATE_PROGRESS_FAILED; /* i.e. EXIT_FAILURE */
+        /* else errno */
+
+        r = ordered_hashmap_replace(map, op->target_id, INT_TO_PTR(status));
+        if (r < 0)
+                log_debug_errno(r, "Failed to update hashmap: %m");
+
+        /* Renew the JobRemoved notification for the Install() call instead. */
+        sd_bus_slot_unref(op->job_finished_slot);
+        r = bus_match_signal_async(
+                        op->bus, &op->job_finished_slot, bus_sysupdate_mgr, "JobRemoved", update_install_finished, NULL, op);
+        if (r < 0)
+                return log_bus_error(r, NULL, op->target_id, "listen for JobRemoved");
+
+        /* With the Acquire() call finished, immediately call Install() to deploy the downloaded update.
+         * This reuses the same Operation struct so the progress reporting continues to be done in the same
+         * slot in the terminal. */
+        r = sd_bus_call_method_async(
+                        op->bus,
+                        NULL,
+                        bus_sysupdate_mgr->destination,
+                        op->target_path,
+                        SYSUPDATE_TARGET_INTERFACE,
+                        "Install",
+                        update_install_started,
+                        op,
+                        "st",
+                        op->acquired_version,
+                        0LU);
+        if (r < 0)
+                return log_bus_error(r, NULL, op->target_id, "call Install");
+        TAKE_PTR(op);
+
+        return 0;
+}
+
 static int update_interrupted(sd_event_source *source, void *userdata) {
         /* Since the event loop is exiting, we will never receive the JobRemoved
          * signal. So, we must free the userdata here. */
@@ -959,6 +1079,8 @@ static int update_interrupted(sd_event_source *source, void *userdata) {
         OrderedHashmap *map = ASSERT_PTR(op->userdata);
         int r;
 
+        /* This call should work regardless of whether we’re cancelling the Acquire() call or the Install()
+         * call. */
         r = sd_bus_call_method(op->bus,
                                bus_sysupdate_mgr->destination,
                                op->job_path,
@@ -977,7 +1099,7 @@ static int update_interrupted(sd_event_source *source, void *userdata) {
         return 0;
 }
 
-static int update_started(sd_bus_message *reply, void *userdata, sd_bus_error *ret_error) {
+static int update_acquire_started(sd_bus_message *reply, void *userdata, sd_bus_error *ret_error) {
         _cleanup_(operation_freep) Operation *op = ASSERT_PTR(userdata);
         OrderedHashmap *map = ASSERT_PTR(op->userdata);
         const sd_bus_error *e;
@@ -1008,6 +1130,12 @@ static int update_started(sd_bus_message *reply, void *userdata, sd_bus_error *r
         op->job_path = strdup(job_path);
         if (!op->job_path)
                 return log_oom();
+
+        /* Store the version for the subsequent Install() call */
+        op->acquired_version = strdup(new_version);
+        if (!op->acquired_version)
+                return log_oom();
+
         if (isempty(new_version))
                 new_version = "latest";
 
@@ -1047,7 +1175,7 @@ static int update_started(sd_bus_message *reply, void *userdata, sd_bus_error *r
         if (r < 0)
                 return log_bus_error(r, NULL, op->target_id, "listen for PropertiesChanged");
 
-        TAKE_PTR(op); /* update_finished/update_interrupted take ownership of the data */
+        TAKE_PTR(op); /* update_acquire_finished/update_interrupted take ownership of the data */
 
         return 0;
 }
@@ -1094,7 +1222,7 @@ static int do_update(sd_bus *bus, char **targets) {
 
                 /* Sign up for notification when the associated job finishes */
                 r = bus_match_signal_async(
-                                op->bus, &op->job_finished_slot, bus_sysupdate_mgr, "JobRemoved", update_finished, NULL, op);
+                                op->bus, &op->job_finished_slot, bus_sysupdate_mgr, "JobRemoved", update_acquire_finished, NULL, op);
                 if (r < 0)
                         return log_bus_error(r, NULL, op->target_id, "listen for JobRemoved");
 
@@ -1104,14 +1232,14 @@ static int do_update(sd_bus *bus, char **targets) {
                                 bus_sysupdate_mgr->destination,
                                 target_paths[i],
                                 SYSUPDATE_TARGET_INTERFACE,
-                                "Update",
-                                update_started,
+                                "Acquire",
+                                update_acquire_started,
                                 op,
                                 "st",
                                 versions[i],
                                 0LU);
                 if (r < 0)
-                        return log_bus_error(r, NULL, targets[i], "call Update");
+                        return log_bus_error(r, NULL, targets[i], "call Acquire");
                 TAKE_PTR(op);
 
                 remaining++;