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