Add --contimeout functionality.
authorjob <job@openbsd.org>
Tue, 2 Aug 2022 18:09:20 +0000 (18:09 +0000)
committerjob <job@openbsd.org>
Tue, 2 Aug 2022 18:09:20 +0000 (18:09 +0000)
Input from deraadt@

OK claudio@

usr.bin/rsync/extern.h
usr.bin/rsync/main.c
usr.bin/rsync/rsync.1
usr.bin/rsync/socket.c

index 3a61272..7de0211 100644 (file)
@@ -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 <kristaps@bsd.lv>
  *
  */
 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.
index e7713cc..36ed383 100644 (file)
@@ -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 <kristaps@bsd.lv>
  *
@@ -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);
 }
index 8144754..d96c4a0 100644 (file)
@@ -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 <kristaps@bsd.lv>
 .\"
@@ -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
index 953b229..c063346 100644 (file)
@@ -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 <kristaps@bsd.lv>
  *
@@ -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;
 }