From 71ea076197410f7f1043762931b67338055a139e Mon Sep 17 00:00:00 2001 From: Timo Teräs Date: Mon, 17 Apr 2023 15:39:04 +0300 Subject: fetch: enable --timeout to set network progress timeout implement it also for connecting to hosts fixes #10869 --- doc/apk.8.scd | 4 ++++ libfetch/common.c | 67 ++++++++++++++++++++++++++++++++++++++++++++----------- libfetch/ftp.c | 2 +- src/apk.c | 5 +++++ 4 files changed, 64 insertions(+), 14 deletions(-) diff --git a/doc/apk.8.scd b/doc/apk.8.scd index aa4b60c..75ef3ac 100644 --- a/doc/apk.8.scd +++ b/doc/apk.8.scd @@ -175,6 +175,10 @@ The following options are available for all commands. processing. The given _REPOFILE_ is relative to the startup directory since apk 2.12.0_rc2. +*--timeout* _TIME_ + Timeout network connections if no progress is made in TIME seconds. + The default is 60 seconds. + *--wait* _TIME_ Wait for TIME seconds to get an exclusive repository lock before failing. diff --git a/libfetch/common.c b/libfetch/common.c index f867373..95a4f6a 100644 --- a/libfetch/common.c +++ b/libfetch/common.c @@ -32,6 +32,7 @@ */ #include +#include #include #include #include @@ -279,6 +280,18 @@ fetch_bind(int sd, int af, const char *addr) } +static int +compute_timeout(const struct timeval *tv) +{ + struct timeval cur; + int timeout; + + gettimeofday(&cur, NULL); + timeout = (tv->tv_sec - cur.tv_sec) * 1000 + (tv->tv_usec - cur.tv_usec) / 1000; + return timeout; +} + + /* * Establish a TCP connection to the specified port on the specified host. */ @@ -289,7 +302,7 @@ fetch_connect(struct url *cache_url, struct url *url, int af, int verbose) char pbuf[10]; const char *bindaddr; struct addrinfo hints, *res, *res0; - int sd, error; + int sd, error, sock_flags = SOCK_CLOEXEC; if (verbose) fetch_info("looking up %s", url->host); @@ -309,9 +322,12 @@ fetch_connect(struct url *cache_url, struct url *url, int af, int verbose) if (verbose) fetch_info("connecting to %s:%d", url->host, url->port); + if (fetchTimeout) + sock_flags |= SOCK_NONBLOCK; + /* try to connect */ for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) { - if ((sd = socket(res->ai_family, res->ai_socktype, + if ((sd = socket(res->ai_family, res->ai_socktype | sock_flags, res->ai_protocol)) == -1) continue; if (bindaddr != NULL && *bindaddr != '\0' && @@ -320,8 +336,41 @@ fetch_connect(struct url *cache_url, struct url *url, int af, int verbose) close(sd); continue; } + if (connect(sd, res->ai_addr, res->ai_addrlen) == 0) break; + + if (fetchTimeout) { + struct timeval timeout_end; + struct pollfd pfd = { .fd = sd, .events = POLLOUT }; + int r = -1; + + gettimeofday(&timeout_end, NULL); + timeout_end.tv_sec += fetchTimeout; + + do { + int timeout_cur = compute_timeout(&timeout_end); + if (timeout_cur < 0) { + errno = ETIMEDOUT; + break; + } + errno = 0; + r = poll(&pfd, 1, timeout_cur); + if (r == -1) { + if (errno == EINTR && fetchRestartCalls) + continue; + break; + } + } while (pfd.revents == 0); + + if (r == 1 && (pfd.revents & POLLOUT) == POLLOUT) { + socklen_t len = sizeof(error); + if (getsockopt(sd, SOL_SOCKET, SO_ERROR, &error, &len) == 0 && + error == 0) + break; + errno = error; + } + } close(sd); } freeaddrinfo(res0); @@ -330,6 +379,9 @@ fetch_connect(struct url *cache_url, struct url *url, int af, int verbose) return (NULL); } + if (sock_flags & SOCK_NONBLOCK) + fcntl(sd, F_SETFL, fcntl(sd, F_GETFL) & ~O_NONBLOCK); + if ((conn = fetch_reopen(sd)) == NULL) { fetch_syserr(); close(sd); @@ -604,17 +656,6 @@ fetch_ssl(conn_t *conn, const struct url *URL, int verbose) return (0); } -static int -compute_timeout(const struct timeval *tv) -{ - struct timeval cur; - int timeout; - - gettimeofday(&cur, NULL); - timeout = (tv->tv_sec - cur.tv_sec) * 1000 + (tv->tv_usec - cur.tv_usec) / 1000; - return timeout; -} - /* * Read a character from a connection w/ timeout */ diff --git a/libfetch/ftp.c b/libfetch/ftp.c index 77790aa..14323dc 100644 --- a/libfetch/ftp.c +++ b/libfetch/ftp.c @@ -692,7 +692,7 @@ ftp_transfer(conn_t *conn, const char *oper, const char *file, const char *op_ar retry_mode: /* open data socket */ - if ((sd = socket(u.ss.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { + if ((sd = socket(u.ss.ss_family, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP)) == -1) { fetch_syserr(); return (NULL); } diff --git a/src/apk.c b/src/apk.c index 7505b1c..3baae46 100644 --- a/src/apk.c +++ b/src/apk.c @@ -99,6 +99,7 @@ static struct apk_repository_list *apk_repository_new(const char *url) OPT(OPT_GLOBAL_repositories_file, APK_OPT_ARG "repositories-file") \ OPT(OPT_GLOBAL_repository, APK_OPT_ARG APK_OPT_SH("X") "repository") \ OPT(OPT_GLOBAL_root, APK_OPT_ARG APK_OPT_SH("p") "root") \ + OPT(OPT_GLOBAL_timeout, APK_OPT_ARG "timeout") \ OPT(OPT_GLOBAL_update_cache, APK_OPT_SH("U") "update-cache") \ OPT(OPT_GLOBAL_verbose, APK_OPT_SH("v") "verbose") \ OPT(OPT_GLOBAL_version, APK_OPT_SH("V") "version") \ @@ -215,6 +216,9 @@ static int option_parse_global(void *ctx, struct apk_db_options *dbopts, int opt case OPT_GLOBAL_cache_max_age: dbopts->cache_max_age = atoi(optarg) * 60; break; + case OPT_GLOBAL_timeout: + fetchTimeout = atoi(optarg); + break; case OPT_GLOBAL_arch: dbopts->arch = optarg; break; @@ -518,6 +522,7 @@ int main(int argc, char **argv) init_openssl(); setup_automatic_flags(); + fetchTimeout = 60; fetchConnectionCacheInit(32, 4); r = parse_options(argc, argv, applet, ctx, &dbopts); -- cgit v1.2.3-60-g2f50