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