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