ha.c

Go to the documentation of this file.
00001 /* $Id: ha.c 4518 2008-07-28 15:39:28Z henningw $
00002  *
00003  * Copyright (C) 2006-2007 VozTelecom Sistemas S.L
00004  *
00005  * This file is part of Kamailio, a free SIP server.
00006  *
00007  * Kamailio is free software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License as published by
00009  * the Free Software Foundation; either version 2 of the License, or
00010  * (at your option) any later version
00011  *
00012  * Kamailio is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License 
00018  * along with this program; if not, write to the Free Software 
00019  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020  */
00021 
00022 
00023 #include <stdlib.h>/*atoi*/
00024 #include <time.h>/*gettimeofday*/
00025 #include <poll.h>/*poll*/
00026 #include "ha.h"
00027 #include "seas.h"
00028 #include "../../mem/mem.h" /*pkg_malloc*/
00029 #include "../../mem/shm_mem.h" /*shm_malloc*/
00030 
00031 /** if any of these global ping vars is set to 0, then
00032  * this kind of ping is DISABLED
00033  */
00034 char *jain_ping_config=0;
00035 int jain_ping_period=0;
00036 int jain_pings_lost=0;
00037 int jain_ping_timeout=0;
00038 
00039 char *servlet_ping_config=0;
00040 int servlet_ping_period=0;
00041 int servlet_pings_lost=0;
00042 int servlet_ping_timeout=0;
00043 
00044 int use_ha=0;
00045 pid_t pinger_pid;
00046 
00047 static inline int parse_ping(char * string,int *ping_period,int *pings_lost,int *ping_timeout);
00048 static inline int send_ping(struct as_entry *the_as,struct timeval *now);
00049 /**
00050  * returns:
00051  * 0 if no High Availability
00052  * 1 if High Availability
00053  * -1 if config error
00054  */
00055 int prepare_ha(void)
00056 {
00057    use_ha=0;
00058    if(!(jain_ping_config || servlet_ping_config)){
00059       jain_pings_lost=servlet_pings_lost=0;
00060       return 0;
00061    }
00062    if(parse_ping(jain_ping_config,&jain_ping_period,&jain_pings_lost,&jain_ping_timeout)<0)
00063       goto error;
00064    if(parse_ping(servlet_ping_config,&servlet_ping_period,&servlet_pings_lost,&servlet_ping_timeout)<0) 
00065       goto error;
00066    LM_DBG("jain: pinging period :%d max pings lost:%d ping timeout:%d\n",
00067          jain_ping_period,jain_pings_lost,jain_ping_timeout);
00068    LM_DBG("servlet: pinging period:%d max pings lost:%d ping timeout:%d\n",
00069          servlet_ping_period,servlet_pings_lost,servlet_ping_timeout);
00070    use_ha=1;
00071    return 1;
00072 error:
00073    return -1;
00074 }
00075 
00076 int print_pingtable(struct ha *ta,int idx,int lock)
00077 {
00078    int i;
00079    if(lock)
00080       lock_get(ta->mutex);
00081    for(i=0;i<ta->size;i++){
00082       if((ta->begin+ta->count)>ta->size){
00083     if ((i<ta->begin && i<((ta->begin+ta->count)%ta->size)) || (i>=ta->begin && i<(ta->begin+ta->count)))
00084        fprintf(stderr,"*");
00085     else
00086        fprintf(stderr,"=");
00087       }else{
00088     if (i>=ta->begin && i<(ta->begin+ta->count))
00089        fprintf(stderr,"*");
00090     else
00091        fprintf(stderr,"=");
00092       }
00093    }
00094    if(lock)
00095       lock_release(ta->mutex);
00096    fprintf(stderr,"\n");
00097    for(i=0;i<ta->size;i++)
00098       if(i==idx)
00099     fprintf(stderr,"-");
00100       else
00101     fprintf(stderr,"%d",i);
00102    fprintf(stderr,"\n");
00103    return 0;
00104 }
00105 
00106 /**
00107  * Parses the PING configuration string. Its format is 
00108  * "ping_period:pings_lost:ping_timeout"
00109  * ping_period : time between pings
00110  * pings_lost: number of lost pings before failure
00111  * ping_timeout: time to consider a ping failed
00112  *
00113  * returns 
00114  * 0 if config is not set
00115  * -1 if config is malformed (unable to parse);
00116  *  1 if config is successfully set
00117  */
00118 static inline int parse_ping(char * string,int *ping_period,int *pings_lost,int *ping_timeout)
00119 {
00120    char *ping_period_s,*pings_lost_s,*ping_timeout_s;
00121    ping_period_s=pings_lost_s=ping_timeout_s=(char *)0;
00122 
00123    if(string==0 || *string==0){
00124       *ping_period=0;
00125       *pings_lost=0;
00126       *ping_timeout=0;
00127       return 0;
00128    }
00129 
00130    if (((*string)<'0')||((*string)>'9')) {
00131       LM_ERR("malformed ping config string. Unparseable :[%s]\n",string);
00132       return -1;
00133    }
00134    ping_period_s=string;
00135    while(*string){
00136       if(*string == ':'){
00137     *string=0;
00138     if (!pings_lost_s && (*(string+1))) {
00139        pings_lost_s=string+1;
00140     }else if (!ping_timeout_s && (*(string+1))) {
00141        ping_timeout_s=string+1;
00142     }else{
00143        LM_ERR("malformed ping config string. Unparseable :[%s]\n",string);
00144        return -1;
00145     }
00146       }
00147       string++;
00148    }
00149    if (!(ping_period_s && pings_lost_s && ping_timeout_s)) {
00150       LM_ERR("malformed ping config string. Unparseable :[%s]\n",string);
00151       return -1;
00152    }
00153    *ping_period =atoi(ping_period_s);
00154    *pings_lost=atoi(pings_lost_s);
00155    *ping_timeout=atoi(ping_timeout_s);
00156    if (*ping_period<=0 || *pings_lost<=0 || *ping_timeout<=0) {
00157       return -1;
00158    }
00159    return 1;
00160 }
00161 
00162 /**
00163  * we spawn a pinger process.
00164  *
00165  * some day we could spawn a pinger-thread instead of a process..
00166  *
00167  * returns:
00168  *    -1 on error;
00169  */
00170 int spawn_pinger(void)
00171 {
00172    int n,next_jain,next_servlet,timeout;
00173    struct timeval now,last_jain,last_servlet;
00174    struct as_entry *as;
00175 
00176    if ((pinger_pid=fork())<0) {
00177       LM_ERR("forking failed!\n");
00178       goto error;
00179    }else if(pinger_pid>0){
00180       return 0;
00181    }
00182    strcpy(whoami,"Pinger Process\n");
00183    is_dispatcher=0;
00184    my_as=0;
00185    /* child */
00186    if(jain_ping_period && servlet_ping_period){
00187       next_jain=next_servlet=0;
00188    }else if(jain_ping_period){
00189       next_servlet=INT_MAX;
00190       next_jain=0;
00191    }else if(servlet_ping_period){
00192       next_jain=INT_MAX;
00193       next_servlet=0;
00194    }else{
00195       next_jain=next_servlet=INT_MAX;
00196    }
00197 
00198    gettimeofday(&last_jain,NULL);
00199    memcpy(&last_servlet,&last_jain,sizeof(struct timeval));
00200 
00201    while(1){
00202       gettimeofday(&now,NULL);
00203       if(next_jain!=INT_MAX){
00204     next_jain=jain_ping_period-((now.tv_sec-last_jain.tv_sec)*1000 + (now.tv_usec-last_jain.tv_usec)/1000);
00205       }
00206       if(next_servlet!=INT_MAX){
00207     next_servlet=servlet_ping_period-((now.tv_sec-last_servlet.tv_sec)*1000 + (now.tv_usec-last_servlet.tv_usec)/1000);
00208       }
00209 
00210       timeout=next_jain<next_servlet?next_jain:next_servlet;
00211 
00212       if ((n=poll(NULL,0,timeout<0?0:timeout))<0) {
00213     LM_ERR("poll returned %d\n",n);
00214     goto error;
00215       }else if(n==0){/*timeout*/
00216     gettimeofday(&now,NULL);
00217     if (jain_ping_period && ((now.tv_sec-last_jain.tv_sec)*1000 + (now.tv_usec-last_jain.tv_usec)/1000)>=jain_ping_period) {
00218        gettimeofday(&last_jain,NULL);
00219        for(as=as_list;as;as=as->next){
00220           if(as->type == AS_TYPE && as->connected){
00221         send_ping(as,&now);
00222           }
00223        }
00224     }
00225     if (servlet_ping_period && ((now.tv_sec-last_servlet.tv_sec)*1000 + (now.tv_usec-last_servlet.tv_usec)/1000)>=servlet_ping_period) {
00226        gettimeofday(&last_servlet,NULL);
00227        for(as=as_list;as;as=as->next){
00228           if(as->type==AS_TYPE && as->connected){
00229         send_ping(as,&now);
00230           }
00231        }
00232     }
00233       }else{/*impossible..*/
00234     LM_ERR("bug:poll returned %d\n",n);
00235     goto error;
00236       }
00237    }
00238    return 0;
00239 error:
00240    return -1;
00241 }
00242 
00243 /**
00244  * sends a ping to the app-server, and uses now as sent time
00245  *
00246  * returns
00247  *    0 on success
00248  *    -1 on error
00249  */
00250 static inline int send_ping(struct as_entry *the_as,struct timeval *now)
00251 {
00252    char *the_ping;
00253    as_msg_p aping;
00254    int pinglen,retval;
00255    unsigned int seqno;
00256    struct ping *pingu;
00257 
00258    aping=(as_msg_p)0;
00259    the_ping=(char *)0;
00260    retval=0;
00261    if (!(aping=shm_malloc(sizeof(as_msg_t)))) {
00262       LM_ERR("out of shm_mem for ping event\n");
00263       retval=-1;
00264       goto error;
00265    }
00266    if (!(the_ping=create_ping_event(&pinglen,0,&seqno))) {
00267       LM_ERR("Unable to create ping event\n");
00268       retval=-1;
00269       goto error;
00270    }
00271    aping->as=the_as;
00272    aping->msg=the_ping;
00273    aping->len=pinglen;
00274    
00275    lock_get(the_as->u.as.jain_pings.mutex);
00276    {
00277       if(the_as->u.as.jain_pings.count==the_as->u.as.jain_pings.size){
00278     LM_ERR("Cant send ping because the pingtable is full (%d pings)\n",\
00279           the_as->u.as.jain_pings.count);
00280     retval=0;
00281     lock_release(the_as->u.as.jain_pings.mutex);
00282     goto error;
00283       }else{
00284     pingu=the_as->u.as.jain_pings.pings+the_as->u.as.jain_pings.end;
00285     the_as->u.as.jain_pings.end=(the_as->u.as.jain_pings.end+1)%the_as->u.as.jain_pings.size;
00286     the_as->u.as.jain_pings.count++;
00287       }
00288       memcpy(&pingu->sent,now,sizeof(struct timeval));
00289       pingu->id=seqno;
00290    }
00291    lock_release(the_as->u.as.jain_pings.mutex);
00292 again:
00293    if(0>write(write_pipe,&aping,sizeof(as_msg_p))){
00294       if(errno==EINTR){
00295     goto again;
00296       }else{
00297     LM_ERR("error sending ping\n");
00298     goto error;
00299       }
00300    }
00301    return 0;
00302 error:
00303    if(aping)
00304       shm_free(aping);
00305    if(the_ping)
00306       shm_free(the_ping);
00307    return retval;
00308 }
00309 
00310 /**
00311  * Initializes the high availability (ha) structure
00312  *
00313  * returns
00314  *    0 on success
00315  *    -1 on error
00316  */
00317 inline int init_pingtable(struct ha *table,int timeout,int maxpings)
00318 {
00319    if(maxpings<=0)
00320       maxpings=1;
00321    table->begin=0;
00322    table->end=0;
00323    table->timed_out_pings=0;
00324    table->size=maxpings;
00325    table->timeout=timeout;
00326 
00327    if (!(table->mutex=lock_alloc())){
00328       LM_ERR("Unable to allocate a lock for the ping table\n");
00329       goto error;
00330    }else 
00331       lock_init(table->mutex);
00332    LM_ERR("alloc'ing %d bytes for %d pings\n",(int)(maxpings*sizeof(struct ping)),maxpings);
00333    if (0==(table->pings=shm_malloc(maxpings*sizeof(struct ping)))){
00334       LM_ERR("Unable to shm_malloc %d bytes for %d pings\n",(int)(maxpings*sizeof(struct ping)),maxpings);
00335       goto error;
00336    }else{
00337       memset(table->pings,0,(maxpings*sizeof(struct ping)));
00338    }
00339    return 0;
00340 error:
00341    destroy_pingtable(table);
00342    return -1;
00343 }
00344 
00345 inline void destroy_pingtable(struct ha *table)
00346 {
00347    if(table->mutex){
00348       lock_dealloc(table->mutex);
00349       table->mutex=0;
00350    }
00351    if(table->pings){
00352       shm_free(table->pings);
00353       table->pings=0;
00354    }
00355 }
00356 
00357 /**
00358  * event_length(4) UNSIGNED INT includes the length 4 bytes itself
00359  * type(1), 
00360  * processor_id(1), 0 means nobody, 0xFF means everybody, 0<N<0xFF means processor with id=N
00361  * flags(4), 
00362  * ping_num(4), 
00363  *
00364  * NOT REENTRANT (uses static local var to store ping seqno.)
00365  * 
00366  * returns
00367  *    0 on error
00368  *    pointer to the buffer on success
00369  *
00370  */
00371 char * create_ping_event(int *evt_len,int flags,unsigned int *seqno)
00372 {
00373    unsigned int i,k;
00374    char *buffer;
00375    static unsigned int ping_seqno=0;
00376 
00377    if (!(buffer=shm_malloc(4+1+1+4+4))) {
00378       LM_ERR("out of shm for ping\n");
00379       return 0;
00380    }
00381    *evt_len=(4+1+1+4+4);
00382    ping_seqno++;
00383    *seqno=ping_seqno;
00384    i=htonl(14);
00385    memcpy(buffer,&i,4);
00386    k=4;
00387    /*type*/
00388    buffer[k++]=(unsigned char)PING_AC;
00389    /*processor_id*/
00390    buffer[k++]=(unsigned char)0xFF;
00391    /*flags*/
00392    flags=htonl(flags);
00393    memcpy(buffer+k,&flags,4);
00394    k+=4;
00395    /*ping sequence number*/
00396    i=htonl(ping_seqno);
00397    memcpy(buffer+k,&i,4);
00398    k+=4;
00399    return buffer;
00400 }

Generated on Wed May 23 06:00:45 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6