From 2eaa6f4e8017c9b29821429ea46ef50fbc7c7c4a Mon Sep 17 00:00:00 2001 From: job Date: Tue, 2 Aug 2022 18:09:20 +0000 Subject: [PATCH] Add --contimeout functionality. Input from deraadt@ OK claudio@ --- usr.bin/rsync/extern.h | 7 +++++- usr.bin/rsync/main.c | 29 +++++++++++++++++++----- usr.bin/rsync/rsync.1 | 9 ++++++-- usr.bin/rsync/socket.c | 51 ++++++++++++++++++++++++++---------------- 4 files changed, 68 insertions(+), 28 deletions(-) diff --git a/usr.bin/rsync/extern.h b/usr.bin/rsync/extern.h index 3a612722b9a..7de0211ba7b 100644 --- a/usr.bin/rsync/extern.h +++ b/usr.bin/rsync/extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: extern.h,v 1.43 2021/10/29 08:00:59 claudio Exp $ */ +/* $OpenBSD: extern.h,v 1.44 2022/08/02 18:09:20 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * @@ -69,6 +69,11 @@ */ extern int poll_timeout; +/* + * Use this for --contimeout. + */ +extern int poll_contimeout; + /* * Operating mode for a client or a server. * Sender means we synchronise local files with those from remote. diff --git a/usr.bin/rsync/main.c b/usr.bin/rsync/main.c index e7713cc548a..36ed383345b 100644 --- a/usr.bin/rsync/main.c +++ b/usr.bin/rsync/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.63 2021/11/03 14:42:12 deraadt Exp $ */ +/* $OpenBSD: main.c,v 1.64 2022/08/02 18:09:20 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * @@ -31,6 +31,7 @@ #include "extern.h" int verbose; +int poll_contimeout; int poll_timeout; /* @@ -286,6 +287,7 @@ static struct opts opts; #define OP_LINK_DEST 1011 #define OP_MAX_SIZE 1012 #define OP_MIN_SIZE 1013 +#define OP_CONTIMEOUT 1014 const struct option lopts[] = { { "address", required_argument, NULL, OP_ADDRESS }, @@ -296,6 +298,7 @@ const struct option lopts[] = { { "link-dest", required_argument, NULL, OP_LINK_DEST }, #endif { "compress", no_argument, NULL, 'z' }, + { "contimeout", required_argument, NULL, OP_CONTIMEOUT }, { "del", no_argument, &opts.del, 1 }, { "delete", no_argument, &opts.del, 1 }, { "devices", no_argument, &opts.devices, 1 }, @@ -411,6 +414,12 @@ main(int argc, char *argv[]) case OP_ADDRESS: opts.address = optarg; break; + case OP_CONTIMEOUT: + poll_contimeout = strtonum(optarg, 0, 60*60, &errstr); + if (errstr != NULL) + errx(ERR_SYNTAX, "timeout is %s: %s", + errstr, optarg); + break; case OP_PORT: opts.port = optarg; break; @@ -503,9 +512,16 @@ basedir: if (opts.port == NULL) opts.port = "rsync"; + /* by default and for --contimeout=0 disable poll_contimeout */ + if (poll_contimeout == 0) + poll_contimeout = -1; + else + poll_contimeout *= 1000; + /* by default and for --timeout=0 disable poll_timeout */ if (poll_timeout == 0) - poll_timeout = -1; else + poll_timeout = -1; + else poll_timeout *= 1000; /* @@ -614,10 +630,11 @@ basedir: usage: fprintf(stderr, "usage: %s" " [-aDglnoprtvx] [-e program] [--address=sourceaddr]\n" - "\t[--compare-dest=dir] [--del] [--exclude] [--exclude-from=file]\n" - "\t[--include] [--include-from=file] [--no-motd] [--numeric-ids]\n" - "\t[--port=portnumber] [--rsync-path=program] [--timeout=seconds]\n" - "\t[--version] source ... directory\n", + "\t[--contimeout=seconds [--compare-dest=dir] [--del] [--exclude]\n" + "\t[--exclude-from=file] [--include] [--include-from=file]\n" + "\t[--no-motd] [--numeric-ids] [--port=portnumber]\n" + "\t[--rsync-path=program] [--timeout=seconds] [--version]\n" + "\tsource ... directory\n", getprogname()); exit(ERR_SYNTAX); } diff --git a/usr.bin/rsync/rsync.1 b/usr.bin/rsync/rsync.1 index 8144754695a..d96c4a0da25 100644 --- a/usr.bin/rsync/rsync.1 +++ b/usr.bin/rsync/rsync.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: rsync.1,v 1.29 2021/11/26 03:41:39 jsg Exp $ +.\" $OpenBSD: rsync.1,v 1.30 2022/08/02 18:09:20 job Exp $ .\" .\" Copyright (c) 2019 Kristaps Dzonsons .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: November 26 2021 $ +.Dd $Mdocdate: August 2 2022 $ .Dt OPENRSYNC 1 .Os .Sh NAME @@ -26,6 +26,7 @@ .Op Fl e Ar program .Op Fl -address Ns = Ns Ar sourceaddr .Op Fl -compare-dest Ns = Ns Ar directory +.Op Fl -contimeout Ns = Ns Ar seconds .Op Fl -del .Op Fl -exclude Ar pattern .Op Fl -exclude-from Ns = Ns Ar file @@ -77,6 +78,10 @@ directories may be provided. If .Ar directory is a relative path, it is relative to the destination directory. +.It Fl -contimeout Ns = Ns Ar seconds +Set the connection timeout in seconds. +Exit if no connection established within the specified time. +The default is 0, which means no timeout. .It Fl D Also transfer device and special files. Shorthand for diff --git a/usr.bin/rsync/socket.c b/usr.bin/rsync/socket.c index 953b229afbc..c063346de61 100644 --- a/usr.bin/rsync/socket.c +++ b/usr.bin/rsync/socket.c @@ -1,4 +1,4 @@ -/* $OpenBSD: socket.c,v 1.31 2021/06/30 13:10:04 claudio Exp $ */ +/* $OpenBSD: socket.c,v 1.32 2022/08/02 18:09:20 job Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * @@ -75,14 +75,18 @@ static int inet_connect(int *sd, const struct source *src, const char *host, const struct source *bsrc, size_t bsrcsz) { - int c, flags; + struct pollfd pfd; + socklen_t optlen; + int c; + int optval; if (*sd != -1) close(*sd); LOG2("trying: %s, %s", src->ip, host); - if ((*sd = socket(src->family, SOCK_STREAM, 0)) == -1) { + if ((*sd = socket(src->family, SOCK_STREAM | SOCK_NONBLOCK, 0)) + == -1) { ERR("socket"); return -1; } @@ -94,34 +98,43 @@ inet_connect(int *sd, const struct source *src, const char *host, /* * Initiate blocking connection. - * We use the blocking connect() instead of passing NONBLOCK to - * the socket() function because we don't need to do anything - * while waiting for this to finish. + * We use non-blocking connect() so we can poll() for contimeout. */ - c = connect(*sd, (const struct sockaddr *)&src->sa, src->salen); + if ((c = connect(*sd, (const struct sockaddr *)&src->sa, src->salen)) + != 0 && errno == EINPROGRESS) { + pfd.fd = *sd; + pfd.events = POLLOUT; + switch (c = poll(&pfd, 1, poll_contimeout)) { + case 1: + optlen = sizeof(optval); + if ((c = getsockopt(*sd, SOL_SOCKET, SO_ERROR, &optval, + &optlen)) == 0) { + errno = optval; + if (optval != 0) + c = -1; + } + break; + case 0: + errno = ETIMEDOUT; + WARNX("connect timeout: %s, %s", src->ip, host); + return 0; + default: + ERR("poll failed"); + return -1; + } + } if (c == -1) { if (errno == EADDRNOTAVAIL) return 0; if (errno == ECONNREFUSED || errno == EHOSTUNREACH) { - WARNX("connect refused: %s, %s", - src->ip, host); + WARNX("connect refused: %s, %s", src->ip, host); return 0; } ERR("connect"); return -1; } - /* Set up non-blocking mode. */ - - if ((flags = fcntl(*sd, F_GETFL, 0)) == -1) { - ERR("fcntl"); - return -1; - } else if (fcntl(*sd, F_SETFL, flags|O_NONBLOCK) == -1) { - ERR("fcntl"); - return -1; - } - return 1; } -- 2.20.1