Avoid I/O via signal-handler thread.
authorWayne Davison <wayned@samba.org>
Sun, 26 May 2013 21:52:50 +0000 (14:52 -0700)
committerWayne Davison <wayned@samba.org>
Sun, 26 May 2013 23:22:56 +0000 (16:22 -0700)
The cleanup code will try to flush the output buffer in some
circumstances, which is not valid if we're handling an async signal
(since it might have interrupted some partial I/O in the main thread).
These signals now set a flag and try to let the main I/O handler take
care of the exit strategy.  Fixes a protocol error that could happen
when trying to exit after a kill signal.

cleanup.c
io.c
rsync.c

index 9de6eab8315fb7658f308298edc043cb8e127dfc..971154ceca4058572ab899aa5178e418ff127323 100644 (file)
--- a/cleanup.c
+++ b/cleanup.c
@@ -34,6 +34,7 @@ extern char *partial_dir;
 extern char *logfile_name;
 
 BOOL shutting_down = False;
+BOOL flush_ok_after_signal = False;
 
 #ifdef HAVE_SIGACTION
 static struct sigaction sigact;
@@ -182,7 +183,12 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
 #include "case_N.h"
                switch_step++;
 
-               if (!code || am_server || am_receiver)
+               if (flush_ok_after_signal) {
+                       flush_ok_after_signal = False;
+                       if (code == RERR_SIGNAL)
+                               io_flush(FULL_FLUSH);
+               }
+               if (!code)
                        io_flush(FULL_FLUSH);
 
                /* FALLTHROUGH */
diff --git a/io.c b/io.c
index 096225fa5cc9c6306f053c37b83179bd3cdbad1c..771fc0fc89439600bb04960fec6d72c5ae184564 100644 (file)
--- a/io.c
+++ b/io.c
@@ -57,6 +57,7 @@ extern int protocol_version;
 extern int remove_source_files;
 extern int preserve_hard_links;
 extern BOOL extra_flist_sending_enabled;
+extern BOOL flush_ok_after_signal;
 extern struct stats stats;
 extern struct file_list *cur_flist;
 #ifdef ICONV_OPTION
@@ -73,6 +74,7 @@ BOOL flist_receiving_enabled = False;
 
 /* Ignore an EOF error if non-zero. See whine_about_eof(). */
 int kluge_around_eof = 0;
+int got_kill_signal = -1; /* is set to 0 only after multiplexed I/O starts */
 
 int sock_f_in = -1;
 int sock_f_out = -1;
@@ -852,6 +854,12 @@ static char *perform_io(size_t needed, int flags)
                        }
                }
 
+               if (got_kill_signal > 0) {
+                       got_kill_signal = -1;
+                       flush_ok_after_signal = True;
+                       exit_cleanup(RERR_SIGNAL);
+               }
+
                /* We need to help prevent deadlock by doing what reading
                 * we can whenever we are here trying to write. */
                if (IN_MULTIPLEXED_AND_READY && !(flags & PIO_NEED_INPUT)) {
@@ -2282,6 +2290,7 @@ void io_start_multiplex_out(int fd)
 
        iobuf.out_empty_len = 4; /* See also OUT_MULTIPLEXED */
        io_start_buffering_out(fd);
+       got_kill_signal = 0;
 
        iobuf.raw_data_header_pos = iobuf.out.pos + iobuf.out.len;
        iobuf.out.len += 4;
@@ -2329,6 +2338,9 @@ int io_end_multiplex_out(int mode)
 
        iobuf.out.len = 0;
        iobuf.out_empty_len = 0;
+       if (got_kill_signal > 0) /* Just in case... */
+               exit_cleanup(RERR_SIGNAL);
+       got_kill_signal = -1;
 
        return ret;
 }
diff --git a/rsync.c b/rsync.c
index cf210099abfe2c1b011622c0ed22b71cffafb2d6..1f9b0297b55c6375da70c9d8857ef598ee7978e1 100644 (file)
--- a/rsync.c
+++ b/rsync.c
@@ -42,6 +42,7 @@ extern int am_generator;
 extern int am_starting_up;
 extern int allow_8bit_chars;
 extern int protocol_version;
+extern int got_kill_signal;
 extern int inc_recurse;
 extern int inplace;
 extern int flist_eof;
@@ -599,6 +600,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
        return updated;
 }
 
+/* This is only called for SIGINT, SIGHUP, and SIGTERM. */
 RETSIGTYPE sig_int(int sig_num)
 {
        /* KLUGE: if the user hits Ctrl-C while ssh is prompting
@@ -610,10 +612,23 @@ RETSIGTYPE sig_int(int sig_num)
         * not ssh waiting for a password, then this tiny delay
         * shouldn't hurt anything. */
        msleep(400);
+
        /* If we're an rsync daemon listener (not a daemon server),
         * we'll exit with status 0 if we received SIGTERM. */
        if (am_daemon && !am_server && sig_num == SIGTERM)
                exit_cleanup(0);
+
+       /* If the signal arrived on the server side (or for the receiver
+        * process on the client), we want to try to do a controlled shutdown
+        * that lets the client side (generator process) know what happened.
+        * To do this, we set a flag and let the normal process handle the
+        * shutdown.  We only attempt this if multiplexed IO is in effect and
+        * we didn't already set the flag. */
+       if (!got_kill_signal && (am_server || am_receiver)) {
+               got_kill_signal = sig_num;
+               return;
+       }
+
        exit_cleanup(RERR_SIGNAL);
 }