X-Git-Url: https://mattmccutchen.net/rsync/rsync-patches.git/blobdiff_plain/bd8bf8b185e8a299fb3c7fdb547ea3ad264dc058..c1ff70aa47e11c5b37634479a0facee775a7b6d9:/time-limit.diff diff --git a/time-limit.diff b/time-limit.diff index 6e053f9..1ec9c19 100644 --- a/time-limit.diff +++ b/time-limit.diff @@ -1,143 +1,303 @@ ---- io.c 16 Jan 2004 16:31:47 -0000 1.119 -+++ io.c 2004-04-19 16:07:57.000000000 -0400 -@@ -47,6 +47,7 @@ static time_t last_io; - static int no_flush; +John Taylor's patch for implementing --time-limit and --stop-at, reworked +to be simpler and more efficient by Wayne Davison. + +Do we need configure support for mktime()? + +To use this patch, run these commands for a successful build: + + patch -p1 0); -+ -+ start_time = get_rsync_start_time(); -+ assert(start_time > 0); ++ if (stop_at_utime && t >= stop_at_utime) { ++ rprintf(FERROR, "run-time limit exceeded\n"); ++ exit_cleanup(RERR_TIMEOUT); ++ } + -+ gettimeofday(&tv, NULL); -+ expiration_time = start_time + (timelimit * 60); ++ if (!io_timeout || am_receiver) ++ return; + -+ return tv.tv_sec > expiration_time; -+} + if (allow_keepalive && we_send_keepalive_messages) { + /* This may put data into iobuf.msg w/o flushing. */ + maybe_send_keepalive(t, False); +diff --git a/options.c b/options.c +--- a/options.c ++++ b/options.c +@@ -112,6 +112,7 @@ size_t bwlimit_writemax = 0; + int ignore_existing = 0; + int ignore_non_existing = 0; + int need_messages_from_generator = 0; ++time_t stop_at_utime = 0; + int max_delete = INT_MIN; + OFF_T max_size = 0; + OFF_T min_size = 0; +@@ -776,6 +777,8 @@ void usage(enum logcode F) + rprintf(F," --password-file=FILE read daemon-access password from FILE\n"); + rprintf(F," --list-only list the files instead of copying them\n"); + rprintf(F," --bwlimit=RATE limit socket I/O bandwidth\n"); ++ rprintf(F," --stop-at=y-m-dTh:m Stop rsync at year-month-dayThour:minute\n"); ++ rprintf(F," --time-limit=MINS Stop rsync after MINS minutes have elapsed\n"); + rprintf(F," --write-batch=FILE write a batched update to FILE\n"); + rprintf(F," --only-write-batch=FILE like --write-batch but w/o updating destination\n"); + rprintf(F," --read-batch=FILE read a batched update from FILE\n"); +@@ -800,6 +803,7 @@ enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, + OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE, + OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, + OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, ++ OPT_STOP_AT, OPT_TIME_LIMIT, + OPT_SERVER, OPT_REFUSED_BASE = 9000}; - /** - * Write len bytes to the file descriptor @p fd. -@@ -837,6 +858,11 @@ static void writefd_unbuffered(int fd,ch - } + static struct poptOption long_options[] = { +@@ -989,6 +993,8 @@ static struct poptOption long_options[] = { + {"no-timeout", 0, POPT_ARG_VAL, &io_timeout, 0, 0, 0 }, + {"contimeout", 0, POPT_ARG_INT, &connect_timeout, 0, 0, 0 }, + {"no-contimeout", 0, POPT_ARG_VAL, &connect_timeout, 0, 0, 0 }, ++ {"stop-at", 0, POPT_ARG_STRING, 0, OPT_STOP_AT, 0, 0 }, ++ {"time-limit", 0, POPT_ARG_STRING, 0, OPT_TIME_LIMIT, 0, 0 }, + {"rsh", 'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 }, + {"rsync-path", 0, POPT_ARG_STRING, &rsync_path, 0, 0, 0 }, + {"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 }, +@@ -1764,6 +1770,36 @@ int parse_arguments(int *argc_p, const char ***argv_p) + return 0; + #endif - sleep_for_bwlimit(ret); ++ case OPT_STOP_AT: ++ arg = poptGetOptArg(pc); ++ if ((stop_at_utime = parse_time(arg)) == (time_t)-1) { ++ snprintf(err_buf, sizeof err_buf, ++ "invalid --stop-at format: %s\n", ++ arg); ++ rprintf(FERROR, "ERROR: %s", err_buf); ++ return 0; ++ } ++ if (stop_at_utime < time(NULL)) { ++ snprintf(err_buf, sizeof err_buf, ++ "--stop-at time is in the past: %s\n", ++ arg); ++ rprintf(FERROR, "ERROR: %s", err_buf); ++ return 0; ++ } ++ break; + -+ if (timelimit && has_timelimit_expired()) { -+ rprintf(FERROR, RSYNC_NAME ": \n%d minute time limit exceeded.\n", timelimit); -+ exit_cleanup(RERR_PARTIAL); ++ case OPT_TIME_LIMIT: ++ arg = poptGetOptArg(pc); ++ if ((stop_at_utime = atol(arg) * 60) <= 0) { ++ snprintf(err_buf, sizeof err_buf, ++ "invalid --time-limit value: %s\n", ++ arg); ++ rprintf(FERROR, "ERROR: %s", err_buf); ++ return 0; + } - - total += ret; - ---- options.c 17 Apr 2004 17:07:23 -0000 1.147 -+++ options.c 2004-04-19 16:07:57.000000000 -0400 -@@ -83,6 +83,7 @@ int safe_symlinks = 0; - int copy_unsafe_links = 0; - int size_only = 0; - int bwlimit = 0; -+int timelimit = 0; - int delete_after = 0; - int only_existing = 0; - int opt_ignore_existing = 0; -@@ -288,6 +289,7 @@ void usage(enum logcode F) - rprintf(F," --log-format=FORMAT log file transfers using specified format\n"); - rprintf(F," --password-file=FILE get password from FILE\n"); - rprintf(F," --bwlimit=KBPS limit I/O bandwidth, KBytes per second\n"); -+ rprintf(F," --timelimit=MINS Stop rsync after MINS minutes\n"); - rprintf(F," --write-batch=PREFIX write batch fileset starting with PREFIX\n"); - rprintf(F," --read-batch=PREFIX read batch fileset starting with PREFIX\n"); - rprintf(F," -h, --help show this help screen\n"); -@@ -377,6 +379,7 @@ static struct poptOption long_options[] - {"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 }, - {"log-format", 0, POPT_ARG_STRING, &log_format, 0, 0, 0 }, - {"bwlimit", 0, POPT_ARG_INT, &bwlimit, 0, 0, 0 }, -+ {"timelimit", 0, POPT_ARG_INT, &timelimit, 0, 0, 0 }, - {"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 }, - {"backup-dir", 0, POPT_ARG_STRING, &backup_dir, 0, 0, 0 }, - {"hard-links", 'H', POPT_ARG_NONE, &preserve_hard_links, 0, 0, 0 }, -@@ -881,6 +884,12 @@ void server_options(char **args,int *arg ++ stop_at_utime += time(NULL); ++ break; ++ + default: + /* A large opt value means that set_refuse_options() + * turned this option off. */ +@@ -2480,6 +2516,15 @@ void server_options(char **args, int *argc_p) + args[ac++] = arg; + } - if (bwlimit) { - if (asprintf(&arg, "--bwlimit=%d", bwlimit) < 0) ++ if (stop_at_utime) { ++ long mins = (stop_at_utime - time(NULL)) / 60; ++ if (mins <= 0) ++ mins = 1; ++ if (asprintf(&arg, "--time-limit=%ld", mins) < 0) + goto oom; + args[ac++] = arg; + } + -+ if (timelimit) { -+ if (asprintf(&arg, "--timelimit=%d", timelimit) < 0) - goto oom; - args[ac++] = arg; - } ---- proto.h 22 Apr 2004 09:58:09 -0000 1.189 -+++ proto.h 2004-04-19 16:07:57.000000000 -0400 -@@ -266,6 +266,7 @@ int u_strcmp(const char *cs1, const char - int unsafe_symlink(const char *dest, const char *src); - char *timestring(time_t t); - int msleep(int t); -+time_t get_rsync_start_time(void); - int cmp_modtime(time_t file1, time_t file2); - int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6); - void *_new_array(unsigned int size, unsigned long num); ---- rsync.yo 22 Apr 2004 21:35:45 -0000 1.161 -+++ rsync.yo 19 Apr 2004 20:50:45 -0000 -@@ -346,6 +346,7 @@ verb( - --log-format=FORMAT log file transfers using specified format - --password-file=FILE get password from FILE - --bwlimit=KBPS limit I/O bandwidth, KBytes per second -+ --timelimit=MINS Stop rsync after MINS minutes - --write-batch=PREFIX write batch fileset starting with PREFIX - --read-batch=PREFIX read batch fileset starting with PREFIX - -h, --help show this help screen -@@ -883,6 +884,10 @@ of rsync transfers, blocks of data are s - transfer was too fast, it will wait before sending the next data block. The - result is an average transfer rate equaling the specified limit. A value - of zero specifies no limit. -+ -+dit(bf(--timelimit=MINS)) This option allows you to specify the maximum -+number of minutes rsync will run for. Ths time starts when the first -+file starts transferring. + if (backup_dir) { + args[ac++] = "--backup-dir"; + args[ac++] = backup_dir; +diff --git a/rsync.yo b/rsync.yo +--- a/rsync.yo ++++ b/rsync.yo +@@ -431,6 +431,8 @@ to the detailed description below for a complete description. verb( + --password-file=FILE read daemon-access password from FILE + --list-only list the files instead of copying them + --bwlimit=RATE limit socket I/O bandwidth ++ --stop-at=y-m-dTh:m Stop rsync at year-month-dayThour:minute ++ --time-limit=MINS Stop rsync after MINS minutes have elapsed + --write-batch=FILE write a batched update to FILE + --only-write-batch=FILE like --write-batch but w/o updating dest + --read-batch=FILE read a batched update from FILE +@@ -2309,6 +2311,19 @@ files can show up as being rapidly sent when the data is quickly buffered, + while other can show up as very slow when the flushing of the output buffer + occurs. This may be fixed in a future version. - dit(bf(--write-batch=PREFIX)) Generate a set of files that can be - transferred as a batch update. Each filename in the set starts with ---- util.c 22 Apr 2004 22:17:15 -0000 1.138 -+++ util.c 2004-04-19 16:07:57.000000000 -0400 -@@ -1049,6 +1049,21 @@ int msleep(int t) - return True; ++dit(bf(--stop-at=y-m-dTh:m)) This option allows you to specify at what ++time to stop rsync, in year-month-dayThour:minute numeric format (e.g. ++2004-12-31T23:59). You can specify a 2 or 4-digit year. You can also ++leave off various items and the result will be the next possible time ++that matches the specified data. For example, "1-30" specifies the next ++January 30th (at midnight), "04:00" specifies the next 4am, "1" ++specifies the next 1st of the month at midnight, and ":59" specifies the ++next 59th minute after the hour. If you prefer, you may separate the ++date numbers using slashes instead of dashes. ++ ++dit(bf(--time-limit=MINS)) This option allows you to specify the maximum ++number of minutes rsync will run for. ++ + dit(bf(--write-batch=FILE)) Record a file that can later be applied to + another identical destination with bf(--read-batch). See the "BATCH MODE" + section for details, and also the bf(--only-write-batch) option. +diff --git a/util.c b/util.c +--- a/util.c ++++ b/util.c +@@ -123,6 +123,133 @@ NORETURN void overflow_exit(const char *str) + exit_cleanup(RERR_MALLOC); } -+/** -+ * Return the time that rsync is started, used by --time-limit -+ */ -+time_t get_rsync_start_time(void) ++/* Allow the user to specify a time in the format yyyy-mm-ddThh:mm while ++ * also allowing abbreviated data. For instance, if the time is omitted, ++ * it defaults to midnight. If the date is omitted, it defaults to the ++ * next possible date in the future with the specified time. Even the ++ * year or year-month can be omitted, again defaulting to the next date ++ * in the future that matches the specified information. A 2-digit year ++ * is also OK, as is using '/' instead of '-'. */ ++time_t parse_time(const char *arg) +{ -+ static struct timeval tval; -+ static int has_set_rsync_start_time = 0; ++ const char *cp; ++ time_t val, now = time(NULL); ++ struct tm t, *today = localtime(&now); ++ int in_date, n; ++ ++ memset(&t, 0, sizeof t); ++ t.tm_year = t.tm_mon = t.tm_mday = -1; ++ t.tm_hour = t.tm_min = t.tm_isdst = -1; ++ cp = arg; ++ if (*cp == 'T' || *cp == 't' || *cp == ':') { ++ cp++; ++ in_date = 0; ++ } else ++ in_date = 1; ++ for ( ; ; cp++) { ++ if (!isDigit(cp)) ++ return -1; ++ ++ n = 0; ++ do { ++ n = n * 10 + *cp++ - '0'; ++ } while (isDigit(cp)); ++ ++ if (*cp == ':') ++ in_date = 0; ++ if (in_date) { ++ if (t.tm_year != -1) ++ return -1; ++ t.tm_year = t.tm_mon; ++ t.tm_mon = t.tm_mday; ++ t.tm_mday = n; ++ if (!*cp) ++ break; ++ if (*cp == 'T' || *cp == 't') { ++ if (!cp[1]) ++ break; ++ in_date = 0; ++ } else if (*cp != '-' && *cp != '/') ++ return -1; ++ continue; ++ } ++ if (t.tm_hour != -1) ++ return -1; ++ t.tm_hour = t.tm_min; ++ t.tm_min = n; ++ if (!*cp) ++ break; ++ if (*cp != ':') ++ return -1; ++ } ++ ++ in_date = 0; ++ if (t.tm_year < 0) { ++ t.tm_year = today->tm_year; ++ in_date = 1; ++ } else if (t.tm_year < 100) { ++ while (t.tm_year < today->tm_year) ++ t.tm_year += 100; ++ } else ++ t.tm_year -= 1900; ++ if (t.tm_mon < 0) { ++ t.tm_mon = today->tm_mon; ++ in_date = 2; ++ } else ++ t.tm_mon--; ++ if (t.tm_mday < 0) { ++ t.tm_mday = today->tm_mday; ++ in_date = 3; ++ } + -+ if (!has_set_rsync_start_time) { -+ gettimeofday(&tval, NULL); -+ has_set_rsync_start_time = 1; ++ n = 0; ++ if (t.tm_min < 0) { ++ t.tm_hour = t.tm_min = 0; ++ } else if (t.tm_hour < 0) { ++ if (in_date != 3) ++ return -1; ++ in_date = 0; ++ t.tm_hour = today->tm_hour; ++ n = 60*60; + } + -+ return tval.tv_sec; ++ if (t.tm_hour > 23 || t.tm_min > 59 ++ || t.tm_mon < 0 || t.tm_mon >= 12 ++ || t.tm_mday < 1 || t.tm_mday > 31 ++ || (val = mktime(&t)) == (time_t)-1) ++ return -1; ++ ++ if (val <= now && in_date) { ++ tweak_date: ++ switch (in_date) { ++ case 3: ++ t.tm_mday++; ++ break; ++ case 2: ++ if (++t.tm_mon == 12) ++ t.tm_mon = 0; ++ else ++ break; ++ case 1: ++ t.tm_year++; ++ break; ++ } ++ if ((val = mktime(&t)) == (time_t)-1) { ++ if (in_date == 3 && t.tm_mday > 28) { ++ t.tm_mday = 1; ++ in_date = 2; ++ goto tweak_date; ++ } ++ return -1; ++ } ++ } ++ if (n) { ++ while (val <= now) ++ val += n; ++ } ++ return val; +} - - /** - * Determine if two file modification times are equivalent (either ++ + int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode) + { + #ifndef CAN_SET_SYMLINK_TIMES