htable.c

00001 
00023 #include <stdio.h>
00024 #include <string.h>
00025 #include <stdlib.h>
00026 #include <sys/types.h>
00027 #include <sys/ipc.h>
00028 #include <unistd.h>
00029 #include <fcntl.h>
00030 
00031 #include "../../sr_module.h"
00032 #include "../../timer.h"
00033 #include "../../route.h"
00034 #include "../../dprint.h"
00035 #include "../../ut.h"
00036 #include "../../rpc.h"
00037 #include "../../rpc_lookup.h"
00038 #include "../../lib/kmi/mi.h"
00039 #include "../../lib/kcore/faked_msg.h"
00040 
00041 #include "../../pvar.h"
00042 #include "ht_api.h"
00043 #include "ht_db.h"
00044 #include "ht_var.h"
00045 #include "api.h"
00046 
00047 
00048 MODULE_VERSION
00049 
00050 int  ht_timer_interval = 20;
00051 int  ht_db_expires_flag = 0;
00052 
00053 static int htable_init_rpc(void);
00054 
00056 static int ht_print(struct sip_msg*, char*, char*);
00057 static int mod_init(void);
00058 static int child_init(int rank);
00059 static void destroy(void);
00060 
00061 static int fixup_ht_rm(void** param, int param_no);
00062 static int ht_rm_name_re(struct sip_msg* msg, char* key, char* foo);
00063 static int ht_rm_value_re(struct sip_msg* msg, char* key, char* foo);
00064 
00065 int ht_param(modparam_t type, void* val);
00066 
00067 static struct mi_root* ht_mi_reload(struct mi_root* cmd_tree, void* param);
00068 static struct mi_root* ht_mi_dump(struct mi_root* cmd_tree, void* param);
00069 static struct mi_root* ht_mi_delete(struct mi_root* cmd_tree, void* param);
00070 
00071 static pv_export_t mod_pvs[] = {
00072         { {"sht", sizeof("sht")-1}, PVT_OTHER, pv_get_ht_cell, pv_set_ht_cell,
00073                 pv_parse_ht_name, 0, 0, 0 },
00074         { {"shtex", sizeof("shtex")-1}, PVT_OTHER, pv_get_ht_cell_expire,
00075                 pv_set_ht_cell_expire,
00076                 pv_parse_ht_name, 0, 0, 0 },
00077         { {"shtcn", sizeof("shtcn")-1}, PVT_OTHER, pv_get_ht_cn, 0,
00078                 pv_parse_ht_name, 0, 0, 0 },
00079         { {"shtcv", sizeof("shtcv")-1}, PVT_OTHER, pv_get_ht_cv, 0,
00080                 pv_parse_ht_name, 0, 0, 0 },
00081         { {"shtinc", sizeof("shtinc")-1}, PVT_OTHER, pv_get_ht_inc, 0,
00082                 pv_parse_ht_name, 0, 0, 0 },
00083         { {"shtdec", sizeof("shtdec")-1}, PVT_OTHER, pv_get_ht_dec, 0,
00084                 pv_parse_ht_name, 0, 0, 0 },
00085         { {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
00086 };
00087 
00088 static mi_export_t mi_cmds[] = {
00089         { "sht_reload",     ht_mi_reload,  0,  0,  0},
00090         { "sht_dump",       ht_mi_dump,    0,  0,  0},
00091         { "sht_delete",     ht_mi_delete,  0,  0,  0},
00092         { 0, 0, 0, 0, 0}
00093 };
00094 
00095 
00096 static cmd_export_t cmds[]={
00097         {"sht_print",       (cmd_function)ht_print,        0, 0, 0,
00098                 ANY_ROUTE},
00099         {"sht_rm_name_re",  (cmd_function)ht_rm_name_re,   1, fixup_ht_rm, 0,
00100                 ANY_ROUTE},
00101         {"sht_rm_value_re", (cmd_function)ht_rm_value_re,  1, fixup_ht_rm, 0,
00102                 ANY_ROUTE},
00103         {"bind_htable",     (cmd_function)bind_htable,     0, 0, 0,
00104                 ANY_ROUTE},
00105         {0,0,0,0,0,0}
00106 };
00107 
00108 static param_export_t params[]={
00109         {"htable",             STR_PARAM|USE_FUNC_PARAM, (void*)ht_param},
00110         {"db_url",             STR_PARAM, &ht_db_url.s},
00111         {"key_name_column",    STR_PARAM, &ht_db_name_column.s},
00112         {"key_type_column",    STR_PARAM, &ht_db_ktype_column.s},
00113         {"value_type_column",  STR_PARAM, &ht_db_vtype_column.s},
00114         {"key_value_column",   STR_PARAM, &ht_db_value_column.s},
00115         {"expires_column",     STR_PARAM, &ht_db_expires_column.s},
00116         {"array_size_suffix",  STR_PARAM, &ht_array_size_suffix.s},
00117         {"fetch_rows",         INT_PARAM, &ht_fetch_rows},
00118         {"timer_interval",     INT_PARAM, &ht_timer_interval},
00119         {"db_expires",         INT_PARAM, &ht_db_expires_flag},
00120         {0,0,0}
00121 };
00122 
00123 
00125 struct module_exports exports= {
00126         "htable",
00127         DEFAULT_DLFLAGS, /* dlopen flags */
00128         cmds,
00129         params,
00130         0,          /* exported statistics */
00131         mi_cmds,    /* exported MI functions */
00132         mod_pvs,    /* exported pseudo-variables */
00133         0,          /* extra processes */
00134         mod_init,   /* module initialization function */
00135         0,
00136         (destroy_function) destroy,
00137         child_init  /* per-child init function */
00138 };
00139 
00143 static int mod_init(void)
00144 {
00145         if(register_mi_mod(exports.name, mi_cmds)!=0)
00146         {
00147                 LM_ERR("failed to register MI commands\n");
00148                 return -1;
00149         }
00150         if(htable_init_rpc()!=0)
00151         {
00152                 LM_ERR("failed to register RPC commands\n");
00153                 return -1;
00154         }
00155 
00156         if(ht_init_tables()!=0)
00157                 return -1;
00158         ht_db_init_params();
00159 
00160         if(ht_db_url.len>0)
00161         {
00162                 if(ht_db_init_con()!=0)
00163                         return -1;
00164                 if(ht_db_open_con()!=0)
00165                         return -1;
00166                 if(ht_db_load_tables()!=0)
00167                 {
00168                         ht_db_close_con();
00169                         return -1;
00170                 }
00171                 ht_db_close_con();
00172         }
00173         if(ht_has_autoexpire())
00174         {
00175                 LM_DBG("starting auto-expire timer\n");
00176                 if(ht_timer_interval<=0)
00177                         ht_timer_interval = 20;
00178                 if(register_timer(ht_timer, 0, ht_timer_interval)<0)
00179                 {
00180                         LM_ERR("failed to register timer function\n");
00181                         return -1;
00182                 }
00183         }
00184         return 0;
00185 }
00186 
00187 
00188 static int child_init(int rank)
00189 {
00190         struct sip_msg *fmsg;
00191         struct run_act_ctx ctx;
00192         int rtb, rt;
00193 
00194         LM_DBG("rank is (%d)\n", rank);
00195         if (rank!=PROC_INIT)
00196                 return 0;
00197         
00198         rt = route_get(&event_rt, "htable:mod-init");
00199         if(rt>=0 && event_rt.rlist[rt]!=NULL) {
00200                 LM_DBG("executing event_route[htable:mod-init] (%d)\n", rt);
00201                 if(faked_msg_init()<0)
00202                         return -1;
00203                 fmsg = faked_msg_next();
00204                 rtb = get_route_type();
00205                 set_route_type(REQUEST_ROUTE);
00206                 init_run_actions_ctx(&ctx);
00207                 run_top_route(event_rt.rlist[rt], fmsg, &ctx);
00208                 if(ctx.run_flags&DROP_R_F)
00209                 {
00210                         LM_ERR("exit due to 'drop' in event route\n");
00211                         return -1;
00212                 }
00213                 set_route_type(rtb);
00214         }
00215 
00216         return 0;
00217 }
00218 
00222 static void destroy(void)
00223 {
00224         /* sync back to db */
00225         if(ht_db_url.len>0)
00226         {
00227                 if(ht_db_init_con()==0)
00228                 {
00229                         if(ht_db_open_con()==0)
00230                         {
00231                                 ht_db_sync_tables();
00232                                 ht_db_close_con();
00233                         }
00234                 }
00235         }
00236         ht_destroy();
00237 }
00238 
00242 static int ht_print(struct sip_msg *msg, char *s1, char *s2)
00243 {
00244         ht_dbg();
00245         return 1;
00246 }
00247 
00248 static int fixup_ht_rm(void** param, int param_no)
00249 {
00250         pv_spec_t *sp;
00251         str s;
00252 
00253         sp = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t));
00254         if(param_no != 1)
00255         {
00256                 LM_ERR("invalid parameter number %d\n", param_no);
00257                 return -1;
00258         }
00259         if (sp == 0)
00260         {
00261                 LM_ERR("no pkg memory left\n");
00262                 return -1;
00263         }
00264         memset(sp, 0, sizeof(pv_spec_t));
00265         s.s = (char*)*param; s.len = strlen(s.s);
00266         if(pv_parse_ht_name(sp, &s)<0)
00267         {
00268                 pkg_free(sp);
00269                 LM_ERR("invalid parameter %d\n", param_no);
00270                 return -1;
00271         }
00272         *param = (void*)sp;
00273         return 0;
00274 }
00275 
00276 static int ht_rm_name_re(struct sip_msg* msg, char* key, char* foo)
00277 {
00278         ht_pv_t *hpv;
00279         str sre;
00280         pv_spec_t *sp;
00281         sp = (pv_spec_t*)key;
00282 
00283         hpv = (ht_pv_t*)sp->pvp.pvn.u.dname;
00284 
00285         if(hpv->ht==NULL)
00286         {
00287                 hpv->ht = ht_get_table(&hpv->htname);
00288                 if(hpv->ht==NULL)
00289                         return 1;
00290         }
00291         if(pv_printf_s(msg, hpv->pve, &sre)!=0)
00292         {
00293                 LM_ERR("cannot get $ht expression\n");
00294                 return -1;
00295         }
00296         if(ht_rm_cell_re(&sre, hpv->ht, 0)<0)
00297                 return -1;
00298         return 1;
00299 }
00300 
00301 static int ht_rm_value_re(struct sip_msg* msg, char* key, char* foo)
00302 {
00303         ht_pv_t *hpv;
00304         str sre;
00305         pv_spec_t *sp;
00306         sp = (pv_spec_t*)key;
00307 
00308         hpv = (ht_pv_t*)sp->pvp.pvn.u.dname;
00309 
00310         if(hpv->ht==NULL)
00311         {
00312                 hpv->ht = ht_get_table(&hpv->htname);
00313                 if(hpv->ht==NULL)
00314                         return 1;
00315         }
00316         if(pv_printf_s(msg, hpv->pve, &sre)!=0)
00317         {
00318                 LM_ERR("cannot get $ht expression\n");
00319                 return -1;
00320         }
00321 
00322         if(ht_rm_cell_re(&sre, hpv->ht, 1)<0)
00323                 return -1;
00324         return 1;
00325 }
00326 
00327 
00328 int ht_param(modparam_t type, void *val)
00329 {
00330         if(val==NULL)
00331                 goto error;
00332 
00333         return ht_table_spec((char*)val);
00334 error:
00335         return -1;
00336 
00337 }
00338 
00339 #define MI_ERR_RELOAD                   "ERROR Reloading data"
00340 #define MI_ERR_RELOAD_LEN               (sizeof(MI_ERR_RELOAD)-1)
00341 static struct mi_root* ht_mi_reload(struct mi_root* cmd_tree, void* param)
00342 {
00343         struct mi_node* node;
00344         str htname;
00345         ht_t *ht;
00346         ht_t nht;
00347         ht_cell_t *first;
00348         ht_cell_t *it;
00349         int i;
00350 
00351         if(ht_db_url.len<=0)
00352                 return init_mi_tree(500, MI_ERR_RELOAD, MI_ERR_RELOAD_LEN);
00353         
00354         if(ht_db_init_con()!=0)
00355                 return init_mi_tree(500, MI_ERR_RELOAD, MI_ERR_RELOAD_LEN);
00356         if(ht_db_open_con()!=0)
00357                 return init_mi_tree(500, MI_ERR_RELOAD, MI_ERR_RELOAD_LEN);
00358 
00359         node = cmd_tree->node.kids;
00360         if(node == NULL)
00361         {
00362                 ht_db_close_con();
00363                 return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
00364         }
00365         htname = node->value;
00366         if(htname.len<=0 || htname.s==NULL)
00367         {
00368                 LM_ERR("bad hash table name\n");
00369                 ht_db_close_con();
00370                 return init_mi_tree( 500, "bad hash table name", 19);
00371         }
00372         ht = ht_get_table(&htname);
00373         if(ht==NULL || ht->dbtable.len<=0)
00374         {
00375                 LM_ERR("bad hash table name\n");
00376                 ht_db_close_con();
00377                 return init_mi_tree( 500, "no such hash table", 18);
00378         }
00379         memcpy(&nht, ht, sizeof(ht_t));
00380         nht.entries = (ht_entry_t*)shm_malloc(nht.htsize*sizeof(ht_entry_t));
00381         if(nht.entries == NULL)
00382         {
00383                 ht_db_close_con();
00384                 return init_mi_tree(500, MI_ERR_RELOAD, MI_ERR_RELOAD_LEN);
00385         }
00386         memset(nht.entries, 0, nht.htsize*sizeof(ht_entry_t));
00387 
00388         if(ht_db_load_table(&nht, &ht->dbtable, 0)<0)
00389         {
00390                 ht_db_close_con();
00391                 return init_mi_tree(500, MI_ERR_RELOAD, MI_ERR_RELOAD_LEN);
00392         }
00393 
00394         /* replace old entries */
00395         for(i=0; i<nht.htsize; i++)
00396         {
00397                 lock_get(&ht->entries[i].lock);
00398                 first = ht->entries[i].first;
00399                 ht->entries[i].first = nht.entries[i].first;
00400                 ht->entries[i].esize = nht.entries[i].esize;
00401                 lock_release(&ht->entries[i].lock);
00402                 nht.entries[i].first = first;
00403         }
00404         /* free old entries */
00405         for(i=0; i<nht.htsize; i++)
00406         {
00407                 first = nht.entries[i].first;
00408                 while(first)
00409                 {
00410                         it = first;
00411                         first = first->next;
00412                         ht_cell_free(it);
00413                 }
00414         }
00415         ht_db_close_con();
00416         return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
00417 }
00418 
00419 static struct mi_root* ht_mi_delete(struct mi_root* cmd_tree, void* param) {
00420         struct mi_node *node;
00421         str *htname, *key;
00422         ht_t *ht;
00423 
00424         node = cmd_tree->node.kids;
00425         if (!node)
00426                 goto param_err;
00427 
00428         htname = &node->value;
00429         if (!htname->len)
00430                 goto param_err;
00431 
00432         node = node->next;
00433         if (!node)
00434                 goto param_err;
00435 
00436         key = &node->value;
00437         if (!key->len)
00438                 goto param_err;
00439 
00440         ht = ht_get_table(htname);
00441         if (!ht)
00442                 return init_mi_tree(404, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00443 
00444         ht_del_cell(ht, key);
00445 
00446         return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
00447 
00448 param_err:
00449         return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
00450 }
00451 
00452 static struct mi_root* ht_mi_dump(struct mi_root* cmd_tree, void* param)
00453 {
00454         struct mi_node* node;
00455         struct mi_node* node2;
00456         struct mi_root *rpl_tree;
00457         struct mi_node *rpl;
00458         str htname;
00459         ht_t *ht;
00460         ht_cell_t *it;
00461         int i;
00462         int len;
00463         char *p;
00464 
00465         node = cmd_tree->node.kids;
00466         if(node == NULL)
00467                 return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
00468         htname = node->value;
00469         if(htname.len<=0 || htname.s==NULL)
00470         {
00471                 LM_ERR("bad hash table name\n");
00472                 return init_mi_tree( 500, "bad hash table name", 19);
00473         }
00474         ht = ht_get_table(&htname);
00475         if(ht==NULL)
00476         {
00477                 LM_ERR("bad hash table name\n");
00478                 return init_mi_tree( 500, "no such hash table", 18);
00479         }
00480 
00481         rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
00482         if (rpl_tree==NULL)
00483                 return 0;
00484         rpl = &rpl_tree->node;
00485 
00486         for(i=0; i<ht->htsize; i++)
00487         {
00488                 lock_get(&ht->entries[i].lock);
00489                 it = ht->entries[i].first;
00490                 if(it)
00491                 {
00492                         /* add entry node */
00493                         p = int2str((unsigned long)i, &len);
00494                         node = add_mi_node_child(rpl, MI_DUP_VALUE, "Entry", 5, p, len);
00495                         if (node==0)
00496                                 goto error;
00497                         while(it)
00498                         {
00499                                 if(it->flags&AVP_VAL_STR) {
00500                                         node2 = add_mi_node_child(node, MI_DUP_VALUE, it->name.s, it->name.len,
00501                                                         it->value.s.s, it->value.s.len);
00502                                 } else {
00503                                         p = sint2str((long)it->value.n, &len);
00504                                         node2 = add_mi_node_child(node, MI_DUP_VALUE, it->name.s, it->name.len,
00505                                                         p, len);
00506                                 }
00507                                 if (node2==0)
00508                                         goto error;
00509                                 it = it->next;
00510                         }
00511                 }
00512                 lock_release(&ht->entries[i].lock);
00513         }
00514 
00515         return rpl_tree;
00516 error:
00517         free_mi_tree(rpl_tree);
00518         return 0;
00519 }
00520 
00521 static const char* htable_dump_doc[2] = {
00522         "Dump the contents of hash table.",
00523         0
00524 };
00525 static const char* htable_delete_doc[2] = {
00526         "Delete one key from a hash table.",
00527         0
00528 };
00529 
00530 static void htable_rpc_delete(rpc_t* rpc, void* c) {
00531         str htname, keyname;
00532         ht_t *ht;
00533 
00534         if (rpc->scan(c, "SS", &htname, &keyname) < 2) {
00535                 rpc->fault(c, 500, "Not enough parameters (htable name & key name");
00536                 return;
00537         }
00538         ht = ht_get_table(&htname);
00539         if (!ht) {
00540                 rpc->fault(c, 500, "No such htable");
00541                 return;
00542         }
00543 
00544         ht_del_cell(ht, &keyname);
00545 }
00546 
00547 static void  htable_rpc_dump(rpc_t* rpc, void* c)
00548 {
00549         str htname;
00550         ht_t *ht;
00551         ht_cell_t *it;
00552         int i;
00553         void* th;
00554         void* ih;
00555         void* vh;
00556 
00557         if (rpc->scan(c, "S", &htname) < 1)
00558         {
00559                 rpc->fault(c, 500, "No htable name given");
00560                 return;
00561         }
00562         ht = ht_get_table(&htname);
00563         if(ht==NULL)
00564         {
00565                 rpc->fault(c, 500, "No such htable");
00566                 return;
00567         }
00568         for(i=0; i<ht->htsize; i++)
00569         {
00570                 lock_get(&ht->entries[i].lock);
00571                 it = ht->entries[i].first;
00572                 if(it)
00573                 {
00574                         /* add entry node */
00575                         if (rpc->add(c, "{", &th) < 0)
00576                         {
00577                                 rpc->fault(c, 500, "Internal error creating rpc");
00578                                 goto error;
00579                         }
00580                         if(rpc->struct_add(th, "dd{",
00581                                                         "entry", i,
00582                                                         "size",  (int)ht->entries[i].esize,
00583                                                         "slot",  &ih)<0)
00584                         {
00585                                 rpc->fault(c, 500, "Internal error creating rpc");
00586                                 goto error;
00587                         }
00588                         while(it)
00589                         {
00590                                 if(rpc->struct_add(ih, "{",
00591                                                         "item", &vh)<0)
00592                                 {
00593                                         rpc->fault(c, 500, "Internal error creating rpc");
00594                                         goto error;
00595                                 }
00596                                 if(it->flags&AVP_VAL_STR) {
00597                                         if(rpc->struct_add(vh, "SS",
00598                                                         "name",  &it->name.s,
00599                                                         "value", &it->value.s)<0)
00600                                         {
00601                                                 rpc->fault(c, 500, "Internal error adding item");
00602                                                 goto error;
00603                                         }
00604                                 } else {
00605                                         if(rpc->struct_add(vh, "Sd",
00606                                                         "name",  &it->name.s,
00607                                                         "value", (int)it->value.n))
00608                                         {
00609                                                 rpc->fault(c, 500, "Internal error adding item");
00610                                                 goto error;
00611                                         }
00612                                 }
00613                                 it = it->next;
00614                         }
00615                 }
00616                 lock_release(&ht->entries[i].lock);
00617         }
00618 
00619         return;
00620 
00621 error:
00622         lock_release(&ht->entries[i].lock);
00623 }
00624 
00625 rpc_export_t htable_rpc[] = {
00626         {"htable.dump", htable_rpc_dump, htable_dump_doc, 0},
00627         {"htable.delete", htable_rpc_delete, htable_delete_doc, 0},
00628         {0, 0, 0, 0}
00629 };
00630 
00631 static int htable_init_rpc(void)
00632 {
00633         if (rpc_register_array(htable_rpc)!=0)
00634         {
00635                 LM_ERR("failed to register RPC commands\n");
00636                 return -1;
00637         }
00638         return 0;
00639 }