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