Commit | Line | Data |
---|---|---|
4a751edb WD |
1 | Eran Tromer writes: |
2 | ||
3 | One feature missing from rsync, and requested on this list before, is | |
4 | on-the-fly conversion of filename character encoding. For example, I | |
5 | often 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 | |
7 | non-Unicode Win32 interface). Other circumstances surely abound. | |
8 | ||
9 | Attached is a patch against rsync 2.6.2 that adds an "--fname-convert" | |
10 | option. When the argument "--fname-convert CONV" is given, rsync pipes | |
11 | every filename through the program CONV, and filename presented to the | |
12 | server will be CONV's output instead of the raw filename. | |
13 | ||
14 | Artificial 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 | ||
20 | Perhaps the most useful case is using iconv: | |
21 | $ rsync --fname-convert 'iconv -f utf8 -t iso8859-8' ... | |
22 | ||
23 | I chose to allow invocation of arbitrary programs instead of using | |
24 | libiconv (or equivalent) in order to avoid external dependencies, and to | |
25 | offer more flexibility. The price is that some heuristics were needed to | |
26 | avoid the deadlock problems that tend to occur when filtering data | |
27 | through a program that uses buffered I/O -- see the comments at the top | |
28 | of the new file fnameconv.c. The delay you may have noticed in the above | |
29 | artificial example using "tr" is due to these heuristics; it occurs just | |
30 | once per rsync invocation, not for every file. | |
31 | ||
32 | I believe there are no server-side security implications, since all | |
33 | conversion is done at the client and the server is oblivious to it. On | |
34 | the client, conversion is done before sanitize_path() and besides, | |
35 | providing a sane converter program is the client's responsibility anyway. | |
36 | ||
37 | In verbose mode the updating of non-regular files is reported via | |
38 | rprintf() by the server, so the client will see the converted filename | |
39 | instead the raw filename -- see my comment in recv_generator(). Fixing | |
40 | this requires some delicate changes so I left it as is, but it seems | |
41 | like a minor concern. | |
42 | ||
43 | Most of the new code is in the new file fnameconv.c. The patch lightly | |
44 | touches some other files, mostly flist.c and the addition/extension of | |
6114c438 WD |
45 | some utility functions. |
46 | ||
47 | Note that you'll need to run 'make proto' after applying this patch. | |
4a751edb WD |
48 | |
49 | ||
0808daa5 | 50 | --- orig/Makefile.in 2004-11-03 11:56:03 |
13bed3dd | 51 | +++ Makefile.in 2004-07-03 20:18:02 |
4a751edb WD |
52 | @@ -35,7 +35,7 @@ OBJS1=rsync.o generator.o receiver.o cle |
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 \ | |
55 | fileio.o batch.o clientname.o | |
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 | |
9c015a83 WD |
61 | --- orig/cleanup.c 2005-01-10 09:46:11 |
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 | |
9c015a83 WD |
71 | @@ -126,6 +127,8 @@ void _exit_cleanup(int code, const char |
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 WD |
79 | if (code) |
80 | --- orig/errcode.h 2003-12-15 08:04:14 | |
81 | +++ errcode.h 2004-07-02 21:38:59 | |
4a751edb WD |
82 | @@ -34,6 +34,7 @@ |
83 | #define RERR_STREAMIO 12 /* error in rsync protocol data stream */ | |
84 | #define RERR_MESSAGEIO 13 /* errors with program diagnostics */ | |
85 | #define RERR_IPC 14 /* error in IPC code */ | |
86 | +#define RERR_FNAMECONV 15 /* error in filename conversion */ | |
87 | ||
88 | #define RERR_SIGNAL 20 /* status returned when sent SIGUSR1, SIGINT */ | |
89 | #define RERR_WAITCHILD 21 /* some error returned by waitpid() */ | |
be73a66e | 90 | --- orig/flist.c 2005-01-24 00:18:21 |
ba50e96c WD |
91 | +++ flist.c 2005-01-21 19:10:03 |
92 | @@ -64,6 +64,7 @@ extern int sanitize_paths; | |
93 | extern int delete_excluded; | |
94 | extern int orig_umask; | |
95 | extern int list_only; | |
4a751edb | 96 | +extern char *fname_convert_cmd; |
4a751edb | 97 | |
ba50e96c WD |
98 | extern struct exclude_list_struct exclude_list; |
99 | extern struct exclude_list_struct server_exclude_list; | |
a6587818 | 100 | @@ -359,7 +360,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 | ||
a6587818 | 112 | @@ -572,6 +576,9 @@ void receive_file_entry(struct file_stru |
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) | |
be73a66e | 122 | @@ -1087,6 +1094,9 @@ struct file_list *send_file_list(int f, |
4a751edb WD |
123 | |
124 | start_write = stats.total_written; | |
125 | ||
126 | + if (!am_server) | |
127 | + init_fname_convert(); | |
128 | + | |
129 | flist = flist_new(f == -1 ? WITHOUT_HLINK : WITH_HLINK, | |
be73a66e | 130 | "send_file_list"); |
4a751edb | 131 | |
be73a66e | 132 | @@ -1262,6 +1272,9 @@ struct file_list *send_file_list(int f, |
9be39c35 | 133 | stats.num_files = flist->count; |
4a751edb WD |
134 | } |
135 | ||
136 | + if (fname_convert_cmd && !am_server) | |
137 | + cleanup_fname_convert(); | |
138 | + | |
139 | if (verbose > 3) | |
ba50e96c | 140 | output_flist(flist, f < 0 ? "delete" : who_am_i()); |
4a751edb | 141 | |
be73a66e | 142 | @@ -1283,6 +1296,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"); | |
a6587818 | 150 | received_flist = flist; |
4a751edb | 151 | |
be73a66e | 152 | @@ -1336,6 +1352,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) | |
ba50e96c | 160 | output_flist(flist, who_am_i()); |
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 | |
236 | + * conversion begins (actually it's not necessarh, but it keeps the proress report | |
237 | + * nice and clean. | |
238 | + **/ | |
239 | +void init_fname_convert() | |
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 | +} | |
ba50e96c | 385 | --- orig/generator.c 2005-01-20 23:05:34 |
13bed3dd | 386 | +++ generator.c 2004-07-03 20:18:02 |
ba50e96c | 387 | @@ -232,6 +232,12 @@ static void generate_and_send_sums(int f |
4a751edb WD |
388 | * |
389 | * @note This comment was added later by mbp who was trying to work it | |
390 | * out. It might be wrong. | |
391 | + * | |
392 | + * TODO: The filename seen in recv_generator is after filename | |
393 | + * conversion. In verbose mode, directories, symlinks and device | |
394 | + * files are printf()ed here but regular files are rprintf()ed on the | |
395 | + * sender (unconverted). To solve the above, move all progress | |
396 | + * reporting to the sender. | |
8cec1ead WD |
397 | */ |
398 | static void recv_generator(char *fname, struct file_struct *file, int i, | |
0808daa5 | 399 | int f_out, int f_out_name) |
a6587818 | 400 | --- orig/log.c 2004-09-07 19:54:51 |
13bed3dd | 401 | +++ log.c 2004-07-03 20:18:02 |
4a751edb WD |
402 | @@ -57,6 +57,7 @@ struct { |
403 | { RERR_STREAMIO , "error in rsync protocol data stream" }, | |
404 | { RERR_MESSAGEIO , "errors with program diagnostics" }, | |
405 | { RERR_IPC , "error in IPC code" }, | |
406 | + { RERR_FNAMECONV , "error in filename conversion" }, | |
407 | { RERR_SIGNAL , "received SIGUSR1 or SIGINT" }, | |
408 | { RERR_WAITCHILD , "some error returned by waitpid()" }, | |
409 | { RERR_MALLOC , "error allocating core memory buffers" }, | |
be73a66e | 410 | --- orig/main.c 2005-01-23 07:27:24 |
2f5fa77e | 411 | +++ main.c 2004-07-22 00:31:47 |
be73a66e | 412 | @@ -335,7 +335,7 @@ static pid_t do_cmd(char *cmd, char *mac |
9be39c35 | 413 | whole_file = 1; |
4a751edb | 414 | ret = local_child(argc, args, f_in, f_out, child_main); |
2f5fa77e | 415 | } else |
4a751edb WD |
416 | - ret = piped_child(args,f_in,f_out); |
417 | + ret = piped_child(args, f_in, f_out, blocking_io, 1); | |
4a751edb | 418 | |
6114c438 | 419 | if (dir) |
2f5fa77e | 420 | free(dir); |
be73a66e | 421 | --- orig/options.c 2005-01-24 00:18:21 |
b4023df5 | 422 | +++ options.c 2005-01-15 21:28:07 |
be73a66e | 423 | @@ -127,6 +127,7 @@ char *basis_dir[MAX_BASIS_DIRS+1]; |
b4023df5 WD |
424 | char *config_file = NULL; |
425 | char *shell_cmd = NULL; | |
426 | char *log_format = NULL; | |
4a751edb | 427 | +char *fname_convert_cmd = NULL; |
b4023df5 WD |
428 | char *password_file = NULL; |
429 | char *rsync_path = RSYNC_PATH; | |
430 | char *backup_dir = NULL; | |
be73a66e | 431 | @@ -292,6 +293,7 @@ void usage(enum logcode F) |
0808daa5 WD |
432 | rprintf(F," --copy-dest=DIR ... and include copies of unchanged files\n"); |
433 | rprintf(F," --link-dest=DIR hardlink to files in DIR when unchanged\n"); | |
4a751edb WD |
434 | rprintf(F," -P equivalent to --partial --progress\n"); |
435 | + rprintf(F," --fname-convert=CMD invoke CMD for filename conversion\n"); | |
436 | rprintf(F," -z, --compress compress file data\n"); | |
437 | rprintf(F," -C, --cvs-exclude auto ignore files in the same way CVS does\n"); | |
438 | rprintf(F," --exclude=PATTERN exclude files matching PATTERN\n"); | |
be73a66e | 439 | @@ -389,6 +391,7 @@ static struct poptOption long_options[] |
0808daa5 WD |
440 | {"compare-dest", 0, POPT_ARG_STRING, 0, OPT_COMPARE_DEST, 0, 0 }, |
441 | {"copy-dest", 0, POPT_ARG_STRING, 0, OPT_COPY_DEST, 0, 0 }, | |
442 | {"link-dest", 0, POPT_ARG_STRING, 0, OPT_LINK_DEST, 0, 0 }, | |
4a751edb WD |
443 | + {"fname-convert", 0, POPT_ARG_STRING, &fname_convert_cmd, 0, 0, 0 }, |
444 | /* TODO: Should this take an optional int giving the compression level? */ | |
445 | {"compress", 'z', POPT_ARG_NONE, &do_compression, 0, 0, 0 }, | |
cc6497db | 446 | {"stats", 0, POPT_ARG_NONE, &do_stats, 0, 0, 0 }, |
82358ffb | 447 | --- orig/pipe.c 2004-08-12 18:34:38 |
13bed3dd | 448 | +++ pipe.c 2004-07-03 20:18:02 |
4a751edb WD |
449 | @@ -23,7 +23,6 @@ |
450 | ||
451 | extern int am_sender; | |
452 | extern int am_server; | |
453 | -extern int blocking_io; | |
454 | extern int orig_umask; | |
9be39c35 | 455 | extern int write_batch; |
2f5fa77e WD |
456 | extern int filesfrom_fd; |
457 | @@ -40,8 +39,10 @@ extern int filesfrom_fd; | |
4a751edb WD |
458 | * If blocking_io is set then use blocking io on both fds. That can be |
459 | * used to cope with badly broken rsh implementations like the one on | |
460 | * Solaris. | |
461 | + * | |
462 | + * If register_child is nonzero then the child is registered for autocleanup. | |
463 | **/ | |
464 | -pid_t piped_child(char **command, int *f_in, int *f_out) | |
465 | +pid_t piped_child(char **command, int *f_in, int *f_out, int blocking_io, int register_child) | |
466 | { | |
467 | pid_t pid; | |
468 | int to_child_pipe[2]; | |
2f5fa77e | 469 | @@ -56,7 +57,7 @@ pid_t piped_child(char **command, int *f |
7628f156 | 470 | exit_cleanup(RERR_IPC); |
4a751edb WD |
471 | } |
472 | ||
4a751edb WD |
473 | - pid = do_fork(); |
474 | + pid = register_child ? do_fork() : fork(); | |
475 | if (pid == -1) { | |
476 | rsyserr(FERROR, errno, "fork"); | |
477 | exit_cleanup(RERR_IPC); | |
ba50e96c | 478 | --- orig/syscall.c 2005-01-19 20:11:10 |
13bed3dd | 479 | +++ syscall.c 2004-07-02 21:39:00 |
ba50e96c | 480 | @@ -270,3 +270,34 @@ char *d_name(struct dirent *di) |
4a751edb WD |
481 | return di->d_name; |
482 | #endif | |
483 | } | |
484 | + | |
485 | +/** | |
486 | + * A wrapper around select(2) that guarantees Linux-like updating of | |
487 | + * the timeout argument to contain the time left, so we can simply | |
488 | + * re-invoke in case of EINTR or EAGAIN. On BSD, select(2) doesn't | |
489 | + * change the timeout argument by itself. | |
490 | + **/ | |
491 | +int do_select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) | |
492 | +{ | |
493 | + struct timeval intended, before, after; | |
494 | + int result; | |
495 | + | |
1750a981 | 496 | + if (timeout == NULL) |
4a751edb WD |
497 | + return select(n, readfds, writefds, exceptfds, timeout); |
498 | + | |
499 | + intended = *timeout; | |
500 | + gettimeofday(&before, NULL); | |
501 | + result = select(n, readfds, writefds, exceptfds, timeout); | |
502 | + gettimeofday(&after, NULL); | |
503 | + timeout->tv_sec = intended.tv_sec - (after.tv_sec - before.tv_sec); | |
504 | + timeout->tv_usec = intended.tv_usec - (after.tv_usec - before.tv_usec); | |
505 | + if (timeout->tv_usec >= 1000000) { | |
506 | + ++timeout->tv_sec; | |
507 | + timeout->tv_usec -= 1000000; | |
508 | + } else if (timeout->tv_usec < 0) { | |
509 | + --(timeout)->tv_sec; | |
510 | + timeout->tv_usec += 1000000; | |
511 | + } | |
512 | + | |
513 | + return result; | |
514 | +} | |
ba50e96c | 515 | --- orig/util.c 2005-01-20 23:05:34 |
13bed3dd | 516 | +++ util.c 2004-07-03 20:18:02 |
ba50e96c | 517 | @@ -1213,3 +1213,55 @@ void *_realloc_array(void *ptr, unsigned |
4a751edb WD |
518 | return malloc(size * num); |
519 | return realloc(ptr, size * num); | |
520 | } | |
521 | + | |
522 | +/** | |
523 | + * Blocks until one of the following happens: | |
524 | + * - read_fd is nonnegative and has data to read | |
525 | + * - write_fd is nonnegative and can be written to | |
526 | + * - something terrible happened to either | |
527 | + * - the timeout (in milliseconds) has elapsed | |
528 | + * Return value is zero iff the timeout occured. | |
529 | + */ | |
530 | +char await_fds(int read_fd, int write_fd, int timeout_ms) | |
531 | +{ | |
532 | + fd_set read_fds, write_fds, except_fds; | |
533 | + struct timeval tv; | |
534 | + int res; | |
535 | + | |
6114c438 WD |
536 | + tv.tv_sec = timeout_ms / 1000; |
537 | + tv.tv_usec = (timeout_ms % 1000) * 1000; | |
4a751edb WD |
538 | + |
539 | + while (1) { | |
6114c438 | 540 | + int maxfd = 0; |
4a751edb WD |
541 | + FD_ZERO(&read_fds); |
542 | + FD_ZERO(&write_fds); | |
543 | + FD_ZERO(&except_fds); | |
544 | + if (write_fd >= 0) { | |
545 | + FD_SET(write_fd, &write_fds); | |
546 | + FD_SET(write_fd, &except_fds); | |
6114c438 WD |
547 | + if (write_fd > maxfd) |
548 | + maxfd = write_fd; | |
4a751edb WD |
549 | + } |
550 | + if (read_fd >= 0) { | |
551 | + FD_SET(read_fd, &read_fds); | |
552 | + FD_SET(read_fd, &except_fds); | |
6114c438 WD |
553 | + if (read_fd > maxfd) |
554 | + maxfd = read_fd; | |
4a751edb WD |
555 | + } |
556 | + | |
6114c438 | 557 | + res = do_select(maxfd+1, &read_fds, &write_fds, &except_fds, &tv); |
4a751edb WD |
558 | + if (res > 0) |
559 | + return 1; | |
4a751edb | 560 | + if (res < 0) { |
6114c438 WD |
561 | + if (errno == EINTR || errno == EAGAIN) |
562 | + continue; /* Retry */ | |
4a751edb WD |
563 | + rprintf(FERROR, "Error awaiting fname converter: %s\n", strerror(errno)); |
564 | + exit_cleanup(RERR_FNAMECONV); | |
565 | + } | |
6114c438 WD |
566 | + if (read_fd >= 0 && (FD_ISSET(read_fd, &read_fds) || FD_ISSET(read_fd, &except_fds))) |
567 | + return 1; | |
568 | + if (write_fd >= 0 && (FD_ISSET(write_fd, &write_fds) || FD_ISSET(write_fd, &except_fds))) | |
569 | + return 1; | |
4a751edb WD |
570 | + return 0; /* res == 0 and no FDs set, hence a timeout. */ |
571 | + } | |
572 | +} |