1 /*
2 * SNMPStats Module
3 * Copyright (C) 2006 SOMA Networks, INC.
4 * Written by: Jeffrey Magder (jmagder@somanetworks.com)
5 *
6 * This file is part of Kamailio, a free SIP server.
7 *
8 * Kamailio is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version
12 *
13 * Kamailio is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21 * USA
22 *
23 */
24
25 /*!
26 * \file
27 * \brief SNMP statistic module, hash table
28 * Hash Stuff;
29 * \author jmagder
30 *
31 * For an overview of its structures, please see hashTable.h
32 *
33 * Potential Performance Improvements: Pass the length of the aor strings around
34 * everywhere, so we don't have to calculate it ourselves.
35 * \ingroup snmpstats
36 * - Module: \ref snmpstats
37 */
38
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include "hashTable.h"
43 #include "../../core/dprint.h"
44 #include "../../core/mem/mem.h"
45
46 #include <net-snmp/net-snmp-config.h>
47 #include <net-snmp/net-snmp-includes.h>
48 #include <net-snmp/agent/net-snmp-agent-includes.h>
49 #include "snmpSIPRegUserTable.h"
50
51
52 /*! Calculates and returns a hash index to a hash table. The index is calculated
53 * by summing up all the characters specified with theString, and using the
54 * hashTableSize as the modulus. */
calculateHashSlot(char * theString,int hashTableSize)55 int calculateHashSlot(char *theString, int hashTableSize)
56 {
57 char *currentCharacter = theString;
58 int runningTotal = 0;
59
60 while(*currentCharacter != '\0') {
61 runningTotal += *currentCharacter;
62 currentCharacter++;
63 }
64
65 return runningTotal % hashTableSize;
66 }
67
68 /*! Searches the hash table specified as theTable, of size 'size', for a record
69 * indexed with 'aor'. If a match is found, then an aorToIndextStruct_t
70 * structure is returned.
71 *
72 * This function is called to discover the map between Kamailio's "aor"
73 * (Address of Records) indexing scheme, and the SNMPStats modules integer
74 * indexing scheme for its contact/user data.
75 *
76 * Returns: the aorToIndexStruct_t mapping structure if a match was found,
77 * or NULL otherwise.
78 */
findHashRecord(hashSlot_t * theTable,char * aor,int size)79 aorToIndexStruct_t *findHashRecord(hashSlot_t *theTable, char *aor, int size)
80 {
81 int hashIndex = calculateHashSlot(aor, size);
82 int aorStringLength = strlen(aor);
83
84 aorToIndexStruct_t *currentRecord = theTable[hashIndex].first;
85
86 while(currentRecord != NULL) {
87
88 /* If the strings are the same length and the same in every
89 * other way, then return the given record. */
90 if(currentRecord->aorLength == aorStringLength
91 && memcmp(currentRecord->aor, aor, aorStringLength) == 0) {
92 return currentRecord;
93 }
94
95 currentRecord = currentRecord->next;
96 }
97
98 return NULL;
99 }
100
101
102 /*! Returns a chunk of memory large enough to store 'size' hashSlot's. The
103 * table will contain mappings between Kamailio's "aor" user/contact indexing
104 * scheme, and SNMPStats integer indexing scheme */
createHashTable(int size)105 hashSlot_t *createHashTable(int size)
106 {
107 hashSlot_t *hashTable = NULL;
108 int numberOfBytes = sizeof(hashSlot_t) * size;
109
110 hashTable = pkg_malloc(numberOfBytes);
111
112 if(!hashTable) {
113 LM_ERR("no more pkg memory");
114 return NULL;
115 }
116
117 memset(hashTable, 0, numberOfBytes);
118
119 return hashTable;
120 }
121
122
123 /*! Inserts the record specified with 'theRecord' into our hash table. */
insertHashRecord(hashSlot_t * theTable,aorToIndexStruct_t * theRecord,int size)124 void insertHashRecord(
125 hashSlot_t *theTable, aorToIndexStruct_t *theRecord, int size)
126 {
127 int hashIndex = calculateHashSlot(theRecord->aor, size);
128
129 /* Link up this record backward so that it points to whatever the last
130 * 'last element' was. */
131 theRecord->prev = theTable[hashIndex].last;
132
133 /* This is the first record in the hash table, so assign the first and
134 * last pointers to this record. */
135 if(theTable[hashIndex].last == NULL) {
136
137 theTable[hashIndex].last = theRecord;
138 theTable[hashIndex].first = theRecord;
139
140 } else {
141
142 /* Make the element that was previously the last element point
143 * to this new record, as its next element. */
144 theTable[hashIndex].last->next = theRecord;
145
146 /* Reassign the 'final element' pointer to this new record. */
147 theTable[hashIndex].last = theRecord;
148 }
149 }
150
151 /*!
152 * This function will search the provided hash table for an entry indexed by
153 * 'aor'. If an entry is found then:
154 *
155 * - Its numContacts counter will be decremented.
156 * - If its numContacts counter reaches zero, then the entry will be removed
157 * from the hash table.
158 *
159 */
deleteUser(hashSlot_t * theTable,char * aor,int hashTableSize)160 void deleteUser(hashSlot_t *theTable, char *aor, int hashTableSize)
161 {
162 int hashIndex = calculateHashSlot(aor, hashTableSize);
163 int searchStringLength = strlen(aor);
164
165 aorToIndexStruct_t *currentRecord = theTable[hashIndex].first;
166
167 while(currentRecord != NULL) {
168
169 /* First make sure both strings are the same length. If so,
170 * then compare all bytes. If this succeeds, then we need to
171 * link up the previous and next element together. */
172 if(currentRecord->aorLength == searchStringLength
173 && memcmp(currentRecord->aor, aor, searchStringLength) == 0) {
174
175 currentRecord->numContacts--;
176
177 /* There are still contacts relying on this user, so
178 * don't delete anything. */
179 if(currentRecord->numContacts > 0) {
180 return;
181 }
182
183 /* There are no more contacts relying on this user, so
184 * delete the row from the table. */
185 deleteRegUserRow(currentRecord->userIndex);
186
187
188 /* Maintenance of the hash table */
189
190 if(currentRecord->prev == NULL) {
191 /* Edge Case: First element in list was just deleted, so set
192 * up the first element to point to the one after the one
193 * just deleted */
194 theTable[hashIndex].first = currentRecord->next;
195 } else {
196 /* Not the first element, so hook up the previous node to
197 * the node after the one just deleted. */
198 currentRecord->prev->next = currentRecord->next;
199 }
200
201 if(currentRecord->next == NULL) {
202 /* Edge Case: The last element has been targetted for
203 * deletion. So move the pointer to the node just before
204 * this one. */
205 theTable[hashIndex].last = currentRecord->prev;
206 } else {
207 /* Not the last element, so hook up next nodes previous
208 * element to this nodes previous. */
209 currentRecord->next->prev = currentRecord->prev;
210 }
211
212 pkg_free(currentRecord);
213
214 /* We are done, so just return. */
215 return;
216 }
217
218 /* Advance to the next records. */
219 currentRecord = currentRecord->next;
220 }
221 }
222
223
224 /*! Returns a aorToIndexStruct_t, holding the given 'userIndex' and 'aor'. The
225 * structure is used to map between the "aor" (Kamailio's way of indexing
226 * users/contacts), and the SNMPStats user and contact integer indexes.
227 *
228 * NOTE: that this record does not make a copy of aor, but instead points
229 * directly to the parameter. Therefore make sure that aor is not on the stack,
230 * and is not going to disappear before this record is deleted.
231 */
createHashRecord(int userIndex,char * aor)232 aorToIndexStruct_t *createHashRecord(int userIndex, char *aor)
233 {
234 int aorLength = strlen(aor);
235
236 aorToIndexStruct_t *theRecord = pkg_malloc(
237 sizeof(aorToIndexStruct_t) + (aorLength + 1) * sizeof(char));
238 if(theRecord == NULL) {
239 LM_ERR("failed to create a mapping record for %s", aor);
240 return NULL;
241 }
242
243 memset(theRecord, 0, sizeof(aorToIndexStruct_t));
244
245 theRecord->aor = (char *)theRecord + sizeof(aorToIndexStruct_t);
246 memcpy(theRecord->aor, aor, aorLength);
247 theRecord->aor[aorLength] = '\0';
248 theRecord->aorLength = aorLength;
249 theRecord->userIndex = userIndex;
250 theRecord->numContacts = 1;
251
252 return theRecord;
253 }
254
255
256 /*! Debugging function. Prints off an entire hash slot. */
printHashSlot(hashSlot_t * theTable,int index)257 void printHashSlot(hashSlot_t *theTable, int index)
258 {
259 aorToIndexStruct_t *currentRecord = theTable[index].first;
260
261 LM_ERR("dumping Hash Slot #%d\n", index);
262
263 while(currentRecord != NULL) {
264 LM_ERR("\tString: %s - Index: %d\n", currentRecord->aor,
265 currentRecord->userIndex);
266 currentRecord = currentRecord->next;
267 }
268 }