Main Page   Alphabetical List   Data Structures   File List   Data Fields   Globals   Related Pages  

socket.c

Go to the documentation of this file.
00001 /* -*- c-file-style: "linux" -*-
00002    
00003    rsync -- fast file replication program
00004    
00005    Copyright (C) 1992-2001 by Andrew Tridgell <tridge@samba.org>
00006    Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
00007    
00008    This program is free software; you can redistribute it and/or modify
00009    it under the terms of the GNU General Public License as published by
00010    the Free Software Foundation; either version 2 of the License, or
00011    (at your option) any later version.
00012    
00013    This program is distributed in the hope that it will be useful,
00014    but WITHOUT ANY WARRANTY; without even the implied warranty of
00015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016    GNU General Public License for more details.
00017    
00018    You should have received a copy of the GNU General Public License
00019    along with this program; if not, write to the Free Software
00020    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021 */
00022 
00023 /**
00024  * @file socket.c
00025  * 
00026  * Socket functions used in rsync.
00027  *
00028  * This file is now converted to use the new-style getaddrinfo()
00029  * interface, which supports IPv6 but is also supported on recent
00030  * IPv4-only machines.  On systems that don't have that interface, we
00031  * emulate it using the KAME implementation.
00032  **/
00033 
00034 #include "rsync.h"
00035 
00036 
00037 /**
00038  * Establish a proxy connection on an open socket to a web proxy by
00039  * using the HTTP CONNECT method.
00040  **/
00041 static int establish_proxy_connection(int fd, char *host, int port)
00042 {
00043         char buffer[1024];
00044         char *cp;
00045 
00046         snprintf(buffer, sizeof(buffer), "CONNECT %s:%d HTTP/1.0\r\n\r\n", host, port);
00047         if (write(fd, buffer, strlen(buffer)) != (int) strlen(buffer)) {
00048                 rprintf(FERROR, "failed to write to proxy: %s\n",
00049                         strerror(errno));
00050                 return -1;
00051         }
00052 
00053         for (cp = buffer; cp < &buffer[sizeof(buffer) - 1]; cp++) {
00054                 if (read(fd, cp, 1) != 1) {
00055                         rprintf(FERROR, "failed to read from proxy: %s\n",
00056                                 strerror(errno));
00057                         return -1;
00058                 }
00059                 if (*cp == '\n')
00060                         break;
00061         }
00062 
00063         if (*cp != '\n')
00064                 cp++;
00065         *cp-- = '\0';
00066         if (*cp == '\r')
00067                 *cp = '\0';
00068         if (strncmp(buffer, "HTTP/", 5) != 0) {
00069                 rprintf(FERROR, "bad response from proxy - %s\n",
00070                         buffer);
00071                 return -1;
00072         }
00073         for (cp = &buffer[5]; isdigit(* (unsigned char *) cp) || (*cp == '.'); cp++)
00074                 ;
00075         while (*cp == ' ')
00076                 cp++;
00077         if (*cp != '2') {
00078                 rprintf(FERROR, "bad response from proxy - %s\n",
00079                         buffer);
00080                 return -1;
00081         }
00082         /* throw away the rest of the HTTP header */
00083         while (1) {
00084                 for (cp = buffer; cp < &buffer[sizeof(buffer) - 1];
00085                      cp++) {
00086                         if (read(fd, cp, 1) != 1) {
00087                                 rprintf(FERROR, "failed to read from proxy: %s\n",
00088                                         strerror(errno));
00089                                 return -1;
00090                         }
00091                         if (*cp == '\n')
00092                                 break;
00093                 }
00094                 if ((cp > buffer) && (*cp == '\n'))
00095                         cp--;
00096                 if ((cp == buffer) && ((*cp == '\n') || (*cp == '\r')))
00097                         break;
00098         }
00099         return 0;
00100 }
00101 
00102 
00103 /**
00104  * Try to set the local address for a newly-created socket.  Return -1
00105  * if this fails.
00106  **/
00107 int try_bind_local(int s,
00108                    int ai_family, int ai_socktype,
00109                    const char *bind_address)
00110 {
00111         int error;
00112         struct addrinfo bhints, *bres_all, *r;
00113 
00114         memset(&bhints, 0, sizeof(bhints));
00115         bhints.ai_family = ai_family;
00116         bhints.ai_socktype = ai_socktype;
00117         bhints.ai_flags = AI_PASSIVE;
00118         if ((error = getaddrinfo(bind_address, NULL, &bhints, &bres_all))) {
00119                 rprintf(FERROR, RSYNC_NAME ": getaddrinfo %s: %s\n",
00120                         bind_address, gai_strerror(error));
00121                 return -1;
00122         }
00123 
00124         for (r = bres_all; r; r = r->ai_next) {
00125                 if (bind(s, r->ai_addr, r->ai_addrlen) == -1)
00126                         continue;
00127                 freeaddrinfo(bres_all);
00128                 return s;
00129         }
00130 
00131         /* no error message; there might be some problem that allows
00132          * creation of the socket but not binding, perhaps if the
00133          * machine has no ipv6 address of this name. */
00134         freeaddrinfo(bres_all);
00135         return -1;
00136 }
00137 
00138 
00139 /**
00140  * Open a socket to a tcp remote host with the specified port .
00141  *
00142  * Based on code from Warren.  Proxy support by Stephen Rothwell.
00143  * getaddrinfo() rewrite contributed by KAME.net.
00144  *
00145  * Now that we support IPv6 we need to look up the remote machine's
00146  * address first, using @p af_hint to set a preference for the type
00147  * of address.  Then depending on whether it has v4 or v6 addresses we
00148  * try to open a connection.
00149  *
00150  * The loop allows for machines with some addresses which may not be
00151  * reachable, perhaps because we can't e.g. route ipv6 to that network
00152  * but we can get ip4 packets through.
00153  *
00154  * @param bind_address Local address to use.  Normally NULL to bind
00155  * the wildcard address.
00156  *
00157  * @param af_hint Address family, e.g. AF_INET or AF_INET6.
00158  **/
00159 int open_socket_out(char *host, int port, const char *bind_address,
00160                     int af_hint)
00161 {
00162         int type = SOCK_STREAM;
00163         int error;
00164         int s;
00165         struct addrinfo hints, *res0, *res;
00166         char portbuf[10];
00167         char *h;
00168         int proxied = 0;
00169         char buffer[1024];
00170         char *cp;
00171 
00172         /* if we have a RSYNC_PROXY env variable then redirect our
00173          * connetcion via a web proxy at the given address. The format
00174          * is hostname:port */
00175         h = getenv("RSYNC_PROXY");
00176         proxied = (h != NULL) && (*h != '\0');
00177 
00178         if (proxied) {
00179                 strlcpy(buffer, h, sizeof(buffer));
00180                 cp = strchr(buffer, ':');
00181                 if (cp == NULL) {
00182                         rprintf(FERROR,
00183                                 "invalid proxy specification: should be HOST:PORT\n");
00184                         return -1;
00185                 }
00186                 *cp++ = '\0';
00187                 strcpy(portbuf, cp);
00188                 h = buffer;
00189                 if (verbose >= 2) {
00190                         rprintf(FINFO, "connection via http proxy %s port %s\n",
00191                                 h, portbuf);
00192                 }
00193         } else {
00194                 snprintf(portbuf, sizeof(portbuf), "%d", port);
00195                 h = host;
00196         }
00197 
00198         memset(&hints, 0, sizeof(hints));
00199         hints.ai_family = af_hint;
00200         hints.ai_socktype = type;
00201         error = getaddrinfo(h, portbuf, &hints, &res0);
00202         if (error) {
00203                 rprintf(FERROR, RSYNC_NAME ": getaddrinfo: %s %s: %s\n",
00204                         h, portbuf, gai_strerror(error));
00205                 return -1;
00206         }
00207 
00208         s = -1;
00209         /* Try to connect to all addresses for this machine until we get
00210          * through.  It might e.g. be multi-homed, or have both IPv4 and IPv6
00211          * addresses.  We need to create a socket for each record, since the
00212          * address record tells us what protocol to use to try to connect. */
00213         for (res = res0; res; res = res->ai_next) {
00214                 s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
00215                 if (s < 0)
00216                         continue;
00217 
00218                 if (bind_address)
00219                         if (try_bind_local(s, res->ai_family, type,
00220                                            bind_address) == -1) {
00221                                 close(s);
00222                                 s = -1;
00223                                 continue;
00224                         }
00225 
00226                 if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
00227                         close(s);
00228                         s = -1;
00229                         continue;
00230                 }
00231                 if (proxied &&
00232                     establish_proxy_connection(s, host, port) != 0) {
00233                         close(s);
00234                         s = -1;
00235                         continue;
00236                 } else
00237                         break;
00238         }
00239         freeaddrinfo(res0);
00240         if (s < 0) {
00241                 rprintf(FERROR, RSYNC_NAME ": failed to connect to %s: %s\n",
00242                         h, strerror(errno));
00243                 return -1;
00244         }
00245         return s;
00246 }
00247 
00248 
00249 /**
00250  * Open an outgoing socket, but allow for it to be intercepted by
00251  * $RSYNC_CONNECT_PROG, which will execute a program across a TCP
00252  * socketpair rather than really opening a socket.
00253  *
00254  * We use this primarily in testing to detect TCP flow bugs, but not
00255  * cause security problems by really opening remote connections.
00256  *
00257  * This is based on the Samba LIBSMB_PROG feature.
00258  *
00259  * @param bind_address Local address to use.  Normally NULL to get the stack default.
00260  **/
00261 int open_socket_out_wrapped (char *host,
00262                              int port,
00263                              const char *bind_address,
00264                              int af_hint)
00265 {
00266         char *prog;
00267 
00268         if ((prog = getenv ("RSYNC_CONNECT_PROG")) != NULL) 
00269                 return sock_exec (prog);
00270         else 
00271                 return open_socket_out (host, port, bind_address,
00272                                         af_hint);
00273 }
00274 
00275 
00276 
00277 /**
00278  * Open a socket of the specified type, port and address for incoming data
00279  *
00280  * Try to be better about handling the results of getaddrinfo(): when
00281  * opening an inbound socket, we might get several address results,
00282  * e.g. for the machine's ipv4 and ipv6 name.  
00283  * 
00284  * If binding a wildcard, then any one of them should do.  If an address
00285  * was specified but it's insufficiently specific then that's not our
00286  * fault.  
00287  * 
00288  * However, some of the advertized addresses may not work because e.g. we
00289  * don't have IPv6 support in the kernel.  In that case go on and try all
00290  * addresses until one succeeds.
00291  * 
00292  * @param bind_address Local address to bind, or NULL to allow it to
00293  * default.
00294  **/
00295 static int open_socket_in(int type, int port, const char *bind_address,
00296                           int af_hint)
00297 {
00298         int one=1;
00299         int s;
00300         struct addrinfo hints, *all_ai, *resp;
00301         char portbuf[10];
00302         int error;
00303 
00304         memset(&hints, 0, sizeof(hints));
00305         hints.ai_family = af_hint;
00306         hints.ai_socktype = type;
00307         hints.ai_flags = AI_PASSIVE;
00308         snprintf(portbuf, sizeof(portbuf), "%d", port);
00309         error = getaddrinfo(bind_address, portbuf, &hints, &all_ai);
00310         if (error) {
00311                 rprintf(FERROR, RSYNC_NAME ": getaddrinfo: bind address %s: %s\n",
00312                         bind_address, gai_strerror(error));
00313                 return -1;
00314         }
00315 
00316         /* We may not be able to create the socket, if for example the
00317          * machine knows about IPv6 in the C library, but not in the
00318          * kernel. */
00319         for (resp = all_ai; resp; resp = resp->ai_next) {
00320                 s = socket(resp->ai_family, resp->ai_socktype,
00321                            resp->ai_protocol);
00322 
00323                 if (s == -1) 
00324                         /* See if there's another address that will work... */
00325                         continue;
00326                 
00327                 setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
00328                            (char *)&one, sizeof one);
00329                 
00330                 /* now we've got a socket - we need to bind it */
00331                 if (bind(s, all_ai->ai_addr, all_ai->ai_addrlen) < 0) {
00332                         /* Nope, try another */
00333                         close(s);
00334                         continue;
00335                 }
00336 
00337                 freeaddrinfo(all_ai);
00338                 return s;
00339         }
00340 
00341         rprintf(FERROR, RSYNC_NAME ": open inbound socket on port %d failed: "
00342                 "%s\n",
00343                 port, 
00344                 strerror(errno));
00345 
00346         freeaddrinfo(all_ai);
00347         return -1; 
00348 }
00349 
00350 
00351 /*
00352  * Determine if a file descriptor is in fact a socket
00353  */
00354 int is_a_socket(int fd)
00355 {
00356         int v;
00357         socklen_t l;
00358         l = sizeof(int);
00359 
00360         /* Parameters to getsockopt, setsockopt etc are very
00361          * unstandardized across platforms, so don't be surprised if
00362          * there are compiler warnings on e.g. SCO OpenSwerver or AIX.
00363          * It seems they all eventually get the right idea.
00364          *
00365          * Debian says: ``The fifth argument of getsockopt and
00366          * setsockopt is in reality an int [*] (and this is what BSD
00367          * 4.* and libc4 and libc5 have).  Some POSIX confusion
00368          * resulted in the present socklen_t.  The draft standard has
00369          * not been adopted yet, but glibc2 already follows it and
00370          * also has socklen_t [*]. See also accept(2).''
00371          *
00372          * We now return to your regularly scheduled programming.  */
00373         return(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0);
00374 }
00375 
00376 
00377 void start_accept_loop(int port, int (*fn)(int ))
00378 {
00379         int s;
00380         extern char *bind_address;
00381         extern int default_af_hint;
00382 
00383         /* open an incoming socket */
00384         s = open_socket_in(SOCK_STREAM, port, bind_address, default_af_hint);
00385         if (s == -1)
00386                 exit_cleanup(RERR_SOCKETIO);
00387 
00388         /* ready to listen */
00389         if (listen(s, 5) == -1) {
00390                 close(s);
00391                 exit_cleanup(RERR_SOCKETIO);
00392         }
00393 
00394 
00395         /* now accept incoming connections - forking a new process
00396            for each incoming connection */
00397         while (1) {
00398                 fd_set fds;
00399                 pid_t pid;
00400                 int fd;
00401                 struct sockaddr_storage addr;
00402                 socklen_t addrlen = sizeof addr;
00403 
00404                 /* close log file before the potentially very long select so
00405                    file can be trimmed by another process instead of growing
00406                    forever */
00407                 log_close();
00408 
00409                 FD_ZERO(&fds);
00410                 FD_SET(s, &fds);
00411 
00412                 if (select(s+1, &fds, NULL, NULL, NULL) != 1) {
00413                         continue;
00414                 }
00415 
00416                 if(!FD_ISSET(s, &fds)) continue;
00417 
00418                 fd = accept(s,(struct sockaddr *)&addr,&addrlen);
00419 
00420                 if (fd == -1) continue;
00421 
00422                 signal(SIGCHLD, SIG_IGN);
00423 
00424                 /* we shouldn't have any children left hanging around
00425                    but I have had reports that on Digital Unix zombies
00426                    are produced, so this ensures that they are reaped */
00427 #ifdef WNOHANG
00428                 while (waitpid(-1, NULL, WNOHANG) > 0);
00429 #endif
00430 
00431                 if ((pid = fork()) == 0) {
00432                         close(s);
00433                         /* open log file in child before possibly giving
00434                            up privileges  */
00435                         log_open();
00436                         _exit(fn(fd));
00437                 } else if (pid < 0) {
00438                         rprintf(FERROR,
00439                                 RSYNC_NAME
00440                                 ": could not create child server process: %s\n",
00441                                 strerror(errno));
00442                         close(fd);
00443                         /* This might have happened because we're
00444                          * overloaded.  Sleep briefly before trying to
00445                          * accept again. */
00446                         sleep(2);
00447                 } else {
00448                         /* Parent doesn't need this fd anymore. */
00449                         close(fd);
00450                 }
00451         }
00452 }
00453 
00454 
00455 enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
00456 
00457 struct
00458 {
00459   char *name;
00460   int level;
00461   int option;
00462   int value;
00463   int opttype;
00464 } socket_options[] = {
00465   {"SO_KEEPALIVE",      SOL_SOCKET,    SO_KEEPALIVE,    0,                 OPT_BOOL},
00466   {"SO_REUSEADDR",      SOL_SOCKET,    SO_REUSEADDR,    0,                 OPT_BOOL},
00467   {"SO_BROADCAST",      SOL_SOCKET,    SO_BROADCAST,    0,                 OPT_BOOL},
00468 #ifdef TCP_NODELAY
00469   {"TCP_NODELAY",       IPPROTO_TCP,   TCP_NODELAY,     0,                 OPT_BOOL},
00470 #endif
00471 #ifdef IPTOS_LOWDELAY
00472   {"IPTOS_LOWDELAY",    IPPROTO_IP,    IP_TOS,          IPTOS_LOWDELAY,    OPT_ON},
00473 #endif
00474 #ifdef IPTOS_THROUGHPUT
00475   {"IPTOS_THROUGHPUT",  IPPROTO_IP,    IP_TOS,          IPTOS_THROUGHPUT,  OPT_ON},
00476 #endif
00477 #ifdef SO_SNDBUF
00478   {"SO_SNDBUF",         SOL_SOCKET,    SO_SNDBUF,       0,                 OPT_INT},
00479 #endif
00480 #ifdef SO_RCVBUF
00481   {"SO_RCVBUF",         SOL_SOCKET,    SO_RCVBUF,       0,                 OPT_INT},
00482 #endif
00483 #ifdef SO_SNDLOWAT
00484   {"SO_SNDLOWAT",       SOL_SOCKET,    SO_SNDLOWAT,     0,                 OPT_INT},
00485 #endif
00486 #ifdef SO_RCVLOWAT
00487   {"SO_RCVLOWAT",       SOL_SOCKET,    SO_RCVLOWAT,     0,                 OPT_INT},
00488 #endif
00489 #ifdef SO_SNDTIMEO
00490   {"SO_SNDTIMEO",       SOL_SOCKET,    SO_SNDTIMEO,     0,                 OPT_INT},
00491 #endif
00492 #ifdef SO_RCVTIMEO
00493   {"SO_RCVTIMEO",       SOL_SOCKET,    SO_RCVTIMEO,     0,                 OPT_INT},
00494 #endif
00495   {NULL,0,0,0,0}};
00496 
00497         
00498 
00499 /**
00500  * Set user socket options
00501  **/
00502 void set_socket_options(int fd, char *options)
00503 {
00504         char *tok;
00505         if (!options || !*options) return;
00506 
00507         options = strdup(options);
00508         
00509         if (!options) out_of_memory("set_socket_options");
00510 
00511         for (tok=strtok(options, " \t,"); tok; tok=strtok(NULL," \t,")) {
00512                 int ret=0,i;
00513                 int value = 1;
00514                 char *p;
00515                 int got_value = 0;
00516 
00517                 if ((p = strchr(tok,'='))) {
00518                         *p = 0;
00519                         value = atoi(p+1);
00520                         got_value = 1;
00521                 }
00522 
00523                 for (i=0;socket_options[i].name;i++)
00524                         if (strcmp(socket_options[i].name,tok)==0)
00525                                 break;
00526 
00527                 if (!socket_options[i].name) {
00528                         rprintf(FERROR,"Unknown socket option %s\n",tok);
00529                         continue;
00530                 }
00531 
00532                 switch (socket_options[i].opttype) {
00533                 case OPT_BOOL:
00534                 case OPT_INT:
00535                         ret = setsockopt(fd,socket_options[i].level,
00536                                          socket_options[i].option,(char *)&value,sizeof(int));
00537                         break;
00538                         
00539                 case OPT_ON:
00540                         if (got_value)
00541                                 rprintf(FERROR,"syntax error - %s does not take a value\n",tok);
00542 
00543                         {
00544                                 int on = socket_options[i].value;
00545                                 ret = setsockopt(fd,socket_options[i].level,
00546                                                  socket_options[i].option,(char *)&on,sizeof(int));
00547                         }
00548                         break;    
00549                 }
00550                 
00551                 if (ret != 0)
00552                         rprintf(FERROR, "failed to set socket option %s: %s\n", tok,
00553                                 strerror(errno));
00554         }
00555 
00556         free(options);
00557 }
00558 
00559 /**
00560  * Become a daemon, discarding the controlling terminal
00561  **/
00562 void become_daemon(void)
00563 {
00564         int i;
00565 
00566         if (fork()) {
00567                 _exit(0);
00568         }
00569 
00570         /* detach from the terminal */
00571 #ifdef HAVE_SETSID
00572         setsid();
00573 #else
00574 #ifdef TIOCNOTTY
00575         i = open("/dev/tty", O_RDWR);
00576         if (i >= 0) {
00577                 ioctl(i, (int) TIOCNOTTY, (char *)0);      
00578                 close(i);
00579         }
00580 #endif /* TIOCNOTTY */
00581 #endif
00582         /* make sure that stdin, stdout an stderr don't stuff things
00583            up (library functions, for example) */
00584         for (i=0;i<3;i++) {
00585                 close(i); 
00586                 open("/dev/null", O_RDWR);
00587         }
00588 }
00589 
00590 
00591 /**
00592  * This is like socketpair but uses tcp. It is used by the Samba
00593  * regression test code.
00594  * 
00595  * The function guarantees that nobody else can attach to the socket,
00596  * or if they do that this function fails and the socket gets closed
00597  * returns 0 on success, -1 on failure the resulting file descriptors
00598  * are symmetrical.
00599  **/
00600 static int socketpair_tcp(int fd[2])
00601 {
00602         int listener;
00603         struct sockaddr_in sock;
00604         struct sockaddr_in sock2;
00605         socklen_t socklen = sizeof(sock);
00606         int connect_done = 0;
00607         
00608         fd[0] = fd[1] = listener = -1;
00609 
00610         memset(&sock, 0, sizeof(sock));
00611         
00612         if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed;
00613 
00614         memset(&sock2, 0, sizeof(sock2));
00615 #ifdef HAVE_SOCKADDR_LEN
00616         sock2.sin_len = sizeof(sock2);
00617 #endif
00618         sock2.sin_family = PF_INET;
00619 
00620         bind(listener, (struct sockaddr *)&sock2, sizeof(sock2));
00621 
00622         if (listen(listener, 1) != 0) goto failed;
00623 
00624         if (getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0) goto failed;
00625 
00626         if ((fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed;
00627 
00628         set_nonblocking(fd[1]);
00629 
00630         sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
00631 
00632         if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) == -1) {
00633                 if (errno != EINPROGRESS) goto failed;
00634         } else {
00635                 connect_done = 1;
00636         }
00637 
00638         if ((fd[0] = accept(listener, (struct sockaddr *)&sock, &socklen)) == -1) goto failed;
00639 
00640         close(listener);
00641         if (connect_done == 0) {
00642                 if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) != 0
00643                     && errno != EISCONN) goto failed;
00644         }
00645 
00646         set_blocking (fd[1]);
00647 
00648         /* all OK! */
00649         return 0;
00650 
00651  failed:
00652         if (fd[0] != -1) close(fd[0]);
00653         if (fd[1] != -1) close(fd[1]);
00654         if (listener != -1) close(listener);
00655         return -1;
00656 }
00657 
00658 
00659 
00660 /**
00661  * Run a program on a local tcp socket, so that we can talk to it's
00662  * stdin and stdout.  This is used to fake a connection to a daemon
00663  * for testing -- not for the normal case of running SSH.
00664  *
00665  * @return a socket which is attached to a subprocess running
00666  * "prog". stdin and stdout are attached. stderr is left attached to
00667  * the original stderr
00668  **/
00669 int sock_exec(const char *prog)
00670 {
00671         int fd[2];
00672         
00673         if (socketpair_tcp(fd) != 0) {
00674                 rprintf (FERROR, RSYNC_NAME
00675                          ": socketpair_tcp failed (%s)\n",
00676                          strerror(errno));
00677                 return -1;
00678         }
00679         if (fork() == 0) {
00680                 close(fd[0]);
00681                 close(0);
00682                 close(1);
00683                 dup(fd[1]);
00684                 dup(fd[1]);
00685                 if (verbose > 3) {
00686                         /* Can't use rprintf because we've forked. */
00687                         fprintf (stderr,
00688                                  RSYNC_NAME ": execute socket program \"%s\"\n",
00689                                  prog);
00690                 }
00691                 exit (system (prog));
00692         }
00693         close (fd[1]);
00694         return fd[0];
00695 }
00696 
00697 
00698 

Generated on Tue Apr 16 12:37:37 2002 for rsync by doxygen1.2.15