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 
urg_handler(int signo)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 
err_dump(char * text)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 
create_serversocket(char * node,char * service)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 
a_readlock(off_t start,off_t len)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 
b_readlock(void)271 void b_readlock(void) {
272   a_readlock(0, 0);
273 }
274 
c_readlock(int group,int channel)275 void c_readlock(int group, int channel) {
276   a_readlock(((char *) &(grp_channel(common, group, channel))) -
277 	     (char *) common, sizeof(CHANNEL));
278 }
279 
a_writelock(off_t start,off_t len)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 
b_writelock(void)301 void b_writelock(void) {
302   a_writelock(0, 0);
303 }
304 
c_writelock(int group,int channel)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 
a_unlock(off_t start,off_t len)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 
b_unlock(void)333 void b_unlock(void)
334 {
335   a_unlock(0, 0);
336 }
337 
c_unlock(int group,int channel)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 
shm_malloc(char * file,int size)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 
print_packet(unsigned char * s,int l)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 
getport(char * port)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 
setipaddress(struct in_addr * ipaddr,char * string)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 
setaddress(struct in_addr * ipaddr,int * port,char * string,int default_port,int * maxc)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 
setaddress_noexitonerror(struct in_addr * ipaddr,int * port,char * string,int default_port)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 
readline(int fd,char * ptr,int maxlen)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 
forward(int fromfd,int tofd,int groupindex,int channelindex)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 
backward(int fromfd,int tofd,int groupindex,int channelindex)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 
stream2(int clientfd,int serverfd,int groupindex,int channelindex)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 
alrm_handler(int signo)754 void alrm_handler(int signo) {
755 }
756 
usr1_handler(int signo)757 void usr1_handler(int signo) {
758 }
759 
chld_handler(int signo)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 
stream(int arg,int groupindex,int index,char * client_address,int client_address_size)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
initialize_release_variables(void)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
usage(void)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 
background(void)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 
makecommon(int argc,char ** argv,int source_port)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 
mycmp(char * s1,char * s2)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 
shell(char * argument)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 
main(int argc,char * argv[])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 }
1930