1 /*
2 * Copyright (C) 2014 Federico Cabiddu (federico.cabiddu@gmail.com)
3 *
4 * This file is part of Kamailio, a free SIP server.
5 *
6 * Kamailio is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version
10 *
11 * Kamailio is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 */
21
22
23 /*!
24 * \file
25 * \brief Functions and definitions related to per user transaction indexing and searching
26 * \ingroup tsilo
27 * Module: \ref tsilo
28 */
29
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "../../core/dprint.h"
34 #include "../../core/ut.h"
35 #include "../../core/hashes.h"
36 #include "../../core/rand/kam_rand.h"
37 #include "ts_hash.h"
38 #include "ts_handlers.h"
39
40 /*! global transaction table */
41 struct ts_table *t_table = 0;
42
43 /*!
44 * \brief Destroy a urecord and free memory
45 * \param urecord destroyed urecord
46 */
free_ts_urecord(struct ts_urecord * urecord)47 void free_ts_urecord(struct ts_urecord *urecord)
48 {
49 LM_DBG("destroying urecord %p\n", urecord);
50 ts_transaction_t* ptr;
51
52 while(urecord->transactions) {
53 ptr = urecord->transactions;
54 urecord->transactions = urecord->transactions->next;
55 free_ts_transaction(ptr);
56 }
57
58 if (urecord->ruri.s) shm_free(urecord->ruri.s);
59
60 shm_free(urecord);
61
62 urecord = 0;
63 }
64
65 /*!
66 * \brief Initialize the per user transactions table
67 * \param size size of the table
68 * \return 0 on success, -1 on failure
69 */
init_ts_table(unsigned int size)70 int init_ts_table(unsigned int size)
71 {
72 unsigned int n;
73 unsigned int i;
74
75 t_table = (struct ts_table*)shm_malloc( sizeof(struct ts_table));
76 if (t_table==0) {
77 SHM_MEM_ERROR;
78 return -1;
79 }
80
81 memset( t_table, 0, sizeof(struct ts_table) );
82
83 t_table->size = size;
84
85 n = (size<MAX_TS_LOCKS)?size:MAX_TS_LOCKS;
86 for( ; n>=MIN_TS_LOCKS ; n-- ) {
87 t_table->locks = lock_set_alloc(n);
88 if (t_table->locks==0)
89 continue;
90 if (lock_set_init(t_table->locks)==0) {
91 lock_set_dealloc(t_table->locks);
92 t_table->locks = 0;
93 continue;
94 }
95 t_table->locks_no = n;
96 break;
97 }
98
99 if (t_table->locks==0) {
100 LM_ERR("unable to allocted at least %d locks for the hash table\n",
101 MIN_TS_LOCKS);
102 goto error;
103 }
104
105 t_table->entries = (ts_entry_t*)shm_malloc(sizeof(ts_entry_t) * size);
106 if (!t_table->entries) {
107 SHM_MEM_ERROR;
108 goto error;
109 }
110
111 for( i=0 ; i<size; i++ ) {
112 memset( &(t_table->entries[i]), 0, sizeof(struct ts_entry) );
113 t_table->entries[i].next_id = kam_rand() % (3*size);
114 t_table->entries[i].lock_idx = i % t_table->locks_no;
115 }
116
117 return 0;
118 error:
119 shm_free( t_table );
120 t_table = NULL;
121 return -1;
122 }
123
124 /*!
125 * \brief Destroy the per user transaction table
126 */
destroy_ts_table(void)127 void destroy_ts_table(void)
128 {
129 struct ts_urecord *ts_u, *l_ts_u;
130 unsigned int i;
131
132 if (t_table==0)
133 return;
134
135 if (t_table->locks) {
136 lock_set_destroy(t_table->locks);
137 lock_set_dealloc(t_table->locks);
138 }
139
140 for( i=0 ; i<t_table->size; i++ ) {
141 ts_u = t_table->entries[i].first;
142 while (ts_u) {
143 l_ts_u = ts_u;
144 ts_u = ts_u->next;
145 free_ts_urecord(l_ts_u);
146 }
147 }
148
149 shm_free(t_table);
150 t_table = 0;
151
152 return;
153 }
154
lock_entry(ts_entry_t * entry)155 void lock_entry(ts_entry_t *entry) {
156 ts_lock(t_table, entry);
157 }
158
unlock_entry(ts_entry_t * entry)159 void unlock_entry(ts_entry_t *entry) {
160 ts_unlock(t_table, entry);
161 }
162
lock_entry_by_ruri(str * ruri)163 void lock_entry_by_ruri(str* ruri)
164 {
165 unsigned int sl;
166
167 sl = core_hash(ruri, 0, 0) & (t_table->size-1);
168 ts_lock(t_table, &t_table->entries[sl]);
169 }
170
unlock_entry_by_ruri(str * ruri)171 void unlock_entry_by_ruri(str* ruri)
172 {
173 unsigned int sl;
174
175 sl = core_hash(ruri, 0, 0) & (t_table->size-1);
176 ts_unlock(t_table, &t_table->entries[sl]);
177 }
178
179 /*
180 * Obtain a urecord pointer if the urecord exists in the table
181 */
get_ts_urecord(str * ruri,struct ts_urecord ** _r)182 int get_ts_urecord(str* ruri, struct ts_urecord** _r)
183 {
184 int sl, i, rurihash;
185 ts_urecord_t* r;
186
187 rurihash = core_hash(ruri, 0, 0);
188 sl = rurihash&(t_table->size-1);
189 r = t_table->entries[sl].first;
190
191 for(i = 0; r!=NULL && i < t_table->entries[sl].n; i++) {
192 if((r->rurihash==rurihash) && (r->ruri.len==ruri->len)
193 && !memcmp(r->ruri.s,ruri->s,ruri->len)){
194 *_r = r;
195 return 0;
196 }
197 r = r->next;
198 }
199
200 return 1; /* Nothing found */
201 }
202
203 /*!
204 * \brief Create and initialize new record structure
205 * \param ruri request uri
206 * \param _r pointer to the new record
207 * \return 0 on success, negative on failure
208 */
new_ts_urecord(str * ruri,ts_urecord_t ** _r)209 int new_ts_urecord(str* ruri, ts_urecord_t** _r)
210 {
211 *_r = (ts_urecord_t*)shm_malloc(sizeof(ts_urecord_t));
212 if (*_r == 0) {
213 SHM_MEM_ERROR;
214 return -1;
215 }
216 memset(*_r, 0, sizeof(ts_urecord_t));
217
218 (*_r)->ruri.s = (char*)shm_malloc(ruri->len);
219 if ((*_r)->ruri.s == 0) {
220 SHM_MEM_ERROR;
221 shm_free(*_r);
222 *_r = 0;
223 return -2;
224 }
225 memcpy((*_r)->ruri.s, ruri->s, ruri->len);
226 (*_r)->ruri.len = ruri->len;
227 (*_r)->rurihash = core_hash(ruri, 0, 0);
228 return 0;
229 }
230
231 /*!
232 * \brief Insert a new record into transactions table
233 * \param ruri request uri
234 * \param _r pointer to the new record
235 * \return 0 on success, -1 on failure
236 */
insert_ts_urecord(str * ruri,ts_urecord_t ** _r)237 int insert_ts_urecord(str* ruri, ts_urecord_t** _r)
238 {
239 ts_entry_t* entry;
240
241 int sl;
242
243 if (new_ts_urecord(ruri, _r) < 0) {
244 LM_ERR("creating urecord failed\n");
245 return -1;
246 }
247
248 sl = ((*_r)->rurihash)&(t_table->size-1);
249 entry = &t_table->entries[sl];
250
251 if (entry->n == 0) {
252 entry->first = entry->last = *_r;
253 } else {
254 (*_r)->prev = entry->last;
255 entry->last->next = *_r;
256 entry->last = *_r;
257 }
258 entry->n++;
259 (*_r)->entry = entry;
260
261 update_stat(stored_ruris, 1);
262 update_stat(total_ruris, 1);
263
264 LM_DBG("urecord entry %p",entry);
265 return 0;
266 }
267
268 /*!
269 * \brief remove a urecord from table and free the memory
270 * \param _r urecord
271 * \return 0 on success, -1 on failure
272 */
remove_ts_urecord(ts_urecord_t * _r)273 void remove_ts_urecord(ts_urecord_t* _r)
274 {
275 ts_entry_t* entry;
276
277 entry = _r->entry;
278
279 if (_r->prev)
280 _r->prev->next = _r->next;
281 if (_r->next)
282 _r->next->prev = _r->prev;
283
284 if (entry->first == _r)
285 entry->first = _r->next;
286 if (entry->last == _r)
287 entry->last = _r->prev;
288
289 update_stat(stored_ruris, -1);
290
291 entry->n--;
292 free_ts_urecord(_r);
293
294 return;
295 }
296
297 /*!
298 * \brief Insert a new transaction structure into urecord
299 * \param t transaction
300 * \param msg SIP message
301 * \param _r urecord
302 * \return 0 on success, -1 otherwise
303 */
insert_ts_transaction(struct cell * t,struct sip_msg * msg,struct ts_urecord * _r)304 int insert_ts_transaction(struct cell* t, struct sip_msg* msg, struct ts_urecord* _r)
305 {
306 ts_transaction_t *ptr, *prev;
307 ts_transaction_t* ts;
308
309 unsigned int tindex;
310 unsigned int tlabel;
311
312 tindex = t->hash_index;
313 tlabel = t->label;
314
315 ptr = prev = 0;
316 ptr = _r->transactions;
317
318 while(ptr) {
319 if ((ptr->tindex == tindex) && (ptr->tlabel == tlabel)) {
320 LM_DBG("transaction already inserted\n");
321 return -1;
322 }
323 prev = ptr;
324 ptr = ptr->next;
325 }
326
327 if ( (ts=new_ts_transaction(tindex, tlabel) ) == 0) {
328 LM_ERR("failed to create new transaction\n");
329 return -1;
330 }
331
332 ts->urecord = _r;
333 /* add the new transaction at the end of the list */
334
335 if (prev) {
336 prev->next = ts;
337 ts->prev = prev;
338 } else {
339 _r->transactions = ts;
340 }
341
342 if (ts_set_tm_callbacks(t, msg, ts) < 0) {
343 LM_ERR("failed to set transaction %d:%d callbacks\n", tindex, tlabel);
344 }
345
346 update_stat(stored_transactions, 1);
347 update_stat(total_transactions, 1);
348
349 return 0;
350 }
351 /*!
352 * \brief Create a new transaction structure
353 * \param tindex transaction index in tm table
354 * \param tlabel transaction label in tm table
355 * \return created transaction structure on success, NULL otherwise
356 */
new_ts_transaction(int tindex,int tlabel)357 ts_transaction_t* new_ts_transaction(int tindex, int tlabel)
358 {
359 ts_transaction_t *ts;
360 int len;
361
362 len = sizeof(ts_transaction_t);
363 ts = (ts_transaction_t*)shm_malloc(len);
364 if (ts==0) {
365 SHM_MEM_ERROR_FMT("len %d\n", len);
366 return 0;
367 }
368
369 memset(ts, 0, len);
370 ts->tindex = tindex;
371 ts->tlabel = tlabel;
372 return ts;
373 }
374
375 /*!
376 * \brief Clone a transaction structure
377 * \param ts transaction to be cloned
378 * \return cloned transaction structure on success, NULL otherwise
379 */
clone_ts_transaction(ts_transaction_t * ts)380 ts_transaction_t* clone_ts_transaction(ts_transaction_t* ts)
381 {
382 ts_transaction_t *ts_clone;
383 int len;
384
385 if (ts == NULL)
386 return NULL;
387
388 len = sizeof(ts_transaction_t);
389 ts_clone = (ts_transaction_t*)shm_malloc(len);
390 if (ts_clone==NULL) {
391 SHM_MEM_ERROR_FMT("len %d\n", len);
392 return NULL;
393 }
394
395 memcpy(ts_clone, ts, len);
396 return ts_clone;
397 }
398
399 /*!
400 * \brief remove a transaction from the urecord transactions list
401 * \param ts_t unlinked transaction
402 */
remove_ts_transaction(ts_transaction_t * ts_t)403 void remove_ts_transaction(ts_transaction_t* ts_t)
404 {
405 if (ts_t->next)
406 ts_t->next->prev = ts_t->prev;
407 if (ts_t->prev)
408 ts_t->prev->next = ts_t->next;
409
410 if (ts_t->urecord->transactions == ts_t)
411 ts_t->urecord->transactions = ts_t->next;
412
413 update_stat(stored_transactions, -1);
414
415 free_ts_transaction((void*)ts_t);
416
417 return;
418 }
419
420
421 /*!
422 * \brief Destroy a transaction and free memory
423 * \param ts_t destroyed transaction
424 */
free_ts_transaction(void * ts_t)425 void free_ts_transaction(void *ts_t)
426 {
427 shm_free((struct ts_transaction*)ts_t);
428 ts_t = 0;
429 }
430