Module: sip-router Branch: andrei/switch Commit: 955535a4f039a3782ef84d4f15bf807ee61af69d URL: http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=955535a4...
Author: Andrei Pelinescu-Onciul andrei@iptel.org Committer: Andrei Pelinescu-Onciul andrei@iptel.org Date: Mon Feb 9 19:01:35 2009 +0100
script engine: switch() and break execution
- support for executing optimized switch()ed (by fix_switch()) - proper break handling: - exit a switch() on break (even if the break is inside an if() block) - if the break is outside a switch, exit the route block (compatibility with older versions, for new scripts using exit or drop instead of break is recommended)
---
action.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- cfg.y | 2 +- route_struct.h | 1 + switch.h | 1 + 4 files changed, 107 insertions(+), 5 deletions(-)
diff --git a/action.c b/action.c index 26edcca..82e187c 100644 --- 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,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; + struct switch_cond_table* sct; + struct switch_jmp_table* sjt; +
/* reset the value of error to E_UNSPEC so avoid unknowledgable functions to return with error (status<0) and not setting it @@ -448,7 +453,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 +709,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 +814,98 @@ 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 FORCE_RPORT_T: msg->msg_flags|=FL_FORCE_RPORT; ret=1; /* continue processing */ @@ -885,7 +983,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 +1033,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.y b/cfg.y index 431054d..42f068a 100644 --- a/cfg.y +++ b/cfg.y @@ -2410,7 +2410,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 ')' ?"); } diff --git a/route_struct.h b/route_struct.h index e87b2d0..a8cfe8f 100644 --- a/route_struct.h +++ b/route_struct.h @@ -105,6 +105,7 @@ enum { NOSUBTYPE=0, STRING_ST, NET_ST, NUMBER_ST, IP_ST, RE_ST, PROXY_ST, /* run flags */ #define EXIT_R_F 1 #define RETURN_R_F 2 +#define BREAK_R_F 4
struct cfg_pos{ diff --git a/switch.h b/switch.h index 4c9f78b..e92cbad 100644 --- a/switch.h +++ b/switch.h @@ -54,6 +54,7 @@ struct switch_jmp_table{ struct switch_cond_table rest; /**< normal cond. table for the rest */ };
+int fix_switch(struct action* t);
#endif /*__switch_h*/