Skip to content

Commit

Permalink
src/install: support for A/B/C update
Browse files Browse the repository at this point in the history
So far, RAUC theoretically supported having three slot groups (e.g.
rootfs.0, rootfs.1, rootfs.2), but the slot selection algorithm
prevented from using them for an A/B/C update.

The algorithm simply selected the 'first' inactive slot group.
The 'first' group is implementation-defined by the data type used (i.e.
by the GHashTable).

As a result, updating always alternated between two slot groups.

This commit now introduces a slightly more sophisticated slot selection
algorithm which always selects the slot group with the oldest
installation date.
Selecting the oldest installation date automatically ensures that all
slots get their turn since once a slot is updated, it has the most
recent timestamp and will be selected last.
A slot that has no installation date yet is considered outdated.

Signed-off-by: Enrico Joerns <[email protected]>
  • Loading branch information
ejoerns committed Jun 24, 2024
1 parent e363453 commit 721e863
Showing 1 changed file with 42 additions and 5 deletions.
47 changes: 42 additions & 5 deletions src/install.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,10 @@ static gchar** get_all_manifest_slot_classes(const RaucManifest *manifest)
}
/* Selects a single appropriate inactive slot of root slot class
*
* Note: This function may be extended to be more sophisticated or follow a
* certain policy for selecting an appropriate slot!
* If a global status file is used and multiple candidates are available, the
* one with the oldest 'installed' timestamp will be selected.
* If a slot with an unset timestamp is found, this one will be preferably
* instead.
*
* @param rootclass name of root slot class
*
Expand All @@ -320,21 +322,56 @@ static gchar** get_all_manifest_slot_classes(const RaucManifest *manifest)
static RaucSlot *select_inactive_slot_class_member(const gchar *rootclass)
{
RaucSlot *iterslot;
RaucSlot *selectslot = NULL;
GHashTableIter iter;

g_return_val_if_fail(rootclass, NULL);

g_hash_table_iter_init(&iter, r_context()->config->slots);
while (g_hash_table_iter_next(&iter, NULL, (gpointer*) &iterslot)) {
gint comp;

if (iterslot->state != ST_INACTIVE)
continue;

if (g_strcmp0(iterslot->sclass, rootclass) == 0) {
return iterslot;
if (g_strcmp0(iterslot->sclass, rootclass) != 0)
continue;

/* For per-slot status, we don't try to access the timestamp information
* and just take the first appropriate slot */
if (g_strcmp0(r_context()->config->statusfile_path, "per-slot") == 0) {
selectslot = iterslot;
break;
}

g_assert_nonnull(iterslot->status);

/* A slot class without a timestamp is always considered out-of-date and updatable */
if (!iterslot->status->installed_timestamp) {
g_message("Slot %s has no valid timestamp. Considering outdated and use as update candidate", iterslot->name);
selectslot = iterslot;
break;
}

/* First slot found */
if (!selectslot) {
g_debug("Selecting slot %s as first update candidate", iterslot->name);
selectslot = iterslot;
continue;
}

/* if currently checked slot is older than candidate use as new candidate */
comp = g_date_time_compare(iterslot->status->installed_timestamp, selectslot->status->installed_timestamp);
if (comp < 0) {
gchar *found = g_date_time_format(iterslot->status->installed_timestamp, RAUC_FORMAT_ISO_8601);
gchar *current = g_date_time_format(selectslot->status->installed_timestamp, RAUC_FORMAT_ISO_8601);
g_debug("Slot '%s' has older timestamp (%s) than slot '%s' (%s). Selecting as update candidate", iterslot->name, found, selectslot->name, current);

selectslot = iterslot;
}
}

return NULL;
return selectslot;
}

/* Map each slot class available to a potential target slot.
Expand Down

0 comments on commit 721e863

Please sign in to comment.