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