Add -vvv trace statement to set_modtime to help with Debian bug
[rsync/rsync.git] / socket.c
CommitLineData
7c1b4daa
MP
1/* -*- c-file-style: "linux" -*-
2
d54765c4
MP
3 rsync -- fast file replication program
4
eecd22ff 5 Copyright (C) 1992-2001 by Andrew Tridgell <tridge@samba.org>
362099a5 6 Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
bc2e93eb
AT
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21*/
22
d5d4b282
MP
23/**
24 * @file socket.c
25 *
26 * Socket functions used in rsync.
362099a5
MP
27 *
28 * This file is now converted to use the new-style getaddrinfo()
29 * interface, which supports IPv6 but is also supported on recent
30 * IPv4-only machines. On systems that don't have that interface, we
31 * emulate it using the KAME implementation.
d5d4b282 32 **/
bc2e93eb 33
f0fca04e
AT
34#include "rsync.h"
35
9a5a8673 36
660c6fbd
MP
37/* Establish a proxy connection on an open socket to a web roxy by
38 * using the CONNECT method. */
4c3b4b25
AT
39static int establish_proxy_connection(int fd, char *host, int port)
40{
41 char buffer[1024];
42 char *cp;
43
8950ac03 44 snprintf(buffer, sizeof(buffer), "CONNECT %s:%d HTTP/1.0\r\n\r\n", host, port);
00d943d5 45 if (write(fd, buffer, strlen(buffer)) != (int) strlen(buffer)) {
660c6fbd 46 rprintf(FERROR, "failed to write to proxy: %s\n",
4c3b4b25
AT
47 strerror(errno));
48 return -1;
49 }
50
51 for (cp = buffer; cp < &buffer[sizeof(buffer) - 1]; cp++) {
52 if (read(fd, cp, 1) != 1) {
660c6fbd
MP
53 rprintf(FERROR, "failed to read from proxy: %s\n",
54 strerror(errno));
4c3b4b25
AT
55 return -1;
56 }
57 if (*cp == '\n')
58 break;
59 }
60
61 if (*cp != '\n')
62 cp++;
63 *cp-- = '\0';
64 if (*cp == '\r')
65 *cp = '\0';
66 if (strncmp(buffer, "HTTP/", 5) != 0) {
67 rprintf(FERROR, "bad response from proxy - %s\n",
68 buffer);
69 return -1;
70 }
71 for (cp = &buffer[5]; isdigit(*cp) || (*cp == '.'); cp++)
72 ;
73 while (*cp == ' ')
74 cp++;
75 if (*cp != '2') {
76 rprintf(FERROR, "bad response from proxy - %s\n",
77 buffer);
78 return -1;
79 }
80 /* throw away the rest of the HTTP header */
81 while (1) {
82 for (cp = buffer; cp < &buffer[sizeof(buffer) - 1];
83 cp++) {
84 if (read(fd, cp, 1) != 1) {
660c6fbd
MP
85 rprintf(FERROR, "failed to read from proxy: %s\n",
86 strerror(errno));
4c3b4b25
AT
87 return -1;
88 }
89 if (*cp == '\n')
90 break;
91 }
92 if ((cp > buffer) && (*cp == '\n'))
93 cp--;
94 if ((cp == buffer) && ((*cp == '\n') || (*cp == '\r')))
95 break;
96 }
97 return 0;
98}
99
100
f8be7d42
MP
101/**
102 * Try to set the local address for a newly-created socket. Return -1
103 * if this fails.
104 **/
105int try_bind_local(int s,
106 int ai_family, int ai_socktype,
107 const char *bind_address)
108{
109 int error;
110 struct addrinfo bhints, *bres_all, *r;
111
112 memset(&bhints, 0, sizeof(bhints));
113 bhints.ai_family = ai_family;
114 bhints.ai_socktype = ai_socktype;
115 bhints.ai_flags = AI_PASSIVE;
98355b80 116 if ((error = getaddrinfo(bind_address, NULL, &bhints, &bres_all))) {
f8be7d42
MP
117 rprintf(FERROR, RSYNC_NAME ": getaddrinfo %s: %s\n",
118 bind_address, gai_strerror(error));
119 return -1;
120 }
121
122 for (r = bres_all; r; r = r->ai_next) {
9ec75284 123 if (bind(s, r->ai_addr, r->ai_addrlen) == -1)
f8be7d42
MP
124 continue;
125 return s;
126 }
127
128 /* no error message; there might be some problem that allows
129 * creation of the socket but not binding, perhaps if the
130 * machine has no ipv6 address of this name. */
131 return -1;
132}
133
eecd22ff 134
d5d4b282
MP
135/**
136 * Open a socket to a tcp remote host with the specified port .
06963d0f 137 *
d5d4b282
MP
138 * Based on code from Warren. Proxy support by Stephen Rothwell.
139 * getaddrinfo() rewrite contributed by KAME.net.
06963d0f 140 *
d5d4b282
MP
141 * Now that we support IPv6 we need to look up the remote machine's
142 * address first, using @p af_hint to set a preference for the type
143 * of address. Then depending on whether it has v4 or v6 addresses we
144 * try to open a connection.
06963d0f 145 *
d5d4b282
MP
146 * The loop allows for machines with some addresses which may not be
147 * reachable, perhaps because we can't e.g. route ipv6 to that network
148 * but we can get ip4 packets through.
149 *
150 * @param bind_address Local address to use. Normally NULL to bind
151 * the wildcard address.
152 *
153 * @param af_hint Address family, e.g. AF_INET or AF_INET6.
06963d0f 154 **/
d5d4b282
MP
155int open_socket_out(char *host, int port, const char *bind_address,
156 int af_hint)
bc2e93eb 157{
f0fca04e 158 int type = SOCK_STREAM;
06963d0f
MP
159 int error;
160 int s;
06963d0f
MP
161 struct addrinfo hints, *res0, *res;
162 char portbuf[10];
4c3b4b25 163 char *h;
4c3b4b25
AT
164 int proxied = 0;
165 char buffer[1024];
166 char *cp;
167
660c6fbd
MP
168 /* if we have a RSYNC_PROXY env variable then redirect our
169 * connetcion via a web proxy at the given address. The format
170 * is hostname:port */
4c3b4b25
AT
171 h = getenv("RSYNC_PROXY");
172 proxied = (h != NULL) && (*h != '\0');
173
174 if (proxied) {
175 strlcpy(buffer, h, sizeof(buffer));
176 cp = strchr(buffer, ':');
177 if (cp == NULL) {
660c6fbd
MP
178 rprintf(FERROR,
179 "invalid proxy specification: should be HOST:PORT\n");
4c3b4b25
AT
180 return -1;
181 }
182 *cp++ = '\0';
06963d0f 183 strcpy(portbuf, cp);
4c3b4b25
AT
184 h = buffer;
185 } else {
06963d0f 186 snprintf(portbuf, sizeof(portbuf), "%d", port);
4c3b4b25 187 h = host;
4c3b4b25 188 }
f0fca04e 189
06963d0f 190 memset(&hints, 0, sizeof(hints));
d5d4b282 191 hints.ai_family = af_hint;
06963d0f
MP
192 hints.ai_socktype = type;
193 error = getaddrinfo(h, portbuf, &hints, &res0);
194 if (error) {
d5d4b282
MP
195 rprintf(FERROR, RSYNC_NAME ": getaddrinfo: %s %s: %s\n",
196 h, portbuf, gai_strerror(error));
f0fca04e
AT
197 return -1;
198 }
199
06963d0f 200 s = -1;
2d6dbe29
MP
201 /* Try to connect to all addresses for this machine until we get
202 * through. It might e.g. be multi-homed, or have both IPv4 and IPv6
203 * addresses. We need to create a socket for each record, since the
204 * address record tells us what protocol to use to try to connect. */
06963d0f
MP
205 for (res = res0; res; res = res->ai_next) {
206 s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
207 if (s < 0)
208 continue;
f0fca04e 209
f8be7d42
MP
210 if (bind_address)
211 if (try_bind_local(s, res->ai_family, type,
212 bind_address) == -1) {
213 close(s);
214 s = -1;
06963d0f
MP
215 continue;
216 }
e30f0657 217
06963d0f
MP
218 if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
219 close(s);
220 s = -1;
221 continue;
222 }
223 if (proxied &&
224 establish_proxy_connection(s, host, port) != 0) {
225 close(s);
226 s = -1;
227 continue;
228 } else
229 break;
4c3b4b25 230 }
06963d0f
MP
231 freeaddrinfo(res0);
232 if (s < 0) {
7ef6aa64
MP
233 rprintf(FERROR, RSYNC_NAME ": failed to connect to %s: %s\n",
234 h, strerror(errno));
f0fca04e
AT
235 return -1;
236 }
06963d0f 237 return s;
f0fca04e
AT
238}
239
240
eecd22ff
MP
241/**
242 * Open an outgoing socket, but allow for it to be intercepted by
243 * $RSYNC_CONNECT_PROG, which will execute a program across a TCP
244 * socketpair rather than really opening a socket.
245 *
246 * We use this primarily in testing to detect TCP flow bugs, but not
247 * cause security problems by really opening remote connections.
248 *
249 * This is based on the Samba LIBSMB_PROG feature.
06963d0f
MP
250 *
251 * @param bind_address Local address to use. Normally NULL to get the stack default.
eecd22ff
MP
252 **/
253int open_socket_out_wrapped (char *host,
254 int port,
d5d4b282
MP
255 const char *bind_address,
256 int af_hint)
eecd22ff
MP
257{
258 char *prog;
259
260 if ((prog = getenv ("RSYNC_CONNECT_PROG")) != NULL)
261 return sock_exec (prog);
262 else
d5d4b282
MP
263 return open_socket_out (host, port, bind_address,
264 af_hint);
eecd22ff
MP
265}
266
267
268
06963d0f
MP
269/**
270 * Open a socket of the specified type, port and address for incoming data
271 *
b8771f96
MP
272 * Try to be better about handling the results of getaddrinfo(): when
273 * opening an inbound socket, we might get several address results,
274 * e.g. for the machine's ipv4 and ipv6 name.
275 *
276 * If binding a wildcard, then any one of them should do. If an address
277 * was specified but it's insufficiently specific then that's not our
278 * fault.
279 *
280 * However, some of the advertized addresses may not work because e.g. we
281 * don't have IPv6 support in the kernel. In that case go on and try all
282 * addresses until one succeeds.
283 *
06963d0f
MP
284 * @param bind_address Local address to bind, or NULL to allow it to
285 * default.
286 **/
d5d4b282
MP
287static int open_socket_in(int type, int port, const char *bind_address,
288 int af_hint)
f0fca04e 289{
f0fca04e 290 int one=1;
06963d0f 291 int s;
13e29995 292 struct addrinfo hints, *all_ai, *resp;
06963d0f
MP
293 char portbuf[10];
294 int error;
295
296 memset(&hints, 0, sizeof(hints));
d5d4b282 297 hints.ai_family = af_hint;
06963d0f
MP
298 hints.ai_socktype = type;
299 hints.ai_flags = AI_PASSIVE;
300 snprintf(portbuf, sizeof(portbuf), "%d", port);
13e29995 301 error = getaddrinfo(bind_address, portbuf, &hints, &all_ai);
06963d0f 302 if (error) {
7ef6aa64
MP
303 rprintf(FERROR, RSYNC_NAME ": getaddrinfo: bind address %s: %s\n",
304 bind_address, gai_strerror(error));
06963d0f
MP
305 return -1;
306 }
06963d0f 307
13e29995
MP
308 /* We may not be able to create the socket, if for example the
309 * machine knows about IPv6 in the C library, but not in the
310 * kernel. */
311 for (resp = all_ai; resp; resp = resp->ai_next) {
312 s = socket(resp->ai_family, resp->ai_socktype,
313 resp->ai_protocol);
314
315 if (s == -1)
316 /* See if there's another address that will work... */
317 continue;
318
319 setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
320 (char *)&one, sizeof one);
321
322 /* now we've got a socket - we need to bind it */
323 if (bind(s, all_ai->ai_addr, all_ai->ai_addrlen) < 0) {
324 /* Nope, try another */
325 close(s);
326 continue;
b8771f96
MP
327 }
328
13e29995 329 return s;
f0fca04e
AT
330 }
331
13e29995
MP
332 rprintf(FERROR, RSYNC_NAME ": open inbound socket on port %d failed: "
333 "%s\n",
334 port,
335 strerror(errno));
b8771f96 336
13e29995 337 freeaddrinfo(all_ai);
b8771f96 338 return -1;
f0fca04e
AT
339}
340
341
7c1b4daa
MP
342/*
343 * Determine if a file descriptor is in fact a socket
344 */
f0fca04e
AT
345int is_a_socket(int fd)
346{
ac2a1a44
MP
347 int v;
348 socklen_t l;
3eb38818 349 l = sizeof(int);
7c1b4daa
MP
350
351 /* Parameters to getsockopt, setsockopt etc are very
352 * unstandardized across platforms, so don't be surprised if
ac2a1a44
MP
353 * there are compiler warnings on e.g. SCO OpenSwerver or AIX.
354 * It seems they all eventually get the right idea.
7c1b4daa
MP
355 *
356 * Debian says: ``The fifth argument of getsockopt and
357 * setsockopt is in reality an int [*] (and this is what BSD
358 * 4.* and libc4 and libc5 have). Some POSIX confusion
359 * resulted in the present socklen_t. The draft standard has
360 * not been adopted yet, but glibc2 already follows it and
361 * also has socklen_t [*]. See also accept(2).''
362 *
363 * We now return to your regularly scheduled programming. */
3eb38818 364 return(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0);
f0fca04e
AT
365}
366
367
8ef4ffd6 368void start_accept_loop(int port, int (*fn)(int ))
f0fca04e
AT
369{
370 int s;
06963d0f 371 extern char *bind_address;
13e29995 372 extern int default_af_hint;
f0fca04e 373
f0fca04e 374 /* open an incoming socket */
13e29995 375 s = open_socket_in(SOCK_STREAM, port, bind_address, default_af_hint);
f0fca04e 376 if (s == -1)
65417579 377 exit_cleanup(RERR_SOCKETIO);
f0fca04e
AT
378
379 /* ready to listen */
380 if (listen(s, 5) == -1) {
381 close(s);
65417579 382 exit_cleanup(RERR_SOCKETIO);
f0fca04e
AT
383 }
384
385
386 /* now accept incoming connections - forking a new process
387 for each incoming connection */
388 while (1) {
389 fd_set fds;
390 int fd;
2d6dbe29 391 struct sockaddr_storage addr;
d54765c4 392 socklen_t addrlen = sizeof addr;
f0fca04e 393
15b84e14
DD
394 /* close log file before the potentially very long select so
395 file can be trimmed by another process instead of growing
396 forever */
397 log_close();
45a83540 398
f0fca04e
AT
399 FD_ZERO(&fds);
400 FD_SET(s, &fds);
401
402 if (select(s+1, &fds, NULL, NULL, NULL) != 1) {
403 continue;
404 }
405
406 if(!FD_ISSET(s, &fds)) continue;
407
2d6dbe29 408 fd = accept(s,(struct sockaddr *)&addr,&addrlen);
f0fca04e
AT
409
410 if (fd == -1) continue;
411
31f440e6
AT
412 signal(SIGCHLD, SIG_IGN);
413
414 /* we shouldn't have any children left hanging around
415 but I have had reports that on Digital Unix zombies
416 are produced, so this ensures that they are reaped */
417#ifdef WNOHANG
0503f060 418 while (waitpid(-1, NULL, WNOHANG) > 0);
31f440e6
AT
419#endif
420
f0fca04e
AT
421 if (fork()==0) {
422 close(s);
15b84e14
DD
423 /* open log file in child before possibly giving
424 up privileges */
425 log_open();
f0fca04e
AT
426 _exit(fn(fd));
427 }
428
429 close(fd);
430 }
f0fca04e
AT
431}
432
433
434enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
435
436struct
437{
438 char *name;
439 int level;
440 int option;
441 int value;
442 int opttype;
443} socket_options[] = {
444 {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
445 {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
446 {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
447#ifdef TCP_NODELAY
448 {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
449#endif
450#ifdef IPTOS_LOWDELAY
451 {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
452#endif
453#ifdef IPTOS_THROUGHPUT
454 {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
455#endif
456#ifdef SO_SNDBUF
457 {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
458#endif
459#ifdef SO_RCVBUF
460 {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
461#endif
462#ifdef SO_SNDLOWAT
463 {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
464#endif
465#ifdef SO_RCVLOWAT
466 {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
467#endif
468#ifdef SO_SNDTIMEO
469 {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
470#endif
471#ifdef SO_RCVTIMEO
472 {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
473#endif
474 {NULL,0,0,0,0}};
475
476
477
478/****************************************************************************
479set user socket options
480****************************************************************************/
481void set_socket_options(int fd, char *options)
482{
483 char *tok;
a6801c39
AT
484 if (!options || !*options) return;
485
f0fca04e
AT
486 options = strdup(options);
487
488 if (!options) out_of_memory("set_socket_options");
489
490 for (tok=strtok(options, " \t,"); tok; tok=strtok(NULL," \t,")) {
491 int ret=0,i;
492 int value = 1;
493 char *p;
494 int got_value = 0;
495
496 if ((p = strchr(tok,'='))) {
497 *p = 0;
498 value = atoi(p+1);
499 got_value = 1;
500 }
501
502 for (i=0;socket_options[i].name;i++)
503 if (strcmp(socket_options[i].name,tok)==0)
504 break;
505
506 if (!socket_options[i].name) {
507 rprintf(FERROR,"Unknown socket option %s\n",tok);
508 continue;
509 }
510
511 switch (socket_options[i].opttype) {
512 case OPT_BOOL:
513 case OPT_INT:
514 ret = setsockopt(fd,socket_options[i].level,
515 socket_options[i].option,(char *)&value,sizeof(int));
516 break;
517
518 case OPT_ON:
519 if (got_value)
520 rprintf(FERROR,"syntax error - %s does not take a value\n",tok);
521
522 {
523 int on = socket_options[i].value;
524 ret = setsockopt(fd,socket_options[i].level,
525 socket_options[i].option,(char *)&on,sizeof(int));
526 }
527 break;
528 }
529
530 if (ret != 0)
660c6fbd
MP
531 rprintf(FERROR, "failed to set socket option %s: %s\n", tok,
532 strerror(errno));
f0fca04e
AT
533 }
534
535 free(options);
536}
537
538/****************************************************************************
539become a daemon, discarding the controlling terminal
540****************************************************************************/
541void become_daemon(void)
542{
b11ed3b1
AT
543 int i;
544
c46ded46 545 if (fork()) {
f0fca04e 546 _exit(0);
c46ded46 547 }
f0fca04e
AT
548
549 /* detach from the terminal */
550#ifdef HAVE_SETSID
551 setsid();
552#else
553#ifdef TIOCNOTTY
c46ded46
AT
554 i = open("/dev/tty", O_RDWR);
555 if (i >= 0) {
556 ioctl(i, (int) TIOCNOTTY, (char *)0);
557 close(i);
f0fca04e
AT
558 }
559#endif /* TIOCNOTTY */
560#endif
b11ed3b1
AT
561 /* make sure that stdin, stdout an stderr don't stuff things
562 up (library functions, for example) */
563 for (i=0;i<3;i++) {
564 close(i);
565 open("/dev/null", O_RDWR);
566 }
bc2e93eb 567}
ff8b29b8 568
eecd22ff
MP
569
570/*******************************************************************
571this is like socketpair but uses tcp. It is used by the Samba
572regression test code
573The function guarantees that nobody else can attach to the socket,
574or if they do that this function fails and the socket gets closed
575returns 0 on success, -1 on failure
576the resulting file descriptors are symmetrical
577 ******************************************************************/
578static int socketpair_tcp(int fd[2])
579{
580 int listener;
581 struct sockaddr_in sock;
582 struct sockaddr_in sock2;
583 socklen_t socklen = sizeof(sock);
584 int connect_done = 0;
585
586 fd[0] = fd[1] = listener = -1;
587
588 memset(&sock, 0, sizeof(sock));
589
590 if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed;
591
592 memset(&sock2, 0, sizeof(sock2));
14579493 593#ifdef HAVE_SOCKADDR_LEN
eecd22ff
MP
594 sock2.sin_len = sizeof(sock2);
595#endif
596 sock2.sin_family = PF_INET;
597
598 bind(listener, (struct sockaddr *)&sock2, sizeof(sock2));
599
600 if (listen(listener, 1) != 0) goto failed;
601
602 if (getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0) goto failed;
603
604 if ((fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed;
605
606 set_nonblocking(fd[1]);
607
608 sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
609
610 if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) == -1) {
611 if (errno != EINPROGRESS) goto failed;
612 } else {
613 connect_done = 1;
614 }
615
616 if ((fd[0] = accept(listener, (struct sockaddr *)&sock, &socklen)) == -1) goto failed;
617
618 close(listener);
619 if (connect_done == 0) {
620 if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) != 0
621 && errno != EISCONN) goto failed;
622 }
623
624 set_blocking (fd[1]);
625
626 /* all OK! */
627 return 0;
628
629 failed:
630 if (fd[0] != -1) close(fd[0]);
631 if (fd[1] != -1) close(fd[1]);
632 if (listener != -1) close(listener);
633 return -1;
634}
635
636
d02984bb
MP
637
638/**
639 * Run a program on a local tcp socket, so that we can talk to it's
255810c0
MP
640 * stdin and stdout. This is used to fake a connection to a daemon
641 * for testing -- not for the normal case of running SSH.
d02984bb
MP
642 *
643 * @return a socket which is attached to a subprocess running
644 * "prog". stdin and stdout are attached. stderr is left attached to
645 * the original stderr
646 **/
eecd22ff
MP
647int sock_exec(const char *prog)
648{
649 int fd[2];
5d264037 650
eecd22ff
MP
651 if (socketpair_tcp(fd) != 0) {
652 rprintf (FERROR, RSYNC_NAME
653 ": socketpair_tcp failed (%s)\n",
654 strerror(errno));
655 return -1;
656 }
657 if (fork() == 0) {
658 close(fd[0]);
659 close(0);
660 close(1);
661 dup(fd[1]);
662 dup(fd[1]);
5d264037
MP
663 if (verbose > 3) {
664 /* Can't use rprintf because we've forked. */
eecd22ff
MP
665 fprintf (stderr,
666 RSYNC_NAME ": execute socket program \"%s\"\n",
667 prog);
5d264037 668 }
eecd22ff
MP
669 exit (system (prog));
670 }
671 close (fd[1]);
672 return fd[0];
673}
674
675
676