mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
401 lines
10 KiB
401 lines
10 KiB
/* $Id$
|
|
*
|
|
* Copyright (C) 2006-2007 VozTelecom Sistemas S.L
|
|
*
|
|
* This file is part of Kamailio, a free SIP server.
|
|
*
|
|
* Kamailio is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* Kamailio is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
|
|
#include <stdlib.h>/*atoi*/
|
|
#include <time.h>/*gettimeofday*/
|
|
#include <poll.h>/*poll*/
|
|
#include "ha.h"
|
|
#include "seas.h"
|
|
#include "../../mem/mem.h" /*pkg_malloc*/
|
|
#include "../../mem/shm_mem.h" /*shm_malloc*/
|
|
|
|
/** if any of these global ping vars is set to 0, then
|
|
* this kind of ping is DISABLED
|
|
*/
|
|
char *jain_ping_config=0;
|
|
int jain_ping_period=0;
|
|
int jain_pings_lost=0;
|
|
int jain_ping_timeout=0;
|
|
|
|
char *servlet_ping_config=0;
|
|
int servlet_ping_period=0;
|
|
int servlet_pings_lost=0;
|
|
int servlet_ping_timeout=0;
|
|
|
|
int use_ha=0;
|
|
pid_t pinger_pid;
|
|
|
|
static inline int parse_ping(char * string,int *ping_period,int *pings_lost,int *ping_timeout);
|
|
static inline int send_ping(struct as_entry *the_as,struct timeval *now);
|
|
/**
|
|
* returns:
|
|
* 0 if no High Availability
|
|
* 1 if High Availability
|
|
* -1 if config error
|
|
*/
|
|
int prepare_ha(void)
|
|
{
|
|
use_ha=0;
|
|
if(!(jain_ping_config || servlet_ping_config)){
|
|
jain_pings_lost=servlet_pings_lost=0;
|
|
return 0;
|
|
}
|
|
if(parse_ping(jain_ping_config,&jain_ping_period,&jain_pings_lost,&jain_ping_timeout)<0)
|
|
goto error;
|
|
if(parse_ping(servlet_ping_config,&servlet_ping_period,&servlet_pings_lost,&servlet_ping_timeout)<0)
|
|
goto error;
|
|
LM_DBG("jain: pinging period :%d max pings lost:%d ping timeout:%d\n",
|
|
jain_ping_period,jain_pings_lost,jain_ping_timeout);
|
|
LM_DBG("servlet: pinging period:%d max pings lost:%d ping timeout:%d\n",
|
|
servlet_ping_period,servlet_pings_lost,servlet_ping_timeout);
|
|
use_ha=1;
|
|
return 1;
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
int print_pingtable(struct ha *ta,int idx,int lock)
|
|
{
|
|
int i;
|
|
if(lock)
|
|
lock_get(ta->mutex);
|
|
for(i=0;i<ta->size;i++){
|
|
if((ta->begin+ta->count)>ta->size){
|
|
if ((i<ta->begin && i<((ta->begin+ta->count)%ta->size)) || (i>=ta->begin && i<(ta->begin+ta->count)))
|
|
fprintf(stderr,"*");
|
|
else
|
|
fprintf(stderr,"=");
|
|
}else{
|
|
if (i>=ta->begin && i<(ta->begin+ta->count))
|
|
fprintf(stderr,"*");
|
|
else
|
|
fprintf(stderr,"=");
|
|
}
|
|
}
|
|
if(lock)
|
|
lock_release(ta->mutex);
|
|
fprintf(stderr,"\n");
|
|
for(i=0;i<ta->size;i++)
|
|
if(i==idx)
|
|
fprintf(stderr,"-");
|
|
else
|
|
fprintf(stderr,"%d",i);
|
|
fprintf(stderr,"\n");
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parses the PING configuration string. Its format is
|
|
* "ping_period:pings_lost:ping_timeout"
|
|
* ping_period : time between pings
|
|
* pings_lost: number of lost pings before failure
|
|
* ping_timeout: time to consider a ping failed
|
|
*
|
|
* returns
|
|
* 0 if config is not set
|
|
* -1 if config is malformed (unable to parse);
|
|
* 1 if config is successfully set
|
|
*/
|
|
static inline int parse_ping(char * string,int *ping_period,int *pings_lost,int *ping_timeout)
|
|
{
|
|
char *ping_period_s,*pings_lost_s,*ping_timeout_s;
|
|
ping_period_s=pings_lost_s=ping_timeout_s=(char *)0;
|
|
|
|
if(string==0 || *string==0){
|
|
*ping_period=0;
|
|
*pings_lost=0;
|
|
*ping_timeout=0;
|
|
return 0;
|
|
}
|
|
|
|
if (((*string)<'0')||((*string)>'9')) {
|
|
LM_ERR("malformed ping config string. Unparseable :[%s]\n",string);
|
|
return -1;
|
|
}
|
|
ping_period_s=string;
|
|
while(*string){
|
|
if(*string == ':'){
|
|
*string=0;
|
|
if (!pings_lost_s && (*(string+1))) {
|
|
pings_lost_s=string+1;
|
|
}else if (!ping_timeout_s && (*(string+1))) {
|
|
ping_timeout_s=string+1;
|
|
}else{
|
|
LM_ERR("malformed ping config string. Unparseable :[%s]\n",string);
|
|
return -1;
|
|
}
|
|
}
|
|
string++;
|
|
}
|
|
if (!(ping_period_s && pings_lost_s && ping_timeout_s)) {
|
|
LM_ERR("malformed ping config string. Unparseable :[%s]\n",string);
|
|
return -1;
|
|
}
|
|
*ping_period =atoi(ping_period_s);
|
|
*pings_lost=atoi(pings_lost_s);
|
|
*ping_timeout=atoi(ping_timeout_s);
|
|
if (*ping_period<=0 || *pings_lost<=0 || *ping_timeout<=0) {
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* we spawn a pinger process.
|
|
*
|
|
* some day we could spawn a pinger-thread instead of a process..
|
|
*
|
|
* returns:
|
|
* -1 on error;
|
|
*/
|
|
int spawn_pinger(void)
|
|
{
|
|
int n,next_jain,next_servlet,timeout;
|
|
struct timeval now,last_jain,last_servlet;
|
|
struct as_entry *as;
|
|
|
|
if ((pinger_pid=fork())<0) {
|
|
LM_ERR("forking failed!\n");
|
|
goto error;
|
|
}else if(pinger_pid>0){
|
|
return 0;
|
|
}
|
|
strcpy(whoami,"Pinger Process\n");
|
|
is_dispatcher=0;
|
|
my_as=0;
|
|
/* child */
|
|
if(jain_ping_period && servlet_ping_period){
|
|
next_jain=next_servlet=0;
|
|
}else if(jain_ping_period){
|
|
next_servlet=INT_MAX;
|
|
next_jain=0;
|
|
}else if(servlet_ping_period){
|
|
next_jain=INT_MAX;
|
|
next_servlet=0;
|
|
}else{
|
|
next_jain=next_servlet=INT_MAX;
|
|
}
|
|
|
|
gettimeofday(&last_jain,NULL);
|
|
memcpy(&last_servlet,&last_jain,sizeof(struct timeval));
|
|
|
|
while(1){
|
|
gettimeofday(&now,NULL);
|
|
if(next_jain!=INT_MAX){
|
|
next_jain=jain_ping_period-((now.tv_sec-last_jain.tv_sec)*1000 + (now.tv_usec-last_jain.tv_usec)/1000);
|
|
}
|
|
if(next_servlet!=INT_MAX){
|
|
next_servlet=servlet_ping_period-((now.tv_sec-last_servlet.tv_sec)*1000 + (now.tv_usec-last_servlet.tv_usec)/1000);
|
|
}
|
|
|
|
timeout=next_jain<next_servlet?next_jain:next_servlet;
|
|
|
|
if ((n=poll(NULL,0,timeout<0?0:timeout))<0) {
|
|
LM_ERR("poll returned %d\n",n);
|
|
goto error;
|
|
}else if(n==0){/*timeout*/
|
|
gettimeofday(&now,NULL);
|
|
if (jain_ping_period && ((now.tv_sec-last_jain.tv_sec)*1000 + (now.tv_usec-last_jain.tv_usec)/1000)>=jain_ping_period) {
|
|
gettimeofday(&last_jain,NULL);
|
|
for(as=as_list;as;as=as->next){
|
|
if(as->type == AS_TYPE && as->connected){
|
|
send_ping(as,&now);
|
|
}
|
|
}
|
|
}
|
|
if (servlet_ping_period && ((now.tv_sec-last_servlet.tv_sec)*1000 + (now.tv_usec-last_servlet.tv_usec)/1000)>=servlet_ping_period) {
|
|
gettimeofday(&last_servlet,NULL);
|
|
for(as=as_list;as;as=as->next){
|
|
if(as->type==AS_TYPE && as->connected){
|
|
send_ping(as,&now);
|
|
}
|
|
}
|
|
}
|
|
}else{/*impossible..*/
|
|
LM_ERR("bug:poll returned %d\n",n);
|
|
goto error;
|
|
}
|
|
}
|
|
return 0;
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* sends a ping to the app-server, and uses now as sent time
|
|
*
|
|
* returns
|
|
* 0 on success
|
|
* -1 on error
|
|
*/
|
|
static inline int send_ping(struct as_entry *the_as,struct timeval *now)
|
|
{
|
|
char *the_ping;
|
|
as_msg_p aping;
|
|
int pinglen,retval;
|
|
unsigned int seqno;
|
|
struct ping *pingu;
|
|
|
|
aping=(as_msg_p)0;
|
|
the_ping=(char *)0;
|
|
retval=0;
|
|
if (!(aping=shm_malloc(sizeof(as_msg_t)))) {
|
|
LM_ERR("out of shm_mem for ping event\n");
|
|
retval=-1;
|
|
goto error;
|
|
}
|
|
if (!(the_ping=create_ping_event(&pinglen,0,&seqno))) {
|
|
LM_ERR("Unable to create ping event\n");
|
|
retval=-1;
|
|
goto error;
|
|
}
|
|
aping->as=the_as;
|
|
aping->msg=the_ping;
|
|
aping->len=pinglen;
|
|
|
|
lock_get(the_as->u.as.jain_pings.mutex);
|
|
{
|
|
if(the_as->u.as.jain_pings.count==the_as->u.as.jain_pings.size){
|
|
LM_ERR("Cant send ping because the pingtable is full (%d pings)\n",\
|
|
the_as->u.as.jain_pings.count);
|
|
retval=0;
|
|
lock_release(the_as->u.as.jain_pings.mutex);
|
|
goto error;
|
|
}else{
|
|
pingu=the_as->u.as.jain_pings.pings+the_as->u.as.jain_pings.end;
|
|
the_as->u.as.jain_pings.end=(the_as->u.as.jain_pings.end+1)%the_as->u.as.jain_pings.size;
|
|
the_as->u.as.jain_pings.count++;
|
|
}
|
|
memcpy(&pingu->sent,now,sizeof(struct timeval));
|
|
pingu->id=seqno;
|
|
}
|
|
lock_release(the_as->u.as.jain_pings.mutex);
|
|
again:
|
|
if(0>write(write_pipe,&aping,sizeof(as_msg_p))){
|
|
if(errno==EINTR){
|
|
goto again;
|
|
}else{
|
|
LM_ERR("error sending ping\n");
|
|
goto error;
|
|
}
|
|
}
|
|
return 0;
|
|
error:
|
|
if(aping)
|
|
shm_free(aping);
|
|
if(the_ping)
|
|
shm_free(the_ping);
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* Initializes the high availability (ha) structure
|
|
*
|
|
* returns
|
|
* 0 on success
|
|
* -1 on error
|
|
*/
|
|
inline int init_pingtable(struct ha *table,int timeout,int maxpings)
|
|
{
|
|
if(maxpings<=0)
|
|
maxpings=1;
|
|
table->begin=0;
|
|
table->end=0;
|
|
table->timed_out_pings=0;
|
|
table->size=maxpings;
|
|
table->timeout=timeout;
|
|
|
|
if (!(table->mutex=lock_alloc())){
|
|
LM_ERR("Unable to allocate a lock for the ping table\n");
|
|
goto error;
|
|
}else
|
|
lock_init(table->mutex);
|
|
LM_ERR("alloc'ing %d bytes for %d pings\n",(int)(maxpings*sizeof(struct ping)),maxpings);
|
|
if (0==(table->pings=shm_malloc(maxpings*sizeof(struct ping)))){
|
|
LM_ERR("Unable to shm_malloc %d bytes for %d pings\n",(int)(maxpings*sizeof(struct ping)),maxpings);
|
|
goto error;
|
|
}else{
|
|
memset(table->pings,0,(maxpings*sizeof(struct ping)));
|
|
}
|
|
return 0;
|
|
error:
|
|
destroy_pingtable(table);
|
|
return -1;
|
|
}
|
|
|
|
inline void destroy_pingtable(struct ha *table)
|
|
{
|
|
if(table->mutex){
|
|
lock_dealloc(table->mutex);
|
|
table->mutex=0;
|
|
}
|
|
if(table->pings){
|
|
shm_free(table->pings);
|
|
table->pings=0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* event_length(4) UNSIGNED INT includes the length 4 bytes itself
|
|
* type(1),
|
|
* processor_id(1), 0 means nobody, 0xFF means everybody, 0<N<0xFF means processor with id=N
|
|
* flags(4),
|
|
* ping_num(4),
|
|
*
|
|
* NOT REENTRANT (uses static local var to store ping seqno.)
|
|
*
|
|
* returns
|
|
* 0 on error
|
|
* pointer to the buffer on success
|
|
*
|
|
*/
|
|
char * create_ping_event(int *evt_len,int flags,unsigned int *seqno)
|
|
{
|
|
unsigned int i,k;
|
|
char *buffer;
|
|
static unsigned int ping_seqno=0;
|
|
|
|
if (!(buffer=shm_malloc(4+1+1+4+4))) {
|
|
LM_ERR("out of shm for ping\n");
|
|
return 0;
|
|
}
|
|
*evt_len=(4+1+1+4+4);
|
|
ping_seqno++;
|
|
*seqno=ping_seqno;
|
|
i=htonl(14);
|
|
memcpy(buffer,&i,4);
|
|
k=4;
|
|
/*type*/
|
|
buffer[k++]=(unsigned char)PING_AC;
|
|
/*processor_id*/
|
|
buffer[k++]=(unsigned char)0xFF;
|
|
/*flags*/
|
|
flags=htonl(flags);
|
|
memcpy(buffer+k,&flags,4);
|
|
k+=4;
|
|
/*ping sequence number*/
|
|
i=htonl(ping_seqno);
|
|
memcpy(buffer+k,&i,4);
|
|
k+=4;
|
|
return buffer;
|
|
}
|