Skip to content
This repository was archived by the owner on Aug 7, 2025. It is now read-only.

Commit ac97f45

Browse files
author
William Douglas
committed
Add --single-step support for update
Allow users to update by stepping through each release between their current version and the latest. This option is primarily for cases where update is failing due to memory or disk space running out when updating normally. Signed-off-by: William Douglas <[email protected]>
1 parent 8cc2d86 commit ac97f45

File tree

8 files changed

+430
-31
lines changed

8 files changed

+430
-31
lines changed

docs/swupd.1.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,8 @@ update
201201
--download Do not perform an update, instead download all resources needed
202202
to perform the update, and exit.
203203

204+
--single-step Update by to latest by first going to each release prior
205+
204206
--update-search-file-index Update the index used by search-file to speed up
205207
searches. Don't enable this if you have download or space restrictions.
206208

src/cmds/update.c

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@
3939
#define FLAG_DOWNLOAD_ONLY 2000
4040
#define FLAG_UPDATE_SEARCH_FILE_INDEX 2001
4141
#define FLAG_UPDATE_3RD_PARTY 2002
42+
#define FLAG_SINGLE_STEP 2003
4243

4344
static int requested_version = -1;
4445
static bool download_only = false;
4546
static bool update_search_file_index = false;
4647
static bool keepcache = false;
48+
static bool cmdline_option_single_step = false;
4749
static char swupd_binary[LINE_MAX] = { 0 };
4850

4951
#ifdef THIRDPARTY
@@ -142,11 +144,11 @@ int add_included_manifests(struct manifest *mom, struct list **subs)
142144
return 0;
143145
}
144146

145-
static enum swupd_code check_versions(int *current_version, int *server_version, int req_version, char *path_prefix)
147+
static enum swupd_code check_versions(int *current_version, int *server_version, int req_version, char *path_prefix, bool single_step)
146148
{
147149
int ret;
148150

149-
ret = read_versions(current_version, server_version, path_prefix);
151+
ret = read_versions(current_version, server_version, path_prefix, single_step);
150152
if (ret != SWUPD_OK) {
151153
return ret;
152154
}
@@ -156,8 +158,12 @@ static enum swupd_code check_versions(int *current_version, int *server_version,
156158
}
157159
if (req_version != -1) {
158160
if (req_version < *current_version) {
159-
error("Requested version for update (%d) must be greater than current version (%d)\n",
160-
req_version, *current_version);
161+
// This error message isn't valid if the
162+
// system has gone through a format bump
163+
if (!on_new_format()) {
164+
error("Requested version for update (%d) must be greater than current version (%d)\n",
165+
req_version, *current_version);
166+
}
161167
return SWUPD_INVALID_OPTION;
162168
}
163169
if (req_version < *server_version) {
@@ -285,7 +291,7 @@ enum swupd_code execute_update_extra(extra_proc_fn_t post_update_fn, extra_proc_
285291

286292
/* get versions */
287293
timelist_timer_start(globals.global_times, "Get versions");
288-
ret = check_versions(&current_version, &server_version, requested_version, globals.path_prefix);
294+
ret = check_versions(&current_version, &server_version, requested_version, globals.path_prefix, cmdline_option_single_step);
289295
if (ret != SWUPD_OK) {
290296
goto clean_curl;
291297
}
@@ -448,7 +454,17 @@ enum swupd_code execute_update_extra(extra_proc_fn_t post_update_fn, extra_proc_
448454
progress_next_step("run_postupdate_scripts", PROGRESS_UNDEFINED);
449455

450456
/* Determine if another update is needed so the scripts block */
451-
int new_current_version = get_current_version(globals.path_prefix);
457+
int new_current_version, new_server_version;
458+
int versions_check = check_versions(&new_current_version, &new_server_version, requested_version, globals.path_prefix, false);
459+
if (versions_check != SWUPD_OK) {
460+
// update was fine, but we aren't seeing the new server version
461+
// for some reason so don't set re_update on single_step
462+
new_server_version = new_current_version;
463+
}
464+
465+
if (cmdline_option_single_step && new_server_version > new_current_version) {
466+
re_update = true;
467+
}
452468
if (on_new_format() && (requested_version == -1 || (requested_version > new_current_version))) {
453469
re_update = true;
454470
}
@@ -560,6 +576,7 @@ static const struct option prog_opts[] = {
560576
#ifdef THIRDPARTY
561577
{ "3rd-party", no_argument, 0, FLAG_UPDATE_3RD_PARTY },
562578
#endif
579+
{ "single-step", no_argument, 0, FLAG_SINGLE_STEP },
563580
};
564581

565582
static void print_help(void)
@@ -579,6 +596,7 @@ static void print_help(void)
579596
#ifdef THIRDPARTY
580597
print(" --3rd-party Also update content from 3rd-party repositories\n");
581598
#endif
599+
print(" --single-step Update to latest by first going to each release prior\n");
582600
print("\n");
583601
}
584602

@@ -617,6 +635,9 @@ static bool parse_opt(int opt, char *optarg)
617635
cmdline_option_3rd_party = optarg_to_bool(optarg);
618636
return true;
619637
#endif
638+
case FLAG_SINGLE_STEP:
639+
cmdline_option_single_step = optarg_to_bool(optarg);
640+
return true;
620641
default:
621642
return false;
622643
}
@@ -697,7 +718,7 @@ enum swupd_code update_main(int argc, char **argv)
697718
ret = check_update();
698719

699720
#ifdef THIRDPARTY
700-
if (cmdline_option_3rd_party) {
721+
if (cmdline_option_3rd_party && !cmdline_option_single_step) {
701722
progress_finish_steps(ret);
702723
info("\nChecking update status of content from 3rd-party repositories\n\n");
703724
ret = third_party_execute_check_update();
@@ -707,7 +728,7 @@ enum swupd_code update_main(int argc, char **argv)
707728
ret = execute_update();
708729

709730
#ifdef THIRDPARTY
710-
if (cmdline_option_3rd_party) {
731+
if (cmdline_option_3rd_party && !cmdline_option_single_step) {
711732
if (ret == SWUPD_OK) {
712733
progress_finish_steps(ret);
713734
info("\nUpdating content from 3rd-party repositories\n\n");

src/swupd.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ extern enum swupd_code walk_tree(struct manifest *, const char *, bool, const re
167167

168168
extern int get_latest_version(char *v_url);
169169
extern int get_int_from_url(const char *url);
170-
extern enum swupd_code read_versions(int *current_version, int *server_version, char *path_prefix);
170+
extern enum swupd_code read_versions(int *current_version, int *server_version, char *path_prefix, bool single_step);
171171
extern int get_current_version(char *path_prefix);
172172
extern bool get_distribution_string(char *path_prefix, char *dist);
173173
extern int get_current_format(void);

src/swupd_lib/version.c

Lines changed: 126 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
int get_int_from_url(const char *url)
3636
{
3737
int err, value;
38-
char tmp_string[MAX_VERSION_STR_SIZE+1];
38+
char tmp_string[MAX_VERSION_STR_SIZE + 1];
3939
struct curl_file_data tmp_data = {
4040
MAX_VERSION_STR_SIZE, 0,
4141
tmp_string
@@ -147,11 +147,11 @@ static int verify_signature(char *url, struct curl_file_data *tmp_version)
147147
return ret;
148148
}
149149

150-
static int get_version_from_url(char *url)
150+
static int get_version_from_url_inmemory(char *url)
151151
{
152152
int ret = 0;
153153
int err = 0;
154-
char version_str[MAX_VERSION_STR_SIZE+1];
154+
char version_str[MAX_VERSION_STR_SIZE + 1];
155155
int sig_verified = 0;
156156

157157
/* struct for version data */
@@ -188,49 +188,154 @@ static int get_version_from_url(char *url)
188188
}
189189
return -SWUPD_ERROR_SIGNATURE_VERIFICATION;
190190
}
191+
return ret;
192+
}
193+
194+
static int get_version_from_file(char *filename, int current_version)
195+
{
196+
FILE *infile;
197+
char *line = NULL;
198+
// start as -EINVAL to report cases of an empty file
199+
int next_version = -EINVAL;
200+
int tmp_version = 0;
201+
int err = 0;
202+
size_t len = 0;
203+
ssize_t read;
204+
205+
infile = fopen(filename, "r");
206+
if (infile == NULL) {
207+
return -EINVAL;
208+
}
209+
210+
while ((read = getline(&line, &len, infile)) != -1) {
211+
err = str_to_int(line, &tmp_version);
212+
if (err) {
213+
break;
214+
}
215+
if (current_version < 0) {
216+
next_version = tmp_version;
217+
break;
218+
} else if (tmp_version <= current_version) {
219+
// We did get a valid version so report it
220+
// but it isn't usable for update
221+
if (next_version < 0) {
222+
next_version = tmp_version;
223+
}
224+
break;
225+
}
226+
next_version = tmp_version;
227+
}
228+
229+
(void)fclose(infile);
230+
231+
if (line) {
232+
FREE(line);
233+
}
234+
235+
if (err) {
236+
return err;
237+
}
238+
return next_version;
239+
}
240+
241+
static int get_version_from_url(char *url, char *filename, int current_version)
242+
{
243+
int ret = 0;
244+
char *sig_filename;
245+
char *sig_url;
246+
char *download_file;
247+
char *download_sig;
248+
249+
string_or_die(&sig_filename, "%s.sig", filename);
250+
string_or_die(&sig_url, "%s.sig", url);
251+
download_file = statedir_get_download_file(filename);
252+
download_sig = statedir_get_download_file(sig_filename);
253+
ret = swupd_curl_get_file(url, download_file);
254+
if (ret) {
255+
goto out;
256+
}
257+
258+
/* Sig check */
259+
if (globals.sigcheck && globals.sigcheck_latest) {
260+
ret = swupd_curl_get_file(sig_url, download_sig);
261+
if (ret) {
262+
ret = -SWUPD_ERROR_SIGNATURE_VERIFICATION;
263+
error("Signature for latest file (%s) is missing\n", url);
264+
goto out;
265+
}
266+
if (!signature_verify(download_file, download_sig, SIGNATURE_IGNORE_EXPIRATION)) {
267+
ret = -SWUPD_ERROR_SIGNATURE_VERIFICATION;
268+
error("Signature verification failed for URL: %s\n", url);
269+
goto out;
270+
}
271+
} else {
272+
warn_nosigcheck(url);
273+
}
274+
275+
ret = get_version_from_file(download_file, current_version);
276+
277+
out:
278+
FREE(sig_filename);
279+
FREE(sig_url);
280+
FREE(download_file);
281+
FREE(download_sig);
191282

192283
return ret;
193284
}
194285

195286
/* this function attempts to download the latest server version string file from
196-
* the preferred server to a memory buffer, returning either a negative integer
197-
* error code or >= 0 representing the server version
287+
* the preferred server, returning either a negative integer error code or >= 0
288+
* representing the server version
198289
*
199290
* if v_url is non-NULL the version at v_url is fetched. If v_url is NULL the
200291
* global version_url is used and the cached version may be used instead of
201-
* attempting to download the version string again. If v_url is the empty string
202-
* the global version_url is used and the cached version is ignored. */
203-
int get_latest_version(char *v_url)
292+
* attempting to download the version string again (unless current_version is
293+
* negative, indicating single-step and the version needs to be recalculated).
294+
* If v_url is the empty string the global version_url is used and the cached
295+
* version is ignored.
296+
*
297+
* if current_version >= 0 it is used to indicate the next version rather than
298+
* the latest version should be returned. */
299+
static int get_version(char *v_url, int current_version)
204300
{
301+
char *filename = "latest";
205302
char *url = NULL;
206303
int ret = 0;
207304
static int cached_version = -1;
208305

209-
if (cached_version > 0 && v_url == NULL) {
306+
if (cached_version > 0 && v_url == NULL && current_version < 0) {
210307
return cached_version;
211308
}
212309

310+
// TODO: why allow "" when we do NULL test above?
311+
// maybe consolidate.
213312
if (v_url == NULL || str_cmp(v_url, "") == 0) {
214313
v_url = globals.version_url;
215314
}
216315

217-
string_or_die(&url, "%s/version/format%s/latest", v_url, globals.format_string);
218-
ret = get_version_from_url(url);
316+
string_or_die(&url, "%s/version/format%s/%s", v_url, globals.format_string, filename);
317+
ret = get_version_from_url(url, filename, current_version);
219318
FREE(url);
220319
cached_version = ret;
221320

222321
return ret;
223322
}
224323

324+
int get_latest_version(char *v_url)
325+
{
326+
return get_version(v_url, -1);
327+
}
328+
225329
/* gets the latest version of the update content regardless of what format we
226330
* are currently in */
227331
int version_get_absolute_latest(void)
228332
{
333+
char *filename = "latest_version";
229334
char *url = NULL;
230335
int ret;
231336

232-
string_or_die(&url, "%s/version/latest_version", globals.version_url);
233-
ret = get_version_from_url(url);
337+
string_or_die(&url, "%s/version/%s", globals.version_url, filename);
338+
ret = get_version_from_url_inmemory(url);
234339
FREE(url);
235340

236341
return ret;
@@ -329,15 +434,21 @@ bool get_distribution_string(char *path_prefix, char *dist)
329434
return true;
330435
}
331436

332-
enum swupd_code read_versions(int *current_version, int *server_version, char *path_prefix)
437+
enum swupd_code read_versions(int *current_version, int *server_version, char *path_prefix, bool single_step)
333438
{
334439
*current_version = get_current_version(path_prefix);
335-
*server_version = get_latest_version("");
336440

337441
if (*current_version < 0) {
338442
error("Unable to determine current OS version\n");
339443
return SWUPD_CURRENT_VERSION_UNKNOWN;
340444
}
445+
446+
if (single_step) {
447+
*server_version = get_version("", *current_version);
448+
} else {
449+
*server_version = get_latest_version("");
450+
}
451+
341452
if (*server_version == -SWUPD_ERROR_SIGNATURE_VERIFICATION) {
342453
error("Unable to determine the server version as signature verification failed\n");
343454
return SWUPD_SIGNATURE_VERIFICATION_FAILED;

swupd.zsh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ local -a update=(
199199
'(-)'{-s,--status}'[Show current OS version and latest version available on server. Equivalent to "swupd check-update"]'
200200
'(help -s --status -k --keepcache)'{-k,--keepcache}'[Do not delete the swupd state directory content after updating the system]'
201201
'(help -s --status)--download[Download all content, but do not actually install the update]'
202+
'(help -s --status)--single-step[Update to latest by first going to each release prior]'
202203
)
203204
local update_official=(
204205
'(help -s --status)--update-search-file-index[Update the index used by search-file to speed up searches]'

test/functional/only_in_ci_system/update-no-disk-space.bats

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,23 @@ test_setup() {
3030

3131
}
3232

33-
@test "UPD045: Updating a system with no disk space left (downloading the current MoM)" {
33+
@test "UPD045: Updating a system with no disk space left (fails to get the server version)" {
3434

3535
# When updating a system and we run out of disk space while downloading the
36-
# MoMs we should not retry the download since it will fail for sure
36+
# server version we should not retry the download since it will fail for sure
3737

3838
# fill up all the space in the disk
3939
sudo dd if=/dev/zero of="$TEST_NAME"/testfs/dummy >& /dev/null || print "Using all space left in disk"
4040

4141
run sudo sh -c "timeout 30 $SWUPD update $SWUPD_OPTS"
4242

43-
assert_status_is "$SWUPD_COULDNT_LOAD_MOM"
43+
assert_status_is "$SWUPD_SERVER_CONNECTION_ERROR"
4444
expected_output=$(cat <<-EOM
4545
Update started
46-
Preparing to update from 10 to 20
47-
Error: Curl - Error downloading to local file - 'file://$ABS_TEST_DIR/web-dir/10/Manifest.MoM.tar'
48-
Error: Curl - Check free space for $ABS_TEST_DIR/testfs/state?
49-
Error: Failed to retrieve 10 MoM manifest
46+
Error: Curl - Cannot close file '$ABS_CACHE_DIR/download/latest' after writing - 'No space left on device'
47+
Error: Curl - Error downloading to local file - 'file://$ABS_TEST_DIR/web-dir/version/formatstaging/latest'
48+
Error: Curl - Check free space for $ABS_STATE_DIR?
49+
Error: Unable to determine the server version
5050
Update failed
5151
EOM
5252
)

test/functional/signature/version-sig-check.bats

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ test_setup() {
9292
No extra files need to be downloaded
9393
Installing files...
9494
Update was applied
95+
Warning: THE SIGNATURE OF file://$ABS_TEST_DIR/web-dir/version/formatstaging/latest WILL NOT BE VERIFIED
9596
Calling post-update helper scripts
9697
Update successful - System updated from version 10 to version 20
9798
EOM
@@ -127,6 +128,7 @@ test_setup() {
127128
No extra files need to be downloaded
128129
Installing files...
129130
Update was applied
131+
Warning: THE SIGNATURE OF file://$ABS_TEST_DIR/web-dir/version/formatstaging/latest WILL NOT BE VERIFIED
130132
Calling post-update helper scripts
131133
Update successful - System updated from version 10 to version 20
132134
EOM

0 commit comments

Comments
 (0)