72307e2d86ac78c273d2568f3e4448c08bab1677
[sip-router] / src / modules / call_obj / call_obj_mod.c
1
2 #include <inttypes.h>
3
4 #include "cobj.h"
5
6 #include "../../core/sr_module.h"
7 #include "../../core/mod_fix.h"
8 #include "../../core/lvalue.h"
9 #include "../../core/rpc.h"
10 #include "../../core/rpc_lookup.h"
11 #include "../../core/trim.h"
12 #include "../../core/kemi.h"
13
14 MODULE_VERSION
15
16 static int w_call_obj_get(struct sip_msg *msg, char *result);
17
18 static int w_call_obj_free(struct sip_msg* msg, char* num_obj);
19
20 static int mod_init(void);
21 static void mod_destroy(void);
22
23 /**
24  * Module parameters
25  */
26 /* Actually, negative or zero values are not allowed. */
27 int call_obj_start = 0;
28 int call_obj_end = 0;
29
30 /* module commands */
31 static cmd_export_t cmds[] = {
32         {"call_obj_get", (cmd_function)w_call_obj_get, 1, fixup_pvar_null, 0, ANY_ROUTE},
33         {"call_obj_free", (cmd_function)w_call_obj_free, 1, fixup_var_str_1, 0, ANY_ROUTE},
34         { 0, 0, 0, 0, 0, 0}
35 };
36
37 static param_export_t params[]={
38         {"start", PARAM_INT, &call_obj_start},
39         {"end", PARAM_INT, &call_obj_end},
40         {0, 0, 0}
41 };
42
43 /* RPC commands. */
44
45 static void rpc_call_obj_free(rpc_t *rpc, void *ctx)
46 {
47         str obj_str;
48         int obj_num;
49         
50         if (rpc->scan(ctx, "S", &obj_str) < 1) {
51                 rpc->fault(ctx, 400, "required object number argument");
52                 return;
53         }
54
55         if (str2int(&obj_str, (unsigned int*)&obj_num)) {
56                 LM_ERR("Cannot convert %.*s to number\n", obj_str.len, obj_str.s);
57                 rpc->fault(ctx, 400, "cannot convert string to number");
58                 return;
59         }
60         LM_DBG("Param value: %d\n", obj_num);
61
62         if (cobj_free(obj_num)) {
63                 LM_ERR("Freeing object: %d\n", obj_num);
64                 rpc->fault(ctx, 500, "error freeing object");
65                 return;
66         }
67
68         return;
69 }
70
71 static void rpc_call_obj_stats(rpc_t *rpc, void *ctx)
72 {
73         cobj_stats_t stats;
74         
75         if (cobj_stats_get(&stats)) {
76                 LM_ERR("Cannot get statistics for module\n");
77                 rpc->fault(ctx, 500, "cannot get statistics for module");
78                 return;
79         }
80
81         if (rpc->rpl_printf(ctx, "Start: %d  End: %d", stats.start, stats.end) < 0) {
82                 return;
83         }
84
85         int total = stats.end - stats.start + 1;
86         double percentage = 100.0 * stats.assigned / total;
87         if (rpc->rpl_printf(ctx, "Total: %d  Assigned: %d  (%.*f%%)",
88                                                 total, stats.assigned, 2, percentage)) {
89                 return;
90         }
91
92         return;
93 }
94
95 static void rpc_call_obj_free_all(rpc_t *rpc, void *ctx)
96 {
97         cobj_free_all();
98
99         return;
100 }
101
102 static void rpc_call_obj_list(rpc_t *rpc, void *ctx)
103 {
104         int duration = 0;
105         int limit = 0; /* Maximum number of objects to return. 0 means unlimited. */
106         cobj_elem_t *list = NULL;
107
108         int rc = rpc->scan(ctx, "d*d", &duration, &limit);
109         if (rc != 1 && rc != 2) {
110                 rpc->fault(ctx, 400, "requires arguments for duration number (and optionally limit)");
111                 goto clean;
112         }
113         
114         if (duration < 0) {
115                 rpc->fault(ctx, 400, "duration argument shouldn\'t be negative");
116                 goto clean;
117         }
118
119         if (limit < 0) {
120                 rpc->fault(ctx, 400, "limit argument shouldn\'t be negative");
121                 goto clean;
122         }
123         
124         uint64_t current_ts;
125         uint64_t dur_ms = duration;
126         dur_ms *= 1000; /* duration in milliseconds */
127         if (get_timestamp(&current_ts)) {
128                 LM_ERR("error getting timestamp");
129                 rpc->fault(ctx, 500, "error getting timestamp");
130                 goto clean;
131         }
132
133         if (current_ts < dur_ms) {
134                 rpc->fault(ctx, 400, "duration is too long");
135                 goto clean;
136         }
137
138         uint64_t timestamp = current_ts - dur_ms;
139         int num = cobj_get_timestamp(timestamp, &list, limit);
140         if (num < 0) {
141                 rpc->fault(ctx, 500, "error getting call list");
142                 goto clean;
143         }
144
145         rpc->rpl_printf(ctx, "Number of calls: %d", num);
146         if (limit && limit < num) {
147                 rpc->rpl_printf(ctx, "Showing only: %d", limit);
148         }
149         cobj_elem_t *elem = list;
150         while (elem) {
151                 rpc->rpl_printf(ctx, "%d  ts: %" PRIu64 "  Call-ID: %.*s", elem->number,
152                                                 elem->timestamp, elem->callid.len, elem->callid.s);
153                 elem = elem->next;
154         }
155
156 clean:
157
158         /* Free list */
159         if (list) {
160                 cobj_free_list(list);
161         }
162         
163         return;
164 }
165
166 static const char* rpc_call_obj_free_all_doc[2] = {
167         "Free all objects at once",
168         0
169 };
170         
171 static const char* rpc_call_obj_stats_doc[2] = {
172         "Show statistics about module objects",
173         0
174 };
175         
176 static const char* rpc_call_obj_free_doc[2] = {
177         "Free an object so it can be assigned again",
178         0
179 };
180
181 static const char* rpc_call_obj_list_doc[2] = {
182         "Get a list of objects with a longer duration (in seconds) than a number",
183         0
184 };
185
186 static rpc_export_t rpc_cmds[] = {
187         {"call_obj.free", rpc_call_obj_free, rpc_call_obj_free_doc, 0},
188         {"call_obj.stats", rpc_call_obj_stats, rpc_call_obj_stats_doc, 0},
189         {"call_obj.free_all", rpc_call_obj_free_all, rpc_call_obj_free_all_doc, 0},
190         {"call_obj.list", rpc_call_obj_list, rpc_call_obj_list_doc, 0},
191         {0, 0, 0, 0}
192 };
193
194 struct module_exports exports = {
195         "call_obj",
196         DEFAULT_DLFLAGS, /* dlopen flags */
197         cmds,
198         params,
199         0,              /* exported RPC methods */
200         0,              /* exported pseudo-variables */
201         0,              /* response function */
202         mod_init,       /* module initialization function */
203         0,              /* per child init function */
204         mod_destroy     /* destroy function */
205 };
206
207 static int mod_init(void)
208 {
209         LM_DBG("Start parameter: %d\n", call_obj_start);
210         LM_DBG("End parameter: %d\n", call_obj_end);
211
212         if (rpc_register_array(rpc_cmds) != 0) {
213                 LM_ERR("failed to register RPC commands\n");
214                 return -1;
215         }
216         
217         if (cobj_init(call_obj_start, call_obj_end)) {
218                 LM_ERR("Could not start module\n");
219                 return -1;
220         }
221         
222         return 0;
223 }
224
225 static void mod_destroy(void)
226 {
227         LM_DBG("cleaning up\n");
228         cobj_destroy();
229 }
230
231 /**
232  * Looks for the Call-ID header
233  * On error content pointed by s is undefined.
234  *
235  * @param msg - the sip message
236  * @param s  pointer to str where we will store callid.
237  *
238  * @returns 0 on success
239  */
240 static int get_call_id(struct sip_msg *msg, str *s)
241 {
242         if (!msg) {
243                 LM_ERR("No message available\n");
244                 return -1;
245         }
246
247         if ( (!msg->callid && parse_headers(msg, HDR_CALLID_F, 0)!=0) || msg->callid==0 ) {
248                 LM_ERR("failed to parse Call-ID\n");
249                 return -1;
250         }
251
252         if( msg->callid->body.s==NULL || msg->callid->body.len==0) {
253                 LM_ERR("cannot parse Call-ID header\n");
254                 return -1;
255         }
256
257         *s = msg->callid->body;
258         trim(s);
259         
260         return 0;
261 }
262
263 /**
264  *
265  */
266 static int w_call_obj_get(struct sip_msg *msg, char *result)
267 {
268         int ret_code = -1; /* It fails by default. */
269
270         if (!msg) {
271                 LM_ERR("No SIP message found\n");
272                 goto clean;
273         }
274         
275         pv_spec_t *res;
276         
277         if(result==NULL)
278         {
279                 LM_ERR("No result variable\n");
280                 goto clean;
281         }
282         res = (pv_spec_t *)result;
283
284         str call_id;
285         if (get_call_id(msg, &call_id)) {
286                 LM_ERR("Cannot get callid header\n");
287                 goto clean;
288         }
289         LM_DBG("CallId: %.*s\n", call_id.len, call_id.s);
290
291         uint64_t current_ts;
292         if (get_timestamp(&current_ts)) {
293                 LM_ERR("error getting timestamp");
294                 goto clean;
295         }
296         
297         int obj = cobj_get(current_ts, &call_id);
298         if (obj == -1) {
299                 LM_ERR("Getting object\n");
300                 goto clean;
301         }
302         /* obj >= 0 */
303         
304         pv_value_t val;
305
306         char *obj_str = NULL;
307         int len = 0;
308         obj_str = int2str((unsigned long)obj, &len);
309         if (!obj_str) {
310                 LM_ERR("Cannot convert number %d to string\n", obj);
311                 goto clean;
312         }
313         
314         memset(&val, 0, sizeof(pv_value_t));
315         val.flags = PV_VAL_STR;
316         val.rs.s = obj_str;
317         val.rs.len = len;
318         LM_DBG("Obj string: %s\n", obj_str);
319         
320         if(res->setf(msg, &res->pvp, (int)EQ_T, &val)<0)
321         {
322                 LM_ERR("setting result PV failed\n");
323                 goto clean;
324         }
325
326         ret_code = 1;
327         
328 clean:
329         return ret_code;
330 }
331
332 /**
333  *
334  */
335 static int w_call_obj_free(struct sip_msg* msg, char* num_obj)
336 {
337         int c_obj_num = 0;
338
339         str num_obj_str;
340         
341         if (get_str_fparam(&num_obj_str, msg, (fparam_t*)num_obj) < 0)
342         {
343                 LM_ERR("failed to get object value\n");
344                 return -1;
345         }
346
347         if (num_obj_str.len == 0)
348         {
349                 LM_ERR("invalid object parameter - empty value\n");
350                 return -1;
351         }
352         LM_DBG("Param string value: %.*s\n", num_obj_str.len, num_obj_str.s);
353
354         if (str2int(&num_obj_str, (unsigned int*)&c_obj_num)) {
355                 LM_ERR("Cannot convert %.*s to number\n", num_obj_str.len, num_obj_str.s);
356                 return -1;
357         }
358         LM_DBG("Param value: %d\n", c_obj_num);
359
360         if (cobj_free(c_obj_num)) {
361                 LM_ERR("Freeing object: %d\n", c_obj_num);
362                 return -1;
363         }
364
365         return 1;
366 }
367
368 /**
369  * Get a free object.
370  *
371  * /return -1 on error.
372  * /return number of free object on success.
373  */
374 static int ki_call_obj_get(sip_msg_t *msg)
375 {
376         str call_id;
377         if (get_call_id(msg, &call_id)) {
378                 LM_ERR("Cannot get callid header\n");
379                 goto error;
380         }
381         LM_DBG("CallId: %.*s\n", call_id.len, call_id.s);
382
383         uint64_t current_ts;
384         if (get_timestamp(&current_ts)) {
385                 LM_ERR("error getting timestamp");
386                 goto error;
387         }
388
389         int obj = cobj_get(current_ts, &call_id);
390         if (obj == -1) {
391                 LM_ERR("Getting object\n");
392                 goto error;
393         }
394         /* obj >= 0 */
395
396         return obj;
397
398 error:
399         return -1;
400 }
401
402 /**
403  * Free an object.
404  *
405  * /param num_obj number of the object to free.
406  * /return 1 on success.
407  * /return 0 on error.
408  */
409 static int ki_call_obj_free(sip_msg_t *msg, int num_obj)
410 {
411         if (cobj_free(num_obj)) {
412                 LM_ERR("Freeing object: %d\n", num_obj);
413                 return 0;
414         }
415
416         return 1;
417 }
418
419 /**
420  *
421  */
422 /* clang-format off */
423 static sr_kemi_t sr_kemi_call_obj_exports[] = {
424         { str_init("call_obj"), str_init("get"),
425                 SR_KEMIP_INT, ki_call_obj_get,
426                 { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
427                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
428         },
429         { str_init("call_obj"), str_init("free"),
430             SR_KEMIP_INT, ki_call_obj_free,
431                 { SR_KEMIP_INT, SR_KEMIP_NONE, SR_KEMIP_NONE,
432                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
433         },
434         { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
435 };
436 /* clang-format on */
437
438 /**
439  *
440  */
441 int mod_register(char *path, int *dlflags, void *p1, void *p2)
442 {
443         sr_kemi_modules_add(sr_kemi_call_obj_exports);
444         return 0;
445 }