summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2001-03-21 13:12:44 +0000
committerAndrew Tridgell <tridge@samba.org>2001-03-21 13:12:44 +0000
commit90ba34e27c74c9fd8a8a00a360132caf20877fc2 (patch)
treef9b707ee4951dbad1eec8b8394cbca1c2e12241d
parent8ee3d639b2733276a93d92132ff4d8102ceb4d94 (diff)
downloadrsync-90ba34e27c74c9fd8a8a00a360132caf20877fc2.tar.gz
I came up with a new way of avoiding the error handling lockup bug in
rsync. It isn't pretty, but it does work and should completely avoid that class of lockup.
-rw-r--r--io.c18
-rw-r--r--log.c60
2 files changed, 68 insertions, 10 deletions
diff --git a/io.c b/io.c
index 894e05a8..649bd758 100644
--- a/io.c
+++ b/io.c
@@ -48,6 +48,8 @@ static void check_timeout(void)
{
extern int am_server, am_daemon;
time_t t;
+
+ err_list_push();
if (!io_timeout) return;
@@ -339,6 +341,8 @@ static void writefd_unbuffered(int fd,char *buf,int len)
int fd_count, count;
struct timeval tv;
+ err_list_push();
+
no_flush++;
while (total < len) {
@@ -462,6 +466,9 @@ static void mplex_write(int fd, enum logcode code, char *buf, int len)
void io_flush(void)
{
int fd = multiplex_out_fd;
+
+ err_list_push();
+
if (!io_buffer_count || no_flush) return;
if (io_multiplexing_out) {
@@ -487,6 +494,7 @@ void io_end_buffering(int fd)
a socket to be flushed. Do an explicit shutdown to try to prevent this */
void io_shutdown(void)
{
+ err_list_push();
if (multiplex_out_fd != -1) close(multiplex_out_fd);
if (io_error_fd != -1) close(io_error_fd);
multiplex_out_fd = -1;
@@ -498,6 +506,8 @@ static void writefd(int fd,char *buf,int len)
{
stats.total_written += len;
+ err_list_push();
+
if (!io_buffer || fd != multiplex_out_fd) {
writefd_unbuffered(fd, buf, len);
return;
@@ -635,14 +645,6 @@ int io_multiplex_write(enum logcode code, char *buf, int len)
return 1;
}
-/* write a message to the special error fd */
-int io_error_write(int f, enum logcode code, char *buf, int len)
-{
- if (f == -1) return 0;
- mplex_write(f, code, buf, len);
- return 1;
-}
-
/* stop output multiplexing */
void io_multiplexing_close(void)
{
diff --git a/log.c b/log.c
index aef3ac8a..a826b185 100644
--- a/log.c
+++ b/log.c
@@ -67,6 +67,59 @@ static char const *rerr_name(int code)
return NULL;
}
+struct err_list {
+ struct err_list *next;
+ char *buf;
+ int len;
+ int written; /* how many bytes we have written so far */
+};
+
+static struct err_list *err_list_head;
+static struct err_list *err_list_tail;
+
+/* add an error message to the pending error list */
+static void err_list_add(int code, char *buf, int len)
+{
+ struct err_list *el;
+ el = (struct err_list *)malloc(sizeof(*el));
+ if (!el) exit_cleanup(RERR_MALLOC);
+ el->next = NULL;
+ el->buf = malloc(len+4);
+ if (!el->buf) exit_cleanup(RERR_MALLOC);
+ memcpy(el->buf+4, buf, len);
+ SIVAL(el->buf, 0, ((code+MPLEX_BASE)<<24) | len);
+ el->len = len+4;
+ el->written = 0;
+ if (err_list_tail) {
+ err_list_tail->next = el;
+ } else {
+ err_list_head = el;
+ }
+ err_list_tail = el;
+}
+
+
+/* try to push errors off the error list onto the wire */
+void err_list_push(void)
+{
+ if (log_error_fd == -1) return;
+
+ while (err_list_head) {
+ struct err_list *el = err_list_head;
+ int n = write(log_error_fd, el->buf+el->written, el->len - el->written);
+ if (n == -1) break;
+ if (n > 0) {
+ el->written += n;
+ }
+ if (el->written == el->len) {
+ free(el->buf);
+ err_list_head = el->next;
+ if (!err_list_head) err_list_tail = NULL;
+ free(el);
+ }
+ }
+}
+
static void logit(int priority, char *buf)
{
@@ -144,6 +197,7 @@ void log_close()
void set_error_fd(int fd)
{
log_error_fd = fd;
+ set_nonblocking(log_error_fd);
}
/* this is the underlying (unformatted) rsync debugging function. Call
@@ -167,8 +221,10 @@ void rwrite(enum logcode code, char *buf, int len)
return;
}
- /* first try to pass it off the our sibling */
- if (am_server && io_error_write(log_error_fd, code, buf, len)) {
+ /* first try to pass it off to our sibling */
+ if (am_server && log_error_fd != -1) {
+ err_list_add(code, buf, len);
+ err_list_push();
return;
}