interprocess_buffer.c

Go to the documentation of this file.
00001 /*
00002  * $Id: interprocess_buffer.c 4764 2008-08-28 14:41:06Z henningw $
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 
00030 /*!
00031  * \file
00032  * \brief SNMP statistic module, interprocess buffer
00033  *
00034  * This file implements the interprocess buffer, used for marshalling data
00035  * exchange from the usrloc module to the openserSIPRegUserTable,
00036  * openserSIPContactTable, and indirectly the openserSIPRegUserLookupTable.
00037  *
00038  * Details on why the interprocess buffer is needed can be found in the comments
00039  * at the top of interprocess_buffer.h
00040  * \ingroup snmpstats
00041  * - Module: \ref snmpstats
00042  */
00043 
00044 
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 "openserSIPContactTable.h"
00051 #include "openserSIPRegUserTable.h"
00052 #include "hashTable.h"
00053 #include "utilities.h"
00054 
00055 #include "../usrloc/ul_callback.h"
00056 
00057 /*!
00058  * The hash table:
00059  *
00060  *    1) maps all aor's to snmp's UserIndex for help in deleting SNMP Rows.
00061  *
00062  *    2) maps a given aor to a contact list. 
00063  */
00064 hashSlot_t *hashTable;
00065 
00066 /*! All interprocess communication is stored between these two declarations. */
00067 interprocessBuffer_t *frontRegUserTableBuffer;
00068 interprocessBuffer_t *endRegUserTableBuffer;
00069 
00070 /*! This is to protect the potential racecondition in which a command is added to
00071  * the buffer while it is being consumed */
00072 gen_lock_t           *interprocessCBLock;
00073 
00074 /*!
00075  * This function takes an element of the interprocess buffer passed to it, and
00076  * handles populating the respective user and contact tables with its contained
00077  * data.  
00078  */
00079 static void executeInterprocessBufferCmd(interprocessBuffer_t *currentBuffer);
00080 
00081 /*!
00082  * Initialize shared memory used to buffer communication between the usrloc
00083  * module and the SNMPStats module.  (Specifically, the user and contact tables)
00084  */
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    lock_init(interprocessCBLock);
00107 
00108    hashTable = createHashTable(HASH_SIZE);
00109     if(hashTable == NULL)
00110     {
00111         LM_ERR("no more shared memory\n");
00112         shm_free(frontRegUserTableBuffer);
00113         frontRegUserTableBuffer = NULL;
00114         shm_free(endRegUserTableBuffer);
00115         endRegUserTableBuffer = NULL;
00116         return -1;
00117     }
00118 
00119    return 1;
00120 }
00121 
00122 /*! USRLOC Callback Handler:
00123  *
00124  * This function should be registered to receive callbacks from the usrloc
00125  * module.  It can be called for any of the callbacks listed in ul_callback.h.
00126  * The callback type will be passed in 'type', and the contact the callback
00127  * applies to will be supplied in 'contactInfo.  This information will be copied
00128  * into the interprocess buffer.  The interprocess buffer will be consumed at a
00129  * later time, when consumeInterprocessBuffer() is called.  
00130  *
00131  * This callback is thread safe with respect to the consumeInterprocessBuffer()
00132  * function.  Specifically, the interprocess buffer should not be corrupted by
00133  * any race conditions between this function and the consumeInterprocessBuffer()
00134  * function.
00135  */
00136 void handleContactCallbacks(ucontact_t *contactInfo, int type, void *param) 
00137 {
00138    char *addressOfRecord;
00139    char *contact;
00140 
00141    interprocessBuffer_t *currentBufferElement;
00142 
00143    currentBufferElement = shm_malloc(sizeof(interprocessBuffer_t));
00144 
00145    if (currentBufferElement == NULL) 
00146    {
00147       goto error;
00148    }
00149 
00150    /* We need to maintain our own copies of the AOR and contact address to
00151     * prevent the corruption of our internal data structures.  
00152     *
00153     * If we do not maintain our own copies, then the AOR and contact adress
00154     * pointed to could be removed and reallocated to another thread before
00155     * we get a chance to consume our interprocess buffer.  */
00156    convertStrToCharString(contactInfo->aor,  &addressOfRecord);
00157    convertStrToCharString(&(contactInfo->c), &contact);
00158 
00159    currentBufferElement->stringName    = addressOfRecord;
00160    currentBufferElement->stringContact = contact;
00161    currentBufferElement->contactInfo   = contactInfo;
00162    currentBufferElement->callbackType  = type;
00163    currentBufferElement->next          = NULL;
00164 
00165 
00166    /* A lock is necessary to prevent a race condition.  Specifically, it
00167     * could happen that we find the front of the buffer to be non-null,
00168     * are scheduled out, the entire buffer (or part of it) is consumed and
00169     * freed, and then we assign our list to deallocated memory. */
00170    lock_get(interprocessCBLock);
00171 
00172    /* This is the first element to be added. */
00173    if (frontRegUserTableBuffer->next == NULL) {
00174       frontRegUserTableBuffer->next     = currentBufferElement;
00175    } else {
00176       endRegUserTableBuffer->next->next = currentBufferElement;
00177    }
00178    
00179    endRegUserTableBuffer->next   = currentBufferElement;
00180 
00181    lock_release(interprocessCBLock);
00182    
00183    return;
00184 
00185 error:
00186    LM_ERR("Not enough shared memory for  openserSIPRegUserTable insert."
00187          " (%s)\n", contactInfo->c.s);
00188 }
00189 
00190 
00191 /*! Interprocess Buffer consumption Function.  This function will iterate over
00192  * every element of the interprocess buffer, and add or remove the specified
00193  * contacts and users.  Whether the contacts are added or removed is dependent
00194  * on if the original element was added as a result of a UL_CONTACT_INSERT or
00195  * UL_CONTACT_EXPIRE callback.
00196  *
00197  * The function will free any memory occupied by the interprocess buffer.
00198  *
00199  * \note This function is believed to be thread safe.  Specifically, it protects
00200  *       corruption of the interprocess buffer through the interprocessCBLock.
00201  *       This ensures no corruption of the buffer by race conditions.  The lock
00202  *       has been designed to be occupied for as short a period as possible, so 
00203  *       as to prevent long waits.  Specifically, once we start consumption of 
00204  *       the list, other processes are free to continue even before we are done.
00205  *       This is made possible by simply changing the head of the interprocess
00206  *       buffer, and then releasing the lock.  
00207  */
00208 void consumeInterprocessBuffer(void) 
00209 {
00210    interprocessBuffer_t *previousBuffer;
00211    interprocessBuffer_t *currentBuffer;
00212    
00213    /* There is nothing to consume, so just exit. */
00214    if (frontRegUserTableBuffer->next == NULL) 
00215    {
00216       return;
00217    }
00218 
00219    /* We are going to consume the entire buffer, but we don't want the
00220     * buffer to change midway through.  So assign the front of the buffer
00221     * to NULL so that any other callbacks from the usrloc module will be
00222     * appended to a new list.  We need to be careful to get a lock first
00223     * though, to avoid race conditions. */
00224    lock_get(interprocessCBLock);
00225 
00226    currentBuffer = frontRegUserTableBuffer->next;
00227    
00228    frontRegUserTableBuffer->next = NULL;
00229    endRegUserTableBuffer->next   = NULL;
00230 
00231    lock_release(interprocessCBLock);
00232 
00233    while (currentBuffer != NULL) {
00234 
00235       executeInterprocessBufferCmd(currentBuffer);
00236 
00237       /* We need to assign the current buffer to a temporary place
00238        * before we move onto the next buffer.  Otherwise the memory
00239        * could be modified between freeing it and moving onto the next
00240        * buffer element. */
00241       previousBuffer = currentBuffer;
00242       currentBuffer = currentBuffer->next;
00243       shm_free(previousBuffer->stringName);
00244       shm_free(previousBuffer->stringContact);
00245       shm_free(previousBuffer);
00246 
00247    }
00248 
00249 }
00250 
00251 
00252 /*!
00253  * This function takes an element of the interprocess buffer passed to it, and
00254  * handles populating the respective user and contact tables with its contained
00255  * data.  
00256  */
00257 static void executeInterprocessBufferCmd(interprocessBuffer_t *currentBuffer) 
00258 {
00259    int delContactIndex;
00260 
00261    aorToIndexStruct_t *currentUser;
00262 
00263    if (currentBuffer->callbackType == UL_CONTACT_INSERT) 
00264    {
00265       /* Add the user if the user doesn't exist, or increment its 
00266        * contact index otherwise. */
00267       updateUser(currentBuffer->stringName);
00268    }
00269    else if (currentBuffer->callbackType != UL_CONTACT_EXPIRE)
00270    {
00271       /* Currently we only support UL_CONTACT_INSERT and
00272        * UL_CONTACT_EXPIRE.  If we receive another callback type, this
00273        * is a bug. */
00274       LM_ERR("found a command on the interprocess buffer that"
00275             " was not an INSERT or EXPIRE");
00276       return;
00277    }
00278 
00279    currentUser =
00280       findHashRecord(hashTable, currentBuffer->stringName, HASH_SIZE);
00281 
00282 
00283    /* This should never happen.  This is more of a sanity check. */
00284    if (currentUser == NULL) {
00285       LM_ERR("Received a request for contact: %s for user: %s who doesn't "
00286             "exists\n", currentBuffer->stringName, 
00287             currentBuffer->stringContact);
00288       return;
00289    } 
00290 
00291    /* This buffer element specified that we need to add a contact.  So lets
00292     * add them */
00293    if (currentBuffer->callbackType == UL_CONTACT_INSERT) {
00294 
00295       /* Increment the contact index, which will be used to generate
00296        * our new row.  */  
00297       currentUser->contactIndex++;
00298 
00299       /* We should do this after we create the row in the snmptable.
00300        * Its easier to delete the SNMP Row than the contact record. */
00301       if(!insertContactRecord(&(currentUser->contactList), 
00302          currentUser->contactIndex, 
00303             currentBuffer->stringContact)) {
00304 
00305          LM_ERR("openserSIPRegUserTable was unable to allocate memory for "
00306                "adding contact: %s to user %s.\n",
00307                currentBuffer->stringName, currentBuffer->stringContact);
00308 
00309          /* We didn't use the index, so decrement it so we can
00310           * use it next time around. */
00311          currentUser->contactIndex--;
00312          
00313          return;
00314       }
00315    
00316       if (!createContactRow(currentUser->userIndex, 
00317                currentUser->contactIndex,
00318                currentBuffer->stringContact, 
00319                currentBuffer->contactInfo)) {
00320       
00321          deleteContactRecord(&(currentUser->contactList), 
00322                currentBuffer->stringContact);
00323 
00324       }
00325 
00326    }
00327    else {
00328 
00329       delContactIndex = 
00330          deleteContactRecord(&(currentUser->contactList), 
00331                currentBuffer->stringContact);
00332 
00333       /* This should never happen.  But its probably wise to check and
00334        * to print out debug messages in case there is a hidden bug.  */
00335       if(delContactIndex == 0) {
00336          
00337          LM_ERR("Received a request to delete contact: %s for user: %s"
00338             "  who doesn't exist\n", currentBuffer->stringName,
00339             currentBuffer->stringContact);
00340          return;
00341 
00342       }     
00343 
00344       deleteContactRow(currentUser->userIndex, delContactIndex);
00345 
00346       deleteUser(hashTable, currentBuffer->stringName, HASH_SIZE);
00347    }
00348 }
00349 
00350 void freeInterprocessBuffer(void)
00351 {
00352     interprocessBuffer_t *currentBuffer, *previousBuffer;
00353 
00354    if (frontRegUserTableBuffer->next == NULL) {
00355         LM_DBG("Nothing to clean\n");
00356       return;
00357    }
00358 
00359    currentBuffer = frontRegUserTableBuffer->next;
00360    
00361    frontRegUserTableBuffer->next = NULL;
00362    endRegUserTableBuffer->next   = NULL;
00363 
00364 
00365    while (currentBuffer != NULL) {
00366 
00367         previousBuffer = currentBuffer;
00368         currentBuffer = currentBuffer->next;
00369         shm_free(previousBuffer->stringName);
00370         shm_free(previousBuffer->stringContact);
00371         shm_free(previousBuffer);
00372 
00373    }
00374 
00375     if(frontRegUserTableBuffer)
00376         shm_free(frontRegUserTableBuffer);
00377 
00378     if(endRegUserTableBuffer)
00379         shm_free(endRegUserTableBuffer);
00380 
00381 }

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