kex: use unsigned long for rpc stats.fetchn values
[sip-router] / src / modules / lrkproxy / lrkproxy_hash.c
1 /*
2  * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com
3  * Copyright (C) 2014-2015 Sipwise GmbH, http://www.sipwise.com
4  * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom
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
9  * it 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,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU 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  USA
21  *
22  */
23
24
25 #include "lrkproxy.h"
26 #include "lrkproxy_hash.h"
27
28 #include "../../core/str.h"
29 #include "../../core/dprint.h"
30 #include "../../core/mem/shm_mem.h"
31 #include "../../core/timer.h"
32
33 static void lrkproxy_hash_table_free_row_lock(gen_lock_t *row_lock);
34
35 static struct lrkproxy_hash_table *lrkproxy_hash_table;
36
37 /* get from sipwise rtpengine */
38 static int str_cmp_str(const str a, const str b) {
39     if (a.len < b.len)
40         return -1;
41     if (a.len > b.len)
42         return 1;
43     if (a.len == 0 && b.len == 0)
44         return 0;
45     return memcmp(a.s, b.s, a.len);
46 }
47
48 /* get from sipwise rtpengine */
49 static int str_equal(str a, str b) {
50     return (str_cmp_str(a, b) == 0);
51 }
52
53 /* get from sipwise rtpengine */
54 static unsigned int str_hash(str s) {
55     unsigned int ret = 5381;
56     str it = s;
57
58     while (it.len > 0) {
59         ret = (ret << 5) + ret + *it.s;
60         it.s++;
61         it.len--;
62     }
63
64     return ret % lrkproxy_hash_table->size;
65 }
66
67 /* lrkproxy hash API */
68 int lrkproxy_hash_table_init(int size) {
69     int i;
70     int hash_table_size;
71
72
73     hash_table_size = size;
74
75
76 //            LM_DBG(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>lrkproxy_hash_table size = %d\n", hash_table_size);
77             LM_INFO(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>lrkproxy_hash_table size = %d\n", hash_table_size);
78
79     // init hashtable
80     lrkproxy_hash_table = shm_malloc(sizeof(struct lrkproxy_hash_table));
81     if (!lrkproxy_hash_table) {
82                 LM_ERR("no shm left to create lrkproxy_hash_table\n");
83         return 0;
84     }
85     memset(lrkproxy_hash_table, 0, sizeof(struct lrkproxy_hash_table));
86     lrkproxy_hash_table->size = hash_table_size;
87
88     // init hashtable row_locks
89     lrkproxy_hash_table->row_locks = shm_malloc(hash_table_size * sizeof(gen_lock_t*));
90     if (!lrkproxy_hash_table->row_locks) {
91                 LM_ERR("no shm left to create lrkproxy_hash_table->row_locks\n");
92         lrkproxy_hash_table_destroy();
93         return 0;
94     }
95     memset(lrkproxy_hash_table->row_locks, 0, hash_table_size * sizeof(gen_lock_t*));
96
97     // init hashtable row_entry_list
98     lrkproxy_hash_table->row_entry_list = shm_malloc(lrkproxy_hash_table->size * sizeof(struct lrkproxy_hash_entry*));
99     if (!lrkproxy_hash_table->row_entry_list) {
100                 LM_ERR("no shm left to create lrkproxy_hash_table->row_entry_list\n");
101         lrkproxy_hash_table_destroy();
102         return 0;
103     }
104     memset(lrkproxy_hash_table->row_entry_list, 0, lrkproxy_hash_table->size * sizeof(struct lrkproxy_hash_entry*));
105
106     // init hashtable row_totals
107     lrkproxy_hash_table->row_totals = shm_malloc(hash_table_size * sizeof(unsigned int));
108     if (!lrkproxy_hash_table->row_totals) {
109                 LM_ERR("no shm left to create lrkproxy_hash_table->row_totals\n");
110         lrkproxy_hash_table_destroy();
111         return 0;
112     }
113     memset(lrkproxy_hash_table->row_totals, 0, hash_table_size * sizeof(unsigned int));
114
115     // init hashtable  row_locks[i], row_entry_list[i] and row_totals[i]
116     for (i = 0; i < hash_table_size; i++) {
117         // alloc hashtable row_locks[i]
118         lrkproxy_hash_table->row_locks[i] = lock_alloc();
119         if (!lrkproxy_hash_table->row_locks[i]) {
120                     LM_ERR("no shm left to create lrkproxy_hash_table->row_locks[%d]\n", i);
121             lrkproxy_hash_table_destroy();
122             return 0;
123         }
124
125         // init hashtable row_locks[i]
126         if (!lock_init(lrkproxy_hash_table->row_locks[i])) {
127                     LM_ERR("fail to init lrkproxy_hash_table->row_locks[%d]\n", i);
128             lrkproxy_hash_table_destroy();
129             return 0;
130         }
131
132         // init hashtable row_entry_list[i]
133         lrkproxy_hash_table->row_entry_list[i] = shm_malloc(sizeof(struct lrkproxy_hash_entry));
134         if (!lrkproxy_hash_table->row_entry_list[i]) {
135                     LM_ERR("no shm left to create lrkproxy_hash_table->row_entry_list[%d]\n", i);
136             lrkproxy_hash_table_destroy();
137             return 0;
138         }
139         memset(lrkproxy_hash_table->row_entry_list[i], 0, sizeof(struct lrkproxy_hash_entry));
140
141         lrkproxy_hash_table->row_entry_list[i]->tout = -1;
142         lrkproxy_hash_table->row_entry_list[i]->next = NULL;
143
144         // init hashtable row_totals[i]
145         lrkproxy_hash_table->row_totals[i] = 0;
146     }
147
148     return 1;
149 }
150
151 int lrkproxy_hash_table_destroy() {
152     int i;
153
154     // check lrkproxy hashtable
155     if (!lrkproxy_hash_table) {
156                 LM_ERR("NULL lrkproxy_hash_table\n");
157         return 1;
158     }
159
160     // check lrkproxy hashtable->row_locks
161     if (!lrkproxy_hash_table->row_locks) {
162                 LM_ERR("NULL lrkproxy_hash_table->row_locks\n");
163         shm_free(lrkproxy_hash_table);
164         lrkproxy_hash_table = NULL;
165         return 1;
166     }
167
168     // destroy hashtable content
169     for (i = 0; i < lrkproxy_hash_table->size; i++) {
170         // lock
171         if (!lrkproxy_hash_table->row_locks[i]) {
172                     LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", i);
173             continue;
174         } else {
175             lock_get(lrkproxy_hash_table->row_locks[i]);
176         }
177
178         // check lrkproxy hashtable->row_entry_list
179         if (!lrkproxy_hash_table->row_entry_list) {
180                     LM_ERR("NULL lrkproxy_hash_table->row_entry_list\n");
181         } else {
182             // destroy hashtable row_entry_list[i]
183             lrkproxy_hash_table_free_row_entry_list(lrkproxy_hash_table->row_entry_list[i]);
184             lrkproxy_hash_table->row_entry_list[i] = NULL;
185         }
186
187         // unlock
188         lock_release(lrkproxy_hash_table->row_locks[i]);
189
190         // destroy hashtable row_locks[i]
191         lrkproxy_hash_table_free_row_lock(lrkproxy_hash_table->row_locks[i]);
192         lrkproxy_hash_table->row_locks[i] = NULL;
193     }
194
195     // destroy hashtable row_entry_list
196     if (!lrkproxy_hash_table->row_entry_list) {
197                 LM_ERR("NULL lrkproxy_hash_table->row_entry_list\n");
198     } else {
199         shm_free(lrkproxy_hash_table->row_entry_list);
200         lrkproxy_hash_table->row_entry_list = NULL;
201     }
202
203     // destroy hashtable row_totals
204     if (!lrkproxy_hash_table->row_totals) {
205                 LM_ERR("NULL lrkproxy_hash_table->row_totals\n");
206     } else {
207         shm_free(lrkproxy_hash_table->row_totals);
208         lrkproxy_hash_table->row_totals = NULL;
209     }
210
211     // destroy hashtable row_locks
212     if (!lrkproxy_hash_table->row_locks) {
213         // should not be the case; just for code symmetry
214                 LM_ERR("NULL lrkproxy_hash_table->row_locks\n");
215     } else {
216         shm_free(lrkproxy_hash_table->row_locks);
217         lrkproxy_hash_table->row_locks = NULL;
218     }
219
220     // destroy hashtable
221     if (!lrkproxy_hash_table) {
222         // should not be the case; just for code symmetry
223                 LM_ERR("NULL lrkproxy_hash_table\n");
224     } else {
225         shm_free(lrkproxy_hash_table);
226         lrkproxy_hash_table = NULL;
227     }
228
229     return 1;
230 }
231
232
233 void lrkproxy_hash_table_free_entry(struct lrkproxy_hash_entry *entry) {
234     if (!entry) {
235                 LM_ERR("try to free a NULL entry\n");
236         return ;
237     }
238
239     // free callid
240     if (entry->callid.s) {
241         shm_free(entry->callid.s);
242     }
243
244     // free viabranch
245     if (entry->viabranch.s) {
246         shm_free(entry->viabranch.s);
247     }
248
249     // free entry
250     shm_free(entry);
251
252     return ;
253 }
254
255 void lrkproxy_hash_table_free_row_entry_list(struct lrkproxy_hash_entry *row_entry_list) {
256     struct lrkproxy_hash_entry *entry, *last_entry;
257
258     if (!row_entry_list) {
259                 LM_ERR("try to free a NULL row_entry_list\n");
260         return ;
261     }
262
263     entry = row_entry_list;
264     while (entry) {
265         last_entry = entry;
266         entry = entry->next;
267         lrkproxy_hash_table_free_entry(last_entry);
268         last_entry = NULL;
269     }
270
271     return ;
272 }
273
274 int lrkproxy_hash_table_insert(str callid, str viabranch, struct lrkproxy_hash_entry *value) {
275     struct lrkproxy_hash_entry *entry, *last_entry;
276     struct lrkproxy_hash_entry *new_entry = (struct lrkproxy_hash_entry *) value;
277     unsigned int hash_index;
278
279     // sanity checks
280     if (!lrkproxy_hash_table_sanity_checks()) {
281                 LM_ERR("sanity checks failed\n");
282         return 0;
283     }
284
285     // get entry list
286     hash_index = str_hash(callid);
287     entry = lrkproxy_hash_table->row_entry_list[hash_index];
288     last_entry = entry;
289
290     // lock
291     if (lrkproxy_hash_table->row_locks[hash_index]) {
292         lock_get(lrkproxy_hash_table->row_locks[hash_index]);
293     } else {
294                 LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", hash_index);
295         return 0;
296     }
297
298     while (entry) {
299         // if found, don't add new entry
300         if (str_equal(entry->callid, new_entry->callid) &&
301             str_equal(entry->viabranch, new_entry->viabranch)) {
302             // unlock
303             lock_release(lrkproxy_hash_table->row_locks[hash_index]);
304                     LM_NOTICE("callid=%.*s, viabranch=%.*s already in hashtable, ignore new value\n",
305                               entry->callid.len, entry->callid.s,
306                               entry->viabranch.len, entry->viabranch.s);
307             return 0;
308         }
309
310         // if expired entry discovered, delete it
311         if (entry->tout < get_ticks()) {
312             // set pointers; exclude entry
313             last_entry->next = entry->next;
314
315             // free current entry; entry points to unknown
316             lrkproxy_hash_table_free_entry(entry);
317
318             // set pointers
319             entry = last_entry;
320
321             // update total
322             lrkproxy_hash_table->row_totals[hash_index]--;
323         }
324
325         // next entry in the list
326         last_entry = entry;
327         entry = entry->next;
328     }
329
330     last_entry->next = new_entry;
331
332     // update total
333     lrkproxy_hash_table->row_totals[hash_index]++;
334
335     // unlock
336     lock_release(lrkproxy_hash_table->row_locks[hash_index]);
337
338     return 1;
339 }
340
341 int lrkproxy_hash_table_remove(str callid, str viabranch, enum lrk_operation op) {
342     struct lrkproxy_hash_entry *entry, *last_entry;
343     unsigned int hash_index;
344     int found = 0;
345
346     // sanity checks
347     if (!lrkproxy_hash_table_sanity_checks()) {
348                 LM_ERR("sanity checks failed\n");
349         return 0;
350     }
351
352     // get first entry from entry list; jump over unused list head
353     hash_index = str_hash(callid);
354     entry = lrkproxy_hash_table->row_entry_list[hash_index];
355     last_entry = entry;
356
357     // lock
358     if (lrkproxy_hash_table->row_locks[hash_index]) {
359         lock_get(lrkproxy_hash_table->row_locks[hash_index]);
360     } else {
361                 LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", hash_index);
362         return 0;
363     }
364
365     while (entry) {
366         // if callid found, delete entry
367         if ((str_equal(entry->callid, callid) && str_equal(entry->viabranch, viabranch)) ||
368             (str_equal(entry->callid, callid) && viabranch.len == 0 && op == OP_DELETE) ||
369             str_equal(entry->callid, callid)){
370             //if ((str_equal(entry->callid, callid) && str_equal(entry->viabranch, viabranch)) ||
371             //    (str_equal(entry->callid, callid) && viabranch.len == 0 && op == OP_DELETE)) {
372             // set pointers; exclude entry
373
374             // set pointers; exclude entry
375             last_entry->next = entry->next;
376
377             // free current entry; entry points to unknown
378             lrkproxy_hash_table_free_entry(entry);
379
380             // set pointers
381             entry = last_entry;
382
383             // update total
384             lrkproxy_hash_table->row_totals[hash_index]--;
385
386             found = 1;
387
388             if (!(viabranch.len == 0 && op == OP_DELETE)) {
389                 // unlock
390                 lock_release(lrkproxy_hash_table->row_locks[hash_index]);
391                 return found;
392             }
393
394             // try to also delete other viabranch entries for callid
395             last_entry = entry;
396             entry = entry->next;
397             continue;
398         }
399
400         // if expired entry discovered, delete it
401         if (entry->tout < get_ticks()) {
402             // set pointers; exclude entry
403             last_entry->next = entry->next;
404
405             // free current entry; entry points to unknown
406             lrkproxy_hash_table_free_entry(entry);
407
408             // set pointers
409             entry = last_entry;
410
411             // update total
412             lrkproxy_hash_table->row_totals[hash_index]--;
413         }
414
415         last_entry = entry;
416         entry = entry->next;
417     }
418
419     // unlock
420     lock_release(lrkproxy_hash_table->row_locks[hash_index]);
421
422     return found;
423 }
424 //struct lrkp_node *lrkproxy_hash_table_lookup(str callid, str viabranch, enum lrk_operation op) {
425 //struct lrkproxy_hash_entry *lrkproxy_hash_table_lookup(str callid, str viabranch, enum lrk_operation op) {
426 struct lrkproxy_hash_entry *lrkproxy_hash_table_lookup(str callid, str viabranch) {
427     struct lrkproxy_hash_entry *entry, *last_entry;
428     unsigned int hash_index;
429 //    struct lrkp_node *node;
430
431     // sanity checks
432     if (!lrkproxy_hash_table_sanity_checks()) {
433                 LM_ERR("sanity checks failed\n");
434         return 0;
435     }
436
437     // get first entry from entry list; jump over unused list head
438     hash_index = str_hash(callid);
439     entry = lrkproxy_hash_table->row_entry_list[hash_index];
440     last_entry = entry;
441
442     // lock
443     if (lrkproxy_hash_table->row_locks[hash_index]) {
444         lock_get(lrkproxy_hash_table->row_locks[hash_index]);
445     } else {
446                 LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", hash_index);
447         return 0;
448     }
449
450     while (entry) {
451         // if callid found, return entry
452 //        if ((str_equal(entry->callid, callid) && str_equal(entry->viabranch, viabranch)) ||
453 //            (str_equal(entry->callid, callid) && viabranch.len == 0 && op == OP_DELETE)) {
454         if (str_equal(entry->callid, callid) && str_equal(entry->viabranch, viabranch)) {
455 //            node = entry->node;
456             // unlock
457             lock_release(lrkproxy_hash_table->row_locks[hash_index]);
458             return entry;
459 //            return node;
460         }
461
462         // if expired entry discovered, delete it
463         if (entry->tout < get_ticks()) {
464             // set pointers; exclude entry
465             last_entry->next = entry->next;
466
467             // free current entry; entry points to unknown
468             lrkproxy_hash_table_free_entry(entry);
469
470             // set pointers
471             entry = last_entry;
472
473             // update total
474             lrkproxy_hash_table->row_totals[hash_index]--;
475         }
476
477         last_entry = entry;
478         entry = entry->next;
479     }
480
481     // unlock
482     lock_release(lrkproxy_hash_table->row_locks[hash_index]);
483
484     return NULL;
485 }
486
487
488 static void lrkproxy_hash_table_free_row_lock(gen_lock_t *row_lock) {
489     if (!row_lock) {
490                 LM_ERR("try to free a NULL lock\n");
491         return ;
492     }
493
494     lock_destroy(row_lock);
495     lock_dealloc(row_lock);
496
497     return ;
498 }
499
500 int lrkproxy_hash_table_sanity_checks() {
501     // check lrkproxy hashtable
502     if (!lrkproxy_hash_table) {
503                 LM_ERR("NULL lrkproxy_hash_table\n");
504         return 0;
505     }
506
507     // check lrkproxy hashtable->row_locks
508     if (!lrkproxy_hash_table->row_locks) {
509                 LM_ERR("NULL lrkproxy_hash_table->row_locks\n");
510         return 0;
511     }
512
513     // check lrkproxy hashtable->row_entry_list
514     if (!lrkproxy_hash_table->row_entry_list) {
515                 LM_ERR("NULL lrkproxy_hash_table->row_entry_list\n");
516         return 0;
517     }
518
519     // check lrkproxy hashtable->row_totals
520     if (!lrkproxy_hash_table->row_totals) {
521                 LM_ERR("NULL lrkproxy_hash_table->row_totals\n");
522         return 0;
523     }
524
525     return 1;
526 }
527