Fixed failing hunks.
[rsync/rsync-patches.git] / fname-convert.diff
CommitLineData
4a751edb
WD
1Eran Tromer writes:
2
3One feature missing from rsync, and requested on this list before, is
4on-the-fly conversion of filename character encoding. For example, I
5often need to sync files having Hebrew filenames from a UTF-8 system
6(Linux) to an ISO8859-8 system (Cygwin on Windows 2000 using the
7non-Unicode Win32 interface). Other circumstances surely abound.
8
9Attached is a patch against rsync 2.6.2 that adds an "--fname-convert"
10option. When the argument "--fname-convert CONV" is given, rsync pipes
11every filename through the program CONV, and filename presented to the
12server will be CONV's output instead of the raw filename.
13
14Artificial example:
15$ touch /tmp/xyz
16$ rsync -fname-convert 'tr y Y' /tmp/xyz /tmp/
17$ ls /tmp/x?z
18/tmp/xyz /tmp/xYz
19
20Perhaps the most useful case is using iconv:
21$ rsync --fname-convert 'iconv -f utf8 -t iso8859-8' ...
22
23I chose to allow invocation of arbitrary programs instead of using
24libiconv (or equivalent) in order to avoid external dependencies, and to
25offer more flexibility. The price is that some heuristics were needed to
26avoid the deadlock problems that tend to occur when filtering data
27through a program that uses buffered I/O -- see the comments at the top
28of the new file fnameconv.c. The delay you may have noticed in the above
29artificial example using "tr" is due to these heuristics; it occurs just
30once per rsync invocation, not for every file.
31
32I believe there are no server-side security implications, since all
33conversion is done at the client and the server is oblivious to it. On
34the client, conversion is done before sanitize_path() and besides,
35providing a sane converter program is the client's responsibility anyway.
36
37In verbose mode the updating of non-regular files is reported via
38rprintf() by the server, so the client will see the converted filename
39instead the raw filename -- see my comment in recv_generator(). Fixing
40this requires some delicate changes so I left it as is, but it seems
41like a minor concern.
42
43Most of the new code is in the new file fnameconv.c. The patch lightly
44touches some other files, mostly flist.c and the addition/extension of
6114c438
WD
45some utility functions.
46
47Note that you'll need to run 'make proto' after applying this patch.
4a751edb
WD
48
49
610969d1
WD
50--- orig/Makefile.in 2005-11-07 04:29:00
51+++ Makefile.in 2005-11-07 04:36:50
fdd483f0 52@@ -34,7 +34,7 @@ OBJS1=rsync.o generator.o receiver.o cle
4a751edb
WD
53 main.o checksum.o match.o syscall.o log.o backup.o
54 OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o \
610969d1 55 fileio.o batch.o clientname.o chmod.o
4a751edb
WD
56-OBJS3=progress.o pipe.o
57+OBJS3=progress.o pipe.o fnameconv.o
58 DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
59 popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
60 popt/popthelp.o popt/poptparse.o
6ba1be7d 61--- orig/cleanup.c 2005-03-05 18:58:38
9c015a83
WD
62+++ cleanup.c 2005-01-10 10:40:51
63@@ -25,6 +25,7 @@ extern int io_error;
4a751edb
WD
64 extern int keep_partial;
65 extern int log_got_error;
9c015a83 66 extern char *partial_dir;
4a751edb
WD
67+extern char *fname_convert_cmd;
68
69 /**
70 * Close all open sockets and files, allowing a (somewhat) graceful
d1cfcd41 71@@ -125,6 +126,8 @@ void _exit_cleanup(int code, const char
9c015a83 72 !partial_dir);
4a751edb
WD
73 }
74 io_flush(FULL_FLUSH);
75+ if (fname_convert_cmd)
76+ cleanup_fname_convert();
77 if (cleanup_fname)
78 do_unlink(cleanup_fname);
13bed3dd 79 if (code)
ba5669f0
WD
80--- orig/errcode.h 2005-09-26 17:09:27
81+++ errcode.h 2005-09-29 17:23:35
82@@ -36,6 +36,7 @@
4a751edb 83 #define RERR_IPC 14 /* error in IPC code */
ba5669f0
WD
84 #define RERR_CRASHED 15 /* sibling crashed */
85 #define RERR_TERMINATED 16 /* sibling terminated abnormally */
86+#define RERR_FNAMECONV 17 /* error in filename conversion */
4a751edb
WD
87
88 #define RERR_SIGNAL 20 /* status returned when sent SIGUSR1, SIGINT */
89 #define RERR_WAITCHILD 21 /* some error returned by waitpid() */
610969d1 90--- orig/flist.c 2005-11-07 04:29:01
d608ca23
WD
91+++ flist.c 2005-03-05 00:29:08
92@@ -57,6 +57,7 @@ extern int copy_unsafe_links;
e20f0bda
WD
93 extern int protocol_version;
94 extern int sanitize_paths;
52f25864 95 extern int orig_umask;
4a751edb 96+extern char *fname_convert_cmd;
d608ca23
WD
97 extern struct stats stats;
98 extern struct file_list *the_file_list;
e20f0bda 99
610969d1 100@@ -334,7 +335,10 @@ void send_file_entry(struct file_struct
4a751edb
WD
101
102 io_write_phase = "send_file_entry";
103
104- f_name_to(file, fname);
105+ if (fname_convert_cmd && !am_server) /* fname conversion always done on client */
106+ convert_fname(fname, f_name(file), MAXPATHLEN);
107+ else
108+ f_name_to(file, fname);
4a751edb
WD
109
110 flags = base_flags;
111
610969d1 112@@ -543,6 +547,9 @@ static struct file_struct *receive_file_
4a751edb
WD
113
114 strlcpy(lastname, thisname, MAXPATHLEN);
115
116+ if (fname_convert_cmd && !am_server) /* fname conversion always done on client */
117+ convert_fname(thisname, lastname, MAXPATHLEN);
118+
82358ffb 119 clean_fname(thisname, 0);
4a751edb
WD
120
121 if (sanitize_paths)
610969d1 122@@ -1073,6 +1080,9 @@ struct file_list *send_file_list(int f,
4a751edb 123 start_write = stats.total_written;
79f132a1 124 gettimeofday(&start_tv, NULL);
4a751edb
WD
125
126+ if (!am_server)
127+ init_fname_convert();
128+
fa7a1616 129 flist = flist_new(WITH_HLINK, "send_file_list");
4a751edb 130
fa7a1616 131 io_start_buffering_out();
610969d1 132@@ -1285,6 +1295,9 @@ struct file_list *send_file_list(int f,
fa7a1616
WD
133 stats.flist_size = stats.total_written - start_write;
134 stats.num_files = flist->count;
4a751edb
WD
135
136+ if (fname_convert_cmd && !am_server)
137+ cleanup_fname_convert();
138+
139 if (verbose > 3)
e20f0bda 140 output_flist(flist);
4a751edb 141
610969d1 142@@ -1305,6 +1318,9 @@ struct file_list *recv_file_list(int f)
4a751edb
WD
143
144 start_read = stats.total_read;
145
146+ if (fname_convert_cmd && !am_server)
147+ init_fname_convert();
148+
149 flist = flist_new(WITH_HLINK, "recv_file_list");
150
d608ca23 151 flist->count = 0;
610969d1 152@@ -1357,6 +1373,9 @@ struct file_list *recv_file_list(int f)
9be39c35 153 io_error |= read_int(f);
4a751edb
WD
154 }
155
156+ if (fname_convert_cmd && !am_server)
157+ cleanup_fname_convert();
158+
159 if (verbose > 3)
e20f0bda 160 output_flist(flist);
4a751edb 161
13bed3dd
WD
162--- orig/fnameconv.c 2004-07-02 21:38:59
163+++ fnameconv.c 2004-07-02 21:38:59
4a751edb
WD
164@@ -0,0 +1,220 @@
165+/* -*- c-file-style: "linux" -*-
166+ *
167+ * Copyright (C) 2004 by Eran Tromer
168+ *
169+ * This program is free software; you can redistribute it and/or modify
170+ * it under the terms of the GNU General Public License as published by
171+ * the Free Software Foundation; either version 2 of the License, or
172+ * (at your option) any later version.
173+ *
174+ * This program is distributed in the hope that it will be useful,
175+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
176+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
177+ * GNU General Public License for more details.
178+ *
179+ * You should have received a copy of the GNU General Public License
180+ * along with this program; if not, write to the Free Software
181+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
182+ */
183+
184+/* Handles filename conversion through an external process. Implements
185+ * two modes of operation:
186+ * In persistent mode, a single filename converter is kept running;
187+ * for each query we feed it a single line and read back a single
188+ * line. This will fail for programs that used buffered I/O, and will
189+ * get into a deadlock.
190+ * In non-persistent mode, a converter is invoked and killed for each
191+ * query. This has a very high overhead, but will work for any
192+ * program.
193+ * We start in persistence mode, and if we suspect a deadlock (i.e.,
194+ * nothing happens for FNAME_CONV_PERSISTENCE_TIMEOUT milliseconds)
195+ * then we smoothly fall back to non-persistent mode.
196+ *
197+ * Filename conversion errors are always considered fatal, since an
198+ * incorrectly named file could cause unpredictable damage.
199+ */
200+
201+#include <rsync.h>
202+
203+#define FNAME_CONV_PERSISTENCE_TIMEOUT 3000 /* milliseconds */
204+
205+static int conv_persistent = 1;
206+static pid_t conv_pid = -1;
207+static int conv_write_fd = -1, conv_read_fd;
208+extern char *fname_convert_cmd;
209+extern int blocking_io;
210+
211+/**
212+ * Splits cmd on spaces.
213+ */
214+static void split_on_spaces(char *cmd, char **parts) {
215+ int nparts = 0;
216+ char *tok;
217+ char *cmd2 = strdup(cmd);
218+ if (!cmd2) {
219+ rprintf(FERROR, "Out of memory while parsing filename filter %s\n", cmd);
220+ exit_cleanup(RERR_MALLOC);
221+ }
222+
223+ for (tok = strtok(cmd2, " "); tok; tok = strtok(NULL, " ")) {
224+ if (nparts >= MAX_ARGS) {
225+ rprintf(FERROR, "Filename conversion command is too long: %s\n", cmd);
226+ exit_cleanup(RERR_SYNTAX);
227+ }
228+ parts[nparts++] = tok;
229+ }
230+ parts[nparts] = NULL;
231+}
232+
233+
234+/**
235+ * Runs the filename converter process. Should be called before filename
fdd483f0 236+ * conversion begins (actually it's not necessarh, but it keeps the progress report
4a751edb
WD
237+ * nice and clean.
238+ **/
fdd483f0 239+void init_fname_convert(void)
4a751edb
WD
240+{
241+ if (fname_convert_cmd && conv_pid < 0) {
242+ char *args[MAX_ARGS];
243+
244+ if (verbose > 2)
245+ rprintf(FINFO, "Running filename converter: %s\n", fname_convert_cmd);
246+ split_on_spaces(fname_convert_cmd, args);
247+ /* Invoke child pipe with non-blocking IO and without registering it for
248+ * autocleanup (the latter may blow up the all_pids table, and is not needed
249+ * since we have our own cleanup handler. */
250+ conv_pid = piped_child(args, &conv_read_fd, &conv_write_fd, 0, 0);
251+ set_nonblocking(conv_write_fd);
252+ set_nonblocking(conv_read_fd);
253+ }
254+}
255+
256+/**
257+ * Kills the filename converter process. Should be called when the file
258+ * list creation is done. We assume that the converter will terminate
259+ * soon after its standard input is closed.
260+ **/
261+void cleanup_fname_convert()
262+{
263+ if (conv_pid >= 0) {
264+ int status;
265+ if (conv_write_fd >= 0) {
266+ close(conv_write_fd);
267+ conv_write_fd = -1;
268+ }
269+ close(conv_read_fd);
270+ waitpid(conv_pid, &status, 0);
271+ conv_pid = -1;
272+ }
273+}
274+
275+/**
276+ * Converts the filename from src into dest, using at most maxlen
277+ * characters of dest.
278+ **/
279+void convert_fname(char *dest, const char *src, unsigned int maxlen)
280+{
281+ int res;
282+ const char *srcp;
283+ char *destp;
284+ unsigned int srcrem, dstrem;
285+
286+ init_fname_convert();
287+
288+ /* Send and receive strings simultaneously to avoid deadlock: */
289+ srcrem = strlen(src)+1; /* chars left to send (incl. terminating LF) */
290+ dstrem = maxlen-1; /* free chars left in dest */
291+ srcp = src;
292+ destp = dest;
293+ while(1) {
294+ /* Write as much as possible: */
295+ if (srcrem > 1) {
296+ res = write(conv_write_fd, srcp, srcrem-1);
297+ if (res < 0 && errno != EAGAIN) {
298+ rprintf(FERROR, "Error writing to fname converter (filename: %s): %s\n", strerror(errno), src);
299+ exit_cleanup(RERR_FNAMECONV);
300+ }
301+ if (res > 0) { /* wrote something */
302+ srcp += res;
303+ srcrem -= res;
304+ }
305+ }
306+ if (srcrem == 1) { /* final LF */
307+ res = write(conv_write_fd, "\n", 1);
308+ if (res < 0 && errno != EAGAIN) {
309+ rprintf(FERROR, "Error writing to fname converter (filename: %s): %s\n", strerror(errno), src);
310+ exit_cleanup(RERR_FNAMECONV);
311+ }
312+ if (res > 0) { /* wrote final LF */
313+ srcrem = 0;
314+ if (!conv_persistent) {
315+ close(conv_write_fd);
316+ conv_write_fd = -1;
317+ }
318+ }
319+ }
320+
321+ /* Read as much as possible: */
322+ res = read(conv_read_fd, destp, dstrem);
323+ if (res < 0 && errno != EAGAIN) {
324+ rprintf(FERROR, "Error reading from filename converter (filename: %s):%s \n", strerror(errno), src);
325+ exit_cleanup(RERR_FNAMECONV);
326+ }
327+ if (res == 0) { /* EOF */
328+ rprintf(FERROR, "EOF from filename converter (filename: %s)\n", src);
329+ exit_cleanup(RERR_FNAMECONV);
330+ }
331+ if (res > 0) {
332+ destp += res;
333+ dstrem -= res;
334+ if (destp[-1] == '\n' || destp[-1] == '\r')
335+ break; /* End of line. Yippy! */
336+ if (dstrem == 0) {
337+ rprintf(FINFO, "Name converter output too long (filename: %s)\n", src);
338+ exit_cleanup(RERR_FNAMECONV);
339+ }
340+ }
341+
342+ /* Await activity */
343+ if (!await_fds(conv_read_fd, !srcrem ? -1 : conv_write_fd, FNAME_CONV_PERSISTENCE_TIMEOUT)) {
344+ if (srcrem == 0 && conv_persistent) {
345+ /* We finished writing but nothing happens. It looks like the converter program
346+ * is using buffered I/O and thus wait to read more input, but we can't give it
347+ * the next filename yet. Fall back to non-persistent mode. */
348+ if (verbose > 0)
349+ rprintf(FINFO, "Filename converter blocked, disabling persistence to recover.\n");
350+
351+ conv_persistent = 0;
352+ close(conv_write_fd);
353+ conv_write_fd = -1;
354+ }
355+ }
356+ }
357+
358+ /* Cleanup and sanity check */
359+ if (!conv_persistent)
360+ cleanup_fname_convert();
361+ if (srcrem > 0) {
362+ close(conv_write_fd);
363+ rprintf(FERROR, "Name converter produced output before reading all its input for file: %s\n", src);
364+ exit_cleanup(RERR_FNAMECONV);
365+ }
366+
367+ /* Chop newline chars */
368+ destp--;
369+ if (destp > dest && *destp == '\n')
370+ --destp;
371+ if (destp > dest && *destp == '\r')
372+ --destp;
373+ if (++destp == dest) {
374+ rprintf(FERROR, "Name converter output is empty (filename: %s)\n", src);
375+ exit_cleanup(RERR_FNAMECONV);
376+ }
377+ *destp = 0;
378+ /* Also, we may have a leading CR left over from a CRLF of the previous line */
379+ if (*dest == '\n')
380+ memmove(dest, dest+1, destp-dest-1);
381+
382+ if (verbose > 2)
383+ rprintf(FINFO, "Converted filename: %s -> %s\n", src, dest);
384+}
610969d1 385--- orig/generator.c 2005-10-30 22:30:28
d608ca23 386+++ generator.c 2005-03-05 00:29:37
cfd729e4 387@@ -594,7 +594,13 @@ static int phase = 0;
d1cfcd41 388 * start sending checksums.
4a751edb 389 *
d1cfcd41
WD
390 * Note that f_out is set to -1 when doing final directory-permission and
391- * modification-time repair. */
392+ * modification-time repair.
4a751edb
WD
393+ *
394+ * TODO: The filename seen in recv_generator is after filename
395+ * conversion. In verbose mode, directories, symlinks and device
396+ * files are printf()ed here but regular files are rprintf()ed on the
397+ * sender (unconverted). To solve the above, move all progress
d1cfcd41 398+ * reporting to the sender. */
d608ca23 399 static void recv_generator(char *fname, struct file_struct *file, int ndx,
e20f0bda 400 int itemizing, int maybe_PERMS_REPORT,
6ba1be7d 401 enum logcode code, int f_out)
cfd729e4 402--- orig/log.c 2005-10-26 16:49:08
ba5669f0
WD
403+++ log.c 2005-09-29 17:23:54
404@@ -67,6 +67,7 @@ struct {
4a751edb 405 { RERR_IPC , "error in IPC code" },
ba5669f0
WD
406 { RERR_CRASHED , "sibling process crashed" },
407 { RERR_TERMINATED , "sibling process terminated abnormally" },
4a751edb
WD
408+ { RERR_FNAMECONV , "error in filename conversion" },
409 { RERR_SIGNAL , "received SIGUSR1 or SIGINT" },
cfd729e4 410 { RERR_WAITCHILD , "waitpid() failed" },
4a751edb 411 { RERR_MALLOC , "error allocating core memory buffers" },
610969d1 412--- orig/main.c 2005-11-02 07:22:12
2f5fa77e 413+++ main.c 2004-07-22 00:31:47
610969d1 414@@ -388,7 +388,7 @@ static pid_t do_cmd(char *cmd, char *mac
9be39c35 415 whole_file = 1;
4a751edb 416 ret = local_child(argc, args, f_in, f_out, child_main);
2f5fa77e 417 } else
4a751edb
WD
418- ret = piped_child(args,f_in,f_out);
419+ ret = piped_child(args, f_in, f_out, blocking_io, 1);
4a751edb 420
6114c438 421 if (dir)
2f5fa77e 422 free(dir);
610969d1 423--- orig/options.c 2005-11-07 04:29:01
cfd729e4
WD
424+++ options.c 2005-10-26 16:49:33
425@@ -141,6 +141,7 @@ char *basis_dir[MAX_BASIS_DIRS+1];
b4023df5
WD
426 char *config_file = NULL;
427 char *shell_cmd = NULL;
428 char *log_format = NULL;
4a751edb 429+char *fname_convert_cmd = NULL;
b4023df5
WD
430 char *password_file = NULL;
431 char *rsync_path = RSYNC_PATH;
432 char *backup_dir = NULL;
610969d1 433@@ -329,6 +330,7 @@ void usage(enum logcode F)
79f132a1 434 rprintf(F," --compare-dest=DIR also compare destination files relative to DIR\n");
6ba1be7d 435 rprintf(F," --copy-dest=DIR ... and include copies of unchanged files\n");
0808daa5 436 rprintf(F," --link-dest=DIR hardlink to files in DIR when unchanged\n");
4a751edb 437+ rprintf(F," --fname-convert=CMD invoke CMD for filename conversion\n");
b78a6aba 438 rprintf(F," -z, --compress compress file data during the transfer\n");
cfd729e4 439 rprintf(F," --compress-level=NUM explicitly set compression level\n");
79f132a1 440 rprintf(F," -C, --cvs-exclude auto-ignore files the same way CVS does\n");
610969d1 441@@ -459,6 +461,7 @@ static struct poptOption long_options[]
6ba1be7d 442 {"copy-dest", 0, POPT_ARG_STRING, 0, OPT_COPY_DEST, 0, 0 },
0808daa5 443 {"link-dest", 0, POPT_ARG_STRING, 0, OPT_LINK_DEST, 0, 0 },
09fb8f03 444 {"fuzzy", 'y', POPT_ARG_NONE, &fuzzy_basis, 0, 0, 0 },
4a751edb 445+ {"fname-convert", 0, POPT_ARG_STRING, &fname_convert_cmd, 0, 0, 0 },
cfd729e4
WD
446 {"compress", 'z', POPT_ARG_NONE, 0, 'z', 0, 0 },
447 {"compress-level", 0, POPT_ARG_INT, &def_compress_level, 'z', 0, 0 },
489b0a72 448 {0, 'P', POPT_ARG_NONE, 0, 'P', 0, 0 },
cfd729e4 449--- orig/pipe.c 2005-10-24 21:04:45
13bed3dd 450+++ pipe.c 2004-07-03 20:18:02
4a751edb
WD
451@@ -23,7 +23,6 @@
452
453 extern int am_sender;
454 extern int am_server;
455-extern int blocking_io;
456 extern int orig_umask;
2f5fa77e 457 extern int filesfrom_fd;
c241c34c
WD
458
459@@ -39,8 +38,10 @@ extern int filesfrom_fd;
4a751edb
WD
460 * If blocking_io is set then use blocking io on both fds. That can be
461 * used to cope with badly broken rsh implementations like the one on
462 * Solaris.
463+ *
464+ * If register_child is nonzero then the child is registered for autocleanup.
465 **/
466-pid_t piped_child(char **command, int *f_in, int *f_out)
467+pid_t piped_child(char **command, int *f_in, int *f_out, int blocking_io, int register_child)
468 {
469 pid_t pid;
470 int to_child_pipe[2];
c241c34c 471@@ -55,7 +56,7 @@ pid_t piped_child(char **command, int *f
7628f156 472 exit_cleanup(RERR_IPC);
4a751edb
WD
473 }
474
4a751edb
WD
475- pid = do_fork();
476+ pid = register_child ? do_fork() : fork();
477 if (pid == -1) {
478 rsyserr(FERROR, errno, "fork");
479 exit_cleanup(RERR_IPC);
ba5669f0 480--- orig/syscall.c 2005-09-15 18:09:15
13bed3dd 481+++ syscall.c 2004-07-02 21:39:00
ba5669f0 482@@ -274,3 +274,34 @@ char *d_name(struct dirent *di)
4a751edb
WD
483 return di->d_name;
484 #endif
485 }
486+
487+/**
488+ * A wrapper around select(2) that guarantees Linux-like updating of
489+ * the timeout argument to contain the time left, so we can simply
490+ * re-invoke in case of EINTR or EAGAIN. On BSD, select(2) doesn't
491+ * change the timeout argument by itself.
492+ **/
493+int do_select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
494+{
495+ struct timeval intended, before, after;
496+ int result;
497+
1750a981 498+ if (timeout == NULL)
4a751edb
WD
499+ return select(n, readfds, writefds, exceptfds, timeout);
500+
501+ intended = *timeout;
502+ gettimeofday(&before, NULL);
503+ result = select(n, readfds, writefds, exceptfds, timeout);
504+ gettimeofday(&after, NULL);
505+ timeout->tv_sec = intended.tv_sec - (after.tv_sec - before.tv_sec);
506+ timeout->tv_usec = intended.tv_usec - (after.tv_usec - before.tv_usec);
507+ if (timeout->tv_usec >= 1000000) {
508+ ++timeout->tv_sec;
509+ timeout->tv_usec -= 1000000;
510+ } else if (timeout->tv_usec < 0) {
511+ --(timeout)->tv_sec;
512+ timeout->tv_usec += 1000000;
513+ }
514+
515+ return result;
516+}
cfd729e4 517--- orig/util.c 2005-10-16 22:38:40
13bed3dd 518+++ util.c 2004-07-03 20:18:02
cfd729e4 519@@ -1353,3 +1353,55 @@ uint32 fuzzy_distance(const char *s1, in
09fb8f03
WD
520
521 return a[len2-1];
4a751edb
WD
522 }
523+
524+/**
525+ * Blocks until one of the following happens:
526+ * - read_fd is nonnegative and has data to read
527+ * - write_fd is nonnegative and can be written to
528+ * - something terrible happened to either
529+ * - the timeout (in milliseconds) has elapsed
530+ * Return value is zero iff the timeout occured.
531+ */
532+char await_fds(int read_fd, int write_fd, int timeout_ms)
533+{
534+ fd_set read_fds, write_fds, except_fds;
535+ struct timeval tv;
536+ int res;
537+
6114c438
WD
538+ tv.tv_sec = timeout_ms / 1000;
539+ tv.tv_usec = (timeout_ms % 1000) * 1000;
4a751edb
WD
540+
541+ while (1) {
6114c438 542+ int maxfd = 0;
4a751edb
WD
543+ FD_ZERO(&read_fds);
544+ FD_ZERO(&write_fds);
545+ FD_ZERO(&except_fds);
546+ if (write_fd >= 0) {
547+ FD_SET(write_fd, &write_fds);
548+ FD_SET(write_fd, &except_fds);
6114c438
WD
549+ if (write_fd > maxfd)
550+ maxfd = write_fd;
4a751edb
WD
551+ }
552+ if (read_fd >= 0) {
553+ FD_SET(read_fd, &read_fds);
554+ FD_SET(read_fd, &except_fds);
6114c438
WD
555+ if (read_fd > maxfd)
556+ maxfd = read_fd;
4a751edb
WD
557+ }
558+
6114c438 559+ res = do_select(maxfd+1, &read_fds, &write_fds, &except_fds, &tv);
4a751edb
WD
560+ if (res > 0)
561+ return 1;
4a751edb 562+ if (res < 0) {
6114c438
WD
563+ if (errno == EINTR || errno == EAGAIN)
564+ continue; /* Retry */
4a751edb
WD
565+ rprintf(FERROR, "Error awaiting fname converter: %s\n", strerror(errno));
566+ exit_cleanup(RERR_FNAMECONV);
567+ }
6114c438
WD
568+ if (read_fd >= 0 && (FD_ISSET(read_fd, &read_fds) || FD_ISSET(read_fd, &except_fds)))
569+ return 1;
570+ if (write_fd >= 0 && (FD_ISSET(write_fd, &write_fds) || FD_ISSET(write_fd, &except_fds)))
571+ return 1;
4a751edb
WD
572+ return 0; /* res == 0 and no FDs set, hence a timeout. */
573+ }
574+}