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-05-02 13:20:12 +0300
commit71ea076197410f7f1043762931b67338055a139e (patch)
tree4101290b7aab166aa59983b2bd4b60877a495136
parent06fef8ad8a936269a1868cbe1421ac72797f9a3e (diff)
downloadapk-tools-71ea076197410f7f1043762931b67338055a139e.tar.gz
apk-tools-71ea076197410f7f1043762931b67338055a139e.tar.bz2
apk-tools-71ea076197410f7f1043762931b67338055a139e.tar.xz
apk-tools-71ea076197410f7f1043762931b67338055a139e.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 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 <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);
@@ -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);