"Fossies" - the Fresh Open Source Software Archive

Member "balance-3.57/balance.c" (28 Apr 2015, 51077 Bytes) of package /linux/privat/balance-3.57.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "balance.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.56_vs_3.57.

    1 /*
    2  * balance - a balancing tcp proxy
    3  * $Revision: 3.57 $
    4  *
    5  * Copyright (c) 2000-2009,2010 by Thomas Obermair (obermair@acm.org)
    6  * and Inlab Software GmbH (info@inlab.de), Gruenwald, Germany.
    7  * All rights reserved.
    8  *
    9  * Thanks to Bernhard Niederhammer for the initial idea and heavy
   10  * testing on *big* machines ...
   11  *
   12  * For license terms, see the file COPYING in this directory.
   13  *
   14  * This program is dedicated to Richard Stevens...
   15  *
   16  *  3.57
   17  *    MAXGROUPS has been increased to 32
   18  *  3.56
   19  *    added out-of-band data handling
   20  *    thanks to Julian Griffiths
   21  *  3.54
   22  *    fixed hash_fold bug regarding incoming IPv4 and IPv6 source addresses
   23  *  3.52
   24  *    thanks to David J. Jilk from Standing Cloud, Inc. for the following:
   25  *    added "nobuffer" functionality to interactive shell IO
   26  *    added new "assign" interactive command
   27  *    fixed locking bug
   28  *  3.50
   29  *    new option -6 forces IPv6 bind (hints.ai_family = AF_INET6)
   30  *  3.49
   31  *    ftok() patch applied (thanks to Vladan Djeric)  
   32  *  3.48
   33  *    Problems with setting IPV6_V6ONLY socket option are now handled 
   34  *    more nicely with a syslog warning message 
   35  *  3.42
   36  *    Balance compiles now on systems where IPV6_V6ONLY is undefined
   37  *  3.35
   38  *    bugfix in autodisable code (thanks to Michael Durket) 
   39  *  3.34
   40  *    syslog logging added (finally)
   41  *    -a autodisable option added (thanks to Mitsuru IWASAKI)
   42  *  3.33
   43  *    SO_KEEPALIVE switched on (suggested and implemented by A. Fluegel)
   44  *    new option -M to use a memory mapped file instead of IPC shared memory
   45  *  3.32
   46  *    /var/run/balance may already exist (thanks to Thomas Steudten)
   47  *  3.31
   48  *    TCP_NODELAY properly switched on (thanks to Kurt J. Lidl). 
   49  *  3.30
   50  *    Code cleanups and fixes (thanks to Kurt J. Lidl)
   51  *  3.28
   52  *    Code cleanup's (thanks to Thomas Steudten)
   53  *    MRTG-Interface (thanks to Brian McCann for the suggestion)
   54  *  3.26
   55  *    bugfix: master process was not found with balance -i
   56  *    unused variable pid removed (BSD)
   57  *  3.24
   58  *    bugfix in channel/group argument parsing (thanks to Enrique G. Paredes)
   59  *    permisions+error messages improvements (thanks to Wojciech Sobczuk)
   60  *  3.22
   61  *    writelock and channelcount patch from Stoyan Genov
   62  *    balance exit codes fix from Chris Wilson
   63  *    /var/run/balance is tried to be autocreated (if not there) 
   64  *    close of 0,1,2 on background operation
   65  *  3.19
   66  *    -h changed to -H
   67  *  3.17
   68  *    -h option added 
   69  *    thanks to Werner Maier
   70  *  3.16
   71  *    fixed missing save_tmout initialization
   72  *    thanks to Eric Andresen
   73  *  3.15
   74  *    first -B support
   75  *  3.14
   76  *    -Wall cleanup
   77  *  3.12
   78  *    alarm(0) added, thanks to Jon Christensen 
   79  *  3.11
   80  *    Bugfix
   81  *  3.10
   82  *    Bugfix for RedHat 7.2
   83  *  3.9
   84  *    Moved rendezvous file to /var/run and cleaned main(), thanks to
   85  *    Kayne Naughton
   86  *  3.8
   87  *    move to sigaction(), thanks to Kayne Naughton
   88  *  3.5
   89  *    Select-Timeout, thanks to Jeff Buhlmann
   90  *  3.2
   91  *    Hash groups and some other improvements
   92  *  2.24:
   93  *    'channel 2 overload' problem fixed, thanks to Ed "KuroiNeko"
   94  *  2.26:
   95  *    'endless loop error' fixed, thanks to Anthony Baxter
   96  *  2.27:
   97  *    strcmp on NULL removed, thanks to Jay. D. Allen
   98  *  2.28:
   99  *    bsent and breceived now unsigned to avoid negative values,
  100  *    thanks to Anthony Baxter
  101  *  2.29:
  102  *    error in setaddress() fixed, thanks to Dirk Datzert
  103  *  2.30:
  104  *    fixing #includes for *bsd compability
  105  *  2.31:
  106  *  2.32:
  107  *    redefied SIGCHLD handling to be compatible with FreeBSD 4.3,
  108  *    BSD/OS 4.2 and BSD/OS 4.0.1
  109  *  2.33
  110  *    finally included SO_REUSEADDR
  111  *
  112  */
  113 
  114 #include <balance.h>
  115 
  116 const char *balance_rcsid = "$Id: balance.c,v 3.57 2015/04/28 07:49:16 t Exp $";
  117 static char *revision = "$Revision: 3.57 $";
  118 
  119 static int release;
  120 static int subrelease;
  121 
  122 static char rendezvousfile[FILENAMELEN];
  123 static int rendezvousfd;
  124 #ifndef NO_MMAP
  125 static int shmfilefd;
  126 #endif
  127 
  128 static int cur_s;
  129 static int cur_r;
  130 
  131 static void  urg_handler(int signo) {
  132     int n;
  133     char buf[256];
  134 
  135     n = recv(cur_r,buf,sizeof buf,MSG_OOB);
  136     if ( n < 0 ) {
  137         //printf("ERROR: recv(2)\n");
  138     }else{
  139 
  140         buf[n] = 0;
  141         //printf("URG '%s' (%d)\n", buf,n);
  142         send(cur_s, buf,strlen(buf),MSG_OOB);
  143     }
  144     signal(SIGURG, urg_handler);
  145 }
  146 
  147 static int err_dump(char *text) {
  148   fprintf(stderr, "balance: %s\n", text);
  149   fflush(stderr);
  150   exit(EX_UNAVAILABLE);
  151 }
  152 
  153 COMMON *common;
  154 
  155 static int hashfailover = 0;
  156 static int autodisable = 0;
  157 static int debugflag = 0;
  158 static int foreground = 0;
  159 static int packetdump = 0;
  160 static int interactive = 0;
  161 static int shmmapfile = 0;
  162 static int bindipv6 = 0;
  163 
  164 static int sockbufsize = 32768;
  165 
  166 static int connect_timeout;
  167 
  168 static char *bindhost = NULL;
  169 static char *outbindhost = NULL;
  170 
  171 static struct timeval sel_tmout  = { 0, 0 }; /* seconds, microseconds */
  172 static struct timeval save_tmout = { 0, 0 }; /* seconds, microseconds */
  173 
  174 int create_serversocket(char* node, char* service) {
  175   struct addrinfo hints;
  176   struct addrinfo *results;
  177   int srv_socket, status, sockopton, sockoptoff;
  178 
  179   bzero(&hints, sizeof(hints));
  180   hints.ai_flags = AI_PASSIVE;
  181 
  182   if(bindipv6) {
  183     if(debugflag) {
  184       fprintf(stderr, "using AF_INET6\n");
  185     }
  186     hints.ai_family = AF_INET6;
  187   } else {
  188     if(debugflag) {
  189       fprintf(stderr, "using AF_UNSPEC\n");
  190     }
  191     hints.ai_family = AF_UNSPEC;
  192   }
  193   hints.ai_socktype = SOCK_STREAM;
  194   hints.ai_protocol = IPPROTO_TCP;
  195 
  196   status = getaddrinfo(node, service, &hints, &results);
  197   if(status != 0) {
  198     fprintf(stderr,"error at getaddrinfo: %s\n", gai_strerror(status));
  199     fprintf(stderr,"exiting.\n");
  200     exit(EX_OSERR);
  201   }
  202 
  203   if(results == NULL) {
  204     fprintf(stderr,"no matching results at getaddrinfo\n");
  205     fprintf(stderr,"exiting.\n");
  206     exit(EX_OSERR);
  207   }
  208 
  209   srv_socket = socket(results->ai_family, results->ai_socktype, results->ai_protocol);
  210   if(srv_socket < 0) {
  211     perror("socket()");
  212     exit(EX_OSERR);
  213   }
  214 
  215   sockoptoff = 0;
  216 
  217 #if defined(IPV6_V6ONLY)
  218   status = setsockopt(srv_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char*) &sockoptoff, sizeof(sockoptoff));
  219   if(status < 0) {
  220     syslog(LOG_WARNING,"setsockopt(IPV6_V6ONLY=0) failed");
  221   }
  222 #endif
  223 
  224   sockopton = 1;
  225 
  226   status = setsockopt(srv_socket, SOL_SOCKET, SO_REUSEADDR, (char*) &sockopton, sizeof(sockopton));
  227 
  228   if(status < 0) {
  229     perror("setsockopt(SO_REUSEADDR=1)");
  230     exit(EX_OSERR);
  231   }
  232 
  233   status = bind(srv_socket, results->ai_addr, results->ai_addrlen);
  234   if(status < 0) {
  235     perror("bind()");
  236     exit(EX_OSERR);
  237   }
  238 
  239   status = listen(srv_socket, SOMAXCONN);
  240   if(status < 0) {
  241     perror("listen()");
  242     exit(EX_OSERR);
  243   }
  244 
  245   return(srv_socket);
  246 }
  247 
  248 /* locking ... */
  249 
  250 int a_readlock(off_t start, off_t len) {
  251   int rc;
  252   struct flock fdata;
  253   fdata.l_type = F_RDLCK;
  254   fdata.l_whence = SEEK_SET;
  255   fdata.l_start = 0;
  256   fdata.l_len = 0;
  257   // fdata.l_sysid=0;
  258   // fdata.l_pid=0;
  259 repeat:
  260   if ((rc = fcntl(rendezvousfd, F_SETLKW, &fdata)) < 0) {
  261     if (errno == EINTR) {
  262       goto repeat;      // 8-)
  263     } else {
  264       perror("readlock");
  265       exit(EX_OSERR);
  266     }
  267   }
  268   return (rc);
  269 }
  270 
  271 void b_readlock(void) {
  272   a_readlock(0, 0);
  273 }
  274 
  275 void c_readlock(int group, int channel) {
  276   a_readlock(((char *) &(grp_channel(common, group, channel))) -
  277          (char *) common, sizeof(CHANNEL));
  278 }
  279 
  280 int a_writelock(off_t start, off_t len) {
  281   int rc;
  282   struct flock fdata;
  283   fdata.l_type = F_WRLCK;
  284   fdata.l_whence = SEEK_SET;
  285   fdata.l_start = 0;
  286   fdata.l_len = 0;
  287   // fdata.l_sysid=0;
  288   // fdata.l_pid=0;
  289 repeat:
  290   if ((rc = fcntl(rendezvousfd, F_SETLKW, &fdata)) < 0) {
  291     if (errno == EINTR) {
  292       goto repeat;      // 8-)
  293     } else {
  294       perror("a_writelock");
  295       exit(EX_OSERR);
  296     }
  297   }
  298   return (rc);
  299 }
  300 
  301 void b_writelock(void) {
  302   a_writelock(0, 0);
  303 }
  304 
  305 void c_writelock(int group, int channel)
  306 {
  307   a_writelock(((char *) &(grp_channel(common, group, channel))) -
  308           (char *) common, sizeof(CHANNEL));
  309 }
  310 
  311 int a_unlock(off_t start, off_t len)
  312 {
  313   int rc;
  314   struct flock fdata;
  315   fdata.l_type = F_UNLCK;
  316   fdata.l_whence = SEEK_SET;
  317   fdata.l_start = 0;
  318   fdata.l_len = 0;
  319   // fdata.l_sysid=0;
  320   // fdata.l_pid=0;
  321 repeat:
  322   if ((rc = fcntl(rendezvousfd, F_SETLK, &fdata)) < 0) {
  323     if (errno == EINTR) {
  324       goto repeat;      // 8-)
  325     } else {
  326       perror("a_unlock");
  327       exit(EX_OSERR);
  328     }
  329   }
  330   return (rc);
  331 }
  332 
  333 void b_unlock(void)
  334 {
  335   a_unlock(0, 0);
  336 }
  337 
  338 void c_unlock(int group, int channel)
  339 {
  340   a_unlock(((char *) &(grp_channel(common, group, channel))) -
  341        (char *) common, sizeof(CHANNEL));
  342 }
  343 
  344 void *shm_malloc(char *file, int size)
  345 {
  346   char *data = NULL;
  347   key_t key;
  348   int shmid;
  349 
  350   if(shmmapfile){
  351 #ifndef NO_MMAP
  352     char shmfile[FILENAMELEN];
  353 
  354     strcpy(shmfile, file);
  355     strcat(shmfile, SHMFILESUFFIX);
  356     shmfilefd = open(shmfile, O_RDWR | O_CREAT, 0644);
  357     if(shmfilefd < 0) {
  358       fprintf(stderr, "Warning: Cannot open file `%s', switching to IPC\n", shmfile);
  359       shmmapfile = 0;
  360     }
  361     if(shmmapfile) {
  362       if(ftruncate(shmfilefd, size) < 0) {
  363         fprintf(stderr, "Warning: Cannot set file size on `%s', switching to IPC\n", shmfile);
  364         close(shmfilefd);
  365         shmmapfile = 0;
  366       }
  367     }
  368     if(shmmapfile) {
  369       data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, shmfilefd, 0);
  370       if(!data || data == MAP_FAILED) {
  371         fprintf(stderr, "Warning: Cannot map file `%s', switching to IPC\n", shmfile);
  372         close(shmfilefd);
  373         shmmapfile = 0;
  374 
  375       }
  376     }
  377 #endif
  378   }
  379 
  380   if(!shmmapfile){
  381 
  382 #if defined (__SVR4) && defined (__sun)
  383 
  384     /* vdjeric:
  385        Solaris ftok() causes frequent collisions because it uses
  386        only the lower 12 bits of the inode number in the 'key'.
  387        See: http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=4265917
  388     */
  389     
  390     FILE *rendezvousfp = NULL;
  391     struct timeval ct;
  392     long int seed;
  393     int i;
  394 
  395     if ((rendezvousfp = fdopen(rendezvousfd, "w+")) == NULL) {
  396       perror("fdopen");
  397       exit(EX_OSERR);
  398     }
  399 
  400     if ((fscanf(rendezvousfp, "0x%x\n", &key)) <= 0) {
  401       gettimeofday(&ct, NULL);
  402       seed = ct.tv_usec * getpid(); 
  403       srand(seed);
  404 
  405       /* Solaris rand() returns values between 0 and 0x7fff, 
  406          so generate key byte by byte */
  407       key = 0;
  408       for (i = 0; i < sizeof(key); i++) {
  409           key = (key << 8) | (rand() & 0xff);
  410       }
  411 
  412       if(fseek(rendezvousfp, 0, SEEK_SET) == -1) {
  413         perror("fseek");
  414         exit(EX_OSERR);
  415       }
  416       if (fprintf(rendezvousfp, "0x%08x\n", key) == -1) {
  417         perror("fprintf");
  418         exit(EX_OSERR);
  419       }
  420       fflush(rendezvousfp);
  421     }
  422 #else
  423     if ((key = ftok(file, 'x')) == -1) {
  424       perror("ftok");
  425       exit(EX_SOFTWARE);
  426     }
  427 #endif
  428 
  429     if ((shmid = shmget(key, size, 0644 | IPC_CREAT)) == -1) {
  430       perror("shmget");
  431       exit(EX_OSERR);
  432     }
  433 
  434     data = shmat(shmid, (void *) 0, 0);
  435     if (data == (char *) (-1)) {
  436      perror("shmat");
  437       exit(EX_OSERR);
  438     }
  439   }
  440 
  441   return (data);
  442 }
  443 
  444 /* readable output of a packet (-p) */
  445 
  446 void print_packet(unsigned char *s, int l)
  447 {
  448   int i, cc;
  449   cc = 0;
  450   for (i = 0; i < l; i++) {
  451     if (isprint(s[i]) && isascii(s[i])) {
  452       if (s[i] == '\\') {
  453     printf("\\\\");
  454     cc += 2;
  455       } else {
  456     printf("%c", s[i]);
  457     cc++;
  458       }
  459     } else {
  460       printf("\\%02X", s[i]);
  461       cc += 3;
  462       if (s[i] == '\n') {
  463     printf("\n");
  464     cc = 0;
  465       }
  466     }
  467     if (cc > 80) {
  468       printf("\n");
  469       cc = 0;
  470     }
  471   }
  472   printf("\n");
  473 }
  474 
  475 int getport(char *port)
  476 {
  477   struct servent *sp;
  478   sp = getservbyname(port, "tcp");
  479   if (sp == NULL) {
  480     return (atoi(port));
  481   } else {
  482     return (ntohs(sp->s_port));
  483   }
  484 }
  485 
  486 void setipaddress(struct in_addr *ipaddr, char *string)
  487 {
  488   struct hostent *hent;
  489   hent = gethostbyname(string);
  490   if (hent == NULL) {
  491     if ((ipaddr->s_addr = inet_addr(string)) == INADDR_NONE) {
  492       fprintf(stderr, "unknown or invalid address [%s]\n", string);
  493       exit(EX_DATAERR);
  494     }
  495   } else {
  496     memcpy(ipaddr, hent->h_addr, hent->h_length);
  497   }
  498 }
  499 
  500 void setaddress(struct in_addr *ipaddr, int *port, char *string,
  501         int default_port, int *maxc)
  502 {
  503   char *host_string = NULL;
  504   char *port_string = NULL;
  505   char *maxc_string = NULL;
  506   char *dup_string = NULL;
  507   char *p = NULL;
  508   char *q = NULL;
  509 
  510   struct hostent *hent;
  511 
  512   if ((dup_string = strdup(string)) == NULL) {
  513     fprintf(stderr, "strdup() failed\n");
  514     exit(EX_OSERR);
  515   }
  516 
  517   host_string = dup_string;
  518   p = index(dup_string, ':');
  519 
  520   if (p != NULL) {
  521     *p = '\000';
  522     port_string = p + 1;
  523     if ((q = index(port_string, ':')) != NULL) {
  524       *q = '\000';
  525       maxc_string = q + 1;
  526     } else {
  527       maxc_string = "";
  528     }
  529   } else {
  530     port_string = "";
  531     maxc_string = "";
  532   }
  533 
  534   // fix for RedHat 7.0/7.1 choke on strcmp with NULL
  535 
  536   if (port_string != NULL && !strcmp(port_string, ""))
  537     port_string = NULL;
  538   if (maxc_string != NULL && !strcmp(maxc_string, ""))
  539     maxc_string = NULL;
  540 
  541   hent = gethostbyname(dup_string);
  542   if (hent == NULL) {
  543     if ((ipaddr->s_addr = inet_addr(dup_string)) == INADDR_NONE) {
  544       fprintf(stderr, "unknown or invalid address [%s]\n", dup_string);
  545       exit(EX_DATAERR);
  546     }
  547   } else {
  548     memcpy(ipaddr, hent->h_addr, hent->h_length);
  549   }
  550 
  551   if (port_string != NULL) {
  552     *port = getport(port_string);
  553   } else {
  554     *port = default_port;
  555   }
  556 
  557   if (maxc_string != NULL) {
  558     *maxc = atoi(maxc_string);
  559   }
  560   free(dup_string);
  561 }
  562 
  563 int setaddress_noexitonerror(struct in_addr *ipaddr, int *port,
  564                  char *string, int default_port)
  565 {
  566   char *host_string;
  567   char *port_string;
  568   struct hostent *hent;
  569   host_string = strtok(string, ":");
  570   port_string = strtok(NULL, ":");
  571   hent = gethostbyname(string);
  572   if (hent == NULL) {
  573     if ((ipaddr->s_addr = inet_addr(string)) == INADDR_NONE) {
  574       return (0);
  575     }
  576   } else {
  577     memcpy(ipaddr, hent->h_addr, hent->h_length);
  578   }
  579 
  580   if (port_string != NULL) {
  581     *port = getport(port_string);
  582   } else {
  583     *port = default_port;
  584   }
  585   return (1);
  586 }
  587 
  588 int readline(int fd, char *ptr, int maxlen)
  589 {
  590   int n, rc;
  591   char c;
  592 
  593   for (n = 1; n < maxlen; n++) {
  594     if ((rc = read(fd, &c, 1)) == 1) {
  595       *ptr++ = c;
  596       if (c == '\n') {
  597     break;
  598       }
  599     } else if (rc == 0) {
  600       if (n == 1) {
  601     return (0);     // EOF, no data read 
  602       } else {
  603     break;          // EOF, some data was read 
  604       }
  605     } else {
  606       return (-1);      // error 
  607     }
  608   }
  609   *ptr = 0;
  610   return (n);
  611 }
  612 
  613 int forward(int fromfd, int tofd, int groupindex, int channelindex)
  614 {
  615   ssize_t rc;
  616   unsigned char buffer[MAXTXSIZE];
  617 
  618     cur_s = tofd;
  619     cur_r = fromfd;
  620 /*
  621   struct sigaction urg_action;
  622 
  623     urg_action.sa_handler = urg_handler;
  624     urg_action.sa_flags = SA_RESTART;
  625     sigemptyset(&urg_action.sa_mask);
  626     sigaction(SIGURG, &urg_action, NULL);
  627 */
  628     signal(SIGURG, &urg_handler);
  629 
  630 
  631 
  632   //rc = read(fromfd, buffer, MAXTXSIZE);
  633   fcntl(fromfd, F_SETOWN,getpid());
  634   rc = recv(fromfd, buffer, MAXTXSIZE, 0);
  635 
  636   if (packetdump) {
  637     printf("-> %d\n", (int) rc);
  638     print_packet(buffer, rc);
  639   }
  640 
  641   if (rc <= 0) {
  642     return (-1);
  643   } else {
  644     if (writen(tofd, buffer, rc) != rc) {
  645       return (-1);
  646     }
  647     c_writelock(groupindex, channelindex);
  648     chn_bsent(common, groupindex, channelindex) += rc;
  649     c_unlock(groupindex, channelindex);
  650   }
  651   return (0);
  652 }
  653 
  654 int backward(int fromfd, int tofd, int groupindex, int channelindex)
  655 {
  656   ssize_t rc;
  657   unsigned char buffer[MAXTXSIZE];
  658   rc = read(fromfd, buffer, MAXTXSIZE);
  659 
  660   if (packetdump) {
  661     printf("-< %d\n", (int) rc);
  662     print_packet(buffer, rc);
  663   }
  664 
  665   if (rc <= 0) {
  666     return (-1);
  667   } else {
  668     if (writen(tofd, buffer, rc) != rc) {
  669       return (-1);
  670     }
  671     c_writelock(groupindex, channelindex);
  672     chn_breceived(common, groupindex, channelindex) += rc;
  673     c_unlock(groupindex, channelindex);
  674   }
  675   return (0);
  676 }
  677 
  678 /*
  679  * the connection is really established, let's transfer the data
  680  *  as efficient as possible :-) 
  681  */
  682 
  683 void stream2(int clientfd, int serverfd, int groupindex, int channelindex)
  684 {
  685   fd_set readfds;
  686   int fdset_width;
  687   int sr;
  688   int optone = 1;
  689 
  690   fdset_width = ((clientfd > serverfd) ? clientfd : serverfd) + 1;
  691 
  692   /* failure is acceptable */
  693   (void) setsockopt(serverfd, IPPROTO_TCP, TCP_NODELAY,
  694     (char *)&optone, (socklen_t)sizeof(optone));
  695   (void) setsockopt(clientfd, IPPROTO_TCP, TCP_NODELAY,
  696     (char *)&optone, (socklen_t)sizeof(optone));
  697   (void) setsockopt(serverfd, SOL_SOCKET, SO_KEEPALIVE,
  698     (char *)&optone, (socklen_t)sizeof(optone));
  699   (void) setsockopt(clientfd, SOL_SOCKET, SO_KEEPALIVE,
  700     (char *)&optone, (socklen_t)sizeof(optone));
  701 
  702   for (;;) {
  703 
  704     FD_ZERO(&readfds);
  705     FD_SET(clientfd, &readfds);
  706     FD_SET(serverfd, &readfds);
  707     /*
  708      * just in case this system modifies the timeout values,
  709      * refresh the values from a saved copy of them.
  710      */
  711     sel_tmout = save_tmout;
  712 
  713     for (;;) {
  714       if (sel_tmout.tv_sec || sel_tmout.tv_usec) {
  715     sr = select(fdset_width, &readfds, NULL, NULL, &sel_tmout);
  716       } else {
  717     sr = select(fdset_width, &readfds, NULL, NULL, NULL);
  718       }
  719       if ((save_tmout.tv_sec || save_tmout.tv_usec) && !sr) {
  720     c_writelock(groupindex, channelindex);
  721     chn_c(common, groupindex, channelindex) -= 1;
  722     c_unlock(groupindex, channelindex);
  723     fprintf(stderr, "timed out after %d seconds\n",
  724         (int) save_tmout.tv_sec);
  725     exit(EX_UNAVAILABLE);
  726       }
  727       if (sr < 0 && errno != EINTR) {
  728     c_writelock(groupindex, channelindex);
  729     chn_c(common, groupindex, channelindex) -= 1;
  730     c_unlock(groupindex, channelindex);
  731     err_dump("select error");
  732       }
  733       if (sr > 0)
  734     break;
  735     }
  736 
  737     if (FD_ISSET(clientfd, &readfds)) {
  738       if (forward(clientfd, serverfd, groupindex, channelindex) < 0) {
  739     break;
  740       }
  741     } else {
  742       if (backward(serverfd, clientfd, groupindex, channelindex) < 0) {
  743     break;
  744       }
  745     }
  746   }
  747   c_writelock(groupindex, channelindex);
  748   chn_c(common, groupindex, channelindex) -= 1;
  749   c_unlock(groupindex, channelindex);
  750   exit(EX_OK);
  751 }
  752 
  753 
  754 void alrm_handler(int signo) {
  755 }
  756 
  757 void usr1_handler(int signo) {
  758 }
  759 
  760 void chld_handler(int signo) {
  761   int status;
  762   while (waitpid(-1, &status, WNOHANG) > 0);
  763 }
  764 
  765 /*
  766  * a channel in a group is selected and we try to establish a connection 
  767  */
  768 
  769 void *stream(int arg, int groupindex, int index, char *client_address,
  770          int client_address_size) {
  771   int startindex;
  772   int sockfd;
  773   int clientfd;
  774   struct sigaction alrm_action;
  775   struct sockaddr_in serv_addr;
  776 
  777   startindex = index;       // lets keep where we start...
  778   clientfd = arg;
  779 
  780   for (;;) {
  781 
  782     if (debugflag) {
  783       fprintf(stderr, "trying group %d channel %d ... ", groupindex,
  784           index);
  785       fflush(stderr);
  786     }
  787 
  788     if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
  789       err_dump("can't open stream socket");
  790     }
  791 
  792     (void) setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sockbufsize,
  793       sizeof(sockbufsize));
  794     (void) setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &sockbufsize,
  795       sizeof(sockbufsize));
  796 
  797     /*
  798      *  if -B is specified, balance tries to bind to it even on
  799      *  outgoing connections 
  800      */
  801 
  802     if (outbindhost != NULL) {
  803       struct sockaddr_in outbind_addr;
  804       bzero((char *) &outbind_addr, sizeof(outbind_addr));
  805       outbind_addr.sin_family = AF_INET;
  806       setipaddress(&outbind_addr.sin_addr, outbindhost);
  807       if (bind
  808       (sockfd, (struct sockaddr *) &outbind_addr,
  809        sizeof(outbind_addr)) < 0) {
  810       }
  811     }
  812 
  813     b_readlock();
  814     bzero((char *) &serv_addr, sizeof(serv_addr));
  815     serv_addr.sin_family = AF_INET;
  816     serv_addr.sin_addr.s_addr =
  817     chn_ipaddr(common, groupindex, index).s_addr;
  818     serv_addr.sin_port = htons(chn_port(common, groupindex, index));
  819     b_unlock();
  820 
  821 
  822     alrm_action.sa_handler = alrm_handler;
  823     alrm_action.sa_flags = 0;   // don't restart !
  824     sigemptyset(&alrm_action.sa_mask);
  825     sigaction(SIGALRM, &alrm_action, NULL);
  826     alarm(connect_timeout);
  827 
  828     if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
  829       if (debugflag) {
  830     if (errno == EINTR) {
  831       fprintf(stderr, "timeout group %d channel %d\n", groupindex,
  832           index);
  833     } else {
  834       fprintf(stderr, "connection refused group %d channel %d\n",
  835           groupindex, index);
  836     }
  837       }
  838 
  839       /* here we've received an error (either 'timeout' or 'connection refused')
  840        * let's start some magical failover mechanisms 
  841        */
  842 
  843       c_writelock(groupindex, index);
  844       chn_c(common, groupindex, index)--;
  845       if(autodisable) {
  846     if(chn_status(common, groupindex, index) != 0) {
  847       if(foreground) {
  848         fprintf(stderr, "connection failed group %d channel %d\n", groupindex, index);
  849         fprintf(stderr, "%s:%d needs to be enabled manually using balance -i after the problem is solved\n", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port));
  850       } else {
  851           syslog(LOG_NOTICE,"connection failed group %d channel %d", groupindex, index);
  852           syslog(LOG_NOTICE,"%s:%d needs to be enabled manually using balance -i after the problem is solved", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port));
  853       }
  854       chn_status(common, groupindex, index) = 0;
  855     }
  856       }
  857       c_unlock(groupindex, index);
  858 
  859       b_readlock();
  860       for (;;) {
  861     for (;;) {
  862       if (grp_type(common, groupindex) == GROUP_RR || hashfailover == 1) {
  863         index++;
  864         if (index >= grp_nchannels(common, groupindex)) {
  865           index = 0;
  866         }
  867         if (index == startindex) {
  868           index = -1;   // Giveup 
  869           break;
  870         }
  871         if (chn_status(common, groupindex, index) == 1 &&
  872         (chn_maxc(common, groupindex, index) == 0 ||
  873          (chn_c(common, groupindex, index) <
  874           chn_maxc(common, groupindex, index)))) {
  875           break;        // new index found 
  876         } else {
  877           continue;
  878         }
  879       } else if (grp_type(common, groupindex) == GROUP_HASH) {
  880 
  881         // If the current group is type hash, we giveup immediately 
  882         index = -1;
  883         break;
  884       } else {
  885         err_dump("PANIC: invalid group in stream()");
  886       }
  887     }
  888 
  889     if (index >= 0) {
  890       // neuer index in groupindex-group found...
  891       break;
  892     } else {
  893     again:
  894       groupindex++;
  895       if (groupindex >= MAXGROUPS) {
  896         // giveup, index=-1.
  897         break;
  898       } else {
  899         if (grp_type(common, groupindex) == GROUP_RR) {
  900 
  901           if (grp_nchannels(common, groupindex) > 0) {
  902         index = grp_current(common, groupindex);
  903         startindex = index; // This fixes the "endless loop error"
  904                     // with all hosts being down and one
  905                     // in the last group... (from Anthony Baxter) 
  906           } else {
  907         goto again;
  908           }
  909           break;
  910         } else if (grp_type(common, groupindex) == GROUP_HASH) {
  911           unsigned int uindex;
  912           uindex = hash_fold((unsigned char*) &(((struct sockaddr_in6 *) &client_address)->sin6_addr), client_address_size);
  913 
  914           if (debugflag) {
  915         fprintf(stderr, "HASH-method: fold returns %u\n", uindex);
  916               }
  917 
  918           index = uindex % grp_nchannels(common, groupindex);
  919           if (debugflag)
  920         fprintf(stderr, "modulo %d gives %d\n",
  921             grp_nchannels(common, groupindex), index);
  922 
  923           if (chn_status(common, groupindex, index) == 1 &&
  924           (chn_maxc(common, groupindex, index) == 0 ||
  925            (chn_c(common, groupindex, index) <
  926             chn_maxc(common, groupindex, index)))
  927           ) {
  928         break;
  929           } else {
  930         goto again; // next group !
  931           }
  932         } else {
  933           err_dump("PANIC: invalid group in stream()");
  934         }
  935       }
  936     }
  937       }
  938       // we drop out here with a new index
  939 
  940       b_unlock();
  941 
  942       if (index >= 0) {
  943     // lets try it again 
  944     close(sockfd);
  945     c_writelock(groupindex, index);
  946     chn_c(common, groupindex, index) += 1;
  947     chn_tc(common, groupindex, index) += 1;
  948     c_unlock(groupindex, index);
  949     continue;
  950       } else {
  951     break;
  952       }
  953 
  954     } else {
  955       alarm(0);         // Cancel the alarm since we successfully connected
  956       if (debugflag) {
  957     fprintf(stderr, "connect to channel %d successful\n", index);
  958       }
  959       // this prevents the 'channel 2 overload problem'
  960 
  961       b_writelock();
  962       grp_current(common, groupindex) = index;
  963       grp_current(common, groupindex)++;
  964       if (grp_current(common, groupindex) >=
  965       grp_nchannels(common, groupindex)) {
  966     grp_current(common, groupindex) = 0;
  967       }
  968       b_unlock();
  969 
  970       // everything's fine ... 
  971 
  972       stream2(clientfd, sockfd, groupindex, index);
  973       // stream2 bekommt den Channel-Index mit
  974       // stream2 never returns, but just in case...
  975       break;
  976     }
  977   }
  978 
  979   close(sockfd);
  980   exit(EX_OK);
  981 }
  982 
  983 static
  984 void initialize_release_variables(void)
  985 {
  986   char *version;
  987   char *revision_copy;
  988   char *token;
  989 
  990   if ((revision_copy = (char *) malloc(strlen(revision) + 1)) == NULL) {
  991     fprintf(stderr, "malloc problem in initialize_release_variables()\n");
  992   } else {
  993     strcpy(revision_copy, revision);
  994     token = strtok(revision_copy, " ");
  995     token = strtok(NULL, " ");
  996     version = token != NULL ? token : "0.0";
  997     release = atoi(version);
  998     if (strlen(version) >= 3) {
  999       subrelease = atoi(version + 2);
 1000     } else {
 1001       subrelease = 0;
 1002     }
 1003     free(revision_copy);
 1004   }
 1005 }
 1006 
 1007 static
 1008 void usage(void)
 1009 {
 1010   fprintf(stderr," _           _\n");
 1011   fprintf(stderr,"| |__   __ _| | __ _ _ __   ___ ___\n");
 1012   fprintf(stderr,"| '_ \\ / _` | |/ _` | '_ \\ / __/ _ \\\n");
 1013   fprintf(stderr,"| |_) | (_| | | (_| | | | | (_|  __/\n");
 1014   fprintf(stderr,"|_.__/ \\__,_|_|\\__,_|_| |_|\\___\\___|\n");
 1015 
 1016 
 1017   fprintf(stderr, "  this is balance %d.%d\n", release, subrelease);
 1018   fprintf(stderr, "  Copyright (c) 2000-2009,2010\n");
 1019   fprintf(stderr, "  by Inlab Software GmbH, Gruenwald, Germany.\n");
 1020   fprintf(stderr, "  All rights reserved.\n");
 1021   fprintf(stderr, "\n");
 1022 
 1023   fprintf(stderr, "usage:\n");
 1024   fprintf(stderr, "  balance [-b addr] [-B addr] [-t sec] [-T sec] [-adfpHM] \\\n");
 1025   fprintf(stderr, "          port [h1[:p1[:maxc1]] [!%%] [ ... hN[:pN[:maxcN]]]]\n");
 1026   fprintf(stderr, "  balance [-b addr] -i [-d] port\n");
 1027   fprintf(stderr, "  balance [-b addr] -c cmd  [-d] port\n");
 1028   fprintf(stderr, "\n");
 1029   fprintf(stderr, "  -a        enable channel autodisable option\n");
 1030   fprintf(stderr, "  -b host   bind to specific address on listen\n");
 1031   fprintf(stderr, "  -B host   bind to specific address for outgoing connections\n");
 1032   fprintf(stderr, "  -c cmd    execute specified interactive command\n");
 1033   fprintf(stderr, "  -d        debugging on\n");
 1034   fprintf(stderr, "  -f        stay in foregound\n");
 1035   fprintf(stderr, "  -i        interactive control\n");
 1036   fprintf(stderr, "  -H        failover even if Hash Type is used\n");
 1037   fprintf(stderr, "  -M        use MMAP instead of SHM for IPC\n");
 1038   fprintf(stderr, "  -p        packetdump\n");
 1039   fprintf(stderr, "  -t sec    specify connect timeout in seconds (default=%d)\n", DEFAULTTIMEOUT);
 1040   fprintf(stderr, "  -T sec    timeout (seconds) for select (0 => never) (default=%d)\n", DEFAULTSELTIMEOUT);
 1041   fprintf(stderr, "   !        separates channelgroups (declaring previous to be Round Robin)\n");
 1042   fprintf(stderr, "   %%        as !, but declaring previous group to be a Hash Type\n");
 1043 
 1044   fprintf(stderr, "\n");
 1045   fprintf(stderr, "examples:\n");
 1046   fprintf(stderr, "  balance smtp mailhost1:smtp mailhost2:25 mailhost3\n");
 1047   fprintf(stderr, "  balance -i smtp\n");
 1048   fprintf(stderr, "  balance -b 2001:DB8::1 80 10.1.1.1 10.1.1.2\n");
 1049   fprintf(stderr, "  balance -b 2001:DB8::1 80\n");
 1050   fprintf(stderr, "\n");
 1051 
 1052   exit(EX_USAGE);
 1053 }
 1054 
 1055 // goto background: 
 1056 
 1057 void background(void) {
 1058   int childpid;
 1059   if ((childpid = fork()) < 0) {
 1060     fprintf(stderr, "cannot fork\n");
 1061     exit(EX_OSERR);
 1062   } else {
 1063     if (childpid > 0) {
 1064       exit(EX_OK);      /* parent */
 1065     }
 1066   }
 1067 #ifdef BalanceBSD
 1068   setpgid(getpid(), 0);
 1069 #else
 1070   setpgrp();
 1071 #endif
 1072   if(chdir("/") <0) 
 1073     fprintf(stderr, "cannot chdir\n");
 1074   close(0);
 1075   close(1);
 1076   close(2);
 1077 }
 1078 
 1079 COMMON *makecommon(int argc, char **argv, int source_port)
 1080 {
 1081   int i;
 1082   int group;
 1083   int channel;
 1084   COMMON *mycommon;
 1085   int numchannels = argc - 1;   // port number is first argument
 1086 
 1087   if (numchannels >= MAXCHANNELS) {
 1088     fprintf(stderr, "MAXCHANNELS exceeded...\n");
 1089     exit(EX_USAGE);
 1090   }
 1091 
 1092   if ((rendezvousfd = open(rendezvousfile, O_RDWR, 0)) < 0) {
 1093     perror("open");
 1094     fprintf(stderr,"check rendezvousfile permissions [%s]\n",rendezvousfile);
 1095     exit(EX_NOINPUT);
 1096   }
 1097 
 1098   b_writelock();
 1099 
 1100   if ((mycommon =
 1101        (COMMON *) shm_malloc(rendezvousfile, sizeof(COMMON))) == NULL) {
 1102     fprintf(stderr, "cannot alloc COMMON struct\n");
 1103     exit(EX_OSERR);
 1104   }
 1105 
 1106   mycommon->pid = getpid();
 1107   mycommon->release = release;
 1108   mycommon->subrelease = subrelease;
 1109 
 1110   for (group = 0; group < MAXGROUPS; group++) {
 1111     grp_nchannels(mycommon, group) = 0;
 1112     grp_current(mycommon, group) = 0;
 1113     grp_type(mycommon, group) = GROUP_RR;   // Default: RR
 1114   }
 1115 
 1116   group = 0;
 1117   channel = 0;
 1118 
 1119   for (i = 1; i < argc; i++) {
 1120     if (!strcmp(argv[i], "!")) {
 1121       // This is a normal "GROUP_RR"-Type of Group
 1122       if(channel <= 0) {
 1123     err_dump("no channels in group");
 1124       }
 1125       grp_type(mycommon, group) = GROUP_RR;
 1126       group++;
 1127       channel = 0;
 1128       if (group >= MAXGROUPS) {
 1129     err_dump("too many groups");
 1130       }
 1131     } else if (!strcmp(argv[i], "%")) {
 1132       // This is a "GROUP_HASH"
 1133       if(channel <= 0) {
 1134     err_dump("no channels in group");
 1135       }
 1136       grp_type(mycommon, group) = GROUP_HASH;
 1137       group++;
 1138       channel = 0;
 1139       if (group >= MAXGROUPS) {
 1140     err_dump("too many groups");
 1141       }
 1142     } else {
 1143       chn_status(mycommon, group, channel) = 1;
 1144       chn_c(mycommon, group, channel) = 0;  // connections...
 1145       chn_tc(mycommon, group, channel) = 0; // total connections...
 1146       chn_maxc(mycommon, group, channel) = 0;   // maxconnections...
 1147       setaddress(&chn_ipaddr(mycommon, group, channel),
 1148          &chn_port(mycommon, group, channel),
 1149          argv[i],
 1150          source_port, &chn_maxc(mycommon, group, channel));
 1151       chn_bsent(mycommon, group, channel) = 0;
 1152       chn_breceived(mycommon, group, channel) = 0;
 1153 
 1154       grp_nchannels(mycommon, group) += 1;
 1155       channel++;
 1156       if (channel >= MAXCHANNELS) {
 1157     err_dump("too many channels in one group");
 1158       }
 1159     }
 1160   }
 1161 
 1162   if (debugflag) {
 1163     fprintf(stderr, "the following channels are active:\n");
 1164     for (group = 0; group <= MAXGROUPS; group++) {
 1165       for (i = 0; i < grp_nchannels(mycommon, group); i++) {
 1166     fprintf(stderr, "%3d %2d %s:%d:%d\n",
 1167         group,
 1168         i,
 1169         inet_ntoa(chn_ipaddr(mycommon, group, i)),
 1170         chn_port(mycommon, group, i),
 1171         chn_maxc(mycommon, group, i));
 1172       }
 1173     }
 1174   }
 1175 
 1176   b_unlock();
 1177   return (mycommon);
 1178 }
 1179 
 1180 int mycmp(char *s1, char *s2)
 1181 {
 1182   int l;
 1183   l = strlen(s1) < strlen(s2) ? strlen(s1) : strlen(s2);
 1184   if (strlen(s1) > strlen(s2)) {
 1185     return (!1);
 1186   } else {
 1187     return (!strncmp(s1, s2, l));
 1188   }
 1189 }
 1190 
 1191 int shell(char *argument)
 1192 {
 1193   int i;
 1194   int currentgroup = 0;
 1195   char line[MAXINPUTLINE];
 1196   char *command;
 1197 
 1198   // DJJ, Standing Cloud, Inc.
 1199   //    In interactive mode, don't buffer stdout/stderr, so that
 1200   //    other programs can operate balance through I/O streams
 1201   setvbuf(stdout, NULL, _IONBF, 0);
 1202   setvbuf(stderr, NULL, _IONBF, 0);
 1203 
 1204   if (common->release == 0) {
 1205     printf("no master process, exiting.\n");
 1206     exit(EX_UNAVAILABLE);
 1207   }
 1208 
 1209   if (common->release != release || common->subrelease != subrelease) {
 1210     printf("release mismatch, expecting %d.%d, got %d.%d, exiting.\n",
 1211        release, subrelease, common->release, common->subrelease);
 1212     exit(EX_DATAERR);
 1213   }
 1214 
 1215   if (kill(common->pid, SIGUSR1) == -1) {
 1216     printf("no master process with pid %d, exiting.\n", common->pid);
 1217     exit(EX_UNAVAILABLE);
 1218   }
 1219 
 1220   if (argument == NULL) {
 1221     printf("\nbalance %d.%d interactive command shell\n", release,
 1222        subrelease);
 1223     printf("PID of master process is %d\n\n", common->pid);
 1224   }
 1225 
 1226   for (;;) {
 1227 
 1228     if (argument == NULL) {
 1229       printf("balance[%d] ", currentgroup);
 1230       if (fgets(line, MAXINPUTLINE, stdin) == NULL) {
 1231     printf("\n");
 1232     exit(EX_OK);
 1233       }
 1234     } else {
 1235       strncpy(line, argument, MAXINPUTLINE);
 1236     }
 1237 
 1238     if ((command = strtok(line, " \t\n")) != NULL) {
 1239       if (mycmp(command, "quit")) {
 1240     exit(EX_OK);
 1241       } else if (mycmp(command, "show")) {
 1242     b_readlock();
 1243     {
 1244       int group;
 1245 
 1246       printf("%3s %4s %2s %3s %16s %5s %4s %11s %4s %11s %11s\n",
 1247          "GRP", "Type", "#", "S", "ip-address", "port", "c", "totalc",
 1248          "maxc", "sent", "rcvd");
 1249       for (group = 0; group <= MAXGROUPS; group++) {
 1250         for (i = 0; i < grp_nchannels(common, group); i++) {
 1251           printf("%3d %4s %2d %3s %16s %5d %4d %11u %4d %11llu %11llu\n",
 1252              group,
 1253              grp_type(common, group) == GROUP_RR ? "RR" : "Hash",
 1254              i,
 1255              chn_status(common, group, i) == 1 ? "ENA" : "dis",
 1256              inet_ntoa(chn_ipaddr(common, group, i)),
 1257              chn_port(common, group, i),
 1258              chn_c(common, group, i),
 1259              chn_tc(common, group, i),
 1260              chn_maxc(common, group, i),
 1261              chn_bsent(common, group, i),
 1262              chn_breceived(common, group, i)
 1263           );
 1264         }
 1265       }
 1266     }
 1267     b_unlock();
 1268       } else if (mycmp(command, "help") || mycmp(command, "?")) {
 1269     printf("available commands:\n");
 1270 
 1271     printf("  create <host> <port>           creates a channel in the current group\n");
 1272         printf("  assign <channel> <host> <port> reassigns a channel in the current group\n");
 1273     printf("  disable <channel>              disables specified channel in current group\n");
 1274     printf("  enable <channel>               enables channel in current group\n");
 1275     printf("  group <group>                  changes current group to <group>\n");
 1276     printf("  hash                           sets distribution scheme of current group to Hash\n");
 1277     printf("  help                           prints this message\n");
 1278     printf("  kill                           kills master process and quits interactive mode\n");
 1279     printf("  maxc <channel> <maxc>          specifies new maxc for channel of current group\n");
 1280     printf("  mrtg-bytes <grp> <ch>          print bytes in/out in MRTG format\n");
 1281     printf("  mrtg-conns <grp> <ch>          print total connections in MRTG format\n");
 1282     printf("  quit                           quit interactive mode\n");
 1283     printf("  reset <channel>                reset all counters of channel in current group\n");
 1284     printf("  rr                             sets distribution scheme of current group to Round Robin\n");
 1285     printf("  show                           show all channels in all groups\n");
 1286     printf("  version                        show version id\n");
 1287 
 1288       } else if (mycmp(command, "kill")) {
 1289     kill(common->pid, SIGKILL);
 1290     sleep(1);
 1291     if (kill(common->pid, SIGUSR1) == -1) {
 1292       printf("shutdown complete, exiting.\n");
 1293       common->release = 0;
 1294       exit(EX_OK);
 1295     } else {
 1296       printf("shutdown failed.\n");
 1297       exit(EX_UNAVAILABLE);
 1298     }
 1299       } else if (mycmp(command, "disable")) {
 1300     char *arg;
 1301     int n;
 1302     if ((arg = strtok(NULL, " \t\n")) != NULL) {
 1303       n = atoi(arg);
 1304       if (n < 0 || n >= grp_nchannels(common, currentgroup)) {
 1305         printf("no such channel %d\n", n);
 1306       } else {
 1307         c_writelock(currentgroup, n);
 1308         if (chn_status(common, currentgroup, n) == 0) {
 1309           printf("channel %d already disabled\n", n);
 1310         } else {
 1311           chn_status(common, currentgroup, n) = 0;
 1312           printf("channel %d disabled\n", n);
 1313         }
 1314         c_unlock(currentgroup, n);
 1315       }
 1316     } else {
 1317       printf("syntax error\n");
 1318     }
 1319       } else if (mycmp(command, "group")) {
 1320     char *arg, n;
 1321     if ((arg = strtok(NULL, " \t\n")) != NULL) {
 1322       n = atoi(arg);
 1323       if (n >= MAXGROUPS || n < 0) {
 1324         printf("value out of range\n");
 1325       } else {
 1326         currentgroup = n;
 1327       }
 1328     } else {
 1329       printf("syntax error\n");
 1330     }
 1331 
 1332       } else if (mycmp(command, "reset")) { // reset channel counters
 1333     char *arg;
 1334     int n;
 1335 
 1336     if ((arg = strtok(NULL, " \t\n")) != NULL) {
 1337       n = atoi(arg);
 1338       if (n < 0 || n >= grp_nchannels(common, currentgroup)) {
 1339         printf("no such channel %d\n", n);
 1340       } else {
 1341         c_writelock(currentgroup, n);
 1342         chn_breceived(common, currentgroup, n) = 0;
 1343         chn_bsent(common, currentgroup, n) = 0;
 1344         chn_tc(common, currentgroup, n) = 0;
 1345         c_unlock(currentgroup, n);
 1346         printf("channel %d counters reset\n", n);
 1347       }
 1348     } else {
 1349       printf("syntax error\n");
 1350     }
 1351 
 1352       } else if (mycmp(command, "enable")) {
 1353 
 1354     char *arg;
 1355     int n;
 1356     if ((arg = strtok(NULL, " \t\n")) != NULL) {
 1357       n = atoi(arg);
 1358       if (n < 0 || n >= grp_nchannels(common, currentgroup)) {
 1359         printf("no such channel %d\n", n);
 1360       } else {
 1361         c_writelock(currentgroup, n);
 1362         if (chn_status(common, currentgroup, n) == 1) {
 1363           printf("channel %d already enabled\n", n);
 1364         } else {
 1365           chn_status(common, currentgroup, n) = 1;
 1366           printf("channel %d enabled\n", n);
 1367         }
 1368         c_unlock(currentgroup, n);
 1369       }
 1370     } else {
 1371       printf("syntax error\n");
 1372     }
 1373 
 1374       } else if (mycmp(command, "create")) {
 1375     char *arg1, *arg2;
 1376     b_writelock();
 1377     if (grp_nchannels(common, currentgroup) >= MAXCHANNELS) {
 1378       printf("no channel slots available\n");
 1379     } else {
 1380       if ((arg1 = strtok(NULL, " \t\n")) != NULL) {
 1381         if ((arg2 = strtok(NULL, " \t\n")) != NULL) {
 1382           chn_status(common, currentgroup,
 1383              grp_nchannels(common, currentgroup)) = 0;
 1384           if (setaddress_noexitonerror
 1385           (&chn_ipaddr
 1386            (common, currentgroup,
 1387             grp_nchannels(common, currentgroup)), &chn_port(common,
 1388                                     currentgroup,
 1389                                     grp_nchannels
 1390                                     (common,
 1391                                      currentgroup)),
 1392            arg1, getport(arg2))) {
 1393         chn_bsent(common, currentgroup,
 1394               grp_nchannels(common, currentgroup)) = 0;
 1395         chn_breceived(common, currentgroup,
 1396                   grp_nchannels(common, currentgroup)) = 0;
 1397         grp_nchannels(common, currentgroup)++;
 1398         printf("channel created\n");
 1399           } else {
 1400         printf("invalid address\n");
 1401           }
 1402         } else {
 1403           printf("syntax error\n");
 1404         }
 1405       } else {
 1406         printf("syntax error\n");
 1407       }
 1408     }
 1409     b_unlock();
 1410 
 1411     } else if (mycmp(command, "assign")) {
 1412         char *arg1, *arg2, *arg3;
 1413 
 1414           if ((arg1 = strtok(NULL, " \t\n")) != NULL) {
 1415         int chn = atoi(arg1);
 1416             if (chn < 0 || chn >= MAXCHANNELS
 1417                       || chn >= grp_nchannels(common, currentgroup)) {
 1418                printf("unknown channel\n");
 1419         } else {
 1420             c_writelock(currentgroup, chn);
 1421             if (chn_status(common, currentgroup, chn) != 0) {
 1422                printf("channel must be disabled to assign new address\n");
 1423             } else if ((arg2 = strtok(NULL, " \t\n")) != NULL) {
 1424                 if ((arg3 = strtok(NULL, " \t\n")) != NULL) {
 1425                    if (setaddress_noexitonerror
 1426                          (&chn_ipaddr(common, currentgroup, chn),
 1427                           &chn_port(common, currentgroup, chn),
 1428                           arg2, getport(arg3))) {
 1429                        printf("channel reassigned\n");
 1430                    } else {
 1431                        printf("invalid address\n");
 1432                    }
 1433                 } else {
 1434                    printf("syntax error\n");
 1435                 }
 1436             } else {
 1437                 printf("syntax error\n");
 1438             }
 1439             c_unlock(currentgroup, chn);
 1440         }
 1441       } else {
 1442         printf("syntax error\n");
 1443       }
 1444 
 1445     } else if (mycmp(command, "maxc")) {
 1446     char *arg1, *arg2;
 1447     b_writelock();
 1448     if ((arg1 = strtok(NULL, " \t\n")) != NULL) {
 1449       if ((arg2 = strtok(NULL, " \t\n")) != NULL) {
 1450         if (atoi(arg1) < 0 || atoi(arg1) >= MAXCHANNELS
 1451         || atoi(arg1) + 1 > grp_nchannels(common, currentgroup)) {
 1452           printf("unknown channel\n");
 1453         } else {
 1454           chn_maxc(common, currentgroup, atoi(arg1)) = atoi(arg2);
 1455           printf("maxc of channel %d changed to %d\n", atoi(arg1),
 1456              atoi(arg2));
 1457         }
 1458       } else {
 1459         printf("syntax error\n");
 1460       }
 1461     } else {
 1462       printf("syntax error\n");
 1463     }
 1464     b_unlock();
 1465 
 1466     } else if (mycmp(command, "mrtg-bytes")) {
 1467     char *arg1, *arg2;
 1468     int mygroup, mychannel;
 1469     b_writelock();
 1470     if ((arg1 = strtok(NULL, " \t\n")) != NULL) {
 1471       if ((arg2 = strtok(NULL, " \t\n")) != NULL) {
 1472             mygroup = atoi(arg1);
 1473             mychannel = atoi(arg2);
 1474         if (mygroup < 0 || mygroup > MAXGROUPS) { 
 1475           printf("unknown group\n");
 1476         } else {
 1477           if(mychannel < 0 || mychannel > grp_nchannels(common, currentgroup)) {
 1478             printf("unknown channel\n");
 1479           } else {
 1480         //
 1481         printf("%llu\n", chn_breceived(common,mygroup,mychannel));
 1482         printf("%llu\n", chn_bsent(common,mygroup,mychannel));
 1483         printf("UNKNOWN\n");
 1484         printf("group %d channel %d\n",mygroup, mychannel);
 1485           }
 1486         }
 1487       } else {
 1488         printf("syntax error\n");
 1489       }
 1490     } else {
 1491       printf("syntax error\n");
 1492     }
 1493     b_unlock();
 1494 
 1495       } else if (mycmp(command, "mrtg-conns")) {
 1496     char *arg1, *arg2;
 1497     int mygroup, mychannel;
 1498     b_writelock();
 1499     if ((arg1 = strtok(NULL, " \t\n")) != NULL) {
 1500       if ((arg2 = strtok(NULL, " \t\n")) != NULL) {
 1501             mygroup = atoi(arg1);
 1502             mychannel = atoi(arg2);
 1503         if (mygroup < 0 || mygroup > MAXGROUPS) { 
 1504           printf("unknown group\n");
 1505         } else {
 1506           if(mychannel < 0 || mychannel > grp_nchannels(common, currentgroup)) {
 1507             printf("unknown channel\n");
 1508           } else {
 1509         //
 1510         printf("%u\n", chn_tc(common,mygroup,mychannel));
 1511         printf("UNKNOWN\n");
 1512         printf("UNKNOWN\n");
 1513         printf("group %d channel %d\n",mygroup, mychannel);
 1514           }
 1515         }
 1516       } else {
 1517         printf("syntax error\n");
 1518       }
 1519     } else {
 1520       printf("syntax error\n");
 1521     }
 1522     b_unlock();
 1523 
 1524       } else if (mycmp(command, "version")) {
 1525     printf("  This is balance %d.%d\n", release, subrelease);
 1526     printf("  MAXGROUPS=%d\n", MAXGROUPS);
 1527     printf("  MAXCHANNELS=%d\n", MAXCHANNELS);
 1528       } else if (mycmp(command, "hash")) {
 1529     b_writelock();
 1530     grp_type(common, currentgroup) = GROUP_HASH;
 1531     b_unlock();
 1532     printf("group %d set to hash\n", currentgroup);
 1533 
 1534       } else if (mycmp(command, "rr")) {
 1535     b_writelock();
 1536     grp_type(common, currentgroup) = GROUP_RR;
 1537     b_unlock();
 1538     printf("group %d set to round robin\n", currentgroup);
 1539 
 1540       } else {
 1541     printf("syntax error\n");
 1542       }
 1543       // printf("\n");
 1544     }
 1545     if (argument != NULL)
 1546       exit(EX_OK);
 1547   }
 1548 }
 1549 
 1550 char bindhost_address[FILENAMELEN];
 1551 
 1552 int main(int argc, char *argv[])
 1553 {
 1554   int startindex;
 1555   int sockfd, newsockfd, childpid;
 1556   unsigned int clilen;
 1557   int c;
 1558   int source_port;
 1559   int fd;
 1560   char *argument = NULL;
 1561   struct stat buffer;
 1562   struct sockaddr_storage cli_addr;
 1563   struct sigaction usr1_action, chld_action;
 1564 #ifdef BalanceBSD
 1565 #else
 1566   struct rlimit r;
 1567 #endif
 1568 
 1569   connect_timeout = DEFAULTTIMEOUT;
 1570   initialize_release_variables();
 1571 
 1572   while ((c = getopt(argc, argv, "c:b:B:t:T:adfpiHM6")) != EOF) {
 1573     switch (c) {
 1574     case '6':
 1575       bindipv6 = 1;
 1576       break;
 1577     case 'a':
 1578       autodisable = 1;
 1579       break;
 1580     case 'b':
 1581       bindhost = optarg;
 1582       break;
 1583     case 'B':
 1584       outbindhost = optarg;
 1585       break;
 1586     case 'c':
 1587       argument = optarg;
 1588       interactive = 1;
 1589       foreground = 1;
 1590       packetdump = 0;
 1591       break;
 1592     case 't':
 1593       connect_timeout = atoi(optarg);
 1594       if (connect_timeout < 1) {
 1595     usage();
 1596       }
 1597       break;
 1598     case 'T':
 1599       sel_tmout.tv_sec = atoi(optarg);
 1600       sel_tmout.tv_usec = 0;
 1601       if (sel_tmout.tv_sec < 1)
 1602     usage();
 1603       save_tmout = sel_tmout;
 1604       break;
 1605     case 'f':
 1606       foreground = 1;
 1607       break;
 1608     case 'd':
 1609       debugflag = 1;
 1610       break;
 1611     case 'p':
 1612       packetdump = 1;
 1613       break;
 1614     case 'i':
 1615       interactive = 1;
 1616       foreground = 1;
 1617       packetdump = 0;
 1618       break;
 1619     case 'H':
 1620       hashfailover = 1;
 1621       break;
 1622     case 'M':
 1623 #ifdef  NO_MMAP
 1624       fprintf(stderr, "Warning: Built without memory mapped file support, using IPC\n");
 1625 #else
 1626       shmmapfile = 1;
 1627 #endif
 1628       break;
 1629     case '?':
 1630     default:
 1631       usage();
 1632     }
 1633   }
 1634 
 1635   if (debugflag) {
 1636     printf("argv[0]=%s\n", argv[0]);
 1637     printf("bindhost=%s\n", bindhost == NULL ? "NULL" : bindhost);
 1638   }
 1639 
 1640   if (interactive) {
 1641     foreground = 1;
 1642     packetdump = 0;
 1643   }
 1644 
 1645   argc -= optind;
 1646   argv += optind;
 1647 
 1648   if (!interactive) {
 1649     if (argc < 1) {
 1650       usage();
 1651     }
 1652   } else {
 1653     if (argc != 1) {
 1654       usage();
 1655     }
 1656   }
 1657 
 1658 
 1659   usr1_action.sa_handler = usr1_handler;
 1660   usr1_action.sa_flags = SA_RESTART;
 1661   sigemptyset(&usr1_action.sa_mask);
 1662   sigaction(SIGUSR1, &usr1_action, NULL);
 1663 
 1664   chld_action.sa_handler = chld_handler;
 1665   chld_action.sa_flags = SA_RESTART;
 1666   sigemptyset(&chld_action.sa_mask);
 1667   sigaction(SIGCHLD, &chld_action, NULL);
 1668   // really dump core if something fails... 
 1669 
 1670 #ifdef BalanceBSD
 1671 #else
 1672   getrlimit(RLIMIT_CORE, &r);
 1673   r.rlim_cur = r.rlim_max;
 1674   setrlimit(RLIMIT_CORE, &r);
 1675 #endif
 1676 
 1677   // get the source port 
 1678 
 1679   if ((source_port = getport(argv[0])) == 0) {
 1680     fprintf(stderr, "invalid port [%s], exiting.\n", argv[0]);
 1681     exit(EX_USAGE);
 1682   }
 1683 
 1684   if (debugflag) {
 1685     fprintf(stderr, "source port %d\n", source_port);
 1686   }
 1687 
 1688   /*
 1689    * Bind our local address so that the client can send to us.
 1690    * Handling of -b option.
 1691    */
 1692 
 1693   if (bindhost != NULL) {
 1694     snprintf(bindhost_address, FILENAMELEN, "%s", bindhost);
 1695   } else {
 1696     snprintf(bindhost_address, FILENAMELEN, "%s", "0.0.0.0");
 1697   }
 1698 
 1699   stat(SHMDIR, &buffer);
 1700   if (!S_ISDIR(buffer.st_mode)) {
 1701     mode_t old = umask(0);
 1702     if (mkdir(SHMDIR, 01777) < 0) {
 1703       if(errno != EEXIST) {
 1704         fprintf(stderr, "ERROR: rendezvous directory not available and/or creatable\n");
 1705         fprintf(stderr, "       please create %s with mode 01777 like this: \n", SHMDIR);
 1706         fprintf(stderr, "       # mkdir -m 01777 %s\n", SHMDIR);
 1707         umask(old);
 1708         exit(EX_UNAVAILABLE);
 1709       }
 1710     }
 1711     umask(old);
 1712   }
 1713 
 1714   sprintf(rendezvousfile, "%sbalance.%d.%s", SHMDIR, source_port,
 1715       bindhost_address);
 1716 
 1717   if (stat(rendezvousfile, &buffer) == -1) {
 1718     // File not existing yet ...
 1719     if ((fd = open(rendezvousfile, O_CREAT | O_RDWR, 0666)) == -1) {
 1720       fprintf(stderr, "cannot create rendezvous file %s\n",
 1721           rendezvousfile);
 1722       exit(EX_OSERR);
 1723     } else {
 1724       if (debugflag)
 1725     fprintf(stderr, "file %s created\n", rendezvousfile);
 1726       close(fd);
 1727     }
 1728   } else {
 1729     if (debugflag)
 1730       fprintf(stderr, "file %s already exists\n", rendezvousfile);
 1731   }
 1732 
 1733   if (interactive) {
 1734     // command mode ! 
 1735     if ((rendezvousfd = open(rendezvousfile, O_RDWR, 0)) < 0) {
 1736       perror("open");
 1737       fprintf(stderr,"check rendezvousfile permissions [%s]\n",rendezvousfile);
 1738       exit(EX_OSERR);
 1739     }
 1740     if ((common =
 1741      (COMMON *) shm_malloc(rendezvousfile, sizeof(COMMON))) == NULL) {
 1742       fprintf(stderr, "cannot alloc COMMON struct\n");
 1743       exit(EX_OSERR);
 1744     }
 1745     shell(argument);
 1746   }
 1747 
 1748   openlog("Balance", LOG_ODELAY | LOG_PID | LOG_CONS, LOG_DAEMON);
 1749 
 1750   /*  Open a TCP socket (an Internet stream socket). */
 1751 
 1752   sockfd = create_serversocket(bindhost, argv[0]);
 1753 
 1754   (void) setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sockbufsize, sizeof(sockbufsize));
 1755   (void) setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &sockbufsize, sizeof(sockbufsize));
 1756 
 1757   // init of common (*after* bind()) 
 1758 
 1759   if (!foreground) {
 1760     background();
 1761   }
 1762 
 1763   common = makecommon(argc, argv, source_port);
 1764 
 1765   for (;;) {
 1766     int index;
 1767     unsigned int uindex;
 1768     int groupindex = 0;     // always start at groupindex 0
 1769 
 1770     clilen = sizeof(cli_addr);
 1771 
 1772     newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
 1773     if (newsockfd < 0) {
 1774       if (debugflag) {
 1775     fprintf(stderr, "accept error %d\n", errno);
 1776       }
 1777       continue;
 1778     }
 1779 
 1780     if (debugflag) {
 1781       char buf[1024];
 1782       inet_ntop(AF_INET6,&(((struct sockaddr_in6*) &cli_addr)->sin6_addr),buf,1024);
 1783       fprintf(stderr, "connect from %s clilen=%d\n", buf, clilen);
 1784     }
 1785 
 1786     /* 
 1787      * the balancing itself:
 1788      * - groupindex = 0
 1789      * - decision wich channel to use for the first try 
 1790      * - client address available in cli_addr
 1791      *
 1792      */
 1793 
 1794     b_writelock();
 1795     for (;;) {
 1796       index = grp_current(common, groupindex);
 1797       for (;;) {
 1798     if (grp_type(common, groupindex) == GROUP_RR) {
 1799       if (chn_status(common, groupindex, index) == 1 &&
 1800           (chn_maxc(common, groupindex, index) == 0 ||
 1801            (chn_c(common, groupindex, index) <
 1802         chn_maxc(common, groupindex, index)))) {
 1803         break;      // channel found
 1804       } else {
 1805         index++;
 1806         if (index >= grp_nchannels(common, groupindex)) {
 1807           index = 0;
 1808         }
 1809         if (index == grp_current(common, groupindex)) {
 1810           index = -1;   // no channel available in this group
 1811           break;
 1812         }
 1813       }
 1814     } else if (grp_type(common, groupindex) == GROUP_HASH) {
 1815       uindex = hash_fold((unsigned char*) &(((struct sockaddr_in6 *) &cli_addr)->sin6_addr), clilen);
 1816    
 1817       if(debugflag) {
 1818         fprintf(stderr, "HASH-method: fold returns %u\n", uindex);
 1819           }
 1820 
 1821       index = uindex % grp_nchannels(common, groupindex);
 1822       if (debugflag)
 1823         fprintf(stderr, "modulo %d gives %d\n",
 1824             grp_nchannels(common, groupindex), index);
 1825       if (chn_status(common, groupindex, index) == 1
 1826           && (chn_maxc(common, groupindex, index) == 0
 1827           || (chn_c(common, groupindex, index) <
 1828               chn_maxc(common, groupindex, index)))
 1829           ) {
 1830         break;      // channel found, channel valid for HASH
 1831       } else {
 1832         if (hashfailover == 1) {
 1833           // if failover even if hash: try next channel in this group.
 1834           if (debugflag)
 1835         fprintf(stderr, "channel disabled - hashfailover.\n");
 1836           startindex = index;
 1837           for (;;) {
 1838         index++;
 1839         if (index >= grp_nchannels(common, groupindex)) {
 1840           index = 0;
 1841         }
 1842         if (index == startindex) {
 1843           if (debugflag)
 1844             fprintf(stderr, "no valid channel in group %d.\n",
 1845                 groupindex);
 1846           index = -1;
 1847           break;
 1848         }
 1849         if (chn_status(common, groupindex, index) == 1 &&
 1850             (chn_maxc(common, groupindex, index) == 0 ||
 1851              (chn_c(common, groupindex, index) <
 1852               chn_maxc(common, groupindex, index)))
 1853             ) {
 1854           if (debugflag)
 1855             fprintf(stderr, "channel choosen: %d in group %d.\n",
 1856                 index, groupindex);
 1857           break;    // channel found
 1858         }
 1859           }
 1860 
 1861         } else {
 1862           if (debugflag)
 1863         fprintf(stderr,
 1864             "no valid channel in group %d. Failover?\n",
 1865             groupindex);
 1866           index = -1;
 1867         }
 1868         break;
 1869       }
 1870     } else {
 1871       err_dump("PANIC: invalid group type");
 1872     }
 1873       }
 1874 
 1875       // Hier fallen wir "raus" mit dem index in der momentanen Gruppe, oder -1
 1876       // wenn nicht moeglich in dieser Gruppe
 1877 
 1878       grp_current(common, groupindex) = index;
 1879       grp_current(common, groupindex)++;    // current index dieser gruppe wieder null, wenn vorher ungueltig (-1)
 1880 
 1881       // Der index der gruppe wird neu berechnet und gespeichert, "index" ist immer noch 
 1882       // -1 oder der zu waehlende index...
 1883 
 1884       if (grp_current(common, groupindex) >=
 1885       grp_nchannels(common, groupindex)) {
 1886     grp_current(common, groupindex) = 0;
 1887       }
 1888 
 1889       if (index >= 0) {
 1890     chn_c(common, groupindex, index)++; // we promise a successful connection 
 1891     chn_tc(common, groupindex, index)++;    // also incrementing the total count 
 1892     // c++ 
 1893     break;                  // index in this group found 
 1894       } else {
 1895     groupindex++;               // try next group !
 1896     if (groupindex >= MAXGROUPS) {
 1897       break;                // end of groups...
 1898     }
 1899       }
 1900     }
 1901 
 1902     b_unlock();
 1903 
 1904     if (index >= 0) {
 1905       if ((childpid = fork()) < 0) {
 1906 
 1907     // the connection is rejected if fork() returns error, 
 1908     // but main process stays alive !
 1909 
 1910     if (debugflag) {
 1911       fprintf(stderr, "fork error\n");
 1912     }
 1913       } else if (childpid == 0) {   // child process 
 1914     close(sockfd);          // close original socket 
 1915 
 1916     // FIX: "#8 SIGPIPE causes unclosed channels"   
 1917 
 1918     signal(SIGPIPE, SIG_IGN);
 1919 
 1920     // process the request: 
 1921 
 1922     stream(newsockfd, groupindex, index, (char *) &cli_addr, clilen);
 1923     exit(EX_OK);
 1924       }
 1925     }
 1926 
 1927     close(newsockfd);       // parent process 
 1928   }
 1929 }