interprocess_buffer.c

Go to the documentation of this file.
00001 /*
00002  * $Id$
00003  *
00004  * SNMPStats Module 
00005  * Copyright (C) 2006 SOMA Networks, INC.
00006  * Written by: Jeffrey Magder (jmagder@somanetworks.com)
00007  *
00008  * This file is part of Kamailio, a free SIP server.
00009  *
00010  * Kamailio is free software; you can redistribute it and/or modify it
00011  * under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version
00014  *
00015  * Kamailio is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00023  * USA
00024  *
00025  * History:
00026  * --------
00027  * 2006-11-23 initial version (jmagder)
00028  */
00029 
00045 #include <net-snmp/net-snmp-config.h>
00046 #include <net-snmp/net-snmp-includes.h>
00047 #include <net-snmp/agent/net-snmp-agent-includes.h>
00048 
00049 #include "interprocess_buffer.h"
00050 #include "snmpSIPContactTable.h"
00051 #include "snmpSIPRegUserTable.h"
00052 #include "hashTable.h"
00053 #include "utilities.h"
00054 
00055 #include "../usrloc/ul_callback.h"
00056 
00064 hashSlot_t *hashTable = NULL;
00065 
00067 interprocessBuffer_t *frontRegUserTableBuffer = NULL;
00068 interprocessBuffer_t *endRegUserTableBuffer = NULL;
00069 
00072 gen_lock_t           *interprocessCBLock = NULL;
00073 
00079 static void executeInterprocessBufferCmd(interprocessBuffer_t *currentBuffer);
00080 
00085 int initInterprocessBuffers(void) 
00086 {
00087         /* Initialize the shared memory that will be used to buffer messages
00088          * over the usrloc module to RegUserTable callback. */
00089         frontRegUserTableBuffer =  shm_malloc(sizeof(interprocessBuffer_t));
00090         endRegUserTableBuffer   =  shm_malloc(sizeof(interprocessBuffer_t));
00091 
00092     if(frontRegUserTableBuffer == NULL || endRegUserTableBuffer == NULL)
00093     {
00094         LM_ERR("no more shared memory\n");
00095         return -1;
00096     }
00097 
00098         memset(frontRegUserTableBuffer, 0x00, sizeof(interprocessBuffer_t));
00099         memset(endRegUserTableBuffer,   0x00, sizeof(interprocessBuffer_t));
00100 
00101         /* Initialize a lock to the interprocess buffer.  The lock will be used
00102          * to control race-conditions that would otherwise occur if an snmp
00103          * command was received while the interprocess buffer was being consumed.
00104          */
00105         interprocessCBLock = lock_alloc();
00106         if(interprocessCBLock==NULL)
00107         {
00108         LM_ERR("cannot allocate the lock\n");
00109         shm_free(frontRegUserTableBuffer);
00110         frontRegUserTableBuffer = NULL;
00111         shm_free(endRegUserTableBuffer);
00112         endRegUserTableBuffer = NULL;
00113         return -1;
00114         }
00115         lock_init(interprocessCBLock);
00116 
00117         hashTable = createHashTable(HASH_SIZE);
00118     if(hashTable == NULL)
00119     {
00120         LM_ERR("no more shared memory\n");
00121                 lock_destroy(interprocessCBLock);
00122                 lock_dealloc(interprocessCBLock);
00123         shm_free(frontRegUserTableBuffer);
00124         frontRegUserTableBuffer = NULL;
00125         shm_free(endRegUserTableBuffer);
00126         endRegUserTableBuffer = NULL;
00127         return -1;
00128     }
00129 
00130         return 1;
00131 }
00132 
00147 void handleContactCallbacks(ucontact_t *contactInfo, int type, void *param) 
00148 {
00149         char *addressOfRecord;
00150         char *contact;
00151 
00152         interprocessBuffer_t *currentBufferElement;
00153 
00154         currentBufferElement = shm_malloc(sizeof(interprocessBuffer_t));
00155 
00156         if (currentBufferElement == NULL) 
00157         {
00158                 goto error;
00159         }
00160 
00161         /* We need to maintain our own copies of the AOR and contact address to
00162          * prevent the corruption of our internal data structures.  
00163          *
00164          * If we do not maintain our own copies, then the AOR and contact adress
00165          * pointed to could be removed and reallocated to another thread before
00166          * we get a chance to consume our interprocess buffer.  */
00167         convertStrToCharString(contactInfo->aor,  &addressOfRecord);
00168         convertStrToCharString(&(contactInfo->c), &contact);
00169 
00170         currentBufferElement->stringName    = addressOfRecord;
00171         currentBufferElement->stringContact = contact;
00172         currentBufferElement->contactInfo   = contactInfo;
00173         currentBufferElement->callbackType  = type;
00174         currentBufferElement->next          = NULL;
00175 
00176 
00177         /* A lock is necessary to prevent a race condition.  Specifically, it
00178          * could happen that we find the front of the buffer to be non-null,
00179          * are scheduled out, the entire buffer (or part of it) is consumed and
00180          * freed, and then we assign our list to deallocated memory. */
00181         lock_get(interprocessCBLock);
00182 
00183         /* This is the first element to be added. */
00184         if (frontRegUserTableBuffer->next == NULL) {
00185                 frontRegUserTableBuffer->next     = currentBufferElement;
00186         } else {
00187                 endRegUserTableBuffer->next->next = currentBufferElement;
00188         }
00189         
00190         endRegUserTableBuffer->next   = currentBufferElement;
00191 
00192         lock_release(interprocessCBLock);
00193         
00194         return;
00195 
00196 error:
00197         LM_ERR("Not enough shared memory for  openserSIPRegUserTable insert."
00198                         " (%s)\n", contactInfo->c.s);
00199 }
00200 
00201 
00219 void consumeInterprocessBuffer(void) 
00220 {
00221         interprocessBuffer_t *previousBuffer;
00222         interprocessBuffer_t *currentBuffer;
00223         
00224         /* There is nothing to consume, so just exit. */
00225         if (frontRegUserTableBuffer->next == NULL) 
00226         {
00227                 return;
00228         }
00229 
00230         /* We are going to consume the entire buffer, but we don't want the
00231          * buffer to change midway through.  So assign the front of the buffer
00232          * to NULL so that any other callbacks from the usrloc module will be
00233          * appended to a new list.  We need to be careful to get a lock first
00234          * though, to avoid race conditions. */
00235         lock_get(interprocessCBLock);
00236 
00237         currentBuffer = frontRegUserTableBuffer->next;
00238         
00239         frontRegUserTableBuffer->next = NULL;
00240         endRegUserTableBuffer->next   = NULL;
00241 
00242         lock_release(interprocessCBLock);
00243 
00244         while (currentBuffer != NULL) {
00245 
00246                 executeInterprocessBufferCmd(currentBuffer);
00247 
00248                 /* We need to assign the current buffer to a temporary place
00249                  * before we move onto the next buffer.  Otherwise the memory
00250                  * could be modified between freeing it and moving onto the next
00251                  * buffer element. */
00252                 previousBuffer = currentBuffer;
00253                 currentBuffer = currentBuffer->next;
00254                 shm_free(previousBuffer->stringName);
00255                 shm_free(previousBuffer->stringContact);
00256                 shm_free(previousBuffer);
00257 
00258         }
00259 
00260 }
00261 
00262 
00268 static void executeInterprocessBufferCmd(interprocessBuffer_t *currentBuffer) 
00269 {
00270         int delContactIndex;
00271 
00272         aorToIndexStruct_t *currentUser;
00273 
00274         if (currentBuffer->callbackType == UL_CONTACT_INSERT) 
00275         {
00276                 /* Add the user if the user doesn't exist, or increment its 
00277                  * contact index otherwise. */
00278                 updateUser(currentBuffer->stringName);
00279         }
00280         else if (currentBuffer->callbackType != UL_CONTACT_EXPIRE)
00281         {
00282                 /* Currently we only support UL_CONTACT_INSERT and
00283                  * UL_CONTACT_EXPIRE.  If we receive another callback type, this
00284                  * is a bug. */
00285                 LM_ERR("found a command on the interprocess buffer that"
00286                                 " was not an INSERT or EXPIRE");
00287                 return;
00288         }
00289 
00290         currentUser =
00291                 findHashRecord(hashTable, currentBuffer->stringName, HASH_SIZE);
00292 
00293 
00294         /* This should never happen.  This is more of a sanity check. */
00295         if (currentUser == NULL) {
00296                 LM_ERR("Received a request for contact: %s for user: %s who doesn't "
00297                                 "exists\n", currentBuffer->stringName, 
00298                                 currentBuffer->stringContact);
00299                 return;
00300         } 
00301 
00302         /* This buffer element specified that we need to add a contact.  So lets
00303          * add them */
00304         if (currentBuffer->callbackType == UL_CONTACT_INSERT) {
00305 
00306                 /* Increment the contact index, which will be used to generate
00307                  * our new row.  */  
00308                 currentUser->contactIndex++;
00309 
00310                 /* We should do this after we create the row in the snmptable.
00311                  * Its easier to delete the SNMP Row than the contact record. */
00312                 if(!insertContactRecord(&(currentUser->contactList), 
00313                         currentUser->contactIndex, 
00314                                 currentBuffer->stringContact)) {
00315 
00316                         LM_ERR("openserSIPRegUserTable was unable to allocate memory for "
00317                                         "adding contact: %s to user %s.\n",
00318                                         currentBuffer->stringName, currentBuffer->stringContact);
00319 
00320                         /* We didn't use the index, so decrement it so we can
00321                          * use it next time around. */
00322                         currentUser->contactIndex--;
00323                         
00324                         return;
00325                 }
00326         
00327                 if (!createContactRow(currentUser->userIndex, 
00328                                         currentUser->contactIndex,
00329                                         currentBuffer->stringContact, 
00330                                         currentBuffer->contactInfo)) {
00331                 
00332                         deleteContactRecord(&(currentUser->contactList), 
00333                                         currentBuffer->stringContact);
00334 
00335                 }
00336 
00337         }
00338         else {
00339 
00340                 delContactIndex = 
00341                         deleteContactRecord(&(currentUser->contactList), 
00342                                         currentBuffer->stringContact);
00343 
00344                 /* This should never happen.  But its probably wise to check and
00345                  * to print out debug messages in case there is a hidden bug.  */
00346                 if(delContactIndex == 0) {
00347                         
00348                         LM_ERR("Received a request to delete contact: %s for user: %s"
00349                                 "  who doesn't exist\n", currentBuffer->stringName,
00350                                 currentBuffer->stringContact);
00351                         return;
00352 
00353                 }               
00354 
00355                 deleteContactRow(currentUser->userIndex, delContactIndex);
00356 
00357                 deleteUser(hashTable, currentBuffer->stringName, HASH_SIZE);
00358         }
00359 }
00360 
00361 void freeInterprocessBuffer(void)
00362 {
00363     interprocessBuffer_t *currentBuffer, *previousBuffer;
00364 
00365         if (frontRegUserTableBuffer==NULL
00366                         || frontRegUserTableBuffer->next == NULL
00367                         || endRegUserTableBuffer==NULL) {
00368         LM_DBG("Nothing to clean\n");
00369                 return;
00370         }
00371 
00372         currentBuffer = frontRegUserTableBuffer->next;
00373         
00374         frontRegUserTableBuffer->next = NULL;
00375         endRegUserTableBuffer->next   = NULL;
00376 
00377 
00378         while (currentBuffer != NULL) {
00379 
00380         previousBuffer = currentBuffer;
00381         currentBuffer = currentBuffer->next;
00382         shm_free(previousBuffer->stringName);
00383         shm_free(previousBuffer->stringContact);
00384         shm_free(previousBuffer);
00385 
00386         }
00387 
00388     if(frontRegUserTableBuffer)
00389         shm_free(frontRegUserTableBuffer);
00390 
00391     if(endRegUserTableBuffer)
00392         shm_free(endRegUserTableBuffer);
00393 
00394 }