Merge branches 'janakj/mysql' and 'master' of ssh://janakj@git.sip-router.org/sip...
authorJan Janak <jan@iptel.org>
Mon, 16 Feb 2009 00:07:20 +0000 (01:07 +0100)
committerJan Janak <jan@iptel.org>
Mon, 16 Feb 2009 00:07:20 +0000 (01:07 +0100)
* 'janakj/mysql' of ssh://janakj@git.sip-router.org/sip-router:

* 'master' of ssh://janakj@git.sip-router.org/sip-router:
  Database flags renamed from DB_* to SRDB_* to avoid conflicts.
  libsrdb1: futex warning fix
  - port from kamailio trunk, r5607
  - sync transformations add/lookup with kamailio
  init_mi_core() exported via mi.h
  mi include file
  MI: core part
  script parsing: while support
  script engine: while() support
  script engine: switch() and break execution
  script engine: switch() fixup and optimizations
  script parsing: C style switch() & case support
  expr engine: minor additions

29 files changed:
NEWS
action.c
cfg.lex
cfg.y
cfg_core.c
cfg_core.h
config.h
lib/kmi/Makefile [new file with mode: 0644]
lib/kmi/attr.c [new file with mode: 0644]
lib/kmi/attr.h [new file with mode: 0644]
lib/kmi/fmt.c [new file with mode: 0644]
lib/kmi/fmt.h [new file with mode: 0644]
lib/kmi/mi.c [new file with mode: 0644]
lib/kmi/mi.h [new file with mode: 0644]
lib/kmi/mi_core.c [new file with mode: 0644]
lib/kmi/mi_core.h [new file with mode: 0644]
lib/kmi/tree.c [new file with mode: 0644]
lib/kmi/tree.h [new file with mode: 0644]
lib/srdb1/db_ut.h
lib/srdb2/db.h
lib/trie/dtrie.c
mi/mi.h
pvapi.c
route.c
route_struct.h
rvalue.c
rvalue.h
switch.c [new file with mode: 0644]
switch.h [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 03852ee..bede08a 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,10 @@ config script changes:
       #!MAXCOMPAT
     where #!KAMAILIO is equivalent with #!OPENSER and #!ALL with #!MAXCOMPAT
   - support for kamailio style pvars
+  - C-like switch()/case (integer only)
+  - while()
+  - max_while_loops - maximum iterations allowed for a while, can be changed
+    at runtime. Default 100.
 
 
 
index 26edcca..079bbfe 100644 (file)
--- a/action.c
+++ b/action.c
@@ -77,6 +77,7 @@
 #ifdef USE_SCTP
 #include "sctp_server.h"
 #endif
+#include "switch.h"
 
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -111,6 +112,11 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
        struct sip_uri *u;
        unsigned short port;
        str* dst_host;
+       int i, flags;
+       struct switch_cond_table* sct;
+       struct switch_jmp_table*  sjt;
+       struct rval_expr* rve;
+
 
        /* reset the value of error to E_UNSPEC so avoid unknowledgable
           functions to return with error (status<0) and not setting it
@@ -448,7 +454,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                        /*ret=((ret=run_actions(rlist[a->val[0].u.number], msg))<0)?ret:1;*/
                        ret=run_actions(h, main_rt.rlist[a->val[0].u.number], msg);
                        h->last_retcode=ret;
-                       h->run_flags&=~RETURN_R_F; /* absorb returns */
+                       h->run_flags&=~(RETURN_R_F|BREAK_R_F); /* absorb return & break */
                        break;
                case EXEC_T:
                        if (a->val[0].type!=STRING_ST){
@@ -704,7 +710,8 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                                                ret=0;
                                                break;
                                        }
-                                       h->run_flags &= ~RETURN_R_F; /* catch returns in expr */
+                                       h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return &
+                                                                                                                           break in expr*/
                                        ret=1;  /*default is continue */
                                        if (v>0) {
                                                if ((a->val[1].type==ACTIONS_ST)&&a->val[1].u.data){
@@ -808,6 +815,122 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                                LOG(L_CRIT,"BUG: do_action: bad module call\n");
                        }
                        break;
+               case EVAL_T:
+                       /* only eval the expression to account for possible
+                          side-effect */
+                       rval_expr_eval_int(h, msg, &v,
+                                       (struct rval_expr*)a->val[0].u.data);
+                       if (h->run_flags & EXIT_R_F){
+                               ret=0;
+                               break;
+                       }
+                       h->run_flags &= ~RETURN_R_F|BREAK_R_F; /* catch return & break in
+                                                                                                         expr */
+                       ret=1; /* default is continue */
+                       break;
+               case SWITCH_COND_T:
+                       sct=(struct switch_cond_table*)a->val[1].u.data;
+                       if (unlikely( rval_expr_eval_int(h, msg, &v,
+                                                                       (struct rval_expr*)a->val[0].u.data) <0)){
+                               /* handle error in expression => use default */
+                               ret=-1;
+                               goto sw_cond_def;
+                       }
+                       if (h->run_flags & EXIT_R_F){
+                               ret=0;
+                               break;
+                       }
+                       h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break
+                                                                                                           in expr */
+                       ret=1; /* default is continue */
+                       for(i=0; i<sct->n; i++)
+                               if (sct->cond[i]==v){
+                                       if (likely(sct->jump[i])){
+                                               ret=run_actions(h, sct->jump[i], msg);
+                                               h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
+                                                                                                          returns passthrough */
+                                       }
+                                       goto skip;
+                               }
+sw_cond_def:
+                       if (sct->def){
+                               ret=run_actions(h, sct->def, msg);
+                               h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
+                                                                                          returns passthrough */
+                       }
+                       break;
+               case SWITCH_JT_T:
+                       sjt=(struct switch_jmp_table*)a->val[1].u.data;
+                       if (unlikely( rval_expr_eval_int(h, msg, &v,
+                                                                       (struct rval_expr*)a->val[0].u.data) <0)){
+                               /* handle error in expression => use default */
+                               ret=-1;
+                               goto sw_jt_def;
+                       }
+                       if (h->run_flags & EXIT_R_F){
+                               ret=0;
+                               break;
+                       }
+                       h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break
+                                                                                                           in expr */
+                       ret=1; /* default is continue */
+                       if (likely(v >= sjt->first && v <= sjt->last)){
+                               if (likely(sjt->tbl[v - sjt->first])){
+                                       ret=run_actions(h, sjt->tbl[v - sjt->first], msg);
+                                       h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
+                                                                                                  returns passthrough */
+                               }
+                               break; 
+                       }else{
+                               for(i=0; i<sjt->rest.n; i++)
+                                       if (sjt->rest.cond[i]==v){
+                                               if (likely(sjt->rest.jump[i])){
+                                                       ret=run_actions(h, sjt->rest.jump[i], msg);
+                                                       h->run_flags &= ~BREAK_R_F; /* catch breaks, but 
+                                                                                                                  let returns pass */
+                                               }
+                                               goto skip;
+                                       }
+                       }
+                       /* not found => try default */
+sw_jt_def:
+                       if (sjt->rest.def){
+                               ret=run_actions(h, sjt->rest.def, msg);
+                               h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
+                                                                                          returns passthrough */
+                       }
+                       break;
+               case BLOCK_T:
+                       if (likely(a->val[0].u.data)){
+                               ret=run_actions(h, (struct action*)a->val[0].u.data, msg);
+                               h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
+                                                                                          returns passthrough */
+                       }
+                       break;
+               case WHILE_T:
+                       i=0;
+                       flags=0;
+                       rve=(struct rval_expr*)a->val[0].u.data;
+                       ret=1;
+                       while(!(flags & BREAK_R_F) && 
+                                       (rval_expr_eval_int(h, msg, &v, rve) == 0) && v){
+                               i++;
+                               if (unlikely(i > cfg_get(core, core_cfg, max_while_loops))){
+                                       LOG(L_ERR, "ERROR: runaway while (%d, %d): more then"
+                                                               " %d loops\n", 
+                                                               rve->fpos.s_line, rve->fpos.s_col,
+                                                               cfg_get(core, core_cfg, max_while_loops));
+                                       ret=-1;
+                                       break;
+                               }
+                               if (likely(a->val[1].u.data)){
+                                       ret=run_actions(h, (struct action*)a->val[1].u.data, msg);
+                                       flags|=h->run_flags;
+                                       h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
+                                                                                                  returns passthrough */
+                               }
+                       }
+                       break;
                case FORCE_RPORT_T:
                        msg->msg_flags|=FL_FORCE_RPORT;
                        ret=1; /* continue processing */
@@ -885,7 +1008,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
                default:
                        LOG(L_CRIT, "BUG: do_action: unknown type %d\n", a->type);
        }
-/*skip:*/
+skip:
        return ret;
 
 error_uri:
@@ -935,7 +1058,9 @@ int run_actions(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 
        for (t=a; t!=0; t=t->next){
                ret=do_action(h, t, msg);
-               if (h->run_flags & (RETURN_R_F|EXIT_R_F)){
+               /* break, return or drop/exit stop execution of the current
+                  block */
+               if (h->run_flags & (BREAK_R_F|RETURN_R_F|EXIT_R_F)){
                        if (h->run_flags & EXIT_R_F){
 #ifdef USE_LONGJMP
                                h->last_retcode=ret;
diff --git a/cfg.lex b/cfg.lex
index fc9eecb..1323303 100644 (file)
--- a/cfg.lex
+++ b/cfg.lex
@@ -187,6 +187,10 @@ ELSE                       "else"
 SET_ADV_ADDRESS        "set_advertised_address"
 SET_ADV_PORT   "set_advertised_port"
 FORCE_SEND_SOCKET      "force_send_socket"
+SWITCH                 "switch"
+CASE                   "case"
+DEFAULT                        "default"
+WHILE                  "while"
 
 /*ACTION LVALUES*/
 URIHOST                        "uri:host"
@@ -372,6 +376,7 @@ MCAST_TTL           "mcast_ttl"
 TOS                    "tos"
 PMTU_DISCOVERY "pmtu_discovery"
 KILL_TIMEOUT   "exit_timeout"|"ser_kill_timeout"
+MAX_WLOOPS             "max_while_loops"
 
 /* stun config variables */
 STUN_REFRESH_INTERVAL "stun_refresh_interval"
@@ -495,6 +500,10 @@ EAT_ABLE   [\ \t\b\r]
                                                                                return SET_ADV_PORT; }
 <INITIAL>{FORCE_SEND_SOCKET}   {       count(); yylval.strval=yytext;
                                                                        return FORCE_SEND_SOCKET; }
+<INITIAL>{SWITCH}      { count(); yylval.strval=yytext; return SWITCH; }
+<INITIAL>{CASE}        { count(); yylval.strval=yytext; return CASE; }
+<INITIAL>{DEFAULT}     { count(); yylval.strval=yytext; return DEFAULT; }
+<INITIAL>{WHILE}       { count(); yylval.strval=yytext; return WHILE; }
 
 <INITIAL>{URIHOST}     { count(); yylval.strval=yytext; return URIHOST; }
 <INITIAL>{URIPORT}     { count(); yylval.strval=yytext; return URIPORT; }
@@ -710,6 +719,8 @@ EAT_ABLE    [\ \t\b\r]
                                                                        return PMTU_DISCOVERY; }
 <INITIAL>{KILL_TIMEOUT}                        {       count(); yylval.strval=yytext;
                                                                        return KILL_TIMEOUT; }
+<INITIAL>{MAX_WLOOPS}                  {       count(); yylval.strval=yytext;
+                                                                       return MAX_WLOOPS; }
 <INITIAL>{SERVER_ID}  { count(); yylval.strval=yytext; return SERVER_ID;}
 <INITIAL>{CFG_DESCRIPTION}     { count(); yylval.strval=yytext; return CFG_DESCRIPTION; }
 <INITIAL>{LOADMODULE}  { count(); yylval.strval=yytext; return LOADMODULE; }
diff --git a/cfg.y b/cfg.y
index 63c9eb4..e1758bf 100644 (file)
--- a/cfg.y
+++ b/cfg.y
@@ -93,6 +93,7 @@
  *               lval=rval_expr, where lval=avp|pvar  (andrei)
  * 2007-12-06  expression are now evaluated in terms of rvalues;
  *             NUMBER is now always positive; cleanup (andrei)
+ * 2009-01-26  case/switch() support (andrei)
 */
 
 %{
 #include "route_struct.h"
 #include "globals.h"
 #include "route.h"
+#include "switch.h"
 #include "dprint.h"
 #include "sr_module.h"
 #include "modparam.h"
 
 extern int yylex();
 static void yyerror(char* s, ...);
+static void yyerror_at(struct cfg_pos* pos, char* s, ...);
 static char* tmp;
 static int i_tmp;
 static unsigned u_tmp;
@@ -199,7 +202,8 @@ static struct action *mod_func_action;
 static struct lvalue* lval_tmp;
 static struct rvalue* rval_tmp;
 
-static void warn(char* s);
+static void warn(char* s, ...);
+static void warn_at(struct cfg_pos* pos, char* s, ...);
 static void get_cpos(struct cfg_pos* pos);
 static struct rval_expr* mk_rve_rval(enum rval_type, void* v);
 static struct rval_expr* mk_rve1(enum rval_expr_op op, struct rval_expr* rve1);
@@ -211,6 +215,8 @@ static struct socket_id* mk_listen_id2(struct name_lst*, int, int);
 static void free_name_lst(struct name_lst* lst);
 static void free_socket_id_lst(struct socket_id* i);
 
+static struct case_stms* mk_case_stm(struct rval_expr* ct, struct action* a);
+
 %}
 
 %union {
@@ -219,6 +225,7 @@ static void free_socket_id_lst(struct socket_id* i);
        char* strval;
        struct expr* expr;
        struct action* action;
+       struct case_stms* case_stms;
        struct net* ipnet;
        struct ip_addr* ipaddr;
        struct socket_id* sockid;
@@ -272,6 +279,10 @@ static void free_socket_id_lst(struct socket_id* i);
 %token SET_ADV_ADDRESS
 %token SET_ADV_PORT
 %token FORCE_SEND_SOCKET
+%token SWITCH
+%token CASE
+%token DEFAULT
+%token WHILE
 %token URIHOST
 %token URIPORT
 %token MAX_LEN
@@ -428,6 +439,7 @@ static void free_socket_id_lst(struct socket_id* i);
 %token TOS
 %token PMTU_DISCOVERY
 %token KILL_TIMEOUT
+%token MAX_WLOOPS
 %token CFG_DESCRIPTION
 %token SERVER_ID
 
@@ -493,6 +505,8 @@ static void free_socket_id_lst(struct socket_id* i);
 %type <intval> intno eint_op eint_op_onsend
 %type <intval> eip_op eip_op_onsend
 %type <action> action actions cmd fcmd if_cmd stm /*exp_stm*/ assign_action
+%type <action> switch_cmd while_cmd
+%type <case_stms> single_case case_stms
 %type <ipaddr> ipv4 ipv6 ipv6addr ip
 %type <ipnet> ipnet
 %type <strval> host
@@ -514,7 +528,7 @@ static void free_socket_id_lst(struct socket_id* i);
 %type <attr> attr_id_any_str
 %type <pvar> pvar
 %type <lval> lval
-%type <rv_expr> rval rval_expr 
+%type <rv_expr> rval rval_expr ct_rval
 %type <lval> avp_pvar
 /* %type <intval> class_id */
 %type <intval> assign_op
@@ -1273,6 +1287,8 @@ assign_stm:
        | PMTU_DISCOVERY error { yyerror("number expected"); }
        | KILL_TIMEOUT EQUAL NUMBER { ser_kill_timeout=$3; }
        | KILL_TIMEOUT EQUAL error { yyerror("number expected"); }
+       | MAX_WLOOPS EQUAL NUMBER { default_core_cfg.max_while_loops=$3; }
+       | MAX_WLOOPS EQUAL error { yyerror("number expected"); }
        | STUN_REFRESH_INTERVAL EQUAL NUMBER { IF_STUN(stun_refresh_interval=$3); }
        | STUN_REFRESH_INTERVAL EQUAL error{ yyerror("number expected"); }
        | STUN_ALLOW_STUN EQUAL NUMBER { IF_STUN(stun_allow_stun=$3); }
@@ -1762,6 +1778,8 @@ actions:
 action:
        fcmd SEMICOLON {$$=$1;}
        | if_cmd {$$=$1;}
+       | switch_cmd {$$=$1;}
+       | while_cmd { $$=$1; }
        | assign_action SEMICOLON {$$=$1;}
        | SEMICOLON /* null action */ {$$=0;}
        | fcmd error { $$=0; yyerror("bad command: missing ';'?"); }
@@ -1770,6 +1788,109 @@ if_cmd:
        IF exp stm              { $$=mk_action( IF_T, 3, EXPR_ST, $2, ACTIONS_ST, $3, NOSUBTYPE, 0); }
        | IF exp stm ELSE stm   { $$=mk_action( IF_T, 3, EXPR_ST, $2, ACTIONS_ST, $3, ACTIONS_ST, $5); }
        ;
+
+ct_rval: rval_expr {
+                       $$=0;
+                       if (!rve_is_constant($1)){
+                               yyerror("constant expected");
+                       }else if (!rve_check_type((enum rval_type*)&i_tmp, $1, 0, 0 ,0)){
+                               yyerror("invalid expression (bad type)");
+                       }else if (i_tmp!=RV_INT){
+                               yyerror("invalid expression type, int expected\n");
+                       }else
+                               $$=$1;
+               }
+;
+single_case:
+       CASE ct_rval COLON actions {
+               $$=0;
+               if ($2==0) yyerror ("bad case label");
+               else if (($$=mk_case_stm($2, $4))==0){
+                               yyerror("internal error: memory allocation failure");
+                               YYABORT;
+               }
+       }
+       | CASE ct_rval COLON {
+               $$=0;
+               if ($2==0) yyerror ("bad case label");
+               else if (($$=mk_case_stm($2, 0))==0){
+                               yyerror("internal error: memory allocation failure");
+                               YYABORT;
+               }
+       }
+       | DEFAULT COLON actions {
+               if (($$=mk_case_stm(0, $3))==0){
+                               yyerror("internal error: memory allocation failure");
+                               YYABORT;
+               }
+       }
+       | DEFAULT COLON {
+               if (($$=mk_case_stm(0, 0))==0){
+                               yyerror("internal error: memory allocation failure");
+                               YYABORT;
+               }
+       }
+       | CASE error { $$=0; yyerror("bad case label"); }
+       | CASE ct_rval COLON error { $$=0; yyerror ("bad case body"); }
+;
+case_stms:
+       case_stms single_case {
+               $$=$1;
+               if ($2==0) yyerror ("bad case");
+               if ($$){
+                       *($$->append)=$2;
+                       if (*($$->append)!=0)
+                               $$->append=&((*($$->append))->next);
+               }
+       }
+       | single_case {
+               $$=$1;
+               if ($1==0) yyerror ("bad case");
+               else $$->append=&($$->next);
+       }
+;
+switch_cmd:
+         SWITCH rval_expr LBRACE case_stms RBRACE { 
+               $$=0;
+               if ($2==0) yyerror("bad expression in switch(...)");
+               else if ($4==0) yyerror ("bad switch body");
+               else{
+                       $$=mk_action(SWITCH_T, 2, RVE_ST, $2, CASE_ST, $4);
+                       if ($$==0) {
+                               yyerror("internal error");
+                               YYABORT;
+                       }
+               }
+       }
+       | SWITCH rval_expr LBRACE RBRACE {
+               $$=0;
+               warn("empty switch()");
+               if ($2==0) yyerror("bad expression in switch(...)");
+               else{
+                       /* it might have sideffects, so leave it for the optimizer */
+                       $$=mk_action(SWITCH_T, 2, RVE_ST, $2, CASE_ST, 0);
+                       if ($$==0) {
+                               yyerror("internal error");
+                               YYABORT;
+                       }
+               }
+       }
+       | SWITCH error { $$=0; yyerror ("bad expression in switch(...)"); }
+       | SWITCH rval_expr LBRACE error RBRACE 
+               {$$=0; yyerror ("bad switch body"); }
+;
+
+while_cmd:
+       WHILE rval_expr stm {
+               if ($2){
+                       if (rve_is_constant($2))
+                               warn_at(&$2->fpos, "constant value in while(...)");
+               }else
+                       yyerror_at(&$2->fpos, "bad while(...) expression");
+               $$=mk_action( WHILE_T, 2, RVE_ST, $2, ACTIONS_ST, $3);
+       }
+;
+
 /* class_id:
        LBRACK ATTR_USER RBRACK { $$ = AVP_CLASS_USER; }
        | LBRACK ATTR_DOMAIN RBRACK { $$ = AVP_CLASS_DOMAIN; }
@@ -2307,7 +2428,7 @@ cmd:
        | RETURN                        {$$=mk_action(DROP_T, 2, NUMBER_ST, (void*)1, NUMBER_ST, (void*)RETURN_R_F); }
        | RETURN NUMBER                 {$$=mk_action(DROP_T, 2, NUMBER_ST, (void*)$2, NUMBER_ST, (void*)RETURN_R_F);}
        | RETURN RETCODE                {$$=mk_action(DROP_T, 2, RETCODE_ST, 0, NUMBER_ST, (void*)RETURN_R_F);}
-       | BREAK                         {$$=mk_action(DROP_T, 2, NUMBER_ST, 0, NUMBER_ST, (void*)RETURN_R_F); }
+       | BREAK                         {$$=mk_action(DROP_T, 2, NUMBER_ST, 0, NUMBER_ST, (void*)BREAK_R_F); }
        | LOG_TOK LPAREN STRING RPAREN  {$$=mk_action(LOG_T, 2, NUMBER_ST, (void*)4, STRING_ST, $3); }
        | LOG_TOK LPAREN NUMBER COMMA STRING RPAREN     {$$=mk_action(LOG_T, 2, NUMBER_ST, (void*)$3, STRING_ST, $5); }
        | LOG_TOK error                 { $$=0; yyerror("missing '(' or ')' ?"); }
@@ -2562,19 +2683,28 @@ static void get_cpos(struct cfg_pos* pos)
 }
 
 
-static void warn(char* s)
+static void warn_at(struct cfg_pos* p, char* format, ...)
 {
-       if (line!=startline)
+       va_list ap;
+       char s[256];
+       
+       va_start(ap, format);
+       vsnprintf(s, sizeof(s), format, ap);
+       va_end(ap);
+       if (p->e_line!=p->s_line)
                LOG(L_WARN, "cfg. warning: (%d,%d-%d,%d): %s\n",
-                                       startline, startcolumn, line, column-1, s);
-       else if (startcolumn!=(column-1))
-               LOG(L_WARN, "cfg. warning: (%d,%d-%d): %s\n", startline, startcolumn,
-                                       column-1, s);
+                                       p->s_line, p->s_col, p->e_line, p->e_col, s);
+       else if (p->s_col!=p->e_col)
+               LOG(L_WARN, "cfg. warning: (%d,%d-%d): %s\n",
+                                       p->s_line, p->s_col, p->e_col, s);
        else
-               LOG(L_WARN, "cfg. warning: (%d,%d): %s\n", startline, startcolumn, s);
+               LOG(L_WARN, "cfg. warning: (%d,%d): %s\n",
+                               p->s_line, p->s_col, s);
        cfg_warnings++;
 }
 
+
+
 static void yyerror_at(struct cfg_pos* p, char* format, ...)
 {
        va_list ap;
@@ -2596,6 +2726,22 @@ static void yyerror_at(struct cfg_pos* p, char* format, ...)
 }
 
 
+
+static void warn(char* format, ...)
+{
+       va_list ap;
+       char s[256];
+       struct cfg_pos pos;
+       
+       get_cpos(&pos);
+       va_start(ap, format);
+       vsnprintf(s, sizeof(s), format, ap);
+       va_end(ap);
+       warn_at(&pos, s);
+}
+
+
+
 static void yyerror(char* format, ...)
 {
        va_list ap;
@@ -2763,6 +2909,23 @@ static void free_socket_id_lst(struct socket_id* lst)
        }
 }
 
+
+static struct case_stms* mk_case_stm(struct rval_expr* ct, struct action* a)
+{
+       struct case_stms* s;
+       s=pkg_malloc(sizeof(*s));
+       if (s==0) {
+               LOG(L_CRIT,"ERROR: cfg. parser: out of memory.\n");
+       } else {
+               memset(s, 0, sizeof(*s));
+               s->ct_rve=ct;
+               s->actions=a;
+               s->next=0;
+               s->append=0;
+       }
+       return s;
+}
+
 /*
 int main(int argc, char ** argv)
 {
index f3c511e..9a2b0b2 100644 (file)
@@ -88,6 +88,7 @@ struct cfg_group_core default_core_cfg = {
 #ifdef SHM_MEM
        0, /* mem_dump_shm */
 #endif
+       DEFAULT_MAX_WHILE_LOOPS, /* max_while_loops */
 };
 
 void   *core_cfg = &default_core_cfg;
@@ -177,5 +178,7 @@ cfg_def_t core_cfg_def[] = {
        {"mem_dump_shm",        CFG_VAR_INT,    0, 0, mem_dump_shm_fixup, 0,
                "dump shared memory status"},
 #endif
+       {"max_while_loops",     CFG_VAR_INT|CFG_ATOMIC, 0, 0, 0, 0,
+               "maximum iterations allowed for a while loop" },
        {0, 0, 0, 0, 0, 0}
 };
index d916563..8e5457a 100644 (file)
@@ -85,6 +85,7 @@ struct cfg_group_core {
 #ifdef SHM_MEM
        int mem_dump_shm;
 #endif
+       int max_while_loops;
 };
 
 extern struct cfg_group_core default_core_cfg;
index 393d1f3..0832eb0 100644 (file)
--- a/config.h
+++ b/config.h
 
 #define DEFAULT_DID "_default"
 
+/*  maximum allowed iterations for a while (to catch runaways) */
+#define DEFAULT_MAX_WHILE_LOOPS 100
+
 #endif
diff --git a/lib/kmi/Makefile b/lib/kmi/Makefile
new file mode 100644 (file)
index 0000000..9a70acf
--- /dev/null
@@ -0,0 +1,9 @@
+include ../../Makefile.defs
+auto_gen=
+NAME:=kmi
+MAJOR_VER=1
+MINOR_VER=0
+BUGFIX_VER=0
+LIBS=
+
+include ../../Makefile.libs
diff --git a/lib/kmi/attr.c b/lib/kmi/attr.c
new file mode 100644 (file)
index 0000000..1f254d0
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * $Id: attr.c 4518 2008-07-28 15:39:28Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * History:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+/*!
+ * \file 
+ * \brief MI :: Attributes
+ * \ingroup mi
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "../../mem/mem.h"
+#include "../../dprint.h"
+#include "attr.h"
+#include "fmt.h"
+
+
+extern char *mi_ap_buf;
+extern int  mi_ap_buf_len;
+
+
+struct mi_attr *add_mi_attr(struct mi_node *node, int flags,
+                                               char *name, int name_len, char *value, int value_len)
+{
+       struct mi_attr *new, *p;
+       int size_mem, name_pos, value_pos;
+
+       if(!node)
+               return NULL;
+
+       if (!name) name_len=0;
+       if (!name_len) name=0;
+       if (!value) value_len=0;
+       if (!value_len) value=0;
+
+       if(!name && !value)
+               return NULL;
+
+       size_mem = sizeof(struct mi_attr);
+       value_pos = name_pos = 0;
+
+       if(name && (flags & MI_DUP_NAME)){
+               name_pos = size_mem;
+               size_mem += name_len;
+       }
+       if(value && (flags & MI_DUP_VALUE)){
+               value_pos = size_mem;
+               size_mem += value_len;
+       }
+
+       new = (struct mi_attr *)pkg_malloc(size_mem);
+       if (!new) {
+               LM_ERR("no more pkg mem (%d)\n",size_mem);
+               return NULL;
+       }
+       memset(new,0,size_mem);
+
+       if (name) {
+               new->name.len = name_len;
+               if(flags & MI_DUP_NAME){
+                       new->name.s = ((char *)new) + name_pos;
+                       strncpy(new->name.s, name, name_len);
+               } else{
+                       new->name.s = name;
+               }
+       }
+
+       if (value) {
+               new->value.len = value_len;
+               if(flags & MI_DUP_VALUE){
+                       new->value.s = ((char *)new) + value_pos;
+                       strncpy(new->value.s, value, value_len);
+               }else{
+                       new->value.s = value;
+               }
+       }
+       if(flags & MI_DUP_NAME){
+               name_pos = size_mem;
+               size_mem += name_len * sizeof(char);
+       }
+       if(flags & MI_DUP_VALUE){
+               value_pos = size_mem;
+               size_mem += value_len * sizeof(char);
+       }
+
+       if(!(node->attributes)){
+               new->next = NULL;
+               return (node->attributes = new);
+       }
+
+       for(p = node->attributes ; p->next ; p = p->next);
+
+       new->next = NULL;
+       p->next = new;
+
+       return new;
+}
+
+
+
+struct mi_attr *addf_mi_attr(struct mi_node *node, int flags,
+                                                       char *name, int name_len, char *fmt_val, ...)
+{
+       va_list ap;
+       char *p;
+       int  len = 0;
+
+       va_start(ap, fmt_val);
+       p = mi_print_fmt( fmt_val, ap, &len);
+       va_end(ap);
+       if (p==NULL)
+               return 0;
+       return add_mi_attr(node, flags|MI_DUP_VALUE, name, name_len, p, len);
+}
+
+
+
+struct mi_attr *get_mi_attr_by_name(struct mi_node *node, char *name, int len)
+{
+       struct mi_attr *head;
+
+       if(!node || !name || !(node->attributes))
+               return NULL;
+
+       for(head = node->attributes ; head->next ; head = head->next)
+               if(len == head->name.len 
+               && !strncasecmp(name, head->name.s, head->name.len))
+                       return head;
+
+       return NULL;
+}
+
+
+void del_mi_attr_list(struct mi_node *node)
+{
+       struct mi_attr *p, *head;
+
+       if(!node || !(node->attributes))
+               return;
+
+       for(head = node->attributes; head ;){
+               p = head->next;
+               pkg_free(head);
+               head = p;
+       }
+
+       node->attributes = NULL;
+}
+
diff --git a/lib/kmi/attr.h b/lib/kmi/attr.h
new file mode 100644 (file)
index 0000000..179dab7
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * $Id: attr.h 4518 2008-07-28 15:39:28Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * History:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+/*!
+ * \file 
+ * \brief MI :: Attributes
+ * \ingroup mi
+ */
+
+#ifndef _MI_ATTR_H
+#define _MI_ATTR_H
+
+#include <stdarg.h>
+#include "../../str.h"
+#include "tree.h"
+
+struct mi_attr{
+       str name;
+       str value;
+       struct mi_attr *next;
+};
+
+
+struct mi_attr *add_mi_attr(struct mi_node *node, int flags,
+       char *name, int name_len, char *value, int value_len);
+
+struct mi_attr *addf_mi_attr(struct mi_node *node, int flags,
+       char *name, int name_len, char *fmt_val, ...);
+
+struct mi_attr *get_mi_attr_by_name(struct mi_node *node,
+       char *name, int len);
+
+void del_mi_attr_list(struct mi_node *node);
+
+
+#endif
+
diff --git a/lib/kmi/fmt.c b/lib/kmi/fmt.c
new file mode 100644 (file)
index 0000000..81ca917
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * $Id: fmt.c 4518 2008-07-28 15:39:28Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * History:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+/*!
+ * \file 
+ * \brief MI :: Format handling
+ * \ingroup mi
+ */
+
+
+#include <string.h>
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+
+char *mi_fmt_buf = 0;
+int  mi_fmt_buf_len = 0;
+
+
+int mi_fmt_init( unsigned int size )
+{
+       mi_fmt_buf = (char*)pkg_malloc(size);
+       if (mi_fmt_buf==NULL) {
+               LM_ERR("no more pkg mem\n");
+               return -1;
+       }
+       mi_fmt_buf_len = size;
+       return 0;
+}
+
+
diff --git a/lib/kmi/fmt.h b/lib/kmi/fmt.h
new file mode 100644 (file)
index 0000000..73a8027
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * $Id: fmt.h 4518 2008-07-28 15:39:28Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * History:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+/*!
+ * \file 
+ * \brief MI :: Format handling
+ * \ingroup mi
+ */
+
+
+
+#ifndef _MI_FMT_H_
+#define _MI_FMT_H_
+
+#include <stdarg.h>
+#include <errno.h>
+
+/*! \brief size of the buffer used for printing the FMT */
+#define DEFAULT_MI_FMT_BUF_SIZE 2048
+
+extern char *mi_fmt_buf;
+extern int  mi_fmt_buf_len;
+
+int mi_fmt_init( unsigned int size );
+
+static inline char* mi_print_fmt(char *fmt, va_list ap, int *len)
+{
+       int n;
+
+       if (mi_fmt_buf==NULL) {
+               if (mi_fmt_init(DEFAULT_MI_FMT_BUF_SIZE)!=0) {
+                       LM_ERR("failed to init\n");
+                       return 0;
+               }
+       }
+
+       n = vsnprintf( mi_fmt_buf, mi_fmt_buf_len, fmt, ap);
+       if (n<0 || n>=mi_fmt_buf_len) {
+               LM_ERR("formatting failed with n=%d, %s\n",n,strerror(errno));
+               return 0;
+       }
+
+       *len = n;
+       return mi_fmt_buf;
+}
+
+#endif
diff --git a/lib/kmi/mi.c b/lib/kmi/mi.c
new file mode 100644 (file)
index 0000000..6dfa709
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * $Id: mi.c 4565 2008-08-05 14:58:52Z klaus_darilion $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * History:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+/*!
+ * \file 
+ * \brief MI :: Attributes
+ * \ingroup mi
+ */
+
+/*!
+ * \defgroup mi Kamailio Management Interface
+ * 
+ * The Kamailio management interface (MI) is a plugin architecture with a few different 
+ * handlers that gives access to the management interface over various transports.
+ *
+ * The Kamailio core and modules register commands to the interface at runtime.
+ * Look into the various module documentation files for information of these
+ * commands.
+ *
+ */
+
+#include <string.h>
+
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "mi.h"
+
+static struct mi_cmd*  mi_cmds = 0;
+static int mi_cmds_no = 0;
+
+
+static inline int get_mi_id( char *name, int len)
+{
+       int n;
+       int i;
+
+       for( n=0,i=0 ; i<len ; n+=name[i] ,i++ );
+       return n;
+}
+
+
+static inline struct mi_cmd* lookup_mi_cmd_id(int id,char *name, int len)
+{
+       int i;
+
+       for( i=0 ; i<mi_cmds_no ; i++ ) {
+               if ( id==mi_cmds[i].id && len==mi_cmds[i].name.len &&
+               memcmp(mi_cmds[i].name.s,name,len)==0 )
+                       return &mi_cmds[i];
+       }
+
+       return 0;
+}
+
+
+int register_mi_mod( char *mod_name, mi_export_t *mis)
+{
+       int ret;
+       int i;
+
+       if (mis==0)
+               return 0;
+
+       for ( i=0 ; mis[i].name ; i++ ) {
+               ret = register_mi_cmd( mis[i].cmd, mis[i].name, mis[i].param,
+                       mis[i].init_f, mis[i].flags);
+               if (ret!=0) {
+                       LM_ERR("failed to register cmd <%s> for module %s\n",
+                                       mis[i].name,mod_name);
+               }
+       }
+       return 0;
+}
+
+
+int init_mi_child(void)
+{
+       int i;
+
+       for ( i=0 ; i<mi_cmds_no ; i++ ) {
+               if ( mi_cmds[i].init_f && mi_cmds[i].init_f()!=0 ) {
+                       LM_ERR("failed to init <%.*s>\n",
+                                       mi_cmds[i].name.len,mi_cmds[i].name.s);
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+
+
+int register_mi_cmd( mi_cmd_f f, char *name, void *param,
+                                                                       mi_child_init_f in, unsigned int flags)
+{
+       struct mi_cmd *cmds;
+       int id;
+       int len;
+
+       if (f==0 || name==0) {
+               LM_ERR("invalid params f=%p, name=%s\n", f, name);
+               return -1;
+       }
+
+       if (flags&MI_NO_INPUT_FLAG && flags&MI_ASYNC_RPL_FLAG) {
+               LM_ERR("invalids flags for <%s> - "
+                       "async functions must take input\n",name);
+       }
+
+       len = strlen(name);
+       id = get_mi_id(name,len);
+
+       if (lookup_mi_cmd_id( id, name, len)) {
+               LM_ERR("command <%.*s> already registered\n", len, name);
+               return -1;
+       }
+
+       cmds = (struct mi_cmd*)pkg_realloc( mi_cmds,
+                       (mi_cmds_no+1)*sizeof(struct mi_cmd) );
+       if (cmds==0) {
+               LM_ERR("no more pkg memory\n");
+               return -1;
+       }
+
+       mi_cmds = cmds;
+       mi_cmds_no++;
+
+       cmds = &cmds[mi_cmds_no-1];
+
+       cmds->f = f;
+       cmds->init_f = in;
+       cmds->flags = flags;
+       cmds->name.s = name;
+       cmds->name.len = len;
+       cmds->id = id;
+       cmds->param = param;
+
+       return 0;
+}
+
+
+struct mi_cmd* lookup_mi_cmd( char *name, int len)
+{
+       int id;
+
+       id = get_mi_id(name,len);
+       return lookup_mi_cmd_id( id, name, len);
+}
+
+
+void get_mi_cmds( struct mi_cmd** cmds, int *size)
+{
+       *cmds = mi_cmds;
+       *size = mi_cmds_no;
+}
+
+
diff --git a/lib/kmi/mi.h b/lib/kmi/mi.h
new file mode 100644 (file)
index 0000000..5c0ac19
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * $Id: mi.h 5003 2008-09-26 11:01:51Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * History:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+/*!
+ * \file 
+ * \brief MI :: Management
+ * \ingroup mi
+ */
+
+
+#ifndef _MI_MI_H_
+#define _MI_MI_H_
+
+#include "../../str.h"
+#include "tree.h"
+
+#define MI_ASYNC_RPL_FLAG   (1<<0)
+#define MI_NO_INPUT_FLAG    (1<<1)
+
+#define MI_ROOT_ASYNC_RPL   ((struct mi_root*)-1)
+
+struct mi_handler;
+
+typedef struct mi_root* (mi_cmd_f)(struct mi_root*, void *param);
+typedef int (mi_child_init_f)(void);
+typedef void (mi_handler_f)(struct mi_root *, struct mi_handler *, int);
+
+
+struct mi_handler {
+       mi_handler_f *handler_f;
+       void * param;
+};
+
+
+struct mi_cmd {
+       int id;
+       str name;
+       mi_child_init_f *init_f;
+       mi_cmd_f *f;
+       unsigned int flags;
+       void *param;
+};
+
+
+typedef struct mi_export_ {
+       char *name;
+       mi_cmd_f *cmd;
+       unsigned int flags;
+       void *param;
+       mi_child_init_f *init_f;
+}mi_export_t;
+
+
+int register_mi_cmd( mi_cmd_f f, char *name, void *param,
+               mi_child_init_f in, unsigned int flags);
+
+int register_mi_mod( char *mod_name, mi_export_t *mis);
+
+int init_mi_child(void);
+
+struct mi_cmd* lookup_mi_cmd( char *name, int len);
+
+static inline struct mi_root* run_mi_cmd(struct mi_cmd *cmd, struct mi_root *t)
+{
+       return cmd->f( t, cmd->param);
+}
+
+void get_mi_cmds( struct mi_cmd** cmds, int *size);
+
+int init_mi_core(void);
+
+#endif
+
diff --git a/lib/kmi/mi_core.c b/lib/kmi/mi_core.c
new file mode 100644 (file)
index 0000000..debd599
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * $Id: mi_core.c 4762 2008-08-28 11:27:31Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * history:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+
+/*!
+ * \file 
+ * \brief MI :: Core 
+ * \ingroup mi
+ */
+
+
+
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#include "../../dprint.h"
+#include "../../globals.h"
+#include "../../ut.h"
+#include "../../pt.h"
+#include "../../mem/mem.h"
+#include "mi.h"
+#include "../../version.h"
+
+#define BUILD_STR __FILE__ " compiled on "__TIME__ " " __DATE__ " with " COMPILER "\n"
+#define BUILD_STR_LEN (sizeof(BUILD_STR)-1)
+
+#ifndef SVNREVISION
+#define SVNREVISION "unknown"
+#endif
+
+static time_t kmi_up_since;
+static str    kmi_up_since_ctime;
+
+static int init_mi_uptime(void)
+{
+       char *p;
+
+       time(&kmi_up_since);
+       p = ctime(&kmi_up_since);
+       kmi_up_since_ctime.len = strlen(p)-1;
+       kmi_up_since_ctime.s = (char*)pkg_malloc(kmi_up_since_ctime.len);
+       if (kmi_up_since_ctime.s==0) {
+               LM_ERR("no more pkg mem\n");
+               return -1;
+       }
+       memcpy(kmi_up_since_ctime.s, p, kmi_up_since_ctime.len);
+       return 0;
+}
+
+
+static struct mi_root *mi_uptime(struct mi_root *cmd, void *param)
+{
+       struct mi_root *rpl_tree;
+       struct mi_node *rpl;
+       struct mi_node *node;
+       time_t now;
+       char   *p;
+
+       rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
+       if (rpl_tree==0)
+               return 0;
+       rpl = &rpl_tree->node;
+
+       time(&now);
+       p = ctime(&now);
+       node = add_mi_node_child( rpl, MI_DUP_VALUE, MI_SSTR("Now"),
+               p, strlen(p)-1);
+       if (node==0)
+               goto error;
+
+       node = add_mi_node_child( rpl, 0, MI_SSTR("Up since"),
+               kmi_up_since_ctime.s, kmi_up_since_ctime.len);
+       if (node==0)
+               goto error;
+
+       node = addf_mi_node_child( rpl, 0, MI_SSTR("Up time"),
+               "%lu [sec]", (unsigned long)difftime(now, kmi_up_since) );
+       if (node==0)
+               goto error;
+
+       return rpl_tree;
+error:
+       LM_ERR("failed to add node\n");
+       free_mi_tree(rpl_tree);
+       return 0;
+}
+
+static struct mi_root *mi_version(struct mi_root *cmd, void *param)
+{
+       struct mi_root *rpl_tree;
+       struct mi_node *rpl;
+       struct mi_node *node;
+
+       rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
+       if (rpl_tree==0)
+               return 0;
+       rpl = &rpl_tree->node;
+
+       node = add_mi_node_child( rpl, 0, MI_SSTR("Server"), SERVER_HDR+8,
+               SERVER_HDR_LEN-8);
+       if (node==0) 
+               goto error;
+
+       node = add_mi_node_child( rpl, 0, MI_SSTR("Build"), BUILD_STR,
+                       BUILD_STR_LEN);
+       if (node==0) 
+               goto error;
+
+#ifndef KAMAILIO_COMPILE_FLAGS
+#define KAMAILIO_COMPILE_FLAGS SER_COMPILE_FLAGS
+#endif
+
+       node = add_mi_node_child( rpl, 0, MI_SSTR("Flags"), KAMAILIO_COMPILE_FLAGS,
+                       sizeof(KAMAILIO_COMPILE_FLAGS)-1);
+       if (node==0) 
+               goto error;     
+       
+       node = add_mi_node_child( rpl, 0, MI_SSTR("SVN"), SVNREVISION,
+                       sizeof(SVNREVISION)-1);
+       if (node==0) 
+               goto error;
+
+               
+       
+       return rpl_tree;
+error:
+       LM_ERR("failed to add node\n");
+       free_mi_tree(rpl_tree);
+       return 0;
+
+}
+
+
+
+static struct mi_root *mi_pwd(struct mi_root *cmd, void *param)
+{
+       static int max_len = 0;
+       static char *cwd_buf = 0;
+       struct mi_root *rpl_tree;
+       struct mi_node *rpl;
+       struct mi_node *node;
+
+       if (cwd_buf==NULL) {
+               max_len = pathmax();
+               cwd_buf = pkg_malloc(max_len);
+               if (cwd_buf==NULL) {
+                       LM_ERR("no more pkg mem\n");
+                       return 0;
+               }
+       }
+
+       rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
+       if (rpl_tree==0)
+               return 0;
+       rpl = &rpl_tree->node;
+
+       if (getcwd(cwd_buf, max_len)==0) {
+               LM_ERR("getcwd failed = %s\n",strerror(errno));
+               goto error;
+       }
+
+       node = add_mi_node_child( rpl, 0, MI_SSTR("WD"), cwd_buf,strlen(cwd_buf));
+       if (node==0) {
+               LM_ERR("failed to add node\n");
+               goto error;
+       }
+
+       return rpl_tree;
+error:
+       free_mi_tree(rpl_tree);
+       return 0;
+}
+
+
+
+static struct mi_root *mi_arg(struct mi_root *cmd, void *param)
+{
+       struct mi_root *rpl_tree;
+       struct mi_node *rpl;
+       struct mi_node *node;
+       int n;
+
+       rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
+       if (rpl_tree==0)
+               return 0;
+       rpl = &rpl_tree->node;
+
+       for ( n=0; n<my_argc ; n++ ) {
+               node = add_mi_node_child(rpl, 0, 0, 0, my_argv[n], strlen(my_argv[n]));
+               if (node==0) {
+                       LM_ERR("failed to add node\n");
+                       free_mi_tree(rpl_tree);
+                       return 0;
+               }
+       }
+
+       return rpl_tree;
+}
+
+
+
+static struct mi_root *mi_which(struct mi_root *cmd, void *param)
+{
+       struct mi_root *rpl_tree;
+       struct mi_cmd  *cmds;
+       struct mi_node *rpl;
+       struct mi_node *node;
+       int size;
+       int i;
+
+       rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
+       if (rpl_tree==0)
+               return 0;
+       rpl = &rpl_tree->node;
+
+       get_mi_cmds( &cmds, &size);
+       for ( i=0 ; i<size ; i++ ) {
+               node = add_mi_node_child( rpl, 0, 0, 0, cmds[i].name.s,
+                       cmds[i].name.len);
+               if (node==0) {
+                       LM_ERR("failed to add node\n");
+                       free_mi_tree(rpl_tree);
+                       return 0;
+               }
+       }
+
+       return rpl_tree;
+}
+
+
+#if 0
+static struct mi_root *mi_ps(struct mi_root *cmd, void *param)
+{
+       struct mi_root *rpl_tree;
+       struct mi_node *rpl;
+       struct mi_node *node;
+       struct mi_attr *attr;
+       char *p;
+       int len;
+       int i;
+
+       rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
+       if (rpl_tree==0)
+               return 0;
+       rpl = &rpl_tree->node;
+
+       for ( i=0 ; i<counted_processes ; i++ ) {
+               node = add_mi_node_child(rpl, 0, MI_SSTR("Process"), 0, 0 );
+               if (node==0)
+                       goto error;
+
+               p = int2str((unsigned long)i, &len);
+               attr = add_mi_attr( node, MI_DUP_VALUE, MI_SSTR("ID"), p, len);
+               if (attr==0)
+                       goto error;
+
+               p = int2str((unsigned long)pt[i].pid, &len);
+               attr = add_mi_attr( node, MI_DUP_VALUE, MI_SSTR("PID"), p, len);
+               if (attr==0)
+                       goto error;
+
+               attr = add_mi_attr( node, 0, MI_SSTR("Type"),
+                       pt[i].desc, strlen(pt[i].desc));
+               if (attr==0)
+                       goto error;
+       }
+
+       return rpl_tree;
+error:
+       LM_ERR("failed to add node\n");
+       free_mi_tree(rpl_tree);
+       return 0;
+}
+#endif
+
+
+static struct mi_root *mi_kill(struct mi_root *cmd, void *param)
+{
+       kill(0, SIGTERM);
+
+       return 0;
+}
+
+
+#if 0
+static struct mi_root *mi_debug(struct mi_root *cmd, void *param)
+{
+       struct mi_root *rpl_tree;
+       struct mi_node *node;
+       char *p;
+       int len;
+       int new_debug;
+
+#ifdef CHANGEABLE_DEBUG_LEVEL
+       node = cmd->node.kids;
+       if (node!=NULL) {
+               if (str2sint( &node->value, &new_debug) < 0)
+                       return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM));
+       } else
+               new_debug = *debug;
+#else
+               new_debug = debug;
+#endif
+
+       rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
+       if (rpl_tree==0)
+               return 0;
+
+       p = sint2str((long)new_debug, &len);
+       node = add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE,
+               MI_SSTR("DEBUG"),p, len);
+       if (node==0) {
+               free_mi_tree(rpl_tree);
+               return 0;
+       }
+
+#ifdef CHANGEABLE_DEBUG_LEVEL
+       *debug = new_debug;
+#endif
+
+       return rpl_tree;
+}
+#endif
+
+
+static mi_export_t mi_core_cmds[] = {
+       { "uptime",   mi_uptime,   MI_NO_INPUT_FLAG,  0,  init_mi_uptime },
+       { "version",  mi_version,  MI_NO_INPUT_FLAG,  0,  0 },
+       { "pwd",      mi_pwd,      MI_NO_INPUT_FLAG,  0,  0 },
+       { "arg",      mi_arg,      MI_NO_INPUT_FLAG,  0,  0 },
+       { "which",    mi_which,    MI_NO_INPUT_FLAG,  0,  0 },
+       { "kill",     mi_kill,     MI_NO_INPUT_FLAG,  0,  0 },
+#if 0
+       { "ps",       mi_ps,       MI_NO_INPUT_FLAG,  0,  0 },
+       { "debug",    mi_debug,                   0,  0,  0 },
+#endif
+       { 0, 0, 0, 0, 0}
+};
+
+
+
+int init_mi_core(void)
+{
+       if (register_mi_mod( "core", mi_core_cmds)<0) {
+               LM_ERR("unable to register core MI cmds\n");
+               return -1;
+       }
+
+       return 0;
+}
diff --git a/lib/kmi/mi_core.h b/lib/kmi/mi_core.h
new file mode 100644 (file)
index 0000000..481aeed
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * $Id: mi_core.h 5003 2008-09-26 11:01:51Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * History:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+/*!
+ * \file 
+ * \brief MI :: Core
+ * \ingroup mi
+ */
+
+
+
+#ifndef _MI_MI_CORE_H_
+#define _MI_MI_CORE_H_
+
+// int init_mi_core(void);
+
+#endif
diff --git a/lib/kmi/tree.c b/lib/kmi/tree.c
new file mode 100644 (file)
index 0000000..4ca6578
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * $Id: tree.c 4518 2008-07-28 15:39:28Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * History:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+/*!
+ * \file 
+ * \brief MI :: Tree 
+ * \ingroup mi
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
+#include "../../dprint.h"
+#include "tree.h"
+#include "fmt.h"
+
+
+static int use_shm = 0;
+
+struct mi_root *init_mi_tree(unsigned int code, char *reason, int reason_len)
+{
+       struct mi_root *root;
+
+       if (use_shm)
+               root = (struct mi_root *)shm_malloc(sizeof(struct mi_root));
+       else
+               root = (struct mi_root *)pkg_malloc(sizeof(struct mi_root));
+       if (!root) {
+               LM_ERR("no more pkg mem\n");
+               return NULL;
+       }
+
+       memset(root,0,sizeof(struct mi_root));
+       root->node.next = root->node.last = &root->node;
+
+       if (reason && reason_len) {
+               root->reason.s = reason;
+               root->reason.len = reason_len;
+       }
+       root->code = code;
+
+       return root;
+}
+
+
+static void free_mi_node(struct mi_node *parent)
+{
+       struct mi_node *p, *q;
+
+       for(p = parent->kids ; p ; ){
+               q = p;
+               p = p->next;
+               free_mi_node(q);
+       }
+
+       if (use_shm) {
+               shm_free(parent);
+       } else {
+               del_mi_attr_list(parent);
+               pkg_free(parent);
+       }
+}
+
+void free_mi_tree(struct mi_root *parent)
+{
+       struct mi_node *p, *q;
+
+       for(p = parent->node.kids ; p ; ){
+               q = p;
+               p = p->next;
+               free_mi_node(q);
+       }
+
+       if (use_shm)
+               shm_free(parent);
+       else
+               pkg_free(parent);
+}
+
+
+static inline struct mi_node *create_mi_node(char *name, int name_len,
+                                                                       char *value, int value_len, int flags)
+{
+       struct mi_node *new;
+       int size_mem;
+       int name_pos;
+       int value_pos;
+
+       if (!name) name_len=0;
+       if (!name_len) name=0;
+       if (!value) value_len=0;
+       if (!value_len) value=0;
+
+       if (!name && !value)
+               return NULL;
+
+       size_mem = sizeof(struct mi_node);
+       value_pos = name_pos = 0;
+
+       if (name && (flags & MI_DUP_NAME)){
+               name_pos = size_mem;
+               size_mem += name_len;
+       }
+       if (value && (flags & MI_DUP_VALUE)){
+               value_pos = size_mem;
+               size_mem += value_len;
+       }
+
+       if (use_shm)
+               new = (struct mi_node *)shm_malloc(size_mem);
+       else
+               new = (struct mi_node *)pkg_malloc(size_mem);
+       if(!new) {
+               LM_ERR("no more pkg mem\n");
+               return NULL;
+       }
+       memset(new,0,size_mem);
+
+       if (name) {
+               new->name.len = name_len;
+               if(flags & MI_DUP_NAME){
+                       new->name.s = ((char *)new) + name_pos;
+                       strncpy(new->name.s, name, name_len);
+               } else{
+                       new->name.s = name;
+               }
+       }
+
+       if (value) {
+               new->value.len = value_len;
+               if(flags & MI_DUP_VALUE){
+                       new->value.s = ((char *)new) + value_pos;
+                       strncpy(new->value.s, value, value_len);
+               }else{
+                       new->value.s = value;
+               }
+       }
+       new->last = new;
+
+       return new;
+}
+
+
+static inline struct mi_node *add_next(struct mi_node *brother,
+                       char *name, int name_len, char *value, int value_len, int flags)
+{
+       struct mi_node *new;
+
+       if(!brother)
+               return NULL;
+       
+       new = create_mi_node(name, name_len, value, value_len, flags);
+       if(!new)
+               return NULL;
+
+       brother->last->next = new;
+       brother->last = new;
+
+       return new;
+}
+
+
+struct mi_node *add_mi_node_sibling( struct mi_node *brother, int flags,
+                                               char *name, int name_len, char *value, int value_len)
+{
+       return add_next(brother, name, name_len, value, value_len, flags);
+}
+
+
+struct mi_node *addf_mi_node_sibling(struct mi_node *brother, int flags,
+                                                       char *name, int name_len, char *fmt_val, ...)
+{
+       va_list ap;
+       char *p;
+       int  len;
+
+       va_start(ap, fmt_val);
+       p = mi_print_fmt( fmt_val, ap, &len);
+       va_end(ap);
+       if (p==NULL)
+               return 0;
+       return add_mi_node_sibling( brother, flags|MI_DUP_VALUE,
+               name, name_len, p, len);
+}
+
+
+struct mi_node *add_mi_node_child( struct mi_node *parent, int flags,
+                                               char *name, int name_len, char *value, int value_len)
+{
+       if(parent->kids){
+               return add_next(parent->kids, name, name_len, value, value_len, flags);
+       }else{
+               parent->kids = create_mi_node(name, name_len, value, value_len, flags);
+               return parent->kids;
+       }
+}
+
+
+struct mi_node *addf_mi_node_child(struct mi_node *parent, int flags,
+                                                       char *name, int name_len, char *fmt_val, ...)
+{
+       va_list ap;
+       char *p;
+       int  len;
+
+       va_start(ap, fmt_val);
+       p = mi_print_fmt( fmt_val, ap, &len);
+       va_end(ap);
+       if (p==NULL)
+               return 0;
+       return add_mi_node_child( parent, flags|MI_DUP_VALUE,
+               name, name_len, p, len);
+}
+
+
+static int clone_mi_node(struct mi_node *org, struct mi_node *parent)
+{
+       struct mi_node *p, *q;
+
+       for(p = org->kids ; p ; p=p->next){
+               q = add_mi_node_child( parent, MI_DUP_VALUE|MI_DUP_NAME,
+                       p->name.s, p->name.len, p->value.s, p->value.len);
+               if (q==NULL)
+                       return -1;
+               if (clone_mi_node( p, q)!=0)
+                       return -1;
+       }
+       return 0;
+}
+
+
+struct mi_root* clone_mi_tree(struct mi_root *org, int shm)
+{
+       struct mi_root *root;
+
+       use_shm = shm?1:0;
+
+       root = init_mi_tree( org->code, org->reason.s, org->reason.len);
+       if (root==NULL)
+               goto done;
+
+       if (clone_mi_node( &(org->node), &(root->node) )!=0 ) {
+               free_mi_tree(root);
+               root = NULL;
+               goto done;
+       }
+
+done:
+       use_shm=0;
+       return root;
+}
+
+
+
+void free_shm_mi_tree(struct mi_root *parent)
+{
+       use_shm = 1;
+       free_mi_tree(parent);
+       use_shm = 0;
+}
diff --git a/lib/kmi/tree.h b/lib/kmi/tree.h
new file mode 100644 (file)
index 0000000..4a42823
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * $Id: tree.h 4518 2008-07-28 15:39:28Z henningw $
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * History:
+ * ---------
+ *  2006-09-08  first version (bogdan)
+ */
+
+/*!
+ * \file 
+ * \brief MI :: Tree
+ * \ingroup mi
+ */
+
+
+
+
+#ifndef _MI_TREE_H
+#define _MI_TREE_H
+
+#include <stdarg.h>
+#include "../../str.h"
+
+struct mi_node;
+struct mi_handler;
+
+#include "attr.h"
+
+#define MI_DUP_NAME   (1<<0)
+#define MI_DUP_VALUE  (1<<1)
+
+#define MI_OK_S              "OK"
+#define MI_OK_LEN            (sizeof(MI_OK_S)-1)
+#define MI_INTERNAL_ERR_S    "Server Internal Error"
+#define MI_INTERNAL_ERR_LEN  (sizeof(MI_INTERNAL_ERR_S)-1)
+#define MI_MISSING_PARM_S    "Too few or too many arguments"
+#define MI_MISSING_PARM_LEN  (sizeof(MI_MISSING_PARM_S)-1)
+#define MI_BAD_PARM_S        "Bad parameter"
+#define MI_BAD_PARM_LEN      (sizeof(MI_BAD_PARM_S)-1)
+
+#define MI_SSTR(_s)           _s,(sizeof(_s)-1)
+#define MI_OK                 MI_OK_S
+#define MI_INTERNAL_ERR       MI_INTERNAL_ERR_S
+#define MI_MISSING_PARM       MI_MISSING_PARM_S
+#define MI_BAD_PARM           MI_BAD_PARM_S
+
+struct mi_node {
+       str value;
+       str name;
+       struct mi_node *kids;
+       struct mi_node *next;
+       struct mi_node *last;
+       struct mi_attr *attributes;
+};
+
+struct mi_root {
+       unsigned int       code;
+       str                reason;
+       struct mi_handler  *async_hdl;
+       struct mi_node     node;
+};
+
+struct mi_root *init_mi_tree(unsigned int code, char *reason, int reason_len);
+
+void free_mi_tree(struct mi_root *parent);
+
+struct mi_node *add_mi_node_sibling(struct mi_node *brother, int flags,
+       char *name, int name_len, char *value, int value_len);
+
+struct mi_node *addf_mi_node_sibling(struct mi_node *brother, int flags,
+       char *name, int name_len, char *fmt_val, ...);
+
+struct mi_node *add_mi_node_child(struct mi_node *parent, int flags,
+       char *name, int name_len, char *value, int value_len);
+
+struct mi_node *addf_mi_node_child(struct mi_node *parent, int flags,
+       char *name, int name_len, char *fmt_val, ...);
+
+struct mi_root* clone_mi_tree(struct mi_root *org, int shm);
+
+void free_shm_mi_tree(struct mi_root *parent);
+
+#endif
+
index 050a75b..9c936df 100644 (file)
@@ -45,6 +45,9 @@
  */
 #ifndef __OS_solaris
        #define _XOPEN_SOURCE 600          /* glibc2 on linux, bsd */
+       #define _BSD_SOURCE 1              /* needed on linux to "fix" the effect
+                                                                                of the above define on 
+                                                                                features.h/unistd.h syscall() */
 #else
        #define _XOPEN_SOURCE_EXTENDED 1   /* solaris */
 #endif
index 80459b8..b93f483 100644 (file)
@@ -52,20 +52,20 @@ extern "C" {
 /*
  * Various database flags shared by modules 
  */
-#define DB_LOAD_SER   (1 << 0)  /* The row should be loaded by SER */
-#define DB_DISABLED   (1 << 1)  /* The row is disabled */
-#define DB_CANON      (1 << 2)  /* Canonical entry (domain or uri) */
-#define DB_IS_TO      (1 << 3)  /* The URI can be used in To */
-#define DB_IS_FROM    (1 << 4)  /* The URI can be used in From */
-#define DB_FOR_SERWEB (1 << 5)  /* Credentials instance can be used by serweb */
-#define DB_PENDING    (1 << 6)
-#define DB_DELETED    (1 << 7)
-#define DB_CALLER_DELETED (1 << 8) /* Accounting table */
-#define DB_CALLEE_DELETED (1 << 9) /* Accounting table */
-#define DB_MULTIVALUE     (1 << 10) /* Attr_types table */
-#define DB_FILL_ON_REG    (1 << 11) /* Attr_types table */
-#define DB_REQUIRED       (1 << 12) /* Attr_types table */
-#define DB_DIR            (1 << 13) /* Domain_settings table */
+#define SRDB_LOAD_SER   (1 << 0)  /* The row should be loaded by SER */
+#define SRDB_DISABLED   (1 << 1)  /* The row is disabled */
+#define SRDB_CANON      (1 << 2)  /* Canonical entry (domain or uri) */
+#define SRDB_IS_TO      (1 << 3)  /* The URI can be used in To */
+#define SRDB_IS_FROM    (1 << 4)  /* The URI can be used in From */
+#define SRDB_FOR_SERWEB (1 << 5)  /* Credentials instance can be used by serweb */
+#define SRDB_PENDING    (1 << 6)
+#define SRDB_DELETED    (1 << 7)
+#define SRDB_CALLER_DELETED (1 << 8) /* Accounting table */
+#define SRDB_CALLEE_DELETED (1 << 9) /* Accounting table */
+#define SRDB_MULTIVALUE     (1 << 10) /* Attr_types table */
+#define SRDB_FILL_ON_REG    (1 << 11) /* Attr_types table */
+#define SRDB_REQUIRED       (1 << 12) /* Attr_types table */
+#define SRDB_DIR            (1 << 13) /* Domain_settings table */
 
 
 struct db_gen;
index 32e6dbd..62e0cc9 100644 (file)
@@ -55,7 +55,7 @@ struct dtrie_node_t *dtrie_init(const unsigned int branches)
                SHM_MEM_ERROR;
                return NULL;
        }
-       LM_DBG("allocate %lu bytes for root at %p",
+       LM_DBG("allocate %lu bytes for root at %p\n",
                        (long unsigned)sizeof(struct dtrie_node_t), root);
        memset(root, 0, sizeof(struct dtrie_node_t));
 
@@ -65,7 +65,7 @@ struct dtrie_node_t *dtrie_init(const unsigned int branches)
                SHM_MEM_ERROR;
                return NULL;
        }
-       LM_DBG("allocate %lu bytes for %d root children pointer at %p",
+       LM_DBG("allocate %lu bytes for %d root children pointer at %p\n",
                        (long unsigned)sizeof(struct dtrie_node_t *) * branches,
                        branches, root->child);
        memset(root->child, 0, sizeof(struct dtrie_node_t *) * branches);
@@ -91,7 +91,7 @@ void dtrie_delete(struct dtrie_node_t *root, struct dtrie_node_t *node,
        }
 
        if (node != root) {
-               LM_DBG("free node at %p", node);
+               LM_DBG("free node at %p\n", node);
                shm_free(node);
        }
 }
@@ -101,7 +101,7 @@ void dtrie_destroy(struct dtrie_node_t **root, dt_delete_func_t delete_payload,
 {
        if ((root!=NULL) && (*root!=NULL)) {
                dtrie_delete(*root, *root, delete_payload, branches);
-               LM_DBG("free root at %p", root);
+               LM_DBG("free root at %p\n", root);
                shm_free(*root);
                *root = NULL;
        }
@@ -139,12 +139,12 @@ int dtrie_insert(struct dtrie_node_t *root, const char *number, const unsigned i
                if (node->child[digit] == NULL) {
                        node->child[digit] = shm_malloc(sizeof(struct dtrie_node_t));
                        assert(node->child[digit] != NULL);
-                       LM_DBG("allocate %lu bytes for node at %p", (long unsigned)sizeof(struct dtrie_node_t), node->child[digit]);
+                       LM_DBG("allocate %lu bytes for node at %p\n", (long unsigned)sizeof(struct dtrie_node_t), node->child[digit]);
                        memset(node->child[digit], 0, sizeof(struct dtrie_node_t));
 
                        node->child[digit]->child = shm_malloc(sizeof(struct dtrie_node_t *) * branches);
                        assert(node->child[digit]->child != NULL);
-                       LM_DBG("allocate %lu bytes for %d root children pointer at %p",
+                       LM_DBG("allocate %lu bytes for %d root children pointer at %p\n",
                                        (long unsigned)sizeof(struct dtrie_node_t *) * branches,
                                        branches, node->child[digit]->child);
                        memset(node->child[digit]->child, 0, sizeof(struct dtrie_node_t *) * branches);
diff --git a/mi/mi.h b/mi/mi.h
index d01d94b..b3c106f 100644 (file)
--- a/mi/mi.h
+++ b/mi/mi.h
 #ifndef _mi_h_
 #define _mi_h_
 
-#include "../str.h"
-
-#define MI_DUP_NAME   (1<<0)
-#define MI_DUP_VALUE  (1<<1)
-
-#define MI_OK_S              "OK"
-#define MI_OK_LEN            (sizeof(MI_OK_S)-1)
-#define MI_INTERNAL_ERR_S    "Server Internal Error"
-#define MI_INTERNAL_ERR_LEN  (sizeof(MI_INTERNAL_ERR_S)-1)
-#define MI_MISSING_PARM_S    "Too few or too many arguments"
-#define MI_MISSING_PARM_LEN  (sizeof(MI_MISSING_PARM_S)-1)
-#define MI_BAD_PARM_S        "Bad parameter"
-#define MI_BAD_PARM_LEN      (sizeof(MI_BAD_PARM_S)-1)
-
-#define MI_SSTR(_s)           _s,(sizeof(_s)-1)
-#define MI_OK                 MI_OK_S
-#define MI_INTERNAL_ERR       MI_INTERNAL_ERR_S
-#define MI_MISSING_PARM       MI_MISSING_PARM_S
-#define MI_BAD_PARM           MI_BAD_PARM_S
-
-
-
-struct mi_attr{
-       str name;
-       str value;
-       struct mi_attr *next;
-};
-
-
-struct mi_node {
-       str value;
-       str name;
-       struct mi_node *kids;
-       struct mi_node *next;
-       struct mi_node *last;
-       struct mi_attr *attributes;
-};
-
-
-struct mi_root {
-       unsigned int       code;
-       str                reason;
-       struct mi_handler  *async_hdl;
-       struct mi_node     node;
-};
-
-typedef struct mi_root* (mi_cmd_f)(struct mi_root*, void *param);
-typedef int (mi_child_init_f)(void);
-
-
-typedef struct mi_export_ {
-       char *name;
-       mi_cmd_f *cmd;
-       unsigned int flags;
-       void *param;
-       mi_child_init_f *init_f;
-}mi_export_t;
-
-
-#define init_mi_tree(code, reason, reason_len) 0
-#define free_mi_tree(parent)
-#define add_mi_node_sibling(node, flags, name, name_len, val, val_len) 0
-#define add_mi_node_child(node, flags, name, name_len, val, val_len) 0
-#define addf_mi_node_child(node, flags, name, name_len, fmt, ...) 0
-
+#include "../lib/kmi/mi.h"
 
 #endif /* _mi_h_ */
 
diff --git a/pvapi.c b/pvapi.c
index 2e34412..fa304a5 100644 (file)
--- a/pvapi.c
+++ b/pvapi.c
@@ -1362,15 +1362,9 @@ int tr_table_add(tr_export_t *e)
        tri = _tr_table[trid%TR_TABLE_SIZE];
        while(tri)
        {
-               if(tri->trid > trid)
-                       break;
-               if(tri->tre.tclass.len > e->tclass.len)
-                       break;
                if(tri->tre.tclass.len==e->tclass.len)
                {
                        found = strncmp(tri->tre.tclass.s, e->tclass.s, e->tclass.len);
-                       if(found>0)
-                               break;
                        if(found==0)
                        {
                                LM_ERR("TR class [%.*s] already exists\n", e->tclass.len,
@@ -1470,11 +1464,6 @@ tr_export_t* tr_lookup_class(str *tclass)
        tri = _tr_table[trid%TR_TABLE_SIZE];
        while(tri)
        {
-               if(tri->trid > trid)
-                       break;
-               if(tri->tre.tclass.len > tclass->len)
-                       break;
-
                if(tri->trid==trid && tri->tre.tclass.len==tclass->len
                                && memcmp(tri->tre.tclass.s, tclass->s, tclass->len)==0)
                        return &(tri->tre);
diff --git a/route.c b/route.c
index ad85c00..54e3236 100644 (file)
--- a/route.c
+++ b/route.c
@@ -82,6 +82,7 @@
 #include "str_hash.h"
 #include "ut.h"
 #include "rvalue.h"
+#include "switch.h"
 
 #define RT_HASH_SIZE   8 /* route names hash */
 
@@ -622,6 +623,10 @@ int fix_actions(struct action* a)
        struct ip_addr ip;
        struct socket_info* si;
        struct lvalue* lval;
+       struct rval_expr* rve;
+       struct rval_expr* err_rve;
+       enum rval_type rve_type, err_type, expected_type;
+
        
        char buf[30]; /* tmp buffer needed for module param fixups */
 
@@ -697,7 +702,78 @@ int fix_actions(struct action* a)
                                                return ret;
                                }
                                break;
-
+                       case SWITCH_T:
+                               if (t->val[0].type!=RVE_ST){
+                                       LOG(L_CRIT, "BUG: fix_actions: invalid subtype"
+                                                               "%d for switch() (should be expr)\n",
+                                                               t->val[0].type);
+                                       return E_BUG;
+                               }else if (t->val[1].type!=CASE_ST){
+                                       LOG(L_CRIT, "BUG: fix_actions: invalid subtype"
+                                                               "%d for switch(...){...}(should be case)\n",
+                                                               t->val[1].type);
+                                       return E_BUG;
+                               }
+                               if (t->val[0].u.data){
+                                       if ((ret=fix_rval_expr(&t->val[0].u.data))<0)
+                                               return ret;
+                               }else{
+                                       LOG(L_CRIT, "BUG: fix_actions: null switch()"
+                                                       " expression\n");
+                                       return E_BUG;
+                               }
+                               if ((ret=fix_switch(t))<0)
+                                       return ret;
+                               break;
+                       case WHILE_T:
+                               if (t->val[0].type!=RVE_ST){
+                                       LOG(L_CRIT, "BUG: fix_actions: invalid subtype"
+                                                               "%d for while() (should be expr)\n",
+                                                               t->val[0].type);
+                                       return E_BUG;
+                               }else if (t->val[1].type!=ACTIONS_ST){
+                                       LOG(L_CRIT, "BUG: fix_actions: invalid subtype"
+                                                               "%d for while(...){...}(should be action)\n",
+                                                               t->val[1].type);
+                                       return E_BUG;
+                               }
+                               rve=(struct rval_expr*)t->val[0].u.data;
+                               if (rve){
+                                       err_rve=0;
+                                       if (!rve_check_type(&rve_type, rve, &err_rve,
+                                                                                       &err_type, &expected_type)){
+                                               if (err_rve)
+                                                       LOG(L_ERR, "fix_actions: invalid expression "
+                                                                       "(%d,%d): subexpression (%d,%d) has type"
+                                                                       " %s,  but %s is expected\n",
+                                                                       rve->fpos.s_line, rve->fpos.s_col,
+                                                                       err_rve->fpos.s_line, err_rve->fpos.s_col,
+                                                                       rval_type_name(err_type),
+                                                                       rval_type_name(expected_type) );
+                                               else
+                                                       LOG(L_ERR, "fix_actions: invalid expression "
+                                                                       "(%d,%d): type mismatch?",
+                                                                       rve->fpos.s_line, rve->fpos.s_col);
+                                               return E_UNSPEC;
+                                       }
+                                       if (rve_type!=RV_INT && rve_type!=RV_NONE){
+                                               LOG(L_ERR, "fix_actions: invalid expression (%d,%d):"
+                                                               " bad type, integer expected\n",
+                                                               rve->fpos.s_line, rve->fpos.s_col);
+                                               return E_UNSPEC;
+                                       }
+                                       if ((ret=fix_rval_expr((void**)&rve))<0)
+                                               return ret;
+                               }else{
+                                       LOG(L_CRIT, "BUG: fix_actions: null while()"
+                                                       " expression\n");
+                                       return E_BUG;
+                               }
+                               if ( t->val[1].u.data && 
+                                       ((ret= fix_actions((struct action*)t->val[1].u.data))<0)){
+                                       return ret;
+                               }
+                               break;
                        case ASSIGN_T:
                        case ADD_T:
                                if (t->val[0].type !=LVAL_ST) {
index 76aac25..85242af 100644 (file)
@@ -70,7 +70,9 @@ enum { METHOD_O=1, URI_O, FROM_URI_O, TO_URI_O, SRCIP_O, SRCPORT_O,
 enum { FORWARD_T=1, SEND_T, DROP_T, LOG_T, ERROR_T, ROUTE_T, EXEC_T,
                SET_HOST_T, SET_HOSTPORT_T, SET_USER_T, SET_USERPASS_T,
                SET_PORT_T, SET_URI_T, SET_HOSTPORTTRANS_T,
-               IF_T, MODULE_T, MODULE3_T, MODULE4_T, MODULE5_T, MODULE6_T, MODULEX_T,
+               IF_T, SWITCH_T /* only until fixup*/,
+               BLOCK_T, EVAL_T, SWITCH_JT_T, SWITCH_COND_T, WHILE_T,
+               MODULE_T, MODULE3_T, MODULE4_T, MODULE5_T, MODULE6_T, MODULEX_T,
                SETFLAG_T, RESETFLAG_T, ISFLAGSET_T ,
                AVPFLAG_OPER_T,
                LEN_GT_T, PREFIX_T, STRIP_T,STRIP_TAIL_T,
@@ -96,11 +98,14 @@ enum { NOSUBTYPE=0, STRING_ST, NET_ST, NUMBER_ST, IP_ST, RE_ST, PROXY_ST,
                MYSELF_ST, STR_ST, SOCKID_ST, SOCKETINFO_ST, ACTION_ST, AVP_ST,
                SELECT_ST, PVAR_ST,
                LVAL_ST,  RVE_ST,
-               RETCODE_ST};
+               RETCODE_ST, CASE_ST,
+               BLOCK_ST, JUMPTABLE_ST, CONDTABLE_ST
+};
 
 /* run flags */
 #define EXIT_R_F   1
 #define RETURN_R_F 2
+#define BREAK_R_F  4
 
 
 struct cfg_pos{
index 9e020ef..4c7472d 100644 (file)
--- a/rvalue.c
+++ b/rvalue.c
@@ -452,6 +452,17 @@ int rve_is_constant(struct rval_expr* rve)
 
 
 
+/** returns true if the expression has side-effects.
+  * @return  1 for possible side-effects, 0 for no side-effects
+  * TODO: add better checks
+  */
+int rve_has_side_effects(struct rval_expr* rve)
+{
+       return !rve_is_constant(rve);
+}
+
+
+
 /** returns true if operator is unary (takes only 1 arg).
   * @return 0 or 1 on
   */
index a694c0d..88fd6f2 100644 (file)
--- a/rvalue.h
+++ b/rvalue.h
@@ -188,6 +188,8 @@ int rval_expr_eval_rvint( struct run_act_ctx* h, struct sip_msg* msg,
 enum rval_type rve_guess_type(struct rval_expr* rve);
 /** returns true if expression is constant. */
 int rve_is_constant(struct rval_expr* rve);
+/** returns true if the expression can have side-effect */
+int rve_has_side_effects(struct rval_expr* rve);
 /** returns 1 if expression is valid (type-wise).*/
 int rve_check_type(enum rval_type* type, struct rval_expr* rve,
                                        struct rval_expr** bad_rve, enum rval_type* bad_type,
@@ -206,6 +208,8 @@ struct rval_expr* mk_rval_expr1(enum rval_expr_op op, struct rval_expr* rve1,
 struct rval_expr* mk_rval_expr2(enum rval_expr_op op, struct rval_expr* rve1,
                                                                                                          struct rval_expr* rve2,
                                                                                                          struct cfg_pos* pos);
+/** destroys a pkg_malloc'ed rve. */
+void rve_destroy(struct rval_expr* rve);
 
 /** fix a rval_expr. */
 int fix_rval_expr(void** p);
diff --git a/switch.c b/switch.c
new file mode 100644 (file)
index 0000000..3e15817
--- /dev/null
+++ b/switch.c
@@ -0,0 +1,374 @@
+/* 
+ * $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.
+ */
+/*
+ * switch.c
+ */
+/*
+ * History:
+ * --------
+ *  2009-02-02  initial version (andrei)
+*/
+
+#include "switch.h"
+#include "rvalue.h"
+#include "route.h"
+#include "mem/mem.h"
+#include "error.h"
+
+#define MAX_JT_SIZE 100  /* maximum jump table size */
+
+
+/** create a cond table structure (pkg_malloc'ed).
+ * @return 0 on error, pointer on success
+ */
+static struct switch_cond_table* mk_switch_cond_table(int n)
+{
+       struct switch_cond_table* sct;
+       
+       /* allocate everything in a single block, better for cache locality */
+       sct=pkg_malloc(ROUND_INT(sizeof(*sct))+
+                                       ROUND_POINTER(n*sizeof(sct->cond[0]))+
+                                       n*sizeof(sct->jump[0]));
+       if (sct==0) return 0;
+       sct->n=n;
+       sct->cond=(int*)((char*)sct+ROUND_INT(sizeof(*sct)));
+       sct->jump=(struct action**)
+                               ((char*)sct->cond+ROUND_POINTER(n*sizeof(sct->cond[0])));
+       sct->def=0;
+       return sct;
+}
+
+
+
+/** create a jump table structure (pkg_malloc'ed).
+ * @param jmp_size - size of the jump table
+ * @param rest - size of the fallback condition table
+ * @return 0 on error, pointer on success
+ */
+static struct switch_jmp_table* mk_switch_jmp_table(int jmp_size, int rest)
+{
+       struct switch_jmp_table* jt;
+       int size;
+       
+       /* alloc everything in a block */
+       size =  ROUND_POINTER(sizeof(*jt))+
+                       ROUND_INT(jmp_size*sizeof(jt->tbl[0]))+
+                       ROUND_POINTER(rest*sizeof(jt->rest.cond[0]))+
+                       rest*sizeof(jt->rest.jump[0]);
+       jt=pkg_malloc(size);
+       if (jt == 0) return 0;
+       memset(jt, 0, size);
+       jt->tbl = (struct action**)((char*) jt + ROUND_POINTER(sizeof(*jt)));
+       jt->rest.cond = (int*)
+                                       ((char*) jt->tbl + ROUND_INT(jmp_size*sizeof(jt->tbl[0])));
+       jt->rest.jump = (struct action**) ((char*) jt->rest.cond + 
+                                                               ROUND_POINTER(rest*sizeof(jt->rest.cond[0])));
+       jt->rest.n=rest;
+       return jt;
+}
+
+
+
+void destroy_case_stms(struct case_stms *lst)
+{
+       struct case_stms* l;
+       struct case_stms* n;
+       
+       for (l=lst; l; l=n){
+               n=l->next;
+               rve_destroy(l->ct_rve);
+               /* TODO: the action list is not freed (missing destroy_action() and
+                  there are some case when we need at least part of the action list
+               */
+               pkg_free(l);
+       }
+}
+
+
+
+/** fixup function for SWITCH_T actions.
+ * can produce 4 different action types:
+ *  - BLOCK_T (actions) - actions grouped in a block, break ends the block
+ *    execution.
+ *  - EVAL_T (cond)  - null switch block, but the condition has to be
+ *                       evaluated due to possible side-effects.
+ *  - SWITCH_COND_T(cond, jumps) - condition table
+ *  - SWITCH_JT_T(cond, jumptable) - jumptable + condition table
+ * TODO: external optimizers that would "flatten" BLOCK_T w/no breaks or
+ * breaks at the end.
+ * 
+ */
+int fix_switch(struct action* t)
+{
+       struct case_stms* c;
+       int n, i, j, ret, val;
+       struct action* a;
+       struct action* block;
+       struct action* def_a;
+       struct action* action_lst;
+       struct action** tail;
+       struct switch_jmp_table* jmp;
+       struct switch_cond_table* sct;
+       int labels_no;
+       struct action** def_jmp_bm;
+       int* cond;
+       struct action*** jmp_bm;
+       int default_found;
+       int first, last, start, end, hits, best_hits;
+       struct rval_expr* sw_rve;
+       
+       ret=E_BUG;
+       cond=0;
+       jmp_bm=0;
+       def_jmp_bm=0;
+       labels_no=0;
+       default_found=0;
+       sw_rve=(struct rval_expr*)t->val[0].u.data;
+       /*  handle null actions: optimize away if no
+          sideffects */
+       if (t->val[1].u.data==0){
+               if (!rve_has_side_effects(sw_rve)){
+                       t->type=BLOCK_T;
+                       rve_destroy(sw_rve);
+                       t->val[0].type=BLOCK_ST;
+                       t->val[0].u.data=0;
+                       DBG("SWITCH: null switch optimized away\n");
+               }else{
+                       t->type=EVAL_T;
+                       t->val[0].type=RVE_ST;
+                       DBG("SWITCH: null switch turned to EVAL_T\n");
+               }
+               return 0;
+       }
+       def_a=0;
+       n=0;
+       for (c=(struct case_stms*)t->val[1].u.data; c; c=c->next){
+               if (c->ct_rve){
+                       if (!rve_is_constant(c->ct_rve)){
+                               LOG(L_ERR, "ERROR: fix_switch: non constant "
+                                               "expression in case\n");
+                               return E_BUG;
+                       }
+                       if (rval_expr_eval_int(0, 0,  &c->int_label, c->ct_rve)
+                                       <0){
+                               LOG(L_ERR, "ERROR: fix_switch: case expression"
+                                               " (%d,%d) has non-interger type\n",
+                                               c->ct_rve->fpos.s_line,
+                                               c->ct_rve->fpos.s_col);
+                               return E_BUG;
+                       }
+                       c->is_default=0;
+                       n++; /* count only non-default cases */
+               }else{
+                       if (default_found){
+                               LOG(L_ERR, "ERROR: fix_switch: more then one \"default\""
+                                               " label found (%d, %d)\n",
+                                               c->ct_rve->fpos.s_line,
+                                               c->ct_rve->fpos.s_col);
+                               return E_UNSPEC;
+                       }
+                       default_found=1;
+                       c->int_label=-1;
+                       c->is_default=1;
+                       def_a=c->actions;
+               }
+               if ( c->actions && ((ret=fix_actions(c->actions))<0))
+                       goto error;
+       }
+       DBG("SWITCH: %d cases, %d default\n", n, default_found);
+       /*: handle n==0 (no case only a default:) */
+       if (n==0){
+               if (default_found){
+                       if (!rve_has_side_effects(sw_rve)){
+                               t->type=BLOCK_T;
+                               rve_destroy(sw_rve);
+                               destroy_case_stms(t->val[1].u.data);
+                               t->val[0].type=BLOCK_ST;
+                               t->val[0].u.data=def_a;
+                               t->val[1].type=0;
+                               t->val[1].u.data=0;
+                               DBG("SWITCH: default only switch optimized away (BLOCK_T)\n");
+                               return 0;
+                       }
+                       DBG("SWITCH: default only switch with side-effect...\n");
+               }else{
+                       LOG(L_CRIT, "BUG: fix_switch: empty switch not expected at this"
+                                       " point\n");
+                       ret=E_BUG;
+                       goto error;
+               }
+       }
+       labels_no=n;
+       cond=pkg_malloc(sizeof(cond[0])*n);
+       jmp_bm=pkg_malloc(sizeof(jmp_bm[0])*n);
+       if (cond==0 || jmp_bm==0){
+               LOG(L_ERR, "ERROR: fix_switch: memory allocation failure\n");
+               ret=E_OUT_OF_MEM;
+               goto error;
+       }
+       
+       /* fill condition table and jump point bookmarks and "flatten" the action 
+          lists (transform them into a single list for the entire switch, rather
+           then one block per case ) */
+       n=0;
+       action_lst=0;
+       tail=&action_lst;
+       for (c=(struct case_stms*)t->val[1].u.data; c; c=c->next){
+               a=c->actions;
+               if (a){
+                       for (; a->next; a=a->next);
+                       if (action_lst==0)
+                               action_lst=c->actions;
+                       else
+                               *tail=c->actions;
+               }
+               if (c->is_default){
+                       def_jmp_bm=tail;
+               } else {
+                       for (j=0; j<n; j++){
+                               if (cond[j]==c->int_label){
+                                       LOG(L_ERR, "ERROR: fix_switch: duplicate case (%d,%d)\n",
+                                                       c->ct_rve->fpos.s_line, c->ct_rve->fpos.s_col);
+                                       ret=E_UNSPEC;
+                                       goto error;
+                               }
+                       }
+                       cond[n]=c->int_label;
+                       jmp_bm[n]=tail;
+                       n++;
+               }
+               if (c->actions)
+                       tail=&a->next;
+       }
+       /* handle constant rve w/ no side-effects: replace the whole case 
+          with the case rve block */
+       if ( (scr_opt_lev>=2) &&
+                       !rve_has_side_effects(sw_rve) && rve_is_constant(sw_rve)){
+               if (rval_expr_eval_int(0, 0,  &val, sw_rve) <0){
+                       LOG(L_ERR, "ERROR: fix_switch: wrong type for switch(...) "
+                                       "expression (%d,%d)\n", 
+                                       sw_rve->fpos.s_line, sw_rve->fpos.s_col);
+                       ret=E_UNSPEC;
+                       goto error;
+               }
+               /* start with the "default:" value in case nothing is found */
+               block=def_jmp_bm?*def_jmp_bm:0;
+               for (i=0; i<n; i++){
+                       if (cond[i]==val){
+                               block=*jmp_bm[i];
+                               break;
+                       }
+               }
+               t->type=BLOCK_T;
+               rve_destroy(sw_rve);
+               t->val[0].type=BLOCK_ST;
+               t->val[0].u.data=block;
+               destroy_case_stms(t->val[1].u.data);
+               t->val[1].type=0;
+               t->val[1].u.data=0;
+               ret=0;
+               DBG("SWITCH: constant switch(%d) with %d cases optimized away to case "
+                               " %d \n", val, n, i);
+               goto end;
+       }
+       /* try to create a jumptable */
+       /* cost: 2 cmp & table lookup
+          => makes sense for more then 3 cases
+          & if size< MAX_JT_SIZE
+       */
+       best_hits=3; /* more then 3 hits needed */
+       start=end=0;
+       for (i=0; i<n; i++){
+               last=first=cond[i];
+               hits=1;
+               for (j=0; j<n; j++){
+                       if ((i==j) || (cond[j]<=first)) continue;
+                       if (cond[j]<last)
+                               hits++;
+                       else if ((cond[j]-first)<MAX_JT_SIZE){
+                               last=cond[j];
+                               hits++;
+                       }
+               }
+               if (hits>best_hits){
+                       best_hits=hits;
+                       start=first;
+                       end=last;
+                       if (hits==n) break;
+               }
+       }
+       if (start!=end){
+               /* build jumptable: end-start entries and
+                with a n-best_hits normal switch table */
+               jmp=mk_switch_jmp_table(end-start+1, n-best_hits);
+               if (jmp==0){
+                       LOG(L_ERR, "ERROR: fix_switch: memory allocation error\n");
+                       ret=E_OUT_OF_MEM;
+                       goto error;
+               }
+               jmp->first=start;
+               jmp->last=end;
+               jmp->rest.n=n-best_hits;
+               jmp->rest.def=def_jmp_bm?*def_jmp_bm:0;
+               /* fill it with default values */
+               for (i=0; i<=(end-start); i++)
+                       jmp->tbl[i]=jmp->rest.def;
+               for (i=0, j=0; i<n; i++){
+                       if (cond[i]>=start && cond[i]<=end){
+                               jmp->tbl[cond[i]-start]=*jmp_bm[i];
+                       }else{
+                               jmp->rest.cond[j]=cond[i];
+                               jmp->rest.jump[j]=*jmp_bm[i];
+                               j++;
+                       }
+               }
+               t->type=SWITCH_JT_T;
+               t->val[1].type=JUMPTABLE_ST;
+               t->val[1].u.data=jmp;
+               ret=0;
+               DBG("SWITCH: optimized to jumptable [%d, %d] and %d condtable,"
+                               "default: %s\n ",
+                               jmp->first, jmp->last, jmp->rest.n, jmp->rest.def?"yes":"no");
+       }else{
+               sct=mk_switch_cond_table(n);
+               if (sct==0){
+                       LOG(L_ERR, "ERROR: fix_switch: memory allocation error\n");
+                       ret=E_OUT_OF_MEM;
+                       goto error;
+               }
+               sct->n=n;
+               for (i=0; i<n; i++){
+                       sct->cond[i]=cond[i];
+                       sct->jump[i]=*jmp_bm[i];
+               }
+               sct->def=def_jmp_bm?*def_jmp_bm:0;
+               t->type=SWITCH_COND_T;
+               t->val[1].type=CONDTABLE_ST;
+               t->val[1].u.data=sct;
+               DBG("SWITCH: optimized to condtable (%d) default: %s\n ",
+                               sct->n, sct->def?"yes":"no");
+               ret=0;
+       }
+end:
+error:
+       if (cond) pkg_free(cond);
+       if (jmp_bm) pkg_free(jmp_bm);
+       return ret;
+}
+
+/* vi: set ts=4 sw=4 tw=79:ai:cindent: */
diff --git a/switch.h b/switch.h
new file mode 100644 (file)
index 0000000..e92cbad
--- /dev/null
+++ b/switch.h
@@ -0,0 +1,61 @@
+/* 
+ * $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.
+ */
+/*
+ * /home/andrei/sr.git/switch.h
+ */
+/*
+ * History:
+ * --------
+ *  2009-02-02  initial version (andrei)
+*/
+
+#ifndef __switch_h
+#define __switch_h
+
+#include "route_struct.h"
+
+struct case_stms{
+       struct rval_expr* ct_rve;
+       struct action* actions;
+       struct case_stms* next;
+       struct case_stms** append;
+       int int_label;
+       int is_default;
+};
+
+
+struct switch_cond_table{
+       int n;                  /**< size */
+       int* cond;              /**< int labels array */
+       struct action** jump;   /**< jump points array */
+       struct action* def; /**< default jump  */
+};
+
+
+struct switch_jmp_table{
+       int first;              /**< first int label in the jump table */
+       int last;               /**< last int label in the jump table */
+       struct action** tbl;    /**< jmp table [v-first] iff first<=v<=last */
+       struct switch_cond_table rest; /**< normal cond. table for the rest */
+};
+
+int fix_switch(struct action* t);
+
+#endif /*__switch_h*/
+
+/* vi: set ts=4 sw=4 tw=79:ai:cindent: */