matrix.c

00001 /*
00002  * $Id: matrix.c 4978 2008-09-23 14:25:02Z henningw $
00003  *
00004  * Copyright (C) 2007 1&1 Internet AG
00005  *
00006  * This file is part of Kamailio, a free SIP server.
00007  *
00008  * Kamailio is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version
00012  *
00013  * Kamailio is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License 
00019  * along with this program; if not, write to the Free Software 
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  */
00022 
00023 #include <string.h>
00024 
00025 #include "../../mem/shm_mem.h"
00026 #include "../../sr_module.h"
00027 #include "../../lib/kmi/mi.h"
00028 #include "../../mem/mem.h"
00029 #include "../../usr_avp.h"
00030 #include "../../locking.h"
00031 #include "../../error.h"
00032 #include "../../ut.h"
00033 #include "../../mod_fix.h"
00034 
00035 #include "db_matrix.h"
00036 
00037 MODULE_VERSION
00038 
00039 
00040 
00041 
00042 #define MAXCOLS 1000
00043 
00044 
00045 
00046 
00047 str matrix_db_url = str_init(DEFAULT_RODB_URL);
00048 
00049 
00050 
00051 
00056 struct multiparam_t {
00057         enum {
00058                 MP_INT,
00059                 MP_STR,
00060                 MP_AVP,
00061                 MP_PVE,
00062         } type;
00063         union {
00064                 int n;
00065                 str s;
00066                 struct {
00067                         unsigned short flags;
00068                         int_str name;
00069                 } a;
00070                 pv_elem_t *p;
00071         } u;
00072 };
00073 
00074 
00075 
00076 
00077 /* ---- fixup functions: */
00078 static int matrix_fixup(void** param, int param_no);
00079 
00080 /* ---- exported commands: */
00081 static int lookup_matrix(struct sip_msg *msg, struct multiparam_t *_first, struct multiparam_t *_second, struct multiparam_t *_dstavp);
00082 
00083 /* ---- module init functions: */
00084 static int mod_init(void);
00085 static int child_init(int rank);
00086 static int mi_child_init(void);
00087 static void mod_destroy(void);
00088 
00089 /* --- fifo functions */
00090 struct mi_root * mi_reload_matrix(struct mi_root* cmd, void* param);  /* usage: kamctl fifo reload_matrix */
00091 
00092 
00093 
00094 
00095 static cmd_export_t cmds[]={
00096         { "matrix", (cmd_function)lookup_matrix, 3, matrix_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
00097         { 0, 0, 0, 0, 0, 0}
00098 };
00099 
00100 
00101 
00102 
00103 static param_export_t params[] = {
00104         matrix_DB_URL
00105         matrix_DB_TABLE
00106         matrix_DB_COLS
00107         { 0, 0, 0}
00108 };
00109 
00110 
00111 
00112 
00113 /* Exported MI functions */
00114 static mi_export_t mi_cmds[] = {
00115         { "reload_matrix", mi_reload_matrix, MI_NO_INPUT_FLAG, 0, mi_child_init },
00116         { 0, 0, 0, 0, 0}
00117 };
00118 
00119 
00120 
00121 
00122 struct module_exports exports= {
00123         "matrix",
00124         DEFAULT_DLFLAGS,
00125         cmds,
00126         params,
00127         0,
00128         mi_cmds,
00129         0,
00130         0,
00131         mod_init,
00132         0,
00133         mod_destroy,
00134         child_init
00135 };
00136 
00137 
00138 
00139 
00140 struct first_t {
00141   struct first_t *next;
00142         int id;
00143         short int second_list[MAXCOLS+1];
00144 };
00145 
00146 
00147 
00148 
00149 struct matrix_t {
00150   struct first_t *head;
00151 };
00152 
00153 
00154 
00155 
00156 static gen_lock_t *lock = NULL;
00157 static struct matrix_t *matrix = NULL;
00158 
00159 
00160 
00161 
00170 static int mp_fixup(void ** param) {
00171         pv_spec_t avp_spec;
00172         struct multiparam_t *mp;
00173         str s;
00174 
00175         mp = (struct multiparam_t *)pkg_malloc(sizeof(struct multiparam_t));
00176         if (mp == NULL) {
00177                 LM_ERR("out of pkg memory\n");
00178                 return -1;
00179         }
00180         memset(mp, 0, sizeof(struct multiparam_t));
00181         
00182         s.s = (char *)(*param);
00183         s.len = strlen(s.s);
00184 
00185         if (s.s[0]!='$') {
00186                 /* This is string */
00187                 mp->type=MP_STR;
00188                 mp->u.s=s;
00189         }
00190         else {
00191                 /* This is a pseudo-variable */
00192                 if (pv_parse_spec(&s, &avp_spec)==0) {
00193                         LM_ERR("pv_parse_spec failed for '%s'\n", (char *)(*param));
00194                         pkg_free(mp);
00195                         return -1;
00196                 }
00197                 if (avp_spec.type==PVT_AVP) {
00198                         /* This is an AVP - could be an id or name */
00199                         mp->type=MP_AVP;
00200                         if(pv_get_avp_name(0, &(avp_spec.pvp), &(mp->u.a.name), &(mp->u.a.flags))!=0) {
00201                                 LM_ERR("Invalid AVP definition <%s>\n", (char *)(*param));
00202                                 pkg_free(mp);
00203                                 return -1;
00204                         }
00205                 } else {
00206                         mp->type=MP_PVE;
00207                         if(pv_parse_format(&s, &(mp->u.p))<0) {
00208                                 LM_ERR("pv_parse_format failed for '%s'\n", (char *)(*param));
00209                                 pkg_free(mp);
00210                                 return -1;
00211                         }
00212                 }
00213         }
00214         *param = (void*)mp;
00215 
00216         return 0;
00217 }
00218 
00219 
00220 
00221 
00229 static int avp_name_fixup(void ** param) {
00230         pv_spec_t avp_spec;
00231         struct multiparam_t *mp;
00232         str s;
00233 
00234         s.s = (char *)(*param);
00235         s.len = strlen(s.s);
00236         if (s.len <= 0) return -1;
00237         if (pv_parse_spec(&s, &avp_spec)==0 || avp_spec.type!=PVT_AVP) {
00238                 LM_ERR("Malformed or non AVP definition <%s>\n", (char *)(*param));
00239                 return -1;
00240         }
00241         
00242         mp = (struct multiparam_t *)pkg_malloc(sizeof(struct multiparam_t));
00243         if (mp == NULL) {
00244                 LM_ERR("out of pkg memory\n");
00245                 return -1;
00246         }
00247         memset(mp, 0, sizeof(struct multiparam_t));
00248         
00249         mp->type=MP_AVP;
00250         if(pv_get_avp_name(0, &(avp_spec.pvp), &(mp->u.a.name), &(mp->u.a.flags))!=0) {
00251                 LM_ERR("Invalid AVP definition <%s>\n", (char *)(*param));
00252                 pkg_free(mp);
00253                 return -1;
00254         }
00255 
00256         *param = (void*)mp;
00257         
00258         return 0;
00259 }
00260 
00261 
00262 
00263 
00264 static int matrix_fixup(void** param, int param_no)
00265 {
00266         if (param_no == 1) {
00267                 /* source id */
00268                 if (mp_fixup(param) < 0) {
00269                         LM_ERR("cannot fixup parameter %d\n", param_no);
00270                         return -1;
00271                 }
00272         }
00273         else if (param_no == 2) {
00274                 /* destination id */
00275                 if (mp_fixup(param) < 0) {
00276                         LM_ERR("cannot fixup parameter %d\n", param_no);
00277                         return -1;
00278                 }
00279         }
00280         else if (param_no == 3) {
00281                 /* destination avp name */
00282                 if (avp_name_fixup(param) < 0) {
00283                         LM_ERR("cannot fixup parameter %d\n", param_no);
00284                         return -1;
00285                 }
00286         }
00287 
00288         return 0;
00289 }
00290 
00291 
00292 
00293 
00294 static void matrix_clear(void)
00295 {
00296         struct first_t *srcitem;
00297         if (matrix) {
00298                 while (matrix->head) {
00299                         srcitem = matrix->head;
00300                         matrix->head = srcitem->next;
00301                         shm_free(srcitem);
00302                 }
00303         }
00304 }
00305 
00306 
00307 
00308 
00309 static int matrix_insert(int first, short int second, int res)
00310 {
00311         struct first_t *srcitem;
00312         int i;
00313 
00314         if ((second<0) || (second>MAXCOLS)) {
00315                 LM_ERR("invalid second value %d\n", second);
00316                 return -1;
00317         }
00318         LM_DBG("searching for %d, %d\n", first, second);
00319         if (matrix) {
00320                 srcitem = matrix->head;
00321                 while (srcitem) {
00322                         if (srcitem->id == first) {
00323                                 srcitem->second_list[second] = res;
00324                                 LM_DBG("inserted (%d, %d, %d)", first, second, res);
00325                                 return 0;
00326                         }
00327                         srcitem = srcitem->next;
00328                 }
00329                 /* not found */
00330                 srcitem = shm_malloc(sizeof(struct first_t));
00331                 if (srcitem == NULL) {
00332                         LM_ERR("out of shared memory.");
00333                         return -1;
00334                 }
00335                 memset(srcitem, 0, sizeof(struct first_t));
00336 
00337                 /* Mark all new cells as empty */
00338                 for (i=0; i<=MAXCOLS; i++) srcitem->second_list[i] = -1;
00339 
00340                 srcitem->next = matrix->head;
00341                 srcitem->id = first;
00342                 srcitem->second_list[second] = res;
00343                 matrix->head = srcitem;
00344         }
00345 
00346         LM_DBG("inserted new row for (%d, %d, %d)", first, second, res);
00347         return 0;
00348 }
00349 
00350 
00351 
00352 
00353 /* Returns the res id if the matrix contains an entry for the given indices, -1 otherwise.
00354  */
00355 static int internal_lookup(int first, short int second)
00356 {
00357         struct first_t *item;
00358 
00359         if ((second<0) || (second>MAXCOLS)) {
00360                 LM_ERR("invalid second value %d\n", second);
00361                 return -1;
00362         }
00363 
00364         if (matrix) {
00365                 item = matrix->head;
00366                 while (item) {
00367                         if (item->id == first) {
00368                                 return item->second_list[second];
00369                         }
00370                         item = item->next;
00371                 }
00372         }
00373 
00374         return -1;
00375 }
00376 
00377 
00378 
00379 
00380 static int lookup_matrix(struct sip_msg *msg, struct multiparam_t *_srctree, struct multiparam_t *_second, struct multiparam_t *_dstavp)
00381 {
00382         int first;
00383         int second;
00384         struct usr_avp *avp;
00385         int_str avp_val;
00386 
00387         switch (_srctree->type) {
00388         case MP_INT:
00389                 first = _srctree->u.n;
00390                 break;
00391         case MP_AVP:
00392                 avp = search_first_avp(_srctree->u.a.flags, _srctree->u.a.name, &avp_val, 0);
00393                 if (!avp) {
00394                         LM_ERR("cannot find srctree AVP\n");
00395                         return -1;
00396                 }
00397                 if ((avp->flags&AVP_VAL_STR)) {
00398                         LM_ERR("cannot process string value in srctree AVP\n");
00399                         return -1;
00400                 }
00401                 else first = avp_val.n;
00402                 break;
00403         default:
00404                 LM_ERR("invalid srctree type\n");
00405                 return -1;
00406         }
00407 
00408         switch (_second->type) {
00409         case MP_INT:
00410                 second = _second->u.n;
00411                 break;
00412         case MP_AVP:
00413                 avp = search_first_avp(_second->u.a.flags, _second->u.a.name, &avp_val, 0);
00414                 if (!avp) {
00415                         LM_ERR("cannot find second_value AVP\n");
00416                         return -1;
00417                 }
00418                 if ((avp->flags&AVP_VAL_STR)) {
00419                         LM_ERR("cannot process string value in second_value AVP\n");
00420                         return -1;
00421                 }
00422                 else second = avp_val.n;
00423                 break;
00424         default:
00425                 LM_ERR("invalid second_value type\n");
00426                 return -1;
00427         }
00428         
00429 
00430         /* critical section start: avoids dirty reads when updating d-tree */
00431         lock_get(lock);
00432 
00433         avp_val.n=internal_lookup(first, second);
00434 
00435         /* critical section end */
00436         lock_release(lock);
00437 
00438         if (avp_val.n<0) {
00439                 LM_INFO("lookup failed\n");
00440                 return -1;
00441         }
00442 
00443         /* set avp ! */
00444         if (add_avp(_dstavp->u.a.flags, _dstavp->u.a.name, avp_val)<0) {
00445                 LM_ERR("add AVP failed\n");
00446                 return -1;
00447         }
00448         LM_INFO("result from lookup: %d\n", avp_val.n);
00449         return 1;
00450 }
00451 
00452 
00453 
00454 
00459 static int db_reload_matrix(void)
00460 {
00461         db_key_t columns[3] = { &matrix_first_col, &matrix_second_col, &matrix_res_col };
00462         db1_res_t *res;
00463         int i;
00464         int n = 0;
00465         
00466         if (matrix_dbf.use_table(matrix_dbh, &matrix_table) < 0) {
00467                 LM_ERR("cannot use table '%.*s'.\n", matrix_table.len, matrix_table.s);
00468                 return -1;
00469         }
00470         if (matrix_dbf.query(matrix_dbh, NULL, NULL, NULL, columns, 0, 3, NULL, &res) < 0) {
00471                 LM_ERR("error while executing query.\n");
00472                 return -1;
00473         }
00474 
00475         /* critical section start: avoids dirty reads when updating d-tree */
00476         lock_get(lock);
00477 
00478         matrix_clear();
00479 
00480         if (RES_COL_N(res) > 2) {
00481                 for(i = 0; i < RES_ROW_N(res); i++) {
00482                         if ((!RES_ROWS(res)[i].values[0].nul) && (!RES_ROWS(res)[i].values[1].nul)) {
00483                                 if ((RES_ROWS(res)[i].values[0].type == DB1_INT) &&
00484                                                 (RES_ROWS(res)[i].values[1].type == DB1_INT) &&
00485                                                 (RES_ROWS(res)[i].values[2].type == DB1_INT)) {
00486                                         matrix_insert(RES_ROWS(res)[i].values[0].val.int_val, RES_ROWS(res)[i].values[1].val.int_val, RES_ROWS(res)[i].values[2].val.int_val);
00487                                         n++;
00488                                 }
00489                                 else {
00490                                         LM_ERR("got invalid result type from query.\n");
00491                                 }
00492                         }
00493                 }
00494         }
00495 
00496         /* critical section end */
00497         lock_release(lock);
00498 
00499         matrix_dbf.free_result(matrix_dbh, res);
00500 
00501         LM_INFO("loaded %d matrix entries.", n);
00502         return n;
00503 }
00504 
00505 
00506 
00507 
00508 static int init_shmlock(void)
00509 {
00510         lock = lock_alloc();
00511         if (!lock) {
00512                 LM_CRIT("cannot allocate memory for lock.\n");
00513                 return -1;
00514         }
00515         if (lock_init(lock) == 0) {
00516                 LM_CRIT("cannot initialize lock.\n");
00517                 return -1;
00518         }
00519 
00520         return 0;
00521 }
00522 
00523 
00524 
00525 
00526 static void destroy_shmlock(void)
00527 {
00528         if (lock) {
00529                 lock_destroy(lock);
00530                 lock_dealloc((void *)lock);
00531                 lock = NULL;
00532         }
00533 }
00534 
00535 
00536 
00537 
00538 struct mi_root * mi_reload_matrix(struct mi_root* cmd, void* param)
00539 {
00540         struct mi_root * tmp = NULL;
00541         if(db_reload_matrix() >= 0) {
00542                 tmp = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
00543         } else {
00544                 tmp = init_mi_tree( 500, "cannot reload matrix", 24);
00545         }
00546 
00547         return tmp;
00548 }
00549 
00550 
00551 
00552 
00553 static int init_matrix(void)
00554 {
00555         matrix = shm_malloc(sizeof(struct matrix_t));
00556         if (!matrix) {
00557                 LM_ERR("out of shared memory\n");
00558                 return -1;
00559         }
00560         memset(matrix, 0, sizeof(struct matrix_t));
00561         if (db_reload_matrix() < 0) {
00562                 LM_ERR("cannot populate matrix\n");
00563                 return -1;
00564         }
00565 
00566         return 0;
00567 }
00568 
00569 
00570 
00571 
00572 static void destroy_matrix(void)
00573 {
00574         if (matrix) {
00575                 matrix_clear();
00576                 shm_free(matrix);
00577         }
00578 }
00579 
00580 
00581 
00582 
00583 static int mod_init(void)
00584 {
00585         matrix_db_vars();
00586 
00587         if (init_shmlock() != 0) return -1;
00588         if (matrix_db_init() != 0) return -1;
00589         if (matrix_db_open() != 0) return -1;
00590         if (init_matrix() != 0) return -1;
00591         matrix_db_close();
00592         return 0;
00593 }
00594 
00595 
00596 
00597 
00598 static int child_init(int rank)
00599 {
00600         if(rank==PROC_INIT || rank==PROC_TCP_MAIN)
00601                 return 0;
00602         if (matrix_db_open() != 0) return -1;
00603         return 0;
00604 }
00605 
00606 
00607 
00608 
00609 static int mi_child_init(void)
00610 {
00611         if (matrix_db_open() != 0) return -1;
00612         return 0;
00613 }
00614 
00615 
00616 
00617 
00618 static void mod_destroy(void)
00619 {
00620         destroy_matrix();
00621         destroy_shmlock();
00622         matrix_db_close();
00623 }