/*
* $Id: interprocess_buffer.c 1425 2006-12-18 22:22:51Z jmagder $
*
* SNMPStats Module
* Copyright (C) 2006 SOMA Networks, INC.
* Written by: Jeffrey Magder (jmagder@somanetworks.com)
*
* This file is part of openser, a free SIP server.
*
* openser 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
*
* openser 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
*
* History:
* --------
* 2006-11-23 initial version (jmagder)
*
* This file implements the interprocess buffer, used for marshalling data
* exchange from the usrloc module to the openserSIPRegUserTable,
* openserSIPContactTable, and indirectly the openserSIPRegUserLookupTable.
* Details on why the interprocess buffer is needed can be found in the comments
* at the top of interprocess_buffer.h
*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "interprocess_buffer.h"
#include "openserSIPContactTable.h"
#include "openserSIPRegUserTable.h"
#include "hashTable.h"
#include "utilities.h"
#include "../usrloc/ul_callback.h"
/*
* 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.
*/
hashSlot_t *hashTable;
/* All interprocess communication is stored between these two declarations. */
interprocessBuffer_t *frontRegUserTableBuffer;
interprocessBuffer_t *endRegUserTableBuffer;
/* This is to protect the potential racecondition in which a command is added to
* the buffer while it is being consumed */
gen_lock_t *interprocessCBLock;
/*
* 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);
/*
* Initialize shared memory used to buffer communication between the usrloc
* module and the SNMPStats module. (Specifically, the user and contact tables)
*/
int initInterprocessBuffers()
{
/* 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));
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();
lock_init(interprocessCBLock);
hashTable = createHashTable(HASH_SIZE);
return 1;
}
/* USRLOC Callback Handler:
*
* 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:
LOG(L_ERR, "ERROR: SNMPStats: Not enough shared memory for "
" openserSIPRegUserTable insert. (%s)\n",
contactInfo->c.s);
}
/* Interprocess Buffer consumption Function. This function will iterate over
* 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.
*
* Note: This function is believed to be thread safe. Specifically, it protects
* 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.
*/
void consumeInterprocessBuffer()
{
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;
shm_free(previousBuffer);
}
}
/*
* 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. */
LOG(L_ERR, "BUG: SNMPStats: Found a command on the interprocess"
" buffer that wasn't an INSERT or EXPIRE");
return;
}
currentUser =
findHashRecord(hashTable, currentBuffer->stringName, HASH_SIZE);
/* This should never happen. This is more of a sanity check. */
if (currentUser == NULL) {
LOG(L_ERR, "ERROR: SNMPStats: Received a request for "
"contact: %s for user: %s who doesn't "
"exists\n", currentBuffer->stringName,
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)) {
LOG(L_ERR, "ERROR: SNMPStats: openserSIPRegUserTable "
"was unable to allocate memory for "
"adding contact: %s to user %s.\n",
currentBuffer->stringName,
currentBuffer->stringContact);
/* 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) {
LOG(L_ERR, "ERROR: SNMPStats: Received a request to delete"
" contact: %s for user: %s who doesn't exist\n",
currentBuffer->stringName,
currentBuffer->stringContact);
return;
}
deleteContactRow(currentUser->userIndex, delContactIndex);
deleteUser(hashTable, currentBuffer->stringName, HASH_SIZE);
}
}
syntax highlighted by Code2HTML, v. 0.9.1