Browse code

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)

Andrei Pelinescu-Onciul authored on 09/02/2009 18:01:35
Showing 4 changed files
... ...
@@ -77,6 +77,7 @@
77 77
 #ifdef USE_SCTP
78 78
 #include "sctp_server.h"
79 79
 #endif
80
+#include "switch.h"
80 81
 
81 82
 #include <sys/types.h>
82 83
 #include <sys/socket.h>
... ...
@@ -111,6 +112,10 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
111 111
 	struct sip_uri *u;
112 112
 	unsigned short port;
113 113
 	str* dst_host;
114
+	int i;
115
+	struct switch_cond_table* sct;
116
+	struct switch_jmp_table*  sjt;
117
+
114 118
 
115 119
 	/* reset the value of error to E_UNSPEC so avoid unknowledgable
116 120
 	   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)
448 448
 			/*ret=((ret=run_actions(rlist[a->val[0].u.number], msg))<0)?ret:1;*/
449 449
 			ret=run_actions(h, main_rt.rlist[a->val[0].u.number], msg);
450 450
 			h->last_retcode=ret;
451
-			h->run_flags&=~RETURN_R_F; /* absorb returns */
451
+			h->run_flags&=~(RETURN_R_F|BREAK_R_F); /* absorb return & break */
452 452
 			break;
453 453
 		case EXEC_T:
454 454
 			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)
704 704
 						ret=0;
705 705
 						break;
706 706
 					}
707
-					h->run_flags &= ~RETURN_R_F; /* catch returns in expr */
707
+					h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return &
708
+															    break in expr*/
708 709
 					ret=1;  /*default is continue */
709 710
 					if (v>0) {
710 711
 						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)
808 808
 				LOG(L_CRIT,"BUG: do_action: bad module call\n");
809 809
 			}
810 810
 			break;
811
+		case EVAL_T:
812
+			/* only eval the expression to account for possible
813
+			   side-effect */
814
+			rval_expr_eval_int(h, msg, &v,
815
+					(struct rval_expr*)a->val[0].u.data);
816
+			if (h->run_flags & EXIT_R_F){
817
+				ret=0;
818
+				break;
819
+			}
820
+			h->run_flags &= ~RETURN_R_F|BREAK_R_F; /* catch return & break in
821
+													  expr */
822
+			ret=1; /* default is continue */
823
+			break;
824
+		case SWITCH_COND_T:
825
+			sct=(struct switch_cond_table*)a->val[1].u.data;
826
+			if (unlikely( rval_expr_eval_int(h, msg, &v,
827
+									(struct rval_expr*)a->val[0].u.data) <0)){
828
+				/* handle error in expression => use default */
829
+				ret=-1;
830
+				goto sw_cond_def;
831
+			}
832
+			if (h->run_flags & EXIT_R_F){
833
+				ret=0;
834
+				break;
835
+			}
836
+			h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break
837
+													    in expr */
838
+			ret=1; /* default is continue */
839
+			for(i=0; i<sct->n; i++)
840
+				if (sct->cond[i]==v){
841
+					if (likely(sct->jump[i])){
842
+						ret=run_actions(h, sct->jump[i], msg);
843
+						h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
844
+													   returns passthrough */
845
+					}
846
+					goto skip;
847
+				}
848
+sw_cond_def:
849
+			if (sct->def){
850
+				ret=run_actions(h, sct->def, msg);
851
+				h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
852
+											   returns passthrough */
853
+			}
854
+			break;
855
+		case SWITCH_JT_T:
856
+			sjt=(struct switch_jmp_table*)a->val[1].u.data;
857
+			if (unlikely( rval_expr_eval_int(h, msg, &v,
858
+									(struct rval_expr*)a->val[0].u.data) <0)){
859
+				/* handle error in expression => use default */
860
+				ret=-1;
861
+				goto sw_jt_def;
862
+			}
863
+			if (h->run_flags & EXIT_R_F){
864
+				ret=0;
865
+				break;
866
+			}
867
+			h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break
868
+													    in expr */
869
+			ret=1; /* default is continue */
870
+			if (likely(v >= sjt->first && v <= sjt->last)){
871
+				if (likely(sjt->tbl[v - sjt->first])){
872
+					ret=run_actions(h, sjt->tbl[v - sjt->first], msg);
873
+					h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
874
+												   returns passthrough */
875
+				}
876
+				break; 
877
+			}else{
878
+				for(i=0; i<sjt->rest.n; i++)
879
+					if (sjt->rest.cond[i]==v){
880
+						if (likely(sjt->rest.jump[i])){
881
+							ret=run_actions(h, sjt->rest.jump[i], msg);
882
+							h->run_flags &= ~BREAK_R_F; /* catch breaks, but 
883
+														   let returns pass */
884
+						}
885
+						goto skip;
886
+					}
887
+			}
888
+			/* not found => try default */
889
+sw_jt_def:
890
+			if (sjt->rest.def){
891
+				ret=run_actions(h, sjt->rest.def, msg);
892
+				h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
893
+											   returns passthrough */
894
+			}
895
+			break;
896
+		case BLOCK_T:
897
+			if (likely(a->val[0].u.data)){
898
+				ret=run_actions(h, (struct action*)a->val[0].u.data, msg);
899
+				h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
900
+											   returns passthrough */
901
+			}
902
+			break;
811 903
 		case FORCE_RPORT_T:
812 904
 			msg->msg_flags|=FL_FORCE_RPORT;
813 905
 			ret=1; /* continue processing */
... ...
@@ -885,7 +983,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
885 885
 		default:
886 886
 			LOG(L_CRIT, "BUG: do_action: unknown type %d\n", a->type);
887 887
 	}
888
-/*skip:*/
888
+skip:
889 889
 	return ret;
890 890
 
891 891
 error_uri:
... ...
@@ -935,7 +1033,9 @@ int run_actions(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
935 935
 
936 936
 	for (t=a; t!=0; t=t->next){
937 937
 		ret=do_action(h, t, msg);
938
-		if (h->run_flags & (RETURN_R_F|EXIT_R_F)){
938
+		/* break, return or drop/exit stop execution of the current
939
+		   block */
940
+		if (h->run_flags & (BREAK_R_F|RETURN_R_F|EXIT_R_F)){
939 941
 			if (h->run_flags & EXIT_R_F){
940 942
 #ifdef USE_LONGJMP
941 943
 				h->last_retcode=ret;
... ...
@@ -2410,7 +2410,7 @@ cmd:
2410 2410
 	| RETURN			{$$=mk_action(DROP_T, 2, NUMBER_ST, (void*)1, NUMBER_ST, (void*)RETURN_R_F); }
2411 2411
 	| RETURN NUMBER			{$$=mk_action(DROP_T, 2, NUMBER_ST, (void*)$2, NUMBER_ST, (void*)RETURN_R_F);}
2412 2412
 	| RETURN RETCODE		{$$=mk_action(DROP_T, 2, RETCODE_ST, 0, NUMBER_ST, (void*)RETURN_R_F);}
2413
-	| BREAK				{$$=mk_action(DROP_T, 2, NUMBER_ST, 0, NUMBER_ST, (void*)RETURN_R_F); }
2413
+	| BREAK				{$$=mk_action(DROP_T, 2, NUMBER_ST, 0, NUMBER_ST, (void*)BREAK_R_F); }
2414 2414
 	| LOG_TOK LPAREN STRING RPAREN	{$$=mk_action(LOG_T, 2, NUMBER_ST, (void*)4, STRING_ST, $3); }
2415 2415
 	| LOG_TOK LPAREN NUMBER COMMA STRING RPAREN	{$$=mk_action(LOG_T, 2, NUMBER_ST, (void*)$3, STRING_ST, $5); }
2416 2416
 	| LOG_TOK error 		{ $$=0; yyerror("missing '(' or ')' ?"); }
... ...
@@ -105,6 +105,7 @@ enum { NOSUBTYPE=0, STRING_ST, NET_ST, NUMBER_ST, IP_ST, RE_ST, PROXY_ST,
105 105
 /* run flags */
106 106
 #define EXIT_R_F   1
107 107
 #define RETURN_R_F 2
108
+#define BREAK_R_F  4
108 109
 
109 110
 
110 111
 struct cfg_pos{
... ...
@@ -54,6 +54,7 @@ struct switch_jmp_table{
54 54
 	struct switch_cond_table rest; /**< normal cond. table for the rest */
55 55
 };
56 56
 
57
+int fix_switch(struct action* t);
57 58
 
58 59
 #endif /*__switch_h*/
59 60