From d94b958400f3b0737df489caa3c536f6d7d97393 Mon Sep 17 00:00:00 2001 From: Wayne Davison Date: Sat, 19 Dec 2009 13:38:25 -0800 Subject: [PATCH] Improve --timeout method to take into account all socket I/O that is going on. The receiving side also switches timeout handling from the receiver to the generator. Given this setup, all keep-alive messages are now sent as empty MSG_DATA messages, with MSG_NOOP messages only being understood and (when necessary) acted upon to forward a keep-alive event to an older receiver. This is both safer and more compatible with older versions. --- generator.c | 8 +----- io.c | 70 +++++++++++++++++++++++++++++------------------------ main.c | 1 - rsync.c | 6 ++--- rsync.h | 4 +-- 5 files changed, 44 insertions(+), 45 deletions(-) diff --git a/generator.c b/generator.c index fad25289..7e32fe1f 100644 --- a/generator.c +++ b/generator.c @@ -69,7 +69,6 @@ extern int io_error; extern int flist_eof; extern int allowed_lull; extern int sock_f_out; -extern int ignore_timeout; extern int protocol_version; extern int file_total; extern int fuzzy_basis; @@ -2212,12 +2211,6 @@ void generate_files(int f_out, const char *local_name) : "enabled"); } - /* Since we often fill up the outgoing socket and then just sit around - * waiting for the other 2 processes to do their thing, we don't want - * to exit on a timeout. If the data stops flowing, the receiver will - * notice that and let us know via the redo pipe (or its closing). */ - ignore_timeout = 1; - dflt_perms = (ACCESSPERMS & ~orig_umask); do { @@ -2309,6 +2302,7 @@ void generate_files(int f_out, const char *local_name) rprintf(FINFO, "generate_files phase=%d\n", phase); write_ndx(f_out, NDX_DONE); + /* Reduce round-trip lag-time for a useless delay-updates phase. */ if (protocol_version >= 29 && !delay_updates) write_ndx(f_out, NDX_DONE); diff --git a/io.c b/io.c index 7675d349..5b3c2ad1 100644 --- a/io.c +++ b/io.c @@ -40,6 +40,7 @@ extern int allowed_lull; extern int am_server; extern int am_daemon; extern int am_sender; +extern int am_receiver; extern int am_generator; extern int inc_recurse; extern int io_error; @@ -61,7 +62,6 @@ extern iconv_t ic_send, ic_recv; #endif const char phase_unknown[] = "unknown"; -int ignore_timeout = 0; int batch_fd = -1; int msgdone_cnt = 0; @@ -119,6 +119,32 @@ static char int_byte_extra[64] = { enum festatus { FES_SUCCESS, FES_REDO, FES_NO_SEND }; +static void check_timeout(void) +{ + time_t t, chk; + + /* On the receiving side, the generator is now handling timeouts, so + * the receiver ignores them. Note that the am_receiver flag is not + * set until the receiver forks from the generator, so timeouts will be + * based on receiving data on the receiving side until that event. */ + if (!io_timeout || am_receiver) + return; + + t = time(NULL); + + if (!last_io_in) + last_io_in = t; + + chk = MAX(last_io_out, last_io_in); + if (t - chk >= io_timeout) { + if (am_server || am_daemon) + exit_cleanup(RERR_TIMEOUT); + rprintf(FERROR, "[%s] io timeout after %d seconds -- exiting\n", + who_am_i(), (int)(t-chk)); + exit_cleanup(RERR_TIMEOUT); + } +} + static void readfd(int fd, char *buffer, size_t N); static void writefd(int fd, const char *buf, size_t len); static void writefd_unbuffered(int fd, const char *buf, size_t len); @@ -178,29 +204,6 @@ static void got_flist_entry_status(enum festatus status, const char *buf) } } -static void check_timeout(void) -{ - time_t t; - - if (!io_timeout || ignore_timeout) - return; - - if (!last_io_in) { - last_io_in = time(NULL); - return; - } - - t = time(NULL); - - if (t - last_io_in >= io_timeout) { - if (!am_server && !am_daemon) { - rprintf(FERROR, "io timeout after %d seconds -- exiting\n", - (int)(t-last_io_in)); - } - exit_cleanup(RERR_TIMEOUT); - } -} - /* Note the fds used for the main socket (which might really be a pipe * for a local transfer, but we can ignore that). */ void io_set_sock_fds(int f_in, int f_out) @@ -945,15 +948,18 @@ void maybe_flush_socket(int important) io_flush(NORMAL_FLUSH); } +/* Older rsync versions used to send either a MSG_NOOP (protocol 30) or a + * raw-data-based keep-alive (protocol 29), both of which implied forwarding of + * the message through the sender. Since the new timeout method does not need + * any forwarding, we just send an empty MSG_DATA message, which works with all + * rsync versions. This avoids any message forwarding, and leaves the raw-data + * stream alone (since we can never be quite sure if that stream is in the + * right state for a keep-alive message). */ void maybe_send_keepalive(void) { if (time(NULL) - last_io_out >= allowed_lull) { - if (!iobuf_out || !iobuf_out_cnt) { - if (protocol_version >= 30) - send_msg(MSG_NOOP, "", 0, 0); - else - send_msg(MSG_DATA, "", 0, 0); - } + if (!iobuf_out || !iobuf_out_cnt) + send_msg(MSG_DATA, "", 0, 0); if (iobuf_out) io_flush(NORMAL_FLUSH); } @@ -1035,6 +1041,9 @@ static int readfd_unbuffered(int fd, char *buf, size_t len) iobuf_in_ndx = 0; break; case MSG_NOOP: + /* Support protocol-30 keep-alive method. */ + if (msg_bytes != 0) + goto invalid_msg; if (am_sender) maybe_send_keepalive(); break; @@ -1515,7 +1524,6 @@ static void writefd_unbuffered(int fd, const char *buf, size_t len) while (!am_server && fd == sock_f_out && io_multiplexing_in) { char buf[1024]; set_io_timeout(30); - ignore_timeout = 0; readfd_unbuffered(sock_f_in, buf, sizeof buf); } exit_cleanup(RERR_STREAMIO); diff --git a/main.c b/main.c index 5dec1c78..f0ad96a0 100644 --- a/main.c +++ b/main.c @@ -33,7 +33,6 @@ extern int list_only; extern int am_root; extern int am_server; extern int am_sender; -extern int am_generator; extern int am_daemon; extern int inc_recurse; extern int blocking_io; diff --git a/rsync.c b/rsync.c index de61cb3a..e14760f4 100644 --- a/rsync.c +++ b/rsync.c @@ -81,7 +81,6 @@ void setup_iconv(void) # endif if (!am_server && !allow_8bit_chars) { - /* It's OK if this fails... */ ic_chck = iconv_open(defset, defset); @@ -305,9 +304,8 @@ int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar *type_ptr, iflags = protocol_version >= 29 ? read_shortint(f_in) : ITEM_TRANSFER | ITEM_MISSING_DATA; - /* Honor the old-style keep-alive indicator. */ - if (protocol_version < 30 - && ndx == cur_flist->used && iflags == ITEM_IS_NEW) { + /* Support the protocol-29 keep-alive style. */ + if (protocol_version < 30 && ndx == cur_flist->used && iflags == ITEM_IS_NEW) { if (am_sender) maybe_send_keepalive(); goto read_loop; diff --git a/rsync.h b/rsync.h index 1bf8f068..c75fbc24 100644 --- a/rsync.h +++ b/rsync.h @@ -32,7 +32,7 @@ #define DEFAULT_LOCK_FILE "/var/run/rsyncd.lock" #define URL_PREFIX "rsync://" -#define SYMLINK_PREFIX "/rsyncd-munged/" +#define SYMLINK_PREFIX "/rsyncd-munged/" /* This MUST have a trailing slash! */ #define SYMLINK_PREFIX_LEN ((int)sizeof SYMLINK_PREFIX - 1) #define BACKUP_SUFFIX "~" @@ -228,7 +228,7 @@ enum msgcode { MSG_FLIST=20, /* extra file list over sibling socket */ MSG_FLIST_EOF=21,/* we've transmitted all the file lists */ MSG_IO_ERROR=22,/* the sending side had an I/O error */ - MSG_NOOP=42, /* a do-nothing message */ + MSG_NOOP=42, /* a do-nothing message (legacy protocol-30 only) */ MSG_SUCCESS=100,/* successfully updated indicated flist index */ MSG_DELETED=101,/* successfully deleted a file on receiving side */ MSG_NO_SEND=102,/* sender failed to open a file we wanted */ -- 2.47.3