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