summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2023-04-17 15:39:04 +0300
committerTimo Teräs <timo.teras@iki.fi>2023-04-17 15:41:29 +0300
commitdcdc0901b4e849b805c5395142539cc03ecd2193 (patch)
treea1ce6fd0e9a93e4c91cd90e1649bf64ef946e6a6
parent61c8a731b6d01cefbf746e538be5f086b1af4b97 (diff)
downloadapk-tools-dcdc0901b4e849b805c5395142539cc03ecd2193.tar.gz
apk-tools-dcdc0901b4e849b805c5395142539cc03ecd2193.tar.bz2
apk-tools-dcdc0901b4e849b805c5395142539cc03ecd2193.tar.xz
apk-tools-dcdc0901b4e849b805c5395142539cc03ecd2193.zip
fetch: enable --timeout to set network progress timeout
implement it also for connecting to hosts fixes #10869
-rw-r--r--doc/apk.8.scd4
-rw-r--r--libfetch/common.c67
-rw-r--r--libfetch/ftp.c2
-rw-r--r--src/apk.c5
4 files changed, 64 insertions, 14 deletions
diff --git a/doc/apk.8.scd b/doc/apk.8.scd
index 316bb14..76083f2 100644
--- a/doc/apk.8.scd
+++ b/doc/apk.8.scd
@@ -185,6 +185,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 c217635..f62b332 100644
--- a/libfetch/common.c
+++ b/libfetch/common.c
@@ -32,6 +32,7 @@
*/
#include <poll.h>
+#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
@@ -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);
@@ -600,17 +652,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 bc7a124..1926ade 100644
--- a/src/apk.c
+++ b/src/apk.c
@@ -86,6 +86,7 @@ static void version(struct apk_out *out, const char *prefix)
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") \
@@ -209,6 +210,9 @@ static int option_parse_global(void *ctx, struct apk_ctx *ac, int opt, const cha
case OPT_GLOBAL_cache_max_age:
ac->cache_max_age = atoi(optarg) * 60;
break;
+ case OPT_GLOBAL_timeout:
+ fetchTimeout = atoi(optarg);
+ break;
case OPT_GLOBAL_arch:
ac->arch = optarg;
break;
@@ -502,6 +506,7 @@ int main(int argc, char **argv)
apk_crypto_init();
setup_automatic_flags(&ctx);
+ fetchTimeout = 60;
fetchConnectionCacheInit(32, 4);
r = parse_options(argc, argv, applet, applet_ctx, &ctx);