1 /**
2 * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.com)
3 *
4 * This file is part of Kamailio, a free SIP server.
5 *
6 * Kamailio is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version
10 *
11 * Kamailio is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 */
21
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <time.h>
26
27 #include "../../lib/srdb1/db_op.h"
28 #include "../../core/sr_module.h"
29 #include "../../lib/srdb1/db.h"
30 #include "../../core/mem/shm_mem.h"
31 #include "../../core/mem/mem.h"
32 #include "../../core/dprint.h"
33 #include "../../core/parser/parse_uri.h"
34 #include "../../core/timer.h"
35 #include "../../core/ut.h"
36 #include "../../core/locking.h"
37 #include "../../core/action.h"
38 #include "../../core/mod_fix.h"
39 #include "../../core/parser/parse_from.h"
40 #include "../../core/rpc.h"
41 #include "../../core/rpc_lookup.h"
42 #include "../../core/kemi.h"
43
44 #include "mtree.h"
45 #include "api.h"
46
47 MODULE_VERSION
48
49
50 #define NR_KEYS 3
51
52 int mt_fetch_rows = 1000;
53
54 /** database connection */
55 static db1_con_t *db_con = NULL;
56 static db_func_t mt_dbf;
57
58 #if 0
59 INSERT INTO version (table_name, table_version) values ('mtree','1');
60 CREATE TABLE mtree (
61 id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,
62 tprefix VARCHAR(32) NOT NULL,
63 tvalue VARCHAR(128) DEFAULT '' NOT NULL,
64 CONSTRAINT tprefix_idx UNIQUE (tprefix)
65 ) ENGINE=MyISAM;
66 INSERT INTO version (table_name, table_version) values ('mtrees','1');
67 CREATE TABLE mtrees (
68 id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,
69 tname VARCHAR(128) NOT NULL,
70 tprefix VARCHAR(32) NOT NULL,
71 tvalue VARCHAR(128) DEFAULT '' NOT NULL,
72 CONSTRAINT tname_tprefix_idx UNIQUE (tname, tprefix)
73 ) ENGINE=MyISAM;
74 #endif
75
76 /** parameters */
77 static str db_url = str_init(DEFAULT_DB_URL);
78 /* default name created by sql scripts is 'mtrees'
79 * - don't set it here with default value, only via config param */
80 static str db_table = str_init("");
81 static str tname_column = str_init("tname");
82 static str tprefix_column = str_init("tprefix");
83 static str tvalue_column = str_init("tvalue");
84
85 /* List of allowed chars for a prefix*/
86 str mt_char_list = str_init("0123456789");
87
88 static str value_param = str_init("$avp(s:tvalue)");
89 static str values_param = str_init("$avp(s:tvalues)");
90 static str dstid_param = str_init("$avp(s:tdstid)");
91 static str weight_param = str_init("$avp(s:tweight)");
92 static str count_param = str_init("$avp(s:tcount)");
93 pv_spec_t pv_value;
94 pv_spec_t pv_values;
95 pv_spec_t pv_dstid;
96 pv_spec_t pv_weight;
97 pv_spec_t pv_count;
98 int _mt_tree_type = MT_TREE_SVAL;
99 int _mt_ignore_duplicates = 0;
100 int _mt_allow_duplicates = 0;
101
102 /* lock, ref counter and flag used for reloading the date */
103 static gen_lock_t *mt_lock = 0;
104 static volatile int mt_tree_refcnt = 0;
105 static volatile int mt_reload_flag = 0;
106
107 int mt_param(modparam_t type, void *val);
108 static int fixup_mt_match(void** param, int param_no);
109 static int w_mt_match(struct sip_msg* msg, char* str1, char* str2,
110 char* str3);
111
112 static int mod_init(void);
113 static void mod_destroy(void);
114 static int child_init(int rank);
115 static int mtree_init_rpc(void);
116 static int bind_mtree(mtree_api_t* api);
117
118 static int mt_match(sip_msg_t *msg, str *tname, str *tomatch,
119 int mval);
120
121 static int mt_load_db(m_tree_t *pt);
122 static int mt_load_db_trees();
123
124 static cmd_export_t cmds[]={
125 {"mt_match", (cmd_function)w_mt_match, 3, fixup_mt_match,
126 0, REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE},
127 {"bind_mtree", (cmd_function)bind_mtree, 0, 0, 0},
128 {0, 0, 0, 0, 0, 0}
129 };
130
131 static param_export_t params[]={
132 {"mtree", PARAM_STRING|USE_FUNC_PARAM, (void*)mt_param},
133 {"db_url", PARAM_STR, &db_url},
134 {"db_table", PARAM_STR, &db_table},
135 {"tname_column", PARAM_STR, &tname_column},
136 {"tprefix_column", PARAM_STR, &tprefix_column},
137 {"tvalue_column", PARAM_STR, &tvalue_column},
138 {"char_list", PARAM_STR, &mt_char_list},
139 {"fetch_rows", INT_PARAM, &mt_fetch_rows},
140 {"pv_value", PARAM_STR, &value_param},
141 {"pv_values", PARAM_STR, &values_param},
142 {"pv_dstid", PARAM_STR, &dstid_param},
143 {"pv_weight", PARAM_STR, &weight_param},
144 {"pv_count", PARAM_STR, &count_param},
145 {"mt_tree_type", INT_PARAM, &_mt_tree_type},
146 {"mt_ignore_duplicates", INT_PARAM, &_mt_ignore_duplicates},
147 {"mt_allow_duplicates", INT_PARAM, &_mt_allow_duplicates},
148 {0, 0, 0}
149 };
150
151 struct module_exports exports = {
152 "mtree",
153 DEFAULT_DLFLAGS,/* dlopen flags */
154 cmds, /*·exported·functions·*/
155 params, /*·exported·functions·*/
156 0, /*·exported·RPC·methods·*/
157 0, /* exported pseudo-variables */
158 0, /* response·function */
159 mod_init, /* module initialization function */
160 child_init, /* per child init function */
161 mod_destroy /* destroy function */
162 };
163
164
165 /**
166 * init module function
167 */
mod_init(void)168 static int mod_init(void)
169 {
170 m_tree_t *pt = NULL;
171
172 if(mtree_init_rpc()!=0)
173 {
174 LM_ERR("failed to register RPC commands\n");
175 return -1;
176 }
177
178 if(pv_parse_spec(&value_param, &pv_value)<00
179 || !(pv_is_w(&pv_value)))
180 {
181 LM_ERR("cannot parse value pv or is read only\n");
182 return -1;
183 }
184
185 if (pv_parse_spec(&values_param, &pv_values) <0
186 || pv_values.type != PVT_AVP) {
187 LM_ERR("cannot parse values avp\n");
188 return -1;
189 }
190
191 if(pv_parse_spec(&dstid_param, &pv_dstid)<0
192 || pv_dstid.type!=PVT_AVP)
193 {
194 LM_ERR("cannot parse dstid avp\n");
195 return -1;
196 }
197
198 if(pv_parse_spec(&weight_param, &pv_weight)<0
199 || pv_weight.type!=PVT_AVP)
200 {
201 LM_ERR("cannot parse dstid avp\n");
202 return -1;
203 }
204
205 if(pv_parse_spec(&count_param, &pv_count)<0
206 || !(pv_is_w(&pv_weight)))
207 {
208 LM_ERR("cannot parse count pv or is read-only\n");
209 return -1;
210 }
211
212 if(mt_fetch_rows<=0)
213 mt_fetch_rows = 1000;
214
215 if(mt_char_list.len<=0)
216 {
217 LM_ERR("invalid prefix char list\n");
218 return -1;
219 }
220 LM_DBG("mt_char_list=%s \n", mt_char_list.s);
221 mt_char_table_init();
222
223 /* binding to mysql module */
224 if(db_bind_mod(&db_url, &mt_dbf))
225 {
226 LM_ERR("database module not found\n");
227 return -1;
228 }
229
230 if (!DB_CAPABILITY(mt_dbf, DB_CAP_ALL))
231 {
232 LM_ERR("database module does not "
233 "implement all functions needed by the module\n");
234 return -1;
235 }
236
237 /* open a connection with the database */
238 db_con = mt_dbf.init(&db_url);
239 if(db_con==NULL)
240 {
241 LM_ERR("failed to connect to the database\n");
242 return -1;
243 }
244
245 LM_DBG("database connection opened successfully\n");
246
247 if ( (mt_lock=lock_alloc())==0) {
248 LM_CRIT("failed to alloc lock\n");
249 goto error1;
250 }
251 if (lock_init(mt_lock)==0 ) {
252 LM_CRIT("failed to init lock\n");
253 goto error1;
254 }
255
256 if(mt_defined_trees())
257 {
258 LM_DBG("static trees defined\n");
259
260 pt = mt_get_first_tree();
261
262 while(pt!=NULL)
263 {
264 LM_DBG("loading from tree <%.*s>\n",
265 pt->tname.len, pt->tname.s);
266
267 /* loading all information from database */
268 if(mt_load_db(pt)!=0)
269 {
270 LM_ERR("cannot load info from database\n");
271 goto error1;
272 }
273 pt = pt->next;
274 }
275 /* reset db_table value */
276 db_table.s = "";
277 db_table.len = 0;
278 } else {
279 if(db_table.len<=0)
280 {
281 LM_ERR("no trees table defined\n");
282 goto error1;
283 }
284 if(mt_init_list_head()<0)
285 {
286 LM_ERR("unable to init trees list head\n");
287 goto error1;
288 }
289 /* loading all information from database */
290 if(mt_load_db_trees()!=0)
291 {
292 LM_ERR("cannot load trees from database\n");
293 goto error1;
294 }
295 }
296 mt_dbf.close(db_con);
297 db_con = 0;
298
299 #if 0
300 mt_print_tree(mt_get_first_tree());
301 #endif
302
303 /* success code */
304 return 0;
305
306 error1:
307 if (mt_lock)
308 {
309 lock_destroy( mt_lock );
310 lock_dealloc( mt_lock );
311 mt_lock = 0;
312 }
313 mt_destroy_trees();
314
315 if(db_con!=NULL)
316 mt_dbf.close(db_con);
317 db_con = 0;
318 return -1;
319 }
320
mt_child_init(void)321 static int mt_child_init(void)
322 {
323 db_con = mt_dbf.init(&db_url);
324 if(db_con==NULL)
325 {
326 LM_ERR("failed to connect to database\n");
327 return -1;
328 }
329
330 return 0;
331 }
332
333
334 /* each child get a new connection to the database */
child_init(int rank)335 static int child_init(int rank)
336 {
337 /* skip child init for non-worker process ranks */
338 if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN)
339 return 0;
340
341 if ( mt_child_init()!=0 )
342 return -1;
343
344 LM_DBG("#%d: database connection opened successfully\n", rank);
345
346 return 0;
347 }
348
349
mod_destroy(void)350 static void mod_destroy(void)
351 {
352 LM_DBG("cleaning up\n");
353 mt_destroy_trees();
354 if (db_con!=NULL && mt_dbf.close!=NULL)
355 mt_dbf.close(db_con);
356 /* destroy lock */
357 if (mt_lock)
358 {
359 lock_destroy( mt_lock );
360 lock_dealloc( mt_lock );
361 mt_lock = 0;
362 }
363
364 }
365
fixup_mt_match(void ** param,int param_no)366 static int fixup_mt_match(void** param, int param_no)
367 {
368 if(param_no==1 || param_no==2) {
369 return fixup_spve_null(param, 1);
370 }
371 if (param_no != 3) {
372 LM_ERR("invalid parameter number %d\n", param_no);
373 return E_UNSPEC;
374 }
375 return fixup_igp_null(param, 1);
376 }
377
378
379 /* use tree tn, match var, by mode, output in avp params */
mt_match(sip_msg_t * msg,str * tname,str * tomatch,int mval)380 static int mt_match(sip_msg_t *msg, str *tname, str *tomatch,
381 int mval)
382 {
383 m_tree_t *tr = NULL;
384
385 if(msg==NULL) {
386 LM_ERR("received null msg\n");
387 return -1;
388 }
389
390 again:
391 lock_get( mt_lock );
392 if (mt_reload_flag) {
393 lock_release( mt_lock );
394 sleep_us(5);
395 goto again;
396 }
397 mt_tree_refcnt++;
398 lock_release( mt_lock );
399
400 tr = mt_get_tree(tname);
401 if(tr==NULL) {
402 /* no tree with such name*/
403 goto error;
404 }
405
406 if(mt_match_prefix(msg, tr, tomatch, mval)<0)
407 {
408 LM_DBG("no prefix found in [%.*s] for [%.*s]\n",
409 tname->len, tname->s,
410 tomatch->len, tomatch->s);
411 goto error;
412 }
413
414 lock_get( mt_lock );
415 mt_tree_refcnt--;
416 lock_release( mt_lock );
417 return 1;
418
419 error:
420 lock_get( mt_lock );
421 mt_tree_refcnt--;
422 lock_release( mt_lock );
423 return -1;
424 }
425
w_mt_match(struct sip_msg * msg,char * ptn,char * pvar,char * pmode)426 static int w_mt_match(struct sip_msg* msg, char* ptn, char* pvar,
427 char* pmode)
428 {
429 str tname;
430 str tomatch;
431 int mval;
432
433 if(msg==NULL)
434 {
435 LM_ERR("received null msg\n");
436 return -1;
437 }
438
439 if(fixup_get_svalue(msg, (gparam_t*)ptn, &tname)<0)
440 {
441 LM_ERR("cannot get the tree name\n");
442 return -1;
443 }
444 if(fixup_get_svalue(msg, (gparam_t*)pvar, &tomatch)<0)
445 {
446 LM_ERR("cannot get the match var\n");
447 return -1;
448 }
449 if(fixup_get_ivalue(msg, (gparam_t*)pmode, &mval)<0)
450 {
451 LM_ERR("cannot get the mode\n");
452 return -1;
453 }
454
455 return mt_match(msg, &tname, &tomatch, mval);
456 }
457
mt_param(modparam_t type,void * val)458 int mt_param(modparam_t type, void *val)
459 {
460 if(val==NULL)
461 goto error;
462
463 return mt_table_spec((char*)val);
464 error:
465 return -1;
466
467 }
468
mt_pack_values(m_tree_t * pt,db1_res_t * db_res,int row,int cols,str * tvalue)469 static int mt_pack_values(m_tree_t *pt, db1_res_t* db_res,
470 int row, int cols, str *tvalue)
471 {
472 static char vbuf[4096];
473 int c;
474 int len;
475 char *p;
476 str iv;
477
478 len = 0;
479 for(c=1; c<cols; c++) {
480 if(VAL_NULL(&RES_ROWS(db_res)[row].values[c])) {
481 len += 1;
482 } else if(RES_ROWS(db_res)[row].values[c].type == DB1_STRING) {
483 len += strlen(RES_ROWS(db_res)[row].values[c].val.string_val);
484 } else if(RES_ROWS(db_res)[row].values[c].type == DB1_STR) {
485 len += RES_ROWS(db_res)[row].values[c].val.str_val.len;
486 } else if(RES_ROWS(db_res)[row].values[c].type == DB1_INT) {
487 len += 12;
488 } else {
489 LM_ERR("unsupported data type for column %d\n", c);
490 return -1;
491 }
492 }
493 if(len + c>=4096) {
494 LM_ERR("too large values (need %d)\n", len+c);
495 return -1;
496 }
497 p = vbuf;
498 for(c=1; c<cols; c++) {
499 if(VAL_NULL(&RES_ROWS(db_res)[row].values[c])) {
500 *p = pt->pack[2];
501 p++;
502 } else if(RES_ROWS(db_res)[row].values[c].type == DB1_STRING) {
503 strcpy(p, RES_ROWS(db_res)[row].values[c].val.string_val);
504 p += strlen(RES_ROWS(db_res)[row].values[c].val.string_val);
505 } else if(RES_ROWS(db_res)[row].values[c].type == DB1_STR) {
506 strncpy(p, RES_ROWS(db_res)[row].values[c].val.str_val.s,
507 RES_ROWS(db_res)[row].values[c].val.str_val.len);
508 p += RES_ROWS(db_res)[row].values[c].val.str_val.len;
509 } else if(RES_ROWS(db_res)[row].values[c].type == DB1_INT) {
510 iv.s = sint2str(RES_ROWS(db_res)[row].values[c].val.int_val, &iv.len);
511 strncpy(p, iv.s, iv.len);
512 p += iv.len;
513 }
514 if(c+1<cols) {
515 *p = pt->pack[1];
516 p++;
517 }
518 }
519 tvalue->s = vbuf;
520 tvalue->len = p - vbuf;
521 LM_DBG("packed: [%.*s]\n", tvalue->len, tvalue->s);
522 return 0;
523 }
524
mt_load_db(m_tree_t * pt)525 static int mt_load_db(m_tree_t *pt)
526 {
527 db_key_t db_cols[MT_MAX_COLS] = {&tprefix_column, &tvalue_column};
528 db_key_t key_cols[1];
529 db_op_t op[1] = {OP_EQ};
530 db_val_t vals[1];
531 str tprefix, tvalue;
532 db1_res_t* db_res = NULL;
533 int i, ret, c;
534 m_tree_t new_tree;
535 m_tree_t *old_tree = NULL;
536 mt_node_t *bk_head = NULL;
537
538 if(pt->ncols>0) {
539 for(c=0; c<pt->ncols; c++) {
540 db_cols[c] = &pt->scols[c];
541 }
542 } else {
543 db_cols[0] = &tprefix_column;
544 db_cols[1] = &tvalue_column;
545 c = 2;
546 }
547 key_cols[0] = &tname_column;
548 VAL_TYPE(vals) = DB1_STRING;
549 VAL_NULL(vals) = 0;
550 VAL_STRING(vals) = pt->tname.s;
551
552 if(db_con==NULL)
553 {
554 LM_ERR("no db connection\n");
555 return -1;
556 }
557
558 old_tree = mt_get_tree(&(pt->tname));
559 if(old_tree==NULL)
560 {
561 LM_ERR("tree definition not found [%.*s]\n", pt->tname.len,
562 pt->tname.s);
563 return -1;
564 }
565 memcpy(&new_tree, old_tree, sizeof(m_tree_t));
566 new_tree.head = 0;
567 new_tree.next = 0;
568 new_tree.nrnodes = 0;
569 new_tree.nritems = 0;
570 new_tree.memsize = 0;
571 new_tree.reload_count++;
572 new_tree.reload_time = (unsigned int)time(NULL);
573
574
575 if (mt_dbf.use_table(db_con, &old_tree->dbtable) < 0)
576 {
577 LM_ERR("failed to use_table\n");
578 return -1;
579 }
580
581 if (DB_CAPABILITY(mt_dbf, DB_CAP_FETCH)) {
582 if(mt_dbf.query(db_con, key_cols, op, vals, db_cols, pt->multi,
583 c, 0, 0) < 0)
584 {
585 LM_ERR("Error while querying db\n");
586 return -1;
587 }
588 if(mt_dbf.fetch_result(db_con, &db_res, mt_fetch_rows)<0)
589 {
590 LM_ERR("Error while fetching result\n");
591 goto error;
592 } else {
593 if(RES_ROW_N(db_res)==0)
594 {
595 goto dbreloaded;
596 }
597 }
598 } else {
599 if((ret=mt_dbf.query(db_con, key_cols, op, vals, db_cols,
600 pt->multi, 2, 0, &db_res))!=0
601 || RES_ROW_N(db_res)<=0 )
602 {
603 if(ret==0)
604 {
605 goto dbreloaded;
606 } else {
607 goto error;
608 }
609 }
610 }
611
612 if(RES_ROW_N(db_res)>0)
613 {
614 if(RES_ROWS(db_res)[0].values[0].type != DB1_STRING
615 || RES_ROWS(db_res)[0].values[1].type != DB1_STRING)
616 {
617 LM_ERR("wrond column types in db table (%d / %d)\n",
618 RES_ROWS(db_res)[0].values[0].type,
619 RES_ROWS(db_res)[0].values[1].type);
620 goto error;
621 }
622 }
623
624 do {
625 for(i=0; i<RES_ROW_N(db_res); i++)
626 {
627 /* check for NULL values ?!?! */
628 tprefix.s = (char*)(RES_ROWS(db_res)[i].values[0].val.string_val);
629 tprefix.len = strlen(ZSW(tprefix.s));
630
631 if(c>2) {
632 if(mt_pack_values(&new_tree, db_res, i, c, &tvalue)<0) {
633 LM_ERR("Error packing values\n");
634 goto error;
635 }
636 } else {
637 tvalue.s = (char*)(RES_ROWS(db_res)[i].values[1].val.string_val);
638 tvalue.len = strlen(ZSW(tvalue.s));
639 }
640
641 if(tprefix.s==NULL || tvalue.s==NULL
642 || tprefix.len<=0 || tvalue.len<=0)
643 {
644 LM_ERR("Error - bad record in db"
645 " (prefix: %p/%d - value: %p/%d)\n",
646 tprefix.s, tprefix.len, tvalue.s, tvalue.len);
647 continue;
648 }
649
650 if(mt_add_to_tree(&new_tree, &tprefix, &tvalue)<0)
651 {
652 LM_ERR("Error adding info to tree\n");
653 goto error;
654 }
655 }
656 if (DB_CAPABILITY(mt_dbf, DB_CAP_FETCH)) {
657 if(mt_dbf.fetch_result(db_con, &db_res, mt_fetch_rows)<0) {
658 LM_ERR("Error while fetching!\n");
659 if (db_res)
660 mt_dbf.free_result(db_con, db_res);
661 goto error;
662 }
663 } else {
664 break;
665 }
666 } while(RES_ROW_N(db_res)>0);
667
668 dbreloaded:
669 mt_dbf.free_result(db_con, db_res);
670
671
672 /* block all readers */
673 lock_get( mt_lock );
674 mt_reload_flag = 1;
675 lock_release( mt_lock );
676
677 while (mt_tree_refcnt) {
678 sleep_us(10);
679 }
680
681 bk_head = old_tree->head;
682 old_tree->head = new_tree.head;
683 old_tree->nrnodes = new_tree.nrnodes;
684 old_tree->nritems = new_tree.nritems;
685 old_tree->memsize = new_tree.memsize;
686 old_tree->reload_count = new_tree.reload_count;
687 old_tree->reload_time = new_tree.reload_time;
688
689 mt_reload_flag = 0;
690
691 /* free old data */
692 if (bk_head!=NULL)
693 mt_free_node(bk_head, new_tree.type);
694
695 return 0;
696
697 error:
698 mt_dbf.free_result(db_con, db_res);
699 if (new_tree.head!=NULL)
700 mt_free_node(new_tree.head, new_tree.type);
701 return -1;
702 }
703
mt_load_db_trees()704 static int mt_load_db_trees()
705 {
706 db_key_t db_cols[3] = {&tname_column, &tprefix_column, &tvalue_column};
707 str tprefix, tvalue, tname;
708 db1_res_t* db_res = NULL;
709 int i, ret;
710 m_tree_t *new_head = NULL;
711 m_tree_t *new_tree = NULL;
712 m_tree_t *old_head = NULL;
713
714 if(db_con==NULL)
715 {
716 LM_ERR("no db connection\n");
717 return -1;
718 }
719
720 if (mt_dbf.use_table(db_con, &db_table) < 0)
721 {
722 LM_ERR("failed to use_table\n");
723 return -1;
724 }
725
726 if (DB_CAPABILITY(mt_dbf, DB_CAP_FETCH))
727 {
728 if(mt_dbf.query(db_con,0,0,0,db_cols,0,3,&tname_column,0) < 0)
729 {
730 LM_ERR("Error while querying db\n");
731 return -1;
732 }
733 if(mt_dbf.fetch_result(db_con, &db_res, mt_fetch_rows)<0)
734 {
735 LM_ERR("Error while fetching result\n");
736 if (db_res)
737 mt_dbf.free_result(db_con, db_res);
738 goto error;
739 } else {
740 if(RES_ROW_N(db_res)==0)
741 {
742 return 0;
743 }
744 }
745 } else {
746 if((ret=mt_dbf.query(db_con, NULL, NULL, NULL, db_cols,
747 0, 3, &tname_column, &db_res))!=0
748 || RES_ROW_N(db_res)<=0 )
749 {
750 mt_dbf.free_result(db_con, db_res);
751 if( ret==0)
752 {
753 return 0;
754 } else {
755 goto error;
756 }
757 }
758 }
759
760 do {
761 for(i=0; i<RES_ROW_N(db_res); i++)
762 {
763 /* check for NULL values ?!?! */
764 tname.s = (char*)(RES_ROWS(db_res)[i].values[0].val.string_val);
765 tprefix.s = (char*)(RES_ROWS(db_res)[i].values[1].val.string_val);
766 tvalue.s = (char*)(RES_ROWS(db_res)[i].values[2].val.string_val);
767
768 if(tprefix.s==NULL || tvalue.s==NULL || tname.s==NULL)
769 {
770 LM_ERR("Error - null fields in db\n");
771 continue;
772 }
773
774 tname.len = strlen(tname.s);
775 tprefix.len = strlen(tprefix.s);
776 tvalue.len = strlen(tvalue.s);
777
778 if(tname.len<=0 || tprefix.len<=0 || tvalue.len<=0)
779 {
780 LM_ERR("Error - bad values in db\n");
781 continue;
782 }
783 new_tree = mt_add_tree(&new_head, &tname, &db_table, NULL,
784 _mt_tree_type, 0);
785 if(new_tree==NULL)
786 {
787 LM_ERR("New tree cannot be initialized\n");
788 goto error;
789 }
790 if(mt_add_to_tree(new_tree, &tprefix, &tvalue)<0)
791 {
792 LM_ERR("Error adding info to tree\n");
793 goto error;
794 }
795 }
796 if (DB_CAPABILITY(mt_dbf, DB_CAP_FETCH)) {
797 if(mt_dbf.fetch_result(db_con, &db_res, mt_fetch_rows)<0) {
798 LM_ERR("Error while fetching!\n");
799 if (db_res)
800 mt_dbf.free_result(db_con, db_res);
801 goto error;
802 }
803 } else {
804 break;
805 }
806 } while(RES_ROW_N(db_res)>0);
807 mt_dbf.free_result(db_con, db_res);
808
809 /* block all readers */
810 lock_get( mt_lock );
811 mt_reload_flag = 1;
812 lock_release( mt_lock );
813
814 while (mt_tree_refcnt) {
815 sleep_us(10);
816 }
817
818 old_head = mt_swap_list_head(new_head);
819
820 mt_reload_flag = 0;
821 /* free old data */
822 if (old_head!=NULL)
823 mt_free_tree(old_head);
824
825 return 0;
826
827 error:
828 mt_dbf.free_result(db_con, db_res);
829 if (new_head!=NULL)
830 mt_free_tree(new_head);
831 return -1;
832 }
833
834
835 /* RPC commands */
rpc_mtree_summary(rpc_t * rpc,void * c)836 void rpc_mtree_summary(rpc_t* rpc, void* c)
837 {
838 str tname = {0, 0};
839 m_tree_t *pt;
840 void* th;
841 void* ih;
842 int found;
843
844 if(!mt_defined_trees())
845 {
846 rpc->fault(c, 500, "Empty tree list");
847 return;
848 }
849
850 /* read optional tree name */
851 if(rpc->scan(c, "*S", &tname)==0)
852 {
853 tname.s = NULL;
854 tname.len = 0;
855 }
856
857 pt = mt_get_first_tree();
858 if(pt==NULL)
859 {
860 rpc->fault(c, 404, "No tree");
861 return;
862 }
863
864 found = 0;
865 while(pt!=NULL)
866 {
867 if(tname.s==NULL
868 || (tname.s!=NULL && pt->tname.len>=tname.len
869 && strncmp(pt->tname.s, tname.s, tname.len)==0))
870 {
871 found = 1;
872 if (rpc->add(c, "{", &th) < 0)
873 {
874 rpc->fault(c, 500, "Internal error creating rpc");
875 return;
876 }
877 if(rpc->struct_add(th, "s{",
878 "table", pt->tname.s,
879 "item", &ih) < 0)
880 {
881 rpc->fault(c, 500, "Internal error creating rpc ih");
882 return;
883 }
884 if(rpc->struct_add(ih, "d", "ttype", pt->type) < 0 ) {
885 rpc->fault(c, 500, "Internal error adding type");
886 return;
887 }
888 if(rpc->struct_add(ih, "d", "memsize", pt->memsize) < 0 ) {
889 rpc->fault(c, 500, "Internal error adding memsize");
890 return;
891 }
892 if(rpc->struct_add(ih, "d", "nrnodes", pt->nrnodes) < 0 ) {
893 rpc->fault(c, 500, "Internal error adding nodes");
894 return;
895 }
896 if(rpc->struct_add(ih, "d", "nritems", pt->nritems) < 0 ) {
897 rpc->fault(c, 500, "Internal error adding items");
898 return;
899 }
900 if(rpc->struct_add(ih, "d", "reload_count",
901 (int)pt->reload_count) < 0 ) {
902 rpc->fault(c, 500, "Internal error adding items");
903 return;
904 }
905 if(rpc->struct_add(ih, "d", "reload_time",
906 (int)pt->reload_time) < 0 ) {
907 rpc->fault(c, 500, "Internal error adding items");
908 return;
909 }
910 }
911 pt = pt->next;
912 }
913
914 if(found==0)
915 {
916 rpc->fault(c, 404, "Tree not found");
917 return;
918 }
919
920 return;
921 }
922
923 static const char* rpc_mtree_summary_doc[2] = {
924 "Print summary of loaded mtree tables",
925 0
926 };
927
rpc_mtree_reload(rpc_t * rpc,void * c)928 void rpc_mtree_reload(rpc_t* rpc, void* c)
929 {
930 str tname = {0, 0};
931 m_tree_t *pt = NULL;
932 int treloaded = 0;
933
934 if(db_table.len>0)
935 {
936 /* re-loading all information from database */
937 if(mt_load_db_trees()!=0)
938 {
939 LM_ERR("cannot re-load mtrees from database\n");
940 goto error;
941 }
942 } else {
943 if(!mt_defined_trees())
944 {
945 LM_ERR("empty mtree list\n");
946 goto error;
947 }
948
949 /* read tree name */
950 if (rpc->scan(c, "S", &tname) != 1) {
951 tname.s = 0;
952 tname.len = 0;
953 } else {
954 if(*tname.s=='.') {
955 tname.s = 0;
956 tname.len = 0;
957 }
958 }
959
960 pt = mt_get_first_tree();
961
962 while(pt!=NULL)
963 {
964 if(tname.s==NULL
965 || (tname.s!=NULL && pt->tname.len>=tname.len
966 && strncmp(pt->tname.s, tname.s, tname.len)==0))
967 {
968 /* re-loading table from database */
969 if(mt_load_db(pt)!=0)
970 {
971 LM_ERR("cannot re-load mtree from database\n");
972 goto error;
973 }
974 treloaded = 1;
975 }
976 pt = pt->next;
977 }
978 if(treloaded == 0) {
979 rpc->fault(c, 500, "No Mtree Name Matching");
980 }
981 }
982
983 return;
984
985 error:
986 rpc->fault(c, 500, "Mtree Reload Failed");
987 }
988
989 static const char* rpc_mtree_reload_doc[2] = {
990 "Reload mtrees from database to memory",
991 0
992 };
993
rpc_mtree_match(rpc_t * rpc,void * ctx)994 void rpc_mtree_match(rpc_t* rpc, void* ctx)
995 {
996 str tname = STR_NULL;
997 str tomatch = STR_NULL;
998 int mode = -1;
999
1000 m_tree_t *tr;
1001
1002 if(!mt_defined_trees())
1003 {
1004 rpc->fault(ctx, 500, "Empty tree list.");
1005 return;
1006 }
1007
1008 if (rpc->scan(ctx, ".SSd", &tname, &tomatch, &mode) < 3) {
1009 rpc->fault(ctx, 500, "Invalid Parameters");
1010 return;
1011 }
1012
1013 if (mode !=0 && mode != 2) {
1014 rpc->fault(ctx, 500, "Invalid parameter 'mode'");
1015 return;
1016 }
1017
1018 again:
1019 lock_get( mt_lock );
1020 if (mt_reload_flag) {
1021 lock_release( mt_lock );
1022 sleep_us(5);
1023 goto again;
1024 }
1025 mt_tree_refcnt++;
1026 lock_release( mt_lock );
1027
1028 tr = mt_get_tree(&tname);
1029 if(tr==NULL)
1030 {
1031 /* no tree with such name*/
1032 rpc->fault(ctx, 404, "Not found tree");
1033 goto error;
1034 }
1035
1036 if(mt_rpc_match_prefix(rpc, ctx, tr, &tomatch, mode)<0)
1037 {
1038 LM_DBG("no prefix found in [%.*s] for [%.*s]\n",
1039 tname.len, tname.s,
1040 tomatch.len, tomatch.s);
1041 rpc->fault(ctx, 404, "Not found");
1042 }
1043
1044 error:
1045 lock_get( mt_lock );
1046 mt_tree_refcnt--;
1047 lock_release( mt_lock );
1048
1049 }
1050
1051 static const char* rpc_mtree_match_doc[6] = {
1052 "Match prefix value against mtree",
1053 "uses three required parameters",
1054 "tname - tree name",
1055 "prefix - prefix for matching",
1056 "mode - mode for matching (0 or 2)",
1057 0
1058 };
1059
1060
rpc_mtree_print_node(rpc_t * rpc,void * ctx,m_tree_t * tree,mt_node_t * pt,char * code,int len)1061 int rpc_mtree_print_node(rpc_t* rpc, void* ctx, m_tree_t *tree, mt_node_t *pt,
1062 char *code, int len)
1063 {
1064 int i;
1065 mt_is_t *tvalues;
1066 str val;
1067 void* th = NULL;
1068 void* ih = NULL;
1069
1070 if(pt==NULL || len>=MT_MAX_DEPTH)
1071 return 0;
1072
1073 for(i=0; i<MT_NODE_SIZE; i++)
1074 {
1075 code[len]=mt_char_list.s[i];
1076 tvalues = pt[i].tvalues;
1077 if (tvalues != NULL)
1078 {
1079 /* add structure node */
1080 if (rpc->add(ctx, "{", &th) < 0)
1081 {
1082 rpc->fault(ctx, 500, "Internal error - node structure");
1083 return -1;
1084 }
1085
1086 val.s = code;
1087 val.len = len+1;
1088 if(rpc->struct_add(th, "SS[",
1089 "tname", &tree->tname,
1090 "tprefix", &val,
1091 "tvalue", &ih)<0)
1092 {
1093 rpc->fault(ctx, 500, "Internal error - attribute fields");
1094 return -1;
1095 }
1096
1097 while (tvalues != NULL) {
1098 if (tree->type == MT_TREE_IVAL) {
1099 if(rpc->array_add(ih, "u",
1100 (unsigned long)tvalues->tvalue.n)<0) {
1101 rpc->fault(ctx, 500, "Internal error - int val");
1102 return -1;
1103 }
1104 } else {
1105 if(rpc->array_add(ih, "S", &tvalues->tvalue.s)<0) {
1106 rpc->fault(ctx, 500, "Internal error - str val");
1107 return -1;
1108 }
1109 }
1110 tvalues = tvalues->next;
1111 }
1112 }
1113 if(rpc_mtree_print_node(rpc, ctx, tree, pt[i].child, code, len+1)<0)
1114 goto error;
1115 }
1116 return 0;
1117 error:
1118 return -1;
1119 }
1120
1121 /**
1122 * "mtree.list" syntax :
1123 * tname
1124 *
1125 * - '.' (dot) means NULL value and will match anything
1126 */
rpc_mtree_list(rpc_t * rpc,void * ctx)1127 void rpc_mtree_list(rpc_t* rpc, void* ctx)
1128 {
1129 str tname = {0, 0};
1130 m_tree_t *pt;
1131 static char code_buf[MT_MAX_DEPTH+1];
1132 int len;
1133
1134 if(!mt_defined_trees())
1135 {
1136 rpc->fault(ctx, 500, "Empty tree list.");
1137 return;
1138 }
1139
1140 if(rpc->scan(ctx, "*.S", &tname)!=1) {
1141 tname.s = NULL;
1142 tname.len = 0;
1143 }
1144
1145 pt = mt_get_first_tree();
1146
1147 while(pt!=NULL)
1148 {
1149 if(tname.s==NULL ||
1150 (tname.s!=NULL && pt->tname.len>=tname.len &&
1151 strncmp(pt->tname.s, tname.s, tname.len)==0))
1152 {
1153 len = 0;
1154 code_buf[0] = '\0';
1155 if(rpc_mtree_print_node(rpc, ctx, pt, pt->head, code_buf, len)<0) {
1156 goto error;
1157 }
1158 }
1159 pt = pt->next;
1160 }
1161
1162 return;
1163
1164 error:
1165 LM_ERR("failed to build rpc response\n");
1166 return;
1167 }
1168
1169 static const char* rpc_mtree_list_doc[6] = {
1170 "List the content of one or all trees",
1171 "Parameters:",
1172 "tname - tree name (optional)",
1173 0
1174 };
1175
1176
1177 rpc_export_t mtree_rpc[] = {
1178 {"mtree.summary", rpc_mtree_summary, rpc_mtree_summary_doc, RET_ARRAY},
1179 {"mtree.reload", rpc_mtree_reload, rpc_mtree_reload_doc, 0},
1180 {"mtree.match", rpc_mtree_match, rpc_mtree_match_doc, 0},
1181 {"mtree.list", rpc_mtree_list, rpc_mtree_list_doc, RET_ARRAY},
1182 {0, 0, 0, 0}
1183 };
1184
mtree_init_rpc(void)1185 static int mtree_init_rpc(void)
1186 {
1187 if (rpc_register_array(mtree_rpc) != 0)
1188 {
1189 LM_ERR("failed to register RPC commands\n");
1190 return -1;
1191 }
1192 return 0;
1193 }
1194
1195
1196 /**
1197 *
1198 */
1199 /* clang-format off */
1200 static sr_kemi_t sr_kemi_mtree_exports[] = {
1201 { str_init("mtree"), str_init("mt_match"),
1202 SR_KEMIP_INT, mt_match,
1203 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_INT,
1204 SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1205 },
1206
1207 { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
1208 };
1209 /* clang-format on */
1210
1211 /**
1212 * load mtree module API
1213 */
bind_mtree(mtree_api_t * api)1214 static int bind_mtree(mtree_api_t* api)
1215 {
1216 if (!api) {
1217 LM_ERR("Invalid parameter value\n");
1218 return -1;
1219 }
1220 api->mt_match = mt_match;
1221
1222 return 0;
1223 }
1224
1225
1226 /**
1227 *
1228 */
mod_register(char * path,int * dlflags,void * p1,void * p2)1229 int mod_register(char *path, int *dlflags, void *p1, void *p2)
1230 {
1231 sr_kemi_modules_add(sr_kemi_mtree_exports);
1232 return 0;
1233 }
1234