modules/snmpstats/interprocess_buffer.c
 2709a6b8  /* * SNMPStats Module * Copyright (C) 2006 SOMA Networks, INC. * Written by: Jeffrey Magder (jmagder@somanetworks.com) * 27642a08  * This file is part of Kamailio, a free SIP server. 2709a6b8  * 27642a08  * Kamailio is free software; you can redistribute it and/or modify it 2709a6b8  * 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 * 27642a08  * Kamailio is distributed in the hope that it will be useful, but 2709a6b8  * 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 9e1ff448  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 2709a6b8  * USA * 94dd7c5f  */ /*! * \file * \brief SNMP statistic module, interprocess buffer 9f97c5e5  * \author jmagder 2709a6b8  * * This file implements the interprocess buffer, used for marshalling data 68d7468c  * exchange from the usrloc module to the kamailioSIPRegUserTable, * kamailioSIPContactTable, and indirectly the kamailioSIPRegUserLookupTable. 94dd7c5f  * 2709a6b8  * Details on why the interprocess buffer is needed can be found in the comments * at the top of interprocess_buffer.h 94dd7c5f  * \ingroup snmpstats ca00a464  * - Module: \ref snmpstats 2709a6b8  */ #include #include #include #include "interprocess_buffer.h" 2a25dc59  #include "snmpSIPContactTable.h" #include "snmpSIPRegUserTable.h" 2709a6b8  #include "hashTable.h" #include "utilities.h" #include "../usrloc/ul_callback.h"  94dd7c5f  /*! 2709a6b8  * The hash table: * * 1) maps all aor's to snmp's UserIndex for help in deleting SNMP Rows. * * 2) maps a given aor to a contact list. */ 06e71ad9  hashSlot_t *hashTable = NULL; 2709a6b8   94dd7c5f  /*! All interprocess communication is stored between these two declarations. */ 06e71ad9  interprocessBuffer_t *frontRegUserTableBuffer = NULL; interprocessBuffer_t *endRegUserTableBuffer = NULL; 2709a6b8   94dd7c5f  /*! This is to protect the potential racecondition in which a command is added to 2709a6b8  * the buffer while it is being consumed */ 06e71ad9  gen_lock_t *interprocessCBLock = NULL; 2709a6b8   94dd7c5f  /*! 2709a6b8  * This function takes an element of the interprocess buffer passed to it, and * handles populating the respective user and contact tables with its contained * data. */ static void executeInterprocessBufferCmd(interprocessBuffer_t *currentBuffer);  94dd7c5f  /*! 2709a6b8  * Initialize shared memory used to buffer communication between the usrloc * module and the SNMPStats module. (Specifically, the user and contact tables) */ 07bd572b  int initInterprocessBuffers(void)  2709a6b8  { /* Initialize the shared memory that will be used to buffer messages * over the usrloc module to RegUserTable callback. */ frontRegUserTableBuffer = shm_malloc(sizeof(interprocessBuffer_t)); endRegUserTableBuffer = shm_malloc(sizeof(interprocessBuffer_t)); 94dd7c5f   98a9961a  if(frontRegUserTableBuffer == NULL || endRegUserTableBuffer == NULL) { LM_ERR("no more shared memory\n"); return -1; } 2709a6b8  memset(frontRegUserTableBuffer, 0x00, sizeof(interprocessBuffer_t)); memset(endRegUserTableBuffer, 0x00, sizeof(interprocessBuffer_t)); /* Initialize a lock to the interprocess buffer. The lock will be used * to control race-conditions that would otherwise occur if an snmp * command was received while the interprocess buffer was being consumed. */ interprocessCBLock = lock_alloc(); 06e71ad9  if(interprocessCBLock==NULL) { LM_ERR("cannot allocate the lock\n"); shm_free(frontRegUserTableBuffer); frontRegUserTableBuffer = NULL; shm_free(endRegUserTableBuffer); endRegUserTableBuffer = NULL; return -1; } 2709a6b8  lock_init(interprocessCBLock); hashTable = createHashTable(HASH_SIZE); 98a9961a  if(hashTable == NULL) { LM_ERR("no more shared memory\n"); 06e71ad9  lock_destroy(interprocessCBLock); lock_dealloc(interprocessCBLock); 98a9961a  shm_free(frontRegUserTableBuffer); frontRegUserTableBuffer = NULL; shm_free(endRegUserTableBuffer); endRegUserTableBuffer = NULL; return -1; } 2709a6b8  return 1; }  94dd7c5f  /*! USRLOC Callback Handler: 2709a6b8  * * This function should be registered to receive callbacks from the usrloc * module. It can be called for any of the callbacks listed in ul_callback.h. * The callback type will be passed in 'type', and the contact the callback * applies to will be supplied in 'contactInfo. This information will be copied * into the interprocess buffer. The interprocess buffer will be consumed at a * later time, when consumeInterprocessBuffer() is called. * * This callback is thread safe with respect to the consumeInterprocessBuffer() * function. Specifically, the interprocess buffer should not be corrupted by * any race conditions between this function and the consumeInterprocessBuffer() * function. */ void handleContactCallbacks(ucontact_t *contactInfo, int type, void *param) { char *addressOfRecord; char *contact; interprocessBuffer_t *currentBufferElement; currentBufferElement = shm_malloc(sizeof(interprocessBuffer_t)); if (currentBufferElement == NULL) { goto error; } /* We need to maintain our own copies of the AOR and contact address to * prevent the corruption of our internal data structures. * * If we do not maintain our own copies, then the AOR and contact adress * pointed to could be removed and reallocated to another thread before * we get a chance to consume our interprocess buffer. */ convertStrToCharString(contactInfo->aor, &addressOfRecord); convertStrToCharString(&(contactInfo->c), &contact); currentBufferElement->stringName = addressOfRecord; currentBufferElement->stringContact = contact; currentBufferElement->contactInfo = contactInfo; currentBufferElement->callbackType = type; currentBufferElement->next = NULL; /* A lock is necessary to prevent a race condition. Specifically, it * could happen that we find the front of the buffer to be non-null, * are scheduled out, the entire buffer (or part of it) is consumed and * freed, and then we assign our list to deallocated memory. */ lock_get(interprocessCBLock); /* This is the first element to be added. */ if (frontRegUserTableBuffer->next == NULL) { frontRegUserTableBuffer->next = currentBufferElement; } else { endRegUserTableBuffer->next->next = currentBufferElement; } endRegUserTableBuffer->next = currentBufferElement; lock_release(interprocessCBLock); return; error: 68d7468c  LM_ERR("Not enough shared memory for kamailioSIPRegUserTable insert." de7fe5e9  " (%s)\n", contactInfo->c.s); 2709a6b8  }  94dd7c5f  /*! Interprocess Buffer consumption Function. This function will iterate over 2709a6b8  * every element of the interprocess buffer, and add or remove the specified * contacts and users. Whether the contacts are added or removed is dependent * on if the original element was added as a result of a UL_CONTACT_INSERT or * UL_CONTACT_EXPIRE callback. * * The function will free any memory occupied by the interprocess buffer. * 94dd7c5f  * \note This function is believed to be thread safe. Specifically, it protects 2709a6b8  * corruption of the interprocess buffer through the interprocessCBLock. * This ensures no corruption of the buffer by race conditions. The lock * has been designed to be occupied for as short a period as possible, so * as to prevent long waits. Specifically, once we start consumption of * the list, other processes are free to continue even before we are done. * This is made possible by simply changing the head of the interprocess * buffer, and then releasing the lock. */ 07bd572b  void consumeInterprocessBuffer(void)  2709a6b8  { interprocessBuffer_t *previousBuffer; interprocessBuffer_t *currentBuffer; /* There is nothing to consume, so just exit. */ if (frontRegUserTableBuffer->next == NULL) { return; } /* We are going to consume the entire buffer, but we don't want the * buffer to change midway through. So assign the front of the buffer * to NULL so that any other callbacks from the usrloc module will be * appended to a new list. We need to be careful to get a lock first * though, to avoid race conditions. */ lock_get(interprocessCBLock); currentBuffer = frontRegUserTableBuffer->next; frontRegUserTableBuffer->next = NULL; endRegUserTableBuffer->next = NULL; lock_release(interprocessCBLock); while (currentBuffer != NULL) { executeInterprocessBufferCmd(currentBuffer); /* We need to assign the current buffer to a temporary place * before we move onto the next buffer. Otherwise the memory * could be modified between freeing it and moving onto the next * buffer element. */ previousBuffer = currentBuffer; currentBuffer = currentBuffer->next; 98a9961a  shm_free(previousBuffer->stringName); shm_free(previousBuffer->stringContact); 2709a6b8  shm_free(previousBuffer); } }  94dd7c5f  /*! 2709a6b8  * This function takes an element of the interprocess buffer passed to it, and * handles populating the respective user and contact tables with its contained * data. */ static void executeInterprocessBufferCmd(interprocessBuffer_t *currentBuffer) { int delContactIndex; aorToIndexStruct_t *currentUser; if (currentBuffer->callbackType == UL_CONTACT_INSERT) { /* Add the user if the user doesn't exist, or increment its * contact index otherwise. */ updateUser(currentBuffer->stringName); } else if (currentBuffer->callbackType != UL_CONTACT_EXPIRE) { /* Currently we only support UL_CONTACT_INSERT and * UL_CONTACT_EXPIRE. If we receive another callback type, this * is a bug. */ de7fe5e9  LM_ERR("found a command on the interprocess buffer that" " was not an INSERT or EXPIRE"); 2709a6b8  return; } currentUser = findHashRecord(hashTable, currentBuffer->stringName, HASH_SIZE); /* This should never happen. This is more of a sanity check. */ if (currentUser == NULL) { de7fe5e9  LM_ERR("Received a request for contact: %s for user: %s who doesn't " "exists\n", currentBuffer->stringName,  2709a6b8  currentBuffer->stringContact); return; } /* This buffer element specified that we need to add a contact. So lets * add them */ if (currentBuffer->callbackType == UL_CONTACT_INSERT) { /* Increment the contact index, which will be used to generate * our new row. */ currentUser->contactIndex++; /* We should do this after we create the row in the snmptable. * Its easier to delete the SNMP Row than the contact record. */ if(!insertContactRecord(&(currentUser->contactList), currentUser->contactIndex, currentBuffer->stringContact)) {  68d7468c  LM_ERR("kamailioSIPRegUserTable was unable to allocate memory for " de7fe5e9  "adding contact: %s to user %s.\n", currentBuffer->stringName, currentBuffer->stringContact); 2709a6b8  /* We didn't use the index, so decrement it so we can * use it next time around. */ currentUser->contactIndex--; return; } if (!createContactRow(currentUser->userIndex, currentUser->contactIndex, currentBuffer->stringContact, currentBuffer->contactInfo)) { deleteContactRecord(&(currentUser->contactList), currentBuffer->stringContact); } } else { delContactIndex = deleteContactRecord(&(currentUser->contactList), currentBuffer->stringContact); /* This should never happen. But its probably wise to check and * to print out debug messages in case there is a hidden bug. */ if(delContactIndex == 0) {  de7fe5e9  LM_ERR("Received a request to delete contact: %s for user: %s" " who doesn't exist\n", currentBuffer->stringName, 2709a6b8  currentBuffer->stringContact); return; } deleteContactRow(currentUser->userIndex, delContactIndex); deleteUser(hashTable, currentBuffer->stringName, HASH_SIZE); } }  98a9961a  void freeInterprocessBuffer(void) { interprocessBuffer_t *currentBuffer, *previousBuffer;  06e71ad9  if (frontRegUserTableBuffer==NULL || frontRegUserTableBuffer->next == NULL || endRegUserTableBuffer==NULL) { 98a9961a  LM_DBG("Nothing to clean\n"); return; } currentBuffer = frontRegUserTableBuffer->next; frontRegUserTableBuffer->next = NULL; endRegUserTableBuffer->next = NULL; while (currentBuffer != NULL) { previousBuffer = currentBuffer; currentBuffer = currentBuffer->next; shm_free(previousBuffer->stringName); shm_free(previousBuffer->stringContact); shm_free(previousBuffer); } 94dd7c5f   98a9961a  if(frontRegUserTableBuffer) shm_free(frontRegUserTableBuffer); if(endRegUserTableBuffer) shm_free(endRegUserTableBuffer); }