Updated patches to work with the current trunk.
[rsync/rsync-patches.git] / time-limit.diff
CommitLineData
00891c37
WD
1John Taylor's patch for implementing --time-limit and --stop-at, reworked
2to be simpler and more efficient by Wayne Davison.
3
ed44ffcf 4Do we need configure support for mktime()?
00891c37 5
03019e41
WD
6To use this patch, run these commands for a successful build:
7
8 patch -p1 <patches/time-limit.diff
9 ./configure (optional if already run)
10 make
11
5214a41b 12based-on: 24079e988fc31af4eba56cd2701fdc5a4154980d
cc3e685d
WD
13diff --git a/io.c b/io.c
14--- a/io.c
15+++ b/io.c
72e5645e 16@@ -57,6 +57,7 @@ extern int remove_source_files;
d608ca23 17 extern int preserve_hard_links;
72e5645e 18 extern BOOL extra_flist_sending_enabled;
9cd8d7aa 19 extern struct stats stats;
d608ca23 20+extern time_t stop_at_utime;
cdcd2137 21 extern struct file_list *cur_flist;
6cbbe66d 22 #ifdef ICONV_OPTION
cdcd2137 23 extern int filesfrom_convert;
5214a41b 24@@ -154,16 +155,24 @@ static void check_timeout(void)
00891c37
WD
25 {
26 time_t t;
c70898d5 27
8ab3ce19
WD
28+ if ((!io_timeout || ignore_timeout) && !stop_at_utime)
29+ return;
30+
00891c37 31+ t = time(NULL);
c70898d5 32+
00891c37
WD
33+ if (stop_at_utime && t >= stop_at_utime) {
34+ rprintf(FERROR, "run-time limit exceeded\n");
35+ exit_cleanup(RERR_TIMEOUT);
36+ }
c70898d5 37+
8ab3ce19
WD
38 if (!io_timeout || ignore_timeout)
39 return;
40
5398d042
WD
41 if (!last_io_in) {
42- last_io_in = time(NULL);
43+ last_io_in = t;
00891c37
WD
44 return;
45 }
c70898d5 46
7ce4946a
WD
47- t = time(NULL);
48-
5398d042 49 if (t - last_io_in >= io_timeout) {
00891c37 50 if (!am_server && !am_daemon) {
a7219d20 51 rprintf(FERROR, "io timeout after %d seconds -- exiting\n",
cc3e685d
WD
52diff --git a/options.c b/options.c
53--- a/options.c
54+++ b/options.c
fc557362 55@@ -112,6 +112,7 @@ size_t bwlimit_writemax = 0;
70891d26
WD
56 int ignore_existing = 0;
57 int ignore_non_existing = 0;
58 int need_messages_from_generator = 0;
00891c37 59+time_t stop_at_utime = 0;
99650e0d 60 int max_delete = INT_MIN;
70891d26
WD
61 OFF_T max_size = 0;
62 OFF_T min_size = 0;
72e5645e 63@@ -776,6 +777,8 @@ void usage(enum logcode F)
fc068916 64 rprintf(F," --password-file=FILE read daemon-access password from FILE\n");
be73a66e 65 rprintf(F," --list-only list the files instead of copying them\n");
5214a41b 66 rprintf(F," --bwlimit=RATE limit socket I/O bandwidth\n");
ed44ffcf
WD
67+ rprintf(F," --stop-at=y-m-dTh:m Stop rsync at year-month-dayThour:minute\n");
68+ rprintf(F," --time-limit=MINS Stop rsync after MINS minutes have elapsed\n");
79f132a1 69 rprintf(F," --write-batch=FILE write a batched update to FILE\n");
c2f72cff 70 rprintf(F," --only-write-batch=FILE like --write-batch but w/o updating destination\n");
79f132a1 71 rprintf(F," --read-batch=FILE read a batched update from FILE\n");
5214a41b 72@@ -800,6 +803,7 @@ enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
5398d042 73 OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE,
fc557362 74 OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG,
5214a41b
WD
75 OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT,
76+ OPT_STOP_AT, OPT_TIME_LIMIT,
0ca6aebe 77 OPT_SERVER, OPT_REFUSED_BASE = 9000};
00891c37
WD
78
79 static struct poptOption long_options[] = {
5214a41b 80@@ -989,6 +993,8 @@ static struct poptOption long_options[] = {
6cbbe66d 81 {"no-timeout", 0, POPT_ARG_VAL, &io_timeout, 0, 0, 0 },
cc3e685d 82 {"contimeout", 0, POPT_ARG_INT, &connect_timeout, 0, 0, 0 },
c0c7984e 83 {"no-contimeout", 0, POPT_ARG_VAL, &connect_timeout, 0, 0, 0 },
00891c37
WD
84+ {"stop-at", 0, POPT_ARG_STRING, 0, OPT_STOP_AT, 0, 0 },
85+ {"time-limit", 0, POPT_ARG_STRING, 0, OPT_TIME_LIMIT, 0, 0 },
6cbbe66d
WD
86 {"rsh", 'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 },
87 {"rsync-path", 0, POPT_ARG_STRING, &rsync_path, 0, 0, 0 },
88 {"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
5214a41b 89@@ -1763,6 +1769,36 @@ int parse_arguments(int *argc_p, const char ***argv_p)
5ff5e82f
WD
90 return 0;
91 #endif
00891c37
WD
92
93+ case OPT_STOP_AT:
94+ arg = poptGetOptArg(pc);
ed44ffcf 95+ if ((stop_at_utime = parse_time(arg)) == (time_t)-1) {
00891c37
WD
96+ snprintf(err_buf, sizeof err_buf,
97+ "invalid --stop-at format: %s\n",
98+ arg);
99+ rprintf(FERROR, "ERROR: %s", err_buf);
100+ return 0;
101+ }
00891c37
WD
102+ if (stop_at_utime < time(NULL)) {
103+ snprintf(err_buf, sizeof err_buf,
104+ "--stop-at time is in the past: %s\n",
105+ arg);
106+ rprintf(FERROR, "ERROR: %s", err_buf);
107+ return 0;
108+ }
109+ break;
110+
111+ case OPT_TIME_LIMIT:
112+ arg = poptGetOptArg(pc);
113+ if ((stop_at_utime = atol(arg) * 60) <= 0) {
114+ snprintf(err_buf, sizeof err_buf,
115+ "invalid --time-limit value: %s\n",
116+ arg);
117+ rprintf(FERROR, "ERROR: %s", err_buf);
118+ return 0;
119+ }
120+ stop_at_utime += time(NULL);
121+ break;
122+
123 default:
124 /* A large opt value means that set_refuse_options()
27a7053c 125 * turned this option off. */
5214a41b 126@@ -2465,6 +2501,15 @@ void server_options(char **args, int *argc_p)
7ce4946a
WD
127 args[ac++] = arg;
128 }
c70898d5 129
00891c37
WD
130+ if (stop_at_utime) {
131+ long mins = (stop_at_utime - time(NULL)) / 60;
132+ if (mins <= 0)
133+ mins = 1;
134+ if (asprintf(&arg, "--time-limit=%ld", mins) < 0)
7ce4946a
WD
135+ goto oom;
136+ args[ac++] = arg;
137+ }
138+
139 if (backup_dir) {
140 args[ac++] = "--backup-dir";
141 args[ac++] = backup_dir;
cc3e685d
WD
142diff --git a/rsync.yo b/rsync.yo
143--- a/rsync.yo
144+++ b/rsync.yo
fc557362 145@@ -431,6 +431,8 @@ to the detailed description below for a complete description. verb(
fc068916 146 --password-file=FILE read daemon-access password from FILE
be73a66e 147 --list-only list the files instead of copying them
5214a41b 148 --bwlimit=RATE limit socket I/O bandwidth
ed44ffcf
WD
149+ --stop-at=y-m-dTh:m Stop rsync at year-month-dayThour:minute
150+ --time-limit=MINS Stop rsync after MINS minutes have elapsed
a7219d20 151 --write-batch=FILE write a batched update to FILE
c2f72cff 152 --only-write-batch=FILE like --write-batch but w/o updating dest
79f132a1 153 --read-batch=FILE read a batched update from FILE
5214a41b
WD
154@@ -2295,6 +2297,19 @@ files can show up as being rapidly sent when the data is quickly buffered,
155 while other can show up as very slow when the flushing of the output buffer
156 occurs. This may be fixed in a future version.
7ce4946a 157
ed44ffcf
WD
158+dit(bf(--stop-at=y-m-dTh:m)) This option allows you to specify at what
159+time to stop rsync, in year-month-dayThour:minute numeric format (e.g.
ab74148e 160+2004-12-31T23:59). You can specify a 2 or 4-digit year. You can also
ed44ffcf
WD
161+leave off various items and the result will be the next possible time
162+that matches the specified data. For example, "1-30" specifies the next
16711fbf
WD
163+January 30th (at midnight), "04:00" specifies the next 4am, "1"
164+specifies the next 1st of the month at midnight, and ":59" specifies the
ab74148e
WD
165+next 59th minute after the hour. If you prefer, you may separate the
166+date numbers using slashes instead of dashes.
00891c37 167+
ed44ffcf 168+dit(bf(--time-limit=MINS)) This option allows you to specify the maximum
00891c37 169+number of minutes rsync will run for.
7ce4946a 170+
9be39c35 171 dit(bf(--write-batch=FILE)) Record a file that can later be applied to
a7219d20 172 another identical destination with bf(--read-batch). See the "BATCH MODE"
388bf7cc 173 section for details, and also the bf(--only-write-batch) option.
cc3e685d
WD
174diff --git a/util.c b/util.c
175--- a/util.c
176+++ b/util.c
91270139 177@@ -123,6 +123,133 @@ NORETURN void overflow_exit(const char *str)
ed44ffcf
WD
178 exit_cleanup(RERR_MALLOC);
179 }
180
181+/* Allow the user to specify a time in the format yyyy-mm-ddThh:mm while
182+ * also allowing abbreviated data. For instance, if the time is omitted,
183+ * it defaults to midnight. If the date is omitted, it defaults to the
184+ * next possible date in the future with the specified time. Even the
185+ * year or year-month can be omitted, again defaulting to the next date
186+ * in the future that matches the specified information. A 2-digit year
187+ * is also OK, as is using '/' instead of '-'. */
188+time_t parse_time(const char *arg)
189+{
190+ const char *cp;
191+ time_t val, now = time(NULL);
192+ struct tm t, *today = localtime(&now);
16711fbf 193+ int in_date, n;
ed44ffcf
WD
194+
195+ memset(&t, 0, sizeof t);
196+ t.tm_year = t.tm_mon = t.tm_mday = -1;
197+ t.tm_hour = t.tm_min = t.tm_isdst = -1;
16711fbf
WD
198+ cp = arg;
199+ if (*cp == 'T' || *cp == 't' || *cp == ':') {
200+ cp++;
201+ in_date = 0;
202+ } else
203+ in_date = 1;
204+ for ( ; ; cp++) {
3cff695b 205+ if (!isDigit(cp))
ed44ffcf 206+ return -1;
16711fbf
WD
207+
208+ n = 0;
209+ do {
210+ n = n * 10 + *cp++ - '0';
3cff695b 211+ } while (isDigit(cp));
16711fbf
WD
212+
213+ if (*cp == ':')
214+ in_date = 0;
ed44ffcf 215+ if (in_date) {
16711fbf
WD
216+ if (t.tm_year != -1)
217+ return -1;
218+ t.tm_year = t.tm_mon;
219+ t.tm_mon = t.tm_mday;
220+ t.tm_mday = n;
221+ if (!*cp)
222+ break;
223+ if (*cp == 'T' || *cp == 't') {
224+ if (!cp[1])
ed44ffcf 225+ break;
16711fbf
WD
226+ in_date = 0;
227+ } else if (*cp != '-' && *cp != '/')
228+ return -1;
229+ continue;
ed44ffcf
WD
230+ }
231+ if (t.tm_hour != -1)
232+ return -1;
233+ t.tm_hour = t.tm_min;
234+ t.tm_min = n;
235+ if (!*cp)
236+ break;
237+ if (*cp != ':')
238+ return -1;
239+ }
240+
241+ in_date = 0;
242+ if (t.tm_year < 0) {
243+ t.tm_year = today->tm_year;
244+ in_date = 1;
245+ } else if (t.tm_year < 100) {
246+ while (t.tm_year < today->tm_year)
247+ t.tm_year += 100;
248+ } else
249+ t.tm_year -= 1900;
250+ if (t.tm_mon < 0) {
251+ t.tm_mon = today->tm_mon;
252+ in_date = 2;
253+ } else
254+ t.tm_mon--;
255+ if (t.tm_mday < 0) {
ed44ffcf
WD
256+ t.tm_mday = today->tm_mday;
257+ in_date = 3;
258+ }
259+
260+ n = 0;
261+ if (t.tm_min < 0) {
262+ t.tm_hour = t.tm_min = 0;
263+ } else if (t.tm_hour < 0) {
264+ if (in_date != 3)
265+ return -1;
266+ in_date = 0;
267+ t.tm_hour = today->tm_hour;
268+ n = 60*60;
269+ }
270+
16711fbf
WD
271+ if (t.tm_hour > 23 || t.tm_min > 59
272+ || t.tm_mon < 0 || t.tm_mon >= 12
273+ || t.tm_mday < 1 || t.tm_mday > 31
274+ || (val = mktime(&t)) == (time_t)-1)
ed44ffcf
WD
275+ return -1;
276+
277+ if (val <= now && in_date) {
278+ tweak_date:
279+ switch (in_date) {
280+ case 3:
281+ t.tm_mday++;
282+ break;
283+ case 2:
284+ if (++t.tm_mon == 12)
285+ t.tm_mon = 0;
286+ else
287+ break;
288+ case 1:
289+ t.tm_year++;
290+ break;
291+ }
292+ if ((val = mktime(&t)) == (time_t)-1) {
293+ if (in_date == 3 && t.tm_mday > 28) {
294+ t.tm_mday = 1;
295+ in_date = 2;
296+ goto tweak_date;
297+ }
298+ return -1;
299+ }
300+ }
301+ if (n) {
302+ while (val <= now)
303+ val += n;
304+ }
305+ return val;
306+}
afcb578c 307+
72e5645e 308 int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
afcb578c 309 {
72e5645e 310 #ifndef CAN_SET_SYMLINK_TIMES