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