cf014b659f42608aa273476f36398ea3771f20fa
[sip-router] / src / modules / call_obj / cobj.c
1 /**
2  * Functionality of call_obj module.
3  */
4
5 #include <stdbool.h>
6 #include <stdint.h>
7 #include <inttypes.h>
8 #include <sys/time.h>
9
10 #include "../../core/mem/shm_mem.h"
11 #include "../../core/locking.h"
12
13 #include "cobj.h"
14
15 /**
16  * Element of the array.
17  * When assigned equals false other contents are undefined.
18  */
19 typedef struct {
20         bool assigned;
21         uint64_t timestamp;
22         str callid;
23 } co_object_t;
24
25 typedef struct {
26         int start; /* Number of first object. */
27         int end; /* Number of last object (included). */
28         /**
29          * Current position of last assigned object.
30          * 0 - no object has been assigned yet.
31          */
32         int cur;
33         int assigned; /* Number of assigned objects at this moment. */
34         gen_lock_t *lock; /* Lock to protect ring array. */
35         co_object_t *ring; /* Array of call objects. */
36 } co_data_t;
37
38 /**
39  * Struct containing all call object related data.
40  */
41 static co_data_t *co_data = NULL;
42
43 /**
44  * Initialize call object module.
45  *
46  * /return 0 on success.
47  */
48 int cobj_init(int start, int end)
49 {
50         
51         if (start == 0) {
52                 LM_ERR("Wrong start value\n");
53                 return -1;
54         }
55         if (end == 0) {
56                 LM_ERR("Wrong end value\n");
57                 return -1;
58         }
59         if (start > end) {
60                 LM_ERR("End value should be greater than start one [%d, %d]\n", start, end);
61                 return -1;
62         }
63
64         co_data = (co_data_t*)shm_malloc(sizeof(co_data_t));
65         if (!co_data) {
66                 LM_ERR("Cannot allocate shm memory for call object\n");
67                 return -1;
68         }
69
70         co_data->start = start;
71         co_data->end = end;
72         co_data->cur = 0; /* No object assigned yet. */
73         co_data->assigned = 0; /* No assigned objects at this moment. */
74         co_data->lock = NULL;
75         co_data->ring = NULL;
76         
77         size_t total_size = (1 + end - start); /* [start, end] */
78         size_t array_size = total_size * sizeof(co_object_t);
79         LM_DBG("Element size: %lu\n", sizeof(co_object_t));
80         LM_DBG("List element size: %lu\n", sizeof(cobj_elem_t));
81         
82         co_data->ring = (co_object_t*)shm_malloc(array_size);
83         if (!co_data->ring) {
84                 LM_ERR("Cannot allocate shm memory for ring in call object\n");
85                 return -1;
86         }
87         LM_DBG("Allocated %lu bytes for the ring\n", array_size);
88
89         /**
90          * Initialize lock.
91          */
92         co_data->lock = lock_alloc();
93         if (!co_data->lock) {
94                 LM_ERR("Cannot allocate lock\n");
95                 return -1;
96         }
97
98         if(lock_init(co_data->lock)==NULL)
99         {
100                 LM_ERR("cannot init the lock\n");
101                 lock_dealloc(co_data->lock);
102                 co_data->lock = NULL;
103                 return -1;
104         }
105         
106         co_data->cur = 0; /* No object assigned yet. */
107
108         co_data->start = start;
109         co_data->end = end;
110         
111         /* Every object is set as free. */
112         int i;
113         for (i=0; i<total_size; i++) {
114                 co_data->ring[i].assigned = false;
115         }
116         /* timestamp, etc is undefined. */
117         
118         LM_DBG("Call object Init: cur=%d  start=%d  end=%d\n",
119                    co_data->cur, co_data->start, co_data->end);
120         return 0;
121 }
122
123 /**
124  * Close call object module.
125  */
126 void cobj_destroy(void)
127 {
128         if (!co_data) {
129                 /* Nothing to free. */
130                 return;
131         }
132         
133         /* Free lock */
134         if (co_data->lock) {
135                 LM_DBG("Freeing lock\n");
136                 lock_destroy(co_data->lock);
137                 lock_dealloc(co_data->lock);
138                 co_data->lock = NULL;
139         }
140
141         /* Free ring array. */
142         if (co_data->ring) {
143                 LM_DBG("Freeing call object ring\n");
144                 shm_free(co_data->ring);
145                 co_data->ring = NULL;
146         }
147
148         assert(co_data);
149         shm_free(co_data);
150         co_data = NULL;
151 }
152
153 /**
154  * Get current timestamp in milliseconds.
155  *
156  * /param ts pointer to timestamp integer.
157  * /return 0 on success.
158  */
159 int get_timestamp(uint64_t *ts)
160 {
161         assert(ts);
162         
163         struct timeval current_time;
164         if (gettimeofday(&current_time, NULL) < 0) {
165                 LM_ERR("failed to get current time!\n");
166                 return -1;
167         }
168
169         *ts = (uint64_t)current_time.tv_sec*1000 +
170                 (uint64_t)current_time.tv_usec/1000;
171         
172         return 0;
173 }
174
175 /**
176  * Fill an object with data.
177  *
178  * /return 0 on success.
179  */
180 static int cobj_fill(co_object_t *obj, uint64_t timestamp, str *callid)
181 {
182         assert(obj->assigned == false);
183         
184         int res = -1;
185         
186         obj->callid.s = (char*)shm_malloc(callid->len + 1); /* +1 Zero at the end */
187         if (!obj->callid.s) {
188                 LM_ERR("Cannot allocate memory for callid\n");
189                 goto clean;
190         }
191         memcpy(obj->callid.s, callid->s, callid->len);
192         obj->callid.s[callid->len] = '\0';
193         obj->callid.len = callid->len;
194
195         /* Assign timestamp */
196         obj->timestamp = timestamp;
197         
198         /* Everything went fine. */
199         obj->assigned = true;
200         res = 0;
201 clean:  
202         return res;
203 }
204
205 /**
206  * Get a free object.
207  *
208  * /param timestamp assign this timestamp to the object we get.
209  * /param callid pointer to callid str.
210  * /return -1 if an error happens.
211  * /return number of a free object on success.
212  */
213 int cobj_get(uint64_t timestamp, str *callid)
214 {
215         assert(callid);
216         assert(callid->len > 0);
217         
218         int res = -1; /* Error by default */
219
220         lock_get(co_data->lock);
221
222         LM_DBG("IN co_data->cur: %d\n", co_data->cur);
223
224         if (co_data->cur == 0) {
225                 /* First object to assign. */
226                 co_object_t *obj = &co_data->ring[0];
227                 if (cobj_fill(obj, timestamp, callid)) {
228                         LM_ERR("Cannot create object 0\n");
229                         goto clean;
230                 }
231         
232                 co_data->cur = co_data->start;
233                 res = co_data->cur;
234                 co_data->assigned++;
235                 LM_DBG("Object found: %d\n", res);
236                 LM_DBG("Current timestamp: %" PRIu64 "\n", obj->timestamp);
237                 LM_DBG("Current Call-ID: %.*s\n", obj->callid.len, obj->callid.s);
238                 goto clean;
239         }
240         assert(co_data->cur >= co_data->start && co_data->cur <= co_data->end);
241
242         /* Find next free position in array. */
243         int pos_cur, pos, pos_max;
244         pos_cur = co_data->cur - co_data->start; /* Last used position in array. */
245         pos = pos_cur + 1; /* Position to check in array. */
246         pos_max = co_data->end - co_data->start; /* Maximum acceptable position in array. */
247         
248         while (pos != pos_cur) {
249                 if (pos > pos_max) {
250                         pos = 0;
251                         continue;
252                 }
253                 assert(pos <= pos_max && pos >= 0);
254
255                 co_object_t *obj = &co_data->ring[pos];
256                 if (obj->assigned == false) {
257                         /* We found a free object. */
258                   if (cobj_fill(obj, timestamp, callid)) {
259                                 LM_ERR("Cannot create object %d\n", pos);
260                                 goto clean;
261                         }
262                         
263                         co_data->cur = pos + co_data->start;
264                         res = co_data->cur;
265                         co_data->assigned++;
266                         LM_DBG("Object found: %d\n", res);
267                         LM_DBG("Current timestamp: %" PRIu64 "\n", obj->timestamp);
268                         LM_DBG("Current Call-ID: %.*s\n", obj->callid.len, obj->callid.s);
269                         goto clean;
270                 }
271
272                 pos++;
273         }
274         
275         /* No free object found. */
276         res = -1;
277         LM_ERR("No free objects available\n");
278         
279 clean:
280
281         LM_DBG("OUT co_data->cur: %d\n", co_data->cur);
282         lock_release(co_data->lock);
283         return res;
284 }
285
286 /**
287  * Free an Object
288  *
289  * /param num number of object to free
290  * /return 0 on success
291  */
292 int cobj_free(int num)
293 {
294         int res = -1; // It fails by default.
295
296         lock_get(co_data->lock);
297
298         if (num < co_data->start || num > co_data->end) {
299                 LM_ERR("Object out of range %d  [%d, %d]\n", num, co_data->start, co_data->end);
300                 goto clean;
301         }
302
303         int pos = num - co_data->start;
304         co_object_t *obj = &co_data->ring[pos];
305         if (obj->assigned == true) {
306                 LM_DBG("Freeing object %d - timestamp: %" PRIu64 " - Call-ID: %.*s\n",
307                            num, obj->timestamp, obj->callid.len, obj->callid.s);
308
309                 if (obj->callid.s) {
310                         shm_free(obj->callid.s);
311                         obj->callid.s = NULL;
312                 }
313
314                 obj->assigned = false;
315                 co_data->assigned--;
316         } else {
317                 LM_WARN("Freeing an already free object: %d\n", num);
318         }
319         res = 0;
320         LM_DBG("Object %d freed\n", num);
321         
322 clean:          
323         lock_release(co_data->lock);
324         return res;
325 }
326
327 /**
328  * Fill data in cobj_stats_t structure passed as pointer.
329  *
330  * /param stats pointer to cobj_stats_t structure.
331  * /return 0 on success
332  */
333 int cobj_stats_get(cobj_stats_t *stats)
334 {
335         int result = -1; /* It fails by default. */
336
337         lock_get(co_data->lock);
338         
339         if (!stats) {
340                 LM_ERR("No cobj_stats_t structure provided\n");
341                 goto clean;
342         }
343
344         stats->start = co_data->start;
345         stats->end = co_data->end;
346         stats->assigned = co_data->assigned;
347         
348         /* TODO */
349
350         /* Everything went fine. */
351         result = 0;
352         
353 clean:
354         lock_release(co_data->lock);
355         return result;
356 }
357
358 /**
359  * Free all objects at once.
360  */
361 void cobj_free_all(void)
362 {
363         lock_get(co_data->lock);        
364
365         int i;
366         int start = co_data->start;
367         int end = co_data->end;
368         int total = end - start + 1;
369
370         /* Free all objects in the array. */
371         for (i=0; i < total; i++) {
372
373                 co_object_t *obj = &co_data->ring[i];
374                 if (obj->assigned == true) {
375                         if (obj->callid.s) {
376                                 shm_free(obj->callid.s);
377                                 obj->callid.s = NULL;
378                         }
379                         obj->assigned = false;
380                 }
381
382         } // for i
383
384         co_data->cur = 0; /* No object assigned yet. */
385         co_data->assigned = 0; /* No assigned objects at this moment. */
386
387         LM_DBG("Objects in range [%d, %d] freed\n", start, end);
388
389         lock_release(co_data->lock);
390 }
391
392 /**
393  * Get all objects which timestamp is less than or equals some value.
394  *
395  * User shall free returned list when not used any more.
396  *
397  * /param ts timestamp to compare.
398  * /param elem returned list. NULL on error of if zero elements.
399  * /param limit maximum number of objects to return. 0 means unlimited.
400  *
401  * /return number of returned objects on success.
402  * /return -1 on error
403  */
404 int cobj_get_timestamp(uint64_t ts, cobj_elem_t **elem, int limit)
405 {
406         assert(elem);
407         assert(limit >= 0);
408
409         LM_DBG("Received timestamp: %" PRIu64 "\n", ts);
410         
411         int res = -1; /* Fail by default; */
412         *elem = NULL; /* Empty list by default. */
413
414         int total = co_data->end - co_data->start + 1;
415         int num_objects = 0; /* Not found any object by now. */
416
417         /* First and last element of the list. */
418         cobj_elem_t *first = NULL;
419         
420         int i;
421         for (i=0; i<total; i++) {
422                 co_object_t *obj = &co_data->ring[i];
423                 if (obj->assigned == true && obj->timestamp <= ts) {
424                         /* Object found */
425
426                         cobj_elem_t *elem_new = (cobj_elem_t*)pkg_malloc(sizeof(cobj_elem_t));
427                         if (!elem_new) {
428                                 LM_ERR("Memory error\n");
429                                 goto clean;
430                         }
431
432                         /* Fill new element with data */
433                         elem_new->number = co_data->start + i;
434                         elem_new->timestamp = obj->timestamp;
435                         elem_new->next = NULL;
436                         elem_new->callid.s = (char*)pkg_malloc(obj->callid.len + 1); /* +1 Zero at the end */
437                         if (!elem_new->callid.s) {
438                                 LM_ERR("Cannot allocate memory for callid\n");
439                                 pkg_free(elem_new);
440                                 elem_new = NULL;
441                                 goto clean;
442                         }
443                         memcpy(elem_new->callid.s, obj->callid.s, obj->callid.len);
444                         elem_new->callid.s[obj->callid.len] = '\0';
445                         elem_new->callid.len = obj->callid.len;
446
447                         /* Insert the element in the ascending ordered list. */
448                         cobj_elem_t *previous = NULL;
449                         cobj_elem_t *tmp = first;
450                         while (tmp) {
451                                 if (elem_new->timestamp <= tmp->timestamp) {
452                                         /* We found the position of the new element. */
453                                         break;
454                                 }
455                                 previous = tmp;
456                                 tmp = tmp->next;
457                         }
458
459                         if (previous) {
460                                 /* Non-void list. */
461                                 elem_new->next = previous->next;
462                                 previous->next = elem_new;
463                                 
464                         } else {
465                                 /* Insert at the beginning. */
466                                 elem_new->next = first;
467                                 first = elem_new;
468                         }
469                         num_objects++;
470
471                         /* Delete an element if we surpassed the limit. */
472                         if (limit && num_objects > limit) {
473                                 tmp = first;
474                                 first = first->next;
475                                 tmp->next = NULL;
476                                 cobj_free_list(tmp);
477                         }
478
479                 } /* if obj->assigned */
480                 
481         } /* for i=0 */
482                  
483         /* Everything went fine */
484         res = num_objects;
485         *elem = first;
486         first = NULL;
487         
488 clean:
489         if (first) {
490                 /* An error occurred */
491                 cobj_free_list(first);
492         }
493         
494         return res;
495 }
496
497 /**
498  * Free an object list.
499  *
500  * /param elem pointer to first element in the list.
501  */
502 void cobj_free_list(cobj_elem_t *elem)
503 {
504         while (elem) {
505                 cobj_elem_t *next = elem->next;
506                 if (elem->callid.s) {
507                         pkg_free(elem->callid.s);
508                 }
509                 pkg_free(elem);
510                 elem = next;
511         }
512 }