script engine: while() support
authorAndrei Pelinescu-Onciul <andrei@iptel.org>
Tue, 10 Feb 2009 21:21:12 +0000 (22:21 +0100)
committerAndrei Pelinescu-Onciul <andrei@iptel.org>
Tue, 10 Feb 2009 21:38:17 +0000 (22:38 +0100)
- support the same while() loops as kamailio (the only difference
  being that max_while_loops can be changed at runtime):
  while(<int expr>) {  .... } with break exiting the while.

action.c
cfg_core.c
cfg_core.h
config.h
route.c
route_struct.h

index 82e187c..079bbfe 100644 (file)
--- a/action.c
+++ b/action.c
@@ -112,9 +112,10 @@ 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;
+       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
@@ -906,6 +907,30 @@ sw_jt_def:
                                                                                           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 */
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/route.c b/route.c
index 5587f12..54e3236 100644 (file)
--- a/route.c
+++ b/route.c
@@ -623,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 */
 
@@ -701,12 +705,12 @@ int fix_actions(struct action* a)
                        case SWITCH_T:
                                if (t->val[0].type!=RVE_ST){
                                        LOG(L_CRIT, "BUG: fix_actions: invalid subtype"
-                                                               "%d for if (should be expr)\n",
+                                                               "%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 action)\n",
+                                                               "%d for switch(...){...}(should be case)\n",
                                                                t->val[1].type);
                                        return E_BUG;
                                }
@@ -721,6 +725,55 @@ int fix_actions(struct action* a)
                                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 a8cfe8f..85242af 100644 (file)
@@ -71,7 +71,7 @@ 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, SWITCH_T /* only until fixup*/,
-               BLOCK_T, EVAL_T, SWITCH_JT_T, SWITCH_COND_T,
+               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,