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