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:
parent
e4bd7a4549
commit
a5259c0245
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue