/* -*- c-file-style: "linux" -*-
- *
- * Copyright (C) 1996-2001 by Andrew Tridgell
+ *
+ * Copyright (C) 1996-2001 by Andrew Tridgell
* Copyright (C) Paul Mackerras 1996
* Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
- *
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
/**
* @file io.c
*
- * Socket and pipe IO utilities used in rsync.
+ * Socket and pipe I/O utilities used in rsync.
*
* rsync provides its own multiplexing system, which is used to send
* stderr and stdout over a single socket. We need this because
static int no_flush;
extern int bwlimit;
+extern size_t bwlimit_writemax;
extern int verbose;
extern int io_timeout;
extern int am_server;
* not very helpful. So instead we try to make io_phase_name point to
* something useful.
*
- * For buffered/multiplexed IO these names will be somewhat
+ * For buffered/multiplexed I/O these names will be somewhat
* approximate; perhaps for ease of support we would rather make the
- * buffer always flush when a single application-level IO finishes.
+ * buffer always flush when a single application-level I/O finishes.
*
* @todo Perhaps we want some simple stack functionality, but there's
* no need to overdo it.
if (last_io && io_timeout && (t-last_io) >= io_timeout) {
if (!am_server && !am_daemon) {
- rprintf(FERROR,"io timeout after %d seconds - exiting\n",
+ rprintf(FERROR, "io timeout after %d seconds - exiting\n",
(int)(t-last_io));
}
exit_cleanup(RERR_TIMEOUT);
/** Setup the fd used to receive MSG_* messages. Only needed when
* we're the generator because the sender and receiver both use the
- * multiplexed IO setup. */
+ * multiplexed I/O setup. */
void set_msg_fd_in(int fd)
{
msg_fd_in = fd;
/** Setup the fd used to send our MSG_* messages. Only needed when
* we're the receiver because the generator and the sender both use
- * the multiplexed IO setup. */
+ * the multiplexed I/O setup. */
void set_msg_fd_out(int fd)
{
msg_fd_out = fd;
if (kludge_around_eof)
exit_cleanup(0);
else {
- rprintf(FERROR,
- "%s: connection unexpectedly closed "
+ rprintf(FERROR, RSYNC_NAME ": connection unexpectedly closed "
"(%.0f bytes read so far)\n",
- RSYNC_NAME, (double)stats.total_read);
+ (double)stats.total_read);
exit_cleanup(RERR_STREAMIO);
}
/* this prevents us trying to write errors on a dead socket */
io_multiplexing_close();
- rprintf(FERROR, "%s: read error: %s\n",
- RSYNC_NAME, strerror(err));
+ rsyserr(FERROR, err, "read error");
exit_cleanup(RERR_STREAMIO);
}
/**
- * Read from a socket with IO timeout. return the number of bytes
+ * Read from a socket with I/O timeout. return the number of bytes
* read. If no bytes can be read then exit, never return a number <= 0.
*
* TODO: If the remote shell connection fails, then current versions
*/
static int read_timeout(int fd, char *buf, size_t len)
{
- int n, ret=0;
+ int n, ret = 0;
io_flush(NORMAL_FLUSH);
whine_about_eof();
return -1; /* doesn't return */
} else if (n < 0) {
- if (errno == EINTR || errno == EWOULDBLOCK ||
- errno == EAGAIN)
+ if (errno == EINTR || errno == EWOULDBLOCK
+ || errno == EAGAIN)
continue;
die_from_readerr(errno);
}
/**
* Read from the file descriptor handling multiplexing - return number
* of bytes read.
- *
- * Never returns <= 0.
+ *
+ * Never returns <= 0.
*/
static int read_unbuffered(int fd, char *buf, size_t len)
{
static void readfd(int fd, char *buffer, size_t N)
{
int ret;
- size_t total=0;
+ size_t total = 0;
while (total < N) {
ret = read_unbuffered(fd, buffer + total, N-total);
* use a bit less bandwidth than specified, because it doesn't make up
* for slow periods. But arguably this is a feature. In addition, we
* ought to take the time used to write the data into account.
+ *
+ * During some phases of big transfers (file FOO is uptodate) this is
+ * called with a small bytes_written every time. As the kernel has to
+ * round small waits up to guarantee that we actually wait at least the
+ * requested number of microseconds, this can become grossly inaccurate.
+ * We therefore keep track of the bytes we've written over time and only
+ * sleep when the accumulated delay is at least 1 tenth of a second.
**/
static void sleep_for_bwlimit(int bytes_written)
{
- struct timeval tv;
+ static struct timeval prior_tv;
+ static long total_written = 0;
+ struct timeval tv, start_tv;
+ long elapsed_usec, sleep_usec;
+
+#define ONE_SEC 1000000L /* # of microseconds in a second */
if (!bwlimit)
return;
- assert(bytes_written > 0);
- assert(bwlimit > 0);
+ total_written += bytes_written;
+
+ gettimeofday(&start_tv, NULL);
+ if (prior_tv.tv_sec) {
+ elapsed_usec = (start_tv.tv_sec - prior_tv.tv_sec) * ONE_SEC
+ + (start_tv.tv_usec - prior_tv.tv_usec);
+ total_written -= elapsed_usec * bwlimit / (ONE_SEC/1024);
+ if (total_written < 0)
+ total_written = 0;
+ }
- tv.tv_usec = bytes_written * 1000 / bwlimit;
- tv.tv_sec = tv.tv_usec / 1000000;
- tv.tv_usec = tv.tv_usec % 1000000;
+ sleep_usec = total_written * (ONE_SEC/1024) / bwlimit;
+ if (sleep_usec < ONE_SEC / 10) {
+ prior_tv = start_tv;
+ return;
+ }
+ tv.tv_sec = sleep_usec / ONE_SEC;
+ tv.tv_usec = sleep_usec % ONE_SEC;
select(0, NULL, NULL, NULL, &tv);
+
+ gettimeofday(&prior_tv, NULL);
+ elapsed_usec = (prior_tv.tv_sec - start_tv.tv_sec) * ONE_SEC
+ + (prior_tv.tv_usec - start_tv.tv_usec);
+ total_written = (sleep_usec - elapsed_usec) * bwlimit / (ONE_SEC/1024);
}
if (msg_fd_in >= 0) {
FD_ZERO(&r_fds);
FD_SET(msg_fd_in,&r_fds);
- if (msg_fd_in > fd_count)
+ if (msg_fd_in > fd_count)
fd_count = msg_fd_in;
}
if (FD_ISSET(fd, &w_fds)) {
int ret;
size_t n = len-total;
+ if (bwlimit && n > bwlimit_writemax)
+ n = bwlimit_writemax;
ret = write(fd,buf+total,n);
if (ret < 0) {
/* Don't try to write errors back
* across the stream */
io_multiplexing_close();
- rprintf(FERROR, RSYNC_NAME
- ": writefd_unbuffered failed to write %ld bytes: phase \"%s\": %s\n",
- (long) len, io_write_phase,
- strerror(errno));
+ rsyserr(FERROR, errno,
+ "writefd_unbuffered failed to write %ld bytes: phase \"%s\"",
+ (long) len, io_write_phase);
exit_cleanup(RERR_STREAMIO);
}
sleep_for_bwlimit(ret);
-
+
total += ret;
if (io_timeout)
void io_flush(int flush_it_all)
{
int fd = multiplex_out_fd;
-
+
msg_list_push(flush_it_all);
if (!io_buffer_count || no_flush)
/**
- * Read a line of up to @p maxlen characters into @p buf. Does not
- * contain a trailing newline or carriage return.
+ * Read a line of up to @p maxlen characters into @p buf (not counting
+ * the trailing null). Strips the (required) trailing newline and all
+ * carriage returns.
*
- * @return 1 for success; 0 for io error or truncation.
+ * @return 1 for success; 0 for I/O error or truncation.
**/
int read_line(int f, char *buf, size_t maxlen)
{
read_buf(f, buf, 1);
if (buf[0] == 0)
return 0;
- if (buf[0] == '\n') {
- buf[0] = 0;
+ if (buf[0] == '\n')
break;
- }
if (buf[0] != '\r') {
buf++;
maxlen--;
}
}
- if (maxlen == 0) {
- *buf = 0;
- return 0;
- }
-
- return 1;
+ *buf = '\0';
+ return maxlen > 0;
}
void io_printf(int fd, const char *format, ...)
{
- va_list ap;
+ va_list ap;
char buf[1024];
int len;