autoupdater: improve handling of interrupted HTTP requests

Check return code of uloop_run() and pass the signal number up when the
loop was interrupted. After cleanup, uninstall uloop's signal handlers
and re-raise the signal to terminate the process.

This allows interrupting the autoupdater using Ctrl-C during downloads,
instead of having it continue with the next mirror (if multple are
configured). As uloop's signal handlers only set a flag to interrupt
uloop_run() and have otherwise no effect, the autoupdater can still only
be interrupted during HTTP requests, ensuring we can't leave the system
in an inconsistent state.
This commit is contained in:
Matthias Schiffer 2023-02-24 21:28:15 +01:00
parent e4bd7a4549
commit a5259c0245
No known key found for this signature in database
GPG Key ID: 16EF3F64CB201D9C
3 changed files with 37 additions and 4 deletions

View File

@ -39,6 +39,7 @@
#include <fcntl.h>
#include <getopt.h>
#include <math.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
@ -281,6 +282,7 @@ static bool autoupdate(const char *mirror, struct settings *s, int lock_fd) {
struct recv_manifest_ctx manifest_ctx = { .s = s };
manifest_ctx.ptr = manifest_ctx.buf;
struct manifest *m = &manifest_ctx.m;
int interrupted = 0;
/**** Get and check manifest *****************************************/
/* Construct manifest URL */
@ -295,6 +297,7 @@ static bool autoupdate(const char *mirror, struct settings *s, int lock_fd) {
int err_code = get_url(manifest_url, recv_manifest_cb, &manifest_ctx, -1, s->old_version);
if (err_code != 0) {
fprintf(stderr, "autoupdater: warning: error downloading manifest: %s\n", uclient_get_errmsg(err_code));
interrupted = uclient_interrupted_signal(err_code);
goto out;
}
@ -362,6 +365,7 @@ static bool autoupdate(const char *mirror, struct settings *s, int lock_fd) {
puts("");
if (err_code != 0) {
fprintf(stderr, "autoupdater: warning: error downloading image: %s\n", uclient_get_errmsg(err_code));
interrupted = uclient_interrupted_signal(err_code);
close(image_ctx.fd);
goto fail_after_download;
}
@ -431,6 +435,14 @@ fail_after_download:
out:
clear_manifest(m);
/* If we were interrupted by a signal, restore original signal handlers
* and re-raise signal to terminate process */
if (interrupted) {
uloop_done();
raise(interrupted);
}
return ret;
}

View File

@ -43,14 +43,21 @@ enum uclient_own_error_code {
UCLIENT_ERROR_CONNECTION_RESET_PREMATURELY,
UCLIENT_ERROR_SIZE_MISMATCH,
UCLIENT_ERROR_STATUS_CODE = 1024,
UCLIENT_ERROR_INTERRUPTED = 2048,
};
const char *uclient_get_errmsg(int code) {
static char http_code_errmsg[16];
static char http_code_errmsg[34];
if (code & UCLIENT_ERROR_STATUS_CODE) {
snprintf(http_code_errmsg, 16, "HTTP error %d",
code & (~UCLIENT_ERROR_STATUS_CODE));
snprintf(http_code_errmsg, sizeof(http_code_errmsg),
"HTTP error %d", code & (~UCLIENT_ERROR_STATUS_CODE));
return http_code_errmsg;
}
if (code & UCLIENT_ERROR_INTERRUPTED) {
snprintf(http_code_errmsg, sizeof(http_code_errmsg),
"Interrupted by signal %d",
code & (~UCLIENT_ERROR_INTERRUPTED));
return http_code_errmsg;
}
switch(code) {
@ -71,6 +78,13 @@ const char *uclient_get_errmsg(int code) {
}
}
int uclient_interrupted_signal(int code) {
if (code & UCLIENT_ERROR_INTERRUPTED)
return code & (~UCLIENT_ERROR_INTERRUPTED);
return 0;
}
static void request_done(struct uclient *cl, int err_code) {
uclient_data(cl)->err_code = err_code;
@ -181,7 +195,13 @@ int get_url(const char *url, void (*read_cb)(struct uclient *cl), void *cb_data,
}
if (uclient_request(cl))
goto err;
uloop_run();
ret = uloop_run();
if (ret) {
/* uloop_run() returns a signal number when interrupted */
ret |= UCLIENT_ERROR_INTERRUPTED;
goto err;
}
if (!d.err_code && d.length >= 0 && d.downloaded != d.length) {
ret = UCLIENT_ERROR_SIZE_MISMATCH;

View File

@ -52,3 +52,4 @@ ssize_t uclient_read_account(struct uclient *cl, char *buf, int len);
int get_url(const char *url, void (*read_cb)(struct uclient *cl), void *cb_data, ssize_t len, const char *firmware_version);
const char *uclient_get_errmsg(int code);
int uclient_interrupted_signal(int code);