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 | |
45 | some utility functions. I took the opportunity to fix an argument | |
46 | parsing buffer overflow bug in main.c. Note that you'll need to run | |
47 | autoconf and 'make proto'. | |
48 | ||
49 | ||
50 | --- Makefile.in 15 May 2004 00:48:11 -0000 1.101 | |
51 | +++ Makefile.in 29 May 2004 21:31:45 -0000 | |
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 | |
61 | --- cleanup.c 13 May 2004 07:08:18 -0000 1.22 | |
62 | +++ cleanup.c 29 May 2004 21:31:45 -0000 | |
63 | @@ -24,6 +24,7 @@ | |
64 | extern int io_error; | |
65 | extern int keep_partial; | |
66 | extern int log_got_error; | |
67 | +extern char *fname_convert_cmd; | |
68 | ||
69 | /** | |
70 | * Close all open sockets and files, allowing a (somewhat) graceful | |
71 | @@ -121,6 +122,8 @@ void _exit_cleanup(int code, const char | |
72 | finish_transfer(cleanup_new_fname, fname, cleanup_file, 0); | |
73 | } | |
74 | io_flush(FULL_FLUSH); | |
75 | + if (fname_convert_cmd) | |
76 | + cleanup_fname_convert(); | |
77 | if (cleanup_fname) | |
78 | do_unlink(cleanup_fname); | |
79 | if (code) { | |
80 | --- errcode.h 15 Dec 2003 08:04:14 -0000 1.8 | |
81 | +++ errcode.h 29 May 2004 21:31:45 -0000 | |
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() */ | |
90 | --- flist.c 29 May 2004 21:21:17 -0000 1.226 | |
91 | +++ flist.c 29 May 2004 21:31:46 -0000 | |
92 | @@ -43,6 +43,7 @@ extern int cvs_exclude; | |
93 | ||
94 | extern int recurse; | |
95 | extern char curr_dir[MAXPATHLEN]; | |
96 | +extern char *fname_convert_cmd; | |
97 | extern char *files_from; | |
98 | extern int filesfrom_fd; | |
99 | ||
100 | @@ -346,7 +347,11 @@ void send_file_entry(struct file_struct | |
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); | |
109 | + | |
110 | ||
111 | flags = base_flags; | |
112 | ||
113 | @@ -559,6 +564,9 @@ void receive_file_entry(struct file_stru | |
114 | ||
115 | strlcpy(lastname, thisname, MAXPATHLEN); | |
116 | ||
117 | + if (fname_convert_cmd && !am_server) /* fname conversion always done on client */ | |
118 | + convert_fname(thisname, lastname, MAXPATHLEN); | |
119 | + | |
120 | clean_fname(thisname); | |
121 | ||
122 | if (sanitize_paths) | |
123 | @@ -1041,6 +1049,9 @@ struct file_list *send_file_list(int f, | |
124 | ||
125 | start_write = stats.total_written; | |
126 | ||
127 | + if (!am_server) | |
128 | + init_fname_convert(); | |
129 | + | |
130 | flist = flist_new(f == -1 ? WITHOUT_HLINK : WITH_HLINK, | |
131 | "send_file_list"); | |
132 | ||
133 | @@ -1215,6 +1226,9 @@ struct file_list *send_file_list(int f, | |
134 | write_batch_flist_info(flist->count, flist->files); | |
135 | } | |
136 | ||
137 | + if (fname_convert_cmd && !am_server) | |
138 | + cleanup_fname_convert(); | |
139 | + | |
140 | if (verbose > 3) | |
141 | output_flist(flist); | |
142 | ||
143 | @@ -1237,6 +1251,9 @@ struct file_list *recv_file_list(int f) | |
144 | ||
145 | start_read = stats.total_read; | |
146 | ||
147 | + if (fname_convert_cmd && !am_server) | |
148 | + init_fname_convert(); | |
149 | + | |
150 | flist = flist_new(WITH_HLINK, "recv_file_list"); | |
151 | ||
152 | flist->count = 0; | |
153 | @@ -1291,6 +1308,9 @@ struct file_list *recv_file_list(int f) | |
154 | } | |
155 | } | |
156 | ||
157 | + if (fname_convert_cmd && !am_server) | |
158 | + cleanup_fname_convert(); | |
159 | + | |
160 | if (verbose > 3) | |
161 | output_flist(flist); | |
162 | ||
163 | --- /dev/null 1 Jan 1970 00:00:00 -0000 | |
164 | +++ fnameconv.c 29 May 2004 21:31:46 -0000 | |
165 | @@ -0,0 +1,220 @@ | |
166 | +/* -*- c-file-style: "linux" -*- | |
167 | + * | |
168 | + * Copyright (C) 2004 by Eran Tromer | |
169 | + * | |
170 | + * This program is free software; you can redistribute it and/or modify | |
171 | + * it under the terms of the GNU General Public License as published by | |
172 | + * the Free Software Foundation; either version 2 of the License, or | |
173 | + * (at your option) any later version. | |
174 | + * | |
175 | + * This program is distributed in the hope that it will be useful, | |
176 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
177 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
178 | + * GNU General Public License for more details. | |
179 | + * | |
180 | + * You should have received a copy of the GNU General Public License | |
181 | + * along with this program; if not, write to the Free Software | |
182 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
183 | + */ | |
184 | + | |
185 | +/* Handles filename conversion through an external process. Implements | |
186 | + * two modes of operation: | |
187 | + * In persistent mode, a single filename converter is kept running; | |
188 | + * for each query we feed it a single line and read back a single | |
189 | + * line. This will fail for programs that used buffered I/O, and will | |
190 | + * get into a deadlock. | |
191 | + * In non-persistent mode, a converter is invoked and killed for each | |
192 | + * query. This has a very high overhead, but will work for any | |
193 | + * program. | |
194 | + * We start in persistence mode, and if we suspect a deadlock (i.e., | |
195 | + * nothing happens for FNAME_CONV_PERSISTENCE_TIMEOUT milliseconds) | |
196 | + * then we smoothly fall back to non-persistent mode. | |
197 | + * | |
198 | + * Filename conversion errors are always considered fatal, since an | |
199 | + * incorrectly named file could cause unpredictable damage. | |
200 | + */ | |
201 | + | |
202 | +#include <rsync.h> | |
203 | + | |
204 | +#define FNAME_CONV_PERSISTENCE_TIMEOUT 3000 /* milliseconds */ | |
205 | + | |
206 | +static int conv_persistent = 1; | |
207 | +static pid_t conv_pid = -1; | |
208 | +static int conv_write_fd = -1, conv_read_fd; | |
209 | +extern char *fname_convert_cmd; | |
210 | +extern int blocking_io; | |
211 | + | |
212 | +/** | |
213 | + * Splits cmd on spaces. | |
214 | + */ | |
215 | +static void split_on_spaces(char *cmd, char **parts) { | |
216 | + int nparts = 0; | |
217 | + char *tok; | |
218 | + char *cmd2 = strdup(cmd); | |
219 | + if (!cmd2) { | |
220 | + rprintf(FERROR, "Out of memory while parsing filename filter %s\n", cmd); | |
221 | + exit_cleanup(RERR_MALLOC); | |
222 | + } | |
223 | + | |
224 | + for (tok = strtok(cmd2, " "); tok; tok = strtok(NULL, " ")) { | |
225 | + if (nparts >= MAX_ARGS) { | |
226 | + rprintf(FERROR, "Filename conversion command is too long: %s\n", cmd); | |
227 | + exit_cleanup(RERR_SYNTAX); | |
228 | + } | |
229 | + parts[nparts++] = tok; | |
230 | + } | |
231 | + parts[nparts] = NULL; | |
232 | +} | |
233 | + | |
234 | + | |
235 | +/** | |
236 | + * Runs the filename converter process. Should be called before filename | |
237 | + * conversion begins (actually it's not necessarh, but it keeps the proress report | |
238 | + * nice and clean. | |
239 | + **/ | |
240 | +void init_fname_convert() | |
241 | +{ | |
242 | + if (fname_convert_cmd && conv_pid < 0) { | |
243 | + char *args[MAX_ARGS]; | |
244 | + | |
245 | + if (verbose > 2) | |
246 | + rprintf(FINFO, "Running filename converter: %s\n", fname_convert_cmd); | |
247 | + split_on_spaces(fname_convert_cmd, args); | |
248 | + /* Invoke child pipe with non-blocking IO and without registering it for | |
249 | + * autocleanup (the latter may blow up the all_pids table, and is not needed | |
250 | + * since we have our own cleanup handler. */ | |
251 | + conv_pid = piped_child(args, &conv_read_fd, &conv_write_fd, 0, 0); | |
252 | + set_nonblocking(conv_write_fd); | |
253 | + set_nonblocking(conv_read_fd); | |
254 | + } | |
255 | +} | |
256 | + | |
257 | +/** | |
258 | + * Kills the filename converter process. Should be called when the file | |
259 | + * list creation is done. We assume that the converter will terminate | |
260 | + * soon after its standard input is closed. | |
261 | + **/ | |
262 | +void cleanup_fname_convert() | |
263 | +{ | |
264 | + if (conv_pid >= 0) { | |
265 | + int status; | |
266 | + if (conv_write_fd >= 0) { | |
267 | + close(conv_write_fd); | |
268 | + conv_write_fd = -1; | |
269 | + } | |
270 | + close(conv_read_fd); | |
271 | + waitpid(conv_pid, &status, 0); | |
272 | + conv_pid = -1; | |
273 | + } | |
274 | +} | |
275 | + | |
276 | +/** | |
277 | + * Converts the filename from src into dest, using at most maxlen | |
278 | + * characters of dest. | |
279 | + **/ | |
280 | +void convert_fname(char *dest, const char *src, unsigned int maxlen) | |
281 | +{ | |
282 | + int res; | |
283 | + const char *srcp; | |
284 | + char *destp; | |
285 | + unsigned int srcrem, dstrem; | |
286 | + | |
287 | + init_fname_convert(); | |
288 | + | |
289 | + /* Send and receive strings simultaneously to avoid deadlock: */ | |
290 | + srcrem = strlen(src)+1; /* chars left to send (incl. terminating LF) */ | |
291 | + dstrem = maxlen-1; /* free chars left in dest */ | |
292 | + srcp = src; | |
293 | + destp = dest; | |
294 | + while(1) { | |
295 | + /* Write as much as possible: */ | |
296 | + if (srcrem > 1) { | |
297 | + res = write(conv_write_fd, srcp, srcrem-1); | |
298 | + if (res < 0 && errno != EAGAIN) { | |
299 | + rprintf(FERROR, "Error writing to fname converter (filename: %s): %s\n", strerror(errno), src); | |
300 | + exit_cleanup(RERR_FNAMECONV); | |
301 | + } | |
302 | + if (res > 0) { /* wrote something */ | |
303 | + srcp += res; | |
304 | + srcrem -= res; | |
305 | + } | |
306 | + } | |
307 | + if (srcrem == 1) { /* final LF */ | |
308 | + res = write(conv_write_fd, "\n", 1); | |
309 | + if (res < 0 && errno != EAGAIN) { | |
310 | + rprintf(FERROR, "Error writing to fname converter (filename: %s): %s\n", strerror(errno), src); | |
311 | + exit_cleanup(RERR_FNAMECONV); | |
312 | + } | |
313 | + if (res > 0) { /* wrote final LF */ | |
314 | + srcrem = 0; | |
315 | + if (!conv_persistent) { | |
316 | + close(conv_write_fd); | |
317 | + conv_write_fd = -1; | |
318 | + } | |
319 | + } | |
320 | + } | |
321 | + | |
322 | + /* Read as much as possible: */ | |
323 | + res = read(conv_read_fd, destp, dstrem); | |
324 | + if (res < 0 && errno != EAGAIN) { | |
325 | + rprintf(FERROR, "Error reading from filename converter (filename: %s):%s \n", strerror(errno), src); | |
326 | + exit_cleanup(RERR_FNAMECONV); | |
327 | + } | |
328 | + if (res == 0) { /* EOF */ | |
329 | + rprintf(FERROR, "EOF from filename converter (filename: %s)\n", src); | |
330 | + exit_cleanup(RERR_FNAMECONV); | |
331 | + } | |
332 | + if (res > 0) { | |
333 | + destp += res; | |
334 | + dstrem -= res; | |
335 | + if (destp[-1] == '\n' || destp[-1] == '\r') | |
336 | + break; /* End of line. Yippy! */ | |
337 | + if (dstrem == 0) { | |
338 | + rprintf(FINFO, "Name converter output too long (filename: %s)\n", src); | |
339 | + exit_cleanup(RERR_FNAMECONV); | |
340 | + } | |
341 | + } | |
342 | + | |
343 | + /* Await activity */ | |
344 | + if (!await_fds(conv_read_fd, !srcrem ? -1 : conv_write_fd, FNAME_CONV_PERSISTENCE_TIMEOUT)) { | |
345 | + if (srcrem == 0 && conv_persistent) { | |
346 | + /* We finished writing but nothing happens. It looks like the converter program | |
347 | + * is using buffered I/O and thus wait to read more input, but we can't give it | |
348 | + * the next filename yet. Fall back to non-persistent mode. */ | |
349 | + if (verbose > 0) | |
350 | + rprintf(FINFO, "Filename converter blocked, disabling persistence to recover.\n"); | |
351 | + | |
352 | + conv_persistent = 0; | |
353 | + close(conv_write_fd); | |
354 | + conv_write_fd = -1; | |
355 | + } | |
356 | + } | |
357 | + } | |
358 | + | |
359 | + /* Cleanup and sanity check */ | |
360 | + if (!conv_persistent) | |
361 | + cleanup_fname_convert(); | |
362 | + if (srcrem > 0) { | |
363 | + close(conv_write_fd); | |
364 | + rprintf(FERROR, "Name converter produced output before reading all its input for file: %s\n", src); | |
365 | + exit_cleanup(RERR_FNAMECONV); | |
366 | + } | |
367 | + | |
368 | + /* Chop newline chars */ | |
369 | + destp--; | |
370 | + if (destp > dest && *destp == '\n') | |
371 | + --destp; | |
372 | + if (destp > dest && *destp == '\r') | |
373 | + --destp; | |
374 | + if (++destp == dest) { | |
375 | + rprintf(FERROR, "Name converter output is empty (filename: %s)\n", src); | |
376 | + exit_cleanup(RERR_FNAMECONV); | |
377 | + } | |
378 | + *destp = 0; | |
379 | + /* Also, we may have a leading CR left over from a CRLF of the previous line */ | |
380 | + if (*dest == '\n') | |
381 | + memmove(dest, dest+1, destp-dest-1); | |
382 | + | |
383 | + if (verbose > 2) | |
384 | + rprintf(FINFO, "Converted filename: %s -> %s\n", src, dest); | |
385 | +} | |
386 | --- generator.c 18 May 2004 08:50:17 -0000 1.85 | |
387 | +++ generator.c 29 May 2004 21:31:46 -0000 | |
388 | @@ -267,6 +267,12 @@ static void generate_and_send_sums(struc | |
389 | * | |
390 | * @note This comment was added later by mbp who was trying to work it | |
391 | * out. It might be wrong. | |
392 | + * | |
393 | + * TODO: The filename seen in recv_generator is after filename | |
394 | + * conversion. In verbose mode, directories, symlinks and device | |
395 | + * files are printf()ed here but regular files are rprintf()ed on the | |
396 | + * sender (unconverted). To solve the above, move all progress | |
397 | + * reporting to the sender. | |
398 | **/ | |
399 | void recv_generator(char *fname, struct file_struct *file, int i, int f_out) | |
400 | { | |
401 | --- log.c 15 May 2004 19:31:16 -0000 1.73 | |
402 | +++ log.c 29 May 2004 21:31:47 -0000 | |
403 | @@ -57,6 +57,7 @@ struct { | |
404 | { RERR_STREAMIO , "error in rsync protocol data stream" }, | |
405 | { RERR_MESSAGEIO , "errors with program diagnostics" }, | |
406 | { RERR_IPC , "error in IPC code" }, | |
407 | + { RERR_FNAMECONV , "error in filename conversion" }, | |
408 | { RERR_SIGNAL , "received SIGUSR1 or SIGINT" }, | |
409 | { RERR_WAITCHILD , "some error returned by waitpid()" }, | |
410 | { RERR_MALLOC , "error allocating core memory buffers" }, | |
411 | --- main.c 19 May 2004 22:19:19 -0000 1.195 | |
412 | +++ main.c 29 May 2004 21:31:47 -0000 | |
413 | @@ -217,7 +217,7 @@ static pid_t do_cmd(char *cmd, char *mac | |
414 | int *f_in, int *f_out) | |
415 | { | |
416 | int i, argc = 0; | |
417 | - char *args[100]; | |
418 | + char *args[MAX_ARGS]; | |
419 | pid_t ret; | |
420 | char *tok, *dir = NULL; | |
421 | int dash_l_set = 0; | |
422 | @@ -232,8 +232,13 @@ static pid_t do_cmd(char *cmd, char *mac | |
423 | if (!cmd) | |
424 | goto oom; | |
425 | ||
426 | - for (tok = strtok(cmd, " "); tok; tok = strtok(NULL, " ")) | |
427 | + for (tok = strtok(cmd, " "); tok; tok = strtok(NULL, " ")) { | |
428 | + if (argc >= MAX_ARGS) { | |
429 | + rprintf(FERROR, "Command is too long\n"); | |
430 | + exit_cleanup(RERR_SYNTAX); | |
431 | + } | |
432 | args[argc++] = tok; | |
433 | + } | |
434 | ||
435 | /* check to see if we've already been given '-l user' in | |
436 | * the remote-shell command */ | |
437 | @@ -296,7 +301,7 @@ static pid_t do_cmd(char *cmd, char *mac | |
438 | create_flist_from_batch(); /* sets batch_flist */ | |
439 | ret = local_child(argc, args, f_in, f_out, child_main); | |
440 | } else { | |
441 | - ret = piped_child(args,f_in,f_out); | |
442 | + ret = piped_child(args, f_in, f_out, blocking_io, 1); | |
443 | } | |
444 | ||
445 | if (dir) free(dir); | |
446 | --- options.c 27 May 2004 21:51:53 -0000 1.153 | |
447 | +++ options.c 29 May 2004 21:31:48 -0000 | |
448 | @@ -125,6 +125,7 @@ char *backup_dir = NULL; | |
449 | char backup_dir_buf[MAXPATHLEN]; | |
450 | int rsync_port = RSYNC_PORT; | |
451 | int link_dest = 0; | |
452 | +char *fname_convert_cmd = NULL; | |
453 | ||
454 | int verbose = 0; | |
455 | int quiet = 0; | |
456 | @@ -268,6 +269,7 @@ void usage(enum logcode F) | |
457 | rprintf(F," --compare-dest=DIR also compare destination files relative to DIR\n"); | |
458 | rprintf(F," --link-dest=DIR create hardlinks to DIR for unchanged files\n"); | |
459 | rprintf(F," -P equivalent to --partial --progress\n"); | |
460 | + rprintf(F," --fname-convert=CMD invoke CMD for filename conversion\n"); | |
461 | rprintf(F," -z, --compress compress file data\n"); | |
462 | rprintf(F," -C, --cvs-exclude auto ignore files in the same way CVS does\n"); | |
463 | rprintf(F," --exclude=PATTERN exclude files matching PATTERN\n"); | |
464 | @@ -364,6 +366,7 @@ static struct poptOption long_options[] | |
465 | {"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 }, | |
466 | {"compare-dest", 0, POPT_ARG_STRING, &compare_dest, 0, 0, 0 }, | |
467 | {"link-dest", 0, POPT_ARG_STRING, &compare_dest, OPT_LINK_DEST, 0, 0 }, | |
468 | + {"fname-convert", 0, POPT_ARG_STRING, &fname_convert_cmd, 0, 0, 0 }, | |
469 | /* TODO: Should this take an optional int giving the compression level? */ | |
470 | {"compress", 'z', POPT_ARG_NONE, &do_compression, 0, 0, 0 }, | |
471 | {"daemon", 0, POPT_ARG_NONE, &daemon_opt, 0, 0, 0 }, | |
472 | --- pipe.c 15 May 2004 19:31:10 -0000 1.7 | |
473 | +++ pipe.c 29 May 2004 21:31:48 -0000 | |
474 | @@ -23,7 +23,6 @@ | |
475 | ||
476 | extern int am_sender; | |
477 | extern int am_server; | |
478 | -extern int blocking_io; | |
479 | extern int orig_umask; | |
480 | extern int read_batch; | |
481 | extern int filesfrom_fd; | |
482 | @@ -40,8 +39,10 @@ extern int filesfrom_fd; | |
483 | * If blocking_io is set then use blocking io on both fds. That can be | |
484 | * used to cope with badly broken rsh implementations like the one on | |
485 | * Solaris. | |
486 | + * | |
487 | + * If register_child is nonzero then the child is registered for autocleanup. | |
488 | **/ | |
489 | -pid_t piped_child(char **command, int *f_in, int *f_out) | |
490 | +pid_t piped_child(char **command, int *f_in, int *f_out, int blocking_io, int register_child) | |
491 | { | |
492 | pid_t pid; | |
493 | int to_child_pipe[2]; | |
494 | @@ -57,7 +58,7 @@ pid_t piped_child(char **command, int *f | |
495 | } | |
496 | ||
497 | ||
498 | - pid = do_fork(); | |
499 | + pid = register_child ? do_fork() : fork(); | |
500 | if (pid == -1) { | |
501 | rsyserr(FERROR, errno, "fork"); | |
502 | exit_cleanup(RERR_IPC); | |
503 | --- syscall.c 18 Feb 2004 22:33:21 -0000 1.30 | |
504 | +++ syscall.c 29 May 2004 21:31:48 -0000 | |
505 | @@ -231,3 +231,34 @@ char *d_name(struct dirent *di) | |
506 | return di->d_name; | |
507 | #endif | |
508 | } | |
509 | + | |
510 | +/** | |
511 | + * A wrapper around select(2) that guarantees Linux-like updating of | |
512 | + * the timeout argument to contain the time left, so we can simply | |
513 | + * re-invoke in case of EINTR or EAGAIN. On BSD, select(2) doesn't | |
514 | + * change the timeout argument by itself. | |
515 | + **/ | |
516 | +int do_select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) | |
517 | +{ | |
518 | + struct timeval intended, before, after; | |
519 | + int result; | |
520 | + | |
521 | + if (timeout == NULL) { | |
522 | + return select(n, readfds, writefds, exceptfds, timeout); | |
523 | + | |
524 | + intended = *timeout; | |
525 | + gettimeofday(&before, NULL); | |
526 | + result = select(n, readfds, writefds, exceptfds, timeout); | |
527 | + gettimeofday(&after, NULL); | |
528 | + timeout->tv_sec = intended.tv_sec - (after.tv_sec - before.tv_sec); | |
529 | + timeout->tv_usec = intended.tv_usec - (after.tv_usec - before.tv_usec); | |
530 | + if (timeout->tv_usec >= 1000000) { | |
531 | + ++timeout->tv_sec; | |
532 | + timeout->tv_usec -= 1000000; | |
533 | + } else if (timeout->tv_usec < 0) { | |
534 | + --(timeout)->tv_sec; | |
535 | + timeout->tv_usec += 1000000; | |
536 | + } | |
537 | + | |
538 | + return result; | |
539 | +} | |
540 | --- util.c 24 May 2004 22:59:16 -0000 1.147 | |
541 | +++ util.c 29 May 2004 21:31:48 -0000 | |
542 | @@ -1135,3 +1135,52 @@ void *_realloc_array(void *ptr, unsigned | |
543 | return malloc(size * num); | |
544 | return realloc(ptr, size * num); | |
545 | } | |
546 | + | |
547 | +/** | |
548 | + * Blocks until one of the following happens: | |
549 | + * - read_fd is nonnegative and has data to read | |
550 | + * - write_fd is nonnegative and can be written to | |
551 | + * - something terrible happened to either | |
552 | + * - the timeout (in milliseconds) has elapsed | |
553 | + * Return value is zero iff the timeout occured. | |
554 | + */ | |
555 | +char await_fds(int read_fd, int write_fd, int timeout_ms) | |
556 | +{ | |
557 | + fd_set read_fds, write_fds, except_fds; | |
558 | + struct timeval tv; | |
559 | + int res; | |
560 | + | |
561 | + tv.tv_sec = timeout_ms/1000; | |
562 | + tv.tv_usec = (timeout_ms%1000)*1000; | |
563 | + | |
564 | + while (1) { | |
565 | + FD_ZERO(&read_fds); | |
566 | + FD_ZERO(&write_fds); | |
567 | + FD_ZERO(&except_fds); | |
568 | + if (write_fd >= 0) { | |
569 | + FD_SET(write_fd, &write_fds); | |
570 | + FD_SET(write_fd, &except_fds); | |
571 | + } | |
572 | + if (read_fd >= 0) { | |
573 | + FD_SET(read_fd, &read_fds); | |
574 | + FD_SET(read_fd, &except_fds); | |
575 | + } | |
576 | + | |
577 | + res = do_select(MAX(0,MAX(read_fd, write_fd)+1), &read_fds, &write_fds, &except_fds, &tv); | |
578 | + if (res > 0) | |
579 | + return 1; | |
580 | + if (read_fd >= 0 && (FD_ISSET(read_fd, &read_fds) || FD_ISSET(read_fd, &except_fds))) | |
581 | + return 1; | |
582 | + if (write_fd >= 0 && (FD_ISSET(write_fd, &write_fds) || FD_ISSET(write_fd, &except_fds))) | |
583 | + return 1; | |
584 | + if (res == EINTR || res == EAGAIN) { | |
585 | + continue; /* Retry */ | |
586 | + } | |
587 | + if (res < 0) { | |
588 | + rprintf(FERROR, "Error awaiting fname converter: %s\n", strerror(errno)); | |
589 | + exit_cleanup(RERR_FNAMECONV); | |
590 | + } | |
591 | + return 0; /* res == 0 and no FDs set, hence a timeout. */ | |
592 | + } | |
593 | +} | |
594 | + |