rpc: added register function and switched to hash table
authorAndrei Pelinescu-Onciul <andrei@iptel.org>
Mon, 11 May 2009 22:39:03 +0000 (00:39 +0200)
committerAndrei Pelinescu-Onciul <andrei@iptel.org>
Wed, 13 May 2009 17:23:39 +0000 (19:23 +0200)
- rpc switched to hashtable
- added rpc register function, that can be called from modules not
  using ser module interface (e.g. kamailio or future sip-router
  module interface).

core_cmd.c
core_cmd.h
main.c
rpc_lookup.c [new file with mode: 0644]
rpc_lookup.h [new file with mode: 0644]
sr_module.c

index c4af7a7..4e33d49 100644 (file)
@@ -31,6 +31,7 @@
 #include "mem/mem.h"
 #include "mem/shm_mem.h"
 #include "sr_module.h"
+#include "rpc_lookup.h"
 #include "dprint.h"
 #include "core_cmd.h"
 #include "globals.h"
@@ -229,18 +230,10 @@ static const char* system_listMethods_doc[] = {
 
 static void system_listMethods(rpc_t* rpc, void* c)
 {
-       struct sr_module* t;
-       rpc_export_t* ptr;
-
-       for(ptr = core_rpc_methods; ptr && ptr->name; ptr++) {
-               if (rpc->add(c, "s", ptr->name) < 0) return;
-       }
-
-       for(t = modules; t; t = t->next) {
-               if (t->mod_interface_ver!=0) continue;
-               for(ptr = t->exports->v0.rpc_methods; ptr && ptr->name; ptr++) {
-                       if (rpc->add(c, "s", ptr->name) < 0) return;
-               }
+       int i;
+       
+       for(i=0; i<rpc_sarray_crt_size; i++){
+               if (rpc->add(c, "s", rpc_sarray[i]->name) < 0) return;
        }
 }
 
@@ -262,40 +255,25 @@ static const char* system_methodHelp_doc[] = {
 
 static void system_methodHelp(rpc_t* rpc, void* c)
 {
-       struct sr_module* t;
-       rpc_export_t* ptr;
+       rpc_export_t* r;
        char* name;
 
        if (rpc->scan(c, "s", &name) < 1) {
                rpc->fault(c, 400, "Method Name Expected");
                return;
        }
-
-       for(t = modules; t; t = t->next) {
-               if (t->mod_interface_ver!=0) continue;
-               for(ptr = t->exports->v0.rpc_methods; ptr && ptr->name; ptr++) {
-                       if (strcmp(name, ptr->name) == 0) {
-                               if (ptr->doc_str && ptr->doc_str[0]) {
-                                       rpc->add(c, "s", ptr->doc_str[0]);
-                               } else {
-                                       rpc->add(c, "s", "undocumented");
-                               }
-                               return;
-                       }
+       
+       r=rpc_lookup(name, strlen(name));
+       if (r==0){
+               rpc->fault(c, 400, "command not found");
+       }else{
+               if (r->doc_str && r->doc_str[0]) {
+                       rpc->add(c, "s", r->doc_str[0]);
+               } else {
+                       rpc->add(c, "s", "undocumented");
                }
        }
-       /* try the core methods too */
-       for (ptr=core_rpc_methods;ptr && ptr->name; ptr++){
-                       if (strcmp(name, ptr->name) == 0) {
-                               if (ptr->doc_str && ptr->doc_str[0]) {
-                                       rpc->add(c, "s", ptr->doc_str[0]);
-                               } else {
-                                       rpc->add(c, "s", "undocumented");
-                               }
-                               return;
-                       }
-       }
-       rpc->fault(c, 400, "command not found");
+       return;
 }
 
 
@@ -701,7 +679,7 @@ static void core_sctpinfo(rpc_t* rpc, void* c)
 /*
  * RPC Methods exported by this module
  */
-rpc_export_t core_rpc_methods[] = {
+static rpc_export_t core_rpc_methods[] = {
        {"system.listMethods",     system_listMethods,     system_listMethods_doc,     RET_ARRAY},
        {"system.methodSignature", system_methodSignature, system_methodSignature_doc, 0        },
        {"system.methodHelp",      system_methodHelp,      system_methodHelp_doc,      0        },
@@ -785,6 +763,28 @@ rpc_export_t core_rpc_methods[] = {
        {0, 0, 0, 0}
 };
 
+
+
+int register_core_rpcs(void)
+{
+       int i;
+       
+       i=rpc_register_array(core_rpc_methods);
+       if (i<0){
+               BUG("failed to register core RPCs\n");
+               goto error;
+       }else if (i>0){
+               ERR("%d duplicate RPCs name detected while registering core RPCs\n",
+                        i);
+               goto error;
+       }
+       return 0;
+error:
+       return -1;
+}
+
+
+
 int rpc_init_time(void)
 {
        char *t;
index b16f2a1..ab90c9e 100644 (file)
@@ -30,8 +30,7 @@
 
 #include "rpc.h"
 
-extern rpc_export_t core_rpc_methods[];
-
+int register_core_rpcs(void);
 int rpc_init_time(void);
 
 #endif /* _CORE_CMD_H */
diff --git a/main.c b/main.c
index 4d7fd1f..6369c5a 100644 (file)
--- a/main.c
+++ b/main.c
 #include "sctp_server.h"
 #endif
 #include "usr_avp.h"
+#include "rpc_lookup.h"
 #include "core_cmd.h"
 #include "flags.h"
 #include "lock_ops_init.h"
@@ -1630,6 +1631,8 @@ int main(int argc, char** argv)
        }
        if (init_routes()<0) goto error;
        if (init_nonsip_hooks()<0) goto error;
+       if (init_rpcs()<0) goto error;
+       if (register_core_rpcs()!=0) goto error;
 
        /* Fix the value of cfg_file variable.*/
        if (fix_cfg_file() < 0) goto error;
diff --git a/rpc_lookup.c b/rpc_lookup.c
new file mode 100644 (file)
index 0000000..606a7ca
--- /dev/null
@@ -0,0 +1,220 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2009 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * SER RPC lookup and register functions
+ */
+/*
+ * History:
+ * --------
+ *  2009-05-11  initial version (andrei)
+*/
+
+#include "rpc.h"
+#include "str_hash.h"
+#include "ut.h"
+
+#define RPC_HASH_SIZE  32
+#define RPC_SARRAY_SIZE        32 /* initial size */
+
+#define RPC_COPY_EXPORT
+
+static struct str_hash_table rpc_hash_table;
+
+/* array of pointer to rpc exports, sorted after their name
+ *  (used by listMethods) */
+rpc_export_t** rpc_sarray;
+int rpc_sarray_crt_size; /* used */
+static int rpc_sarray_max_size; /* number of entries alloc'ed */
+
+/** init the rpc hash table.
+  * @return 0 on success, -1 on error
+  */
+int init_rpcs()
+{
+       if (str_hash_alloc(&rpc_hash_table, RPC_HASH_SIZE)<0)
+               return -1;
+       str_hash_init(&rpc_hash_table);
+       rpc_sarray_max_size=RPC_SARRAY_SIZE;
+       rpc_sarray=pkg_malloc(sizeof(*rpc_sarray)* rpc_sarray_max_size);
+       rpc_sarray_crt_size=0;
+       return 0;
+}
+
+
+
+void destroy_rpcs()
+{
+       int r;
+       struct str_hash_entry* e;
+       struct str_hash_entry* bak;
+       for (r=0; r<rpc_hash_table.size; r++){
+               clist_foreach_safe(&rpc_hash_table.table[r], e, bak, next){
+                       pkg_free(e);
+               }
+       }
+       pkg_free(rpc_hash_table.table);
+       pkg_free(rpc_sarray);
+       rpc_hash_table.table=0;
+       rpc_hash_table.size=0;
+       rpc_sarray=0;
+       rpc_sarray_crt_size=0;
+       rpc_sarray_max_size=0;
+}
+
+
+
+/** adds a new rpc to the hash table (no checks).
+ * @return 0 on success, -1 on error, 1 on duplicate
+ */
+static int rpc_hash_add(struct rpc_export* rpc)
+{
+       struct str_hash_entry* e;
+       int name_len;
+       int doc0_len, doc1_len;
+       struct rpc_export* n_rpc;
+       struct rpc_export** r;
+       
+       name_len=strlen(rpc->name);
+       doc0_len=rpc->doc_str[0]?strlen(rpc->doc_str[0]):0;
+       doc1_len=rpc->doc_str[1]?strlen(rpc->doc_str[1]):0;
+       /* alloc everything into one block */
+       e=pkg_malloc(ROUND_POINTER(sizeof(struct str_hash_entry))
+#ifdef RPC_COPY_EXPORT
+                                                               +ROUND_POINTER(sizeof(*rpc))+2*sizeof(char*)+
+                                                               +name_len+1+doc0_len+(rpc->doc_str[0]!=0)
+                                                               +doc1_len+(rpc->doc_str[1]!=0)
+#endif /* RPC_COPY_EXPORT */
+                                                               );
+       if (e==0){
+               ERR("out of memory\n");
+               goto error;
+       }
+#ifdef RPC_COPY_EXPORT
+       n_rpc=(rpc_export_t*)((char*)e+
+                       ROUND_POINTER(sizeof(struct str_hash_entry)));
+       /* copy rpc into n_rpc */
+       *n_rpc=*rpc;
+       n_rpc->doc_str=(const char**)((char*)n_rpc+ROUND_POINTER(sizeof(*rpc)));
+       n_rpc->name=(char*)n_rpc->doc_str+2*sizeof(char*);
+       memcpy((char*)n_rpc->name, rpc->name, name_len);
+       *((char*)&n_rpc->name[name_len])=0;
+       if (rpc->doc_str[0]){
+               n_rpc->doc_str[0]=&n_rpc->name[name_len+1];
+               memcpy((char*)n_rpc->doc_str[0], rpc->doc_str[0], doc0_len);
+               *(char*)&(n_rpc->doc_str[0][doc0_len])=0;
+       }else{
+               n_rpc->doc_str[0]=0;
+       }
+       if (rpc->doc_str[1]){
+               n_rpc->doc_str[1]=n_rpc->doc_str[0]?&n_rpc->doc_str[0][doc0_len+1]:
+                                                       &n_rpc->name[name_len+1];;
+               memcpy((char*)n_rpc->doc_str[1], rpc->doc_str[1], doc1_len);
+               *(char*)&(n_rpc->doc_str[1][doc1_len])=0;
+       }else{
+               n_rpc->doc_str[1]=0;
+       }
+#else /* RPC_COPY_EXPORT */
+       n_rpc=rpc;
+#endif /* RPC_COPY_EXPORT */
+       
+       e->key.s=(char*)n_rpc->name;
+       e->key.len=name_len;
+       e->flags=0;
+       e->u.p=n_rpc;
+       str_hash_add(&rpc_hash_table, e);
+       
+       /* insert it into the sorted array */
+       if (rpc_sarray_max_size<=rpc_sarray_crt_size){
+               /* array must be increased */
+               r=pkg_realloc(rpc_sarray, 2*rpc_sarray_max_size*sizeof(*rpc_sarray));
+               if (r==0){
+                       ERR("out of memory while adding RPC to the sorted list\n");
+                       goto error;
+               }
+               rpc_sarray=r;
+               rpc_sarray_max_size*=2;
+       };
+       /* insert into array, sorted */
+       for (r=rpc_sarray;r<(rpc_sarray+rpc_sarray_crt_size); r++){
+               if (strcmp(n_rpc->name, (*r)->name)<0)
+                       break;
+       }
+       if (r!=(rpc_sarray+rpc_sarray_crt_size))
+               memmove(r+1, r, (int)(long)((char*)(rpc_sarray+rpc_sarray_crt_size)-
+                                                                                       (char*)r));
+       rpc_sarray_crt_size++;
+       *r=n_rpc;
+       return 0;
+error:
+       return -1;
+}
+
+
+
+/** lookup an rpc export after its name.
+ * @return pointer to rpc export on success, 0 on error
+ */
+rpc_export_t* rpc_lookup(const char* name, int len)
+{
+       struct str_hash_entry* e;
+       
+       e=str_hash_get(&rpc_hash_table, (char*)name, len);
+       return e?(rpc_export_t*)e->u.p:0;
+}
+
+
+
+/** register a new rpc.
+ * @return 0 on success, -1 on error, 1 on duplicate
+ */
+int rpc_register(rpc_export_t* rpc)
+{
+       
+       /* check if the entry is already registered */
+       if (rpc_lookup(rpc->name, strlen(rpc->name)))
+               return 1;
+       if (rpc_hash_add(rpc)!=0) return -1;
+       return 0;
+}
+
+
+
+/** register all the rpc in a null-terminated array.
+  * @return 0 on success, >0 if duplicates were found (number of 
+  * duplicates), -1 on error
+  */
+int rpc_register_array(rpc_export_t* rpc_array)
+{
+       rpc_export_t* rpc;
+       int ret,i;
+       
+       ret=0;
+       for (rpc=rpc_array; rpc && rpc->name; rpc++){
+               i=rpc_register(rpc);
+               if (i!=0){
+                       if (i<0) goto error;
+                       ret++;
+               }
+       }
+       return ret;
+error:
+       return -1;
+}
+
+
+/* vi: set ts=4 sw=4 tw=79:ai:cindent: */
diff --git a/rpc_lookup.h b/rpc_lookup.h
new file mode 100644 (file)
index 0000000..841fde2
--- /dev/null
@@ -0,0 +1,46 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2009 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * SER RPC lookup and register functions
+ */
+/*
+ * History:
+ * --------
+ *  2009-05-11  initial version (andrei)
+*/
+
+#ifndef __rpc_lookup_h
+#define __rpc_lookup_h
+
+#include "rpc.h"
+/* must be exported for listing the rpcs */
+extern rpc_export_t** rpc_sarray;
+extern int rpc_sarray_crt_size;
+
+int init_rpcs();
+int destroy_rpcs();
+
+rpc_export_t* rpc_lookup(const char* name, int len);
+int rpc_register(rpc_export_t* rpc);
+int rpc_register_array(rpc_export_t* rpc_array);
+
+
+
+#endif /*__rpc_lookup_h*/
+
+/* vi: set ts=4 sw=4 tw=79:ai:cindent: */
index 3ff127f..a3e51e6 100644 (file)
@@ -53,6 +53,7 @@
 #include "flags.h"
 #include "trim.h"
 #include "globals.h"
+#include "rpc_lookup.h"
 
 #include <sys/stat.h>
 #include <regex.h>
@@ -147,7 +148,7 @@ int register_builtin_modules()
 static int register_module(unsigned ver, union module_exports_u* e,
                                        char* path, void* handle)
 {
-       int ret;
+       int ret, i;
        struct sr_module* mod;
 
        ret=-1;
@@ -166,8 +167,8 @@ static int register_module(unsigned ver, union module_exports_u* e,
        mod->next=modules;
        modules=mod;
 
-       /* register module pseudo-variables */
        if (ver==1 && e->v1.items) {
+               /* register module pseudo-variables for kamailio modules */
                LM_DBG("register PV from: %s\n", e->c.name);
                if (register_pvars_mod(e->c.name, e->v1.items)!=0) {
                        LM_ERR("failed to register pseudo-variables for module %s\n",
@@ -175,6 +176,18 @@ static int register_module(unsigned ver, union module_exports_u* e,
                        pkg_free(mod);
                        return -1;
                }
+       }else if (ver==0 && e->v0.rpc_methods){
+               /* register rpcs for ser modules */
+               i=rpc_register_array(e->v0.rpc_methods);
+               if (i<0){
+                       ERR("failed to register RPCs for module %s\n", e->c.name);
+                       goto error;
+               }else if (i>0){
+                       ERR("%d duplicate RPCs name detected while registering RPCs"
+                                       " declared in modules %s\n", i, e->c.name);
+                       goto error;
+               }
+               /* i==0 => success */
        }
 
        return 0;
@@ -489,31 +502,7 @@ cmd_function find_export(char* name, int param_no, int flags)
 
 rpc_export_t* find_rpc_export(char* name, int flags)
 {
-       struct sr_module* t;
-       rpc_export_t* rpc;
-
-            /* Scan the list of core methods first, they are always
-             * present
-             */
-       for(rpc = core_rpc_methods; rpc && rpc->name; rpc++) {
-               if ((strcmp(name, rpc->name) == 0) &&
-                   ((rpc->flags & flags) == flags)
-                   ) {
-                       return rpc;
-               }
-       }
-            /* Continue with modules if not found */
-       for(t = modules; t; t = t->next) {
-               if (t->mod_interface_ver!=0) continue;
-               for(rpc = t->exports->v0.rpc_methods; rpc && rpc->name; rpc++) {
-                       if ((strcmp(name, rpc->name) == 0) &&
-                           ((rpc->flags & flags) == flags)
-                          ) {
-                               return rpc;
-                       }
-               }
-       }
-       return 0;
+       return rpc_lookup((char*)name, strlen(name));
 }