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 112
 	struct sip_uri *u;
112 113
 	unsigned short port;
113 114
 	str* dst_host;
115
+	int i;
116
+	struct switch_cond_table* sct;
117
+	struct switch_jmp_table*  sjt;
118
+
114 119
 
115 120
 	/* reset the value of error to E_UNSPEC so avoid unknowledgable
116 121
 	   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 453
 			/*ret=((ret=run_actions(rlist[a->val[0].u.number], msg))<0)?ret:1;*/
449 454
 			ret=run_actions(h, main_rt.rlist[a->val[0].u.number], msg);
450 455
 			h->last_retcode=ret;
451
-			h->run_flags&=~RETURN_R_F; /* absorb returns */
456
+			h->run_flags&=~(RETURN_R_F|BREAK_R_F); /* absorb return & break */
452 457
 			break;
453 458
 		case EXEC_T:
454 459
 			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 709
 						ret=0;
705 710
 						break;
706 711
 					}
707
-					h->run_flags &= ~RETURN_R_F; /* catch returns in expr */
712
+					h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return &
713
+															    break in expr*/
708 714
 					ret=1;  /*default is continue */
709 715
 					if (v>0) {
710 716
 						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 814
 				LOG(L_CRIT,"BUG: do_action: bad module call\n");
809 815
 			}
810 816
 			break;
817
+		case EVAL_T:
818
+			/* only eval the expression to account for possible
819
+			   side-effect */
820
+			rval_expr_eval_int(h, msg, &v,
821
+					(struct rval_expr*)a->val[0].u.data);
822
+			if (h->run_flags & EXIT_R_F){
823
+				ret=0;
824
+				break;
825
+			}
826
+			h->run_flags &= ~RETURN_R_F|BREAK_R_F; /* catch return & break in
827
+													  expr */
828
+			ret=1; /* default is continue */
829
+			break;
830
+		case SWITCH_COND_T:
831
+			sct=(struct switch_cond_table*)a->val[1].u.data;
832
+			if (unlikely( rval_expr_eval_int(h, msg, &v,
833
+									(struct rval_expr*)a->val[0].u.data) <0)){
834
+				/* handle error in expression => use default */
835
+				ret=-1;
836
+				goto sw_cond_def;
837
+			}
838
+			if (h->run_flags & EXIT_R_F){
839
+				ret=0;
840
+				break;
841
+			}
842
+			h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break
843
+													    in expr */
844
+			ret=1; /* default is continue */
845
+			for(i=0; i<sct->n; i++)
846
+				if (sct->cond[i]==v){
847
+					if (likely(sct->jump[i])){
848
+						ret=run_actions(h, sct->jump[i], msg);
849
+						h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
850
+													   returns passthrough */
851
+					}
852
+					goto skip;
853
+				}
854
+sw_cond_def:
855
+			if (sct->def){
856
+				ret=run_actions(h, sct->def, msg);
857
+				h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
858
+											   returns passthrough */
859
+			}
860
+			break;
861
+		case SWITCH_JT_T:
862
+			sjt=(struct switch_jmp_table*)a->val[1].u.data;
863
+			if (unlikely( rval_expr_eval_int(h, msg, &v,
864
+									(struct rval_expr*)a->val[0].u.data) <0)){
865
+				/* handle error in expression => use default */
866
+				ret=-1;
867
+				goto sw_jt_def;
868
+			}
869
+			if (h->run_flags & EXIT_R_F){
870
+				ret=0;
871
+				break;
872
+			}
873
+			h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break
874
+													    in expr */
875
+			ret=1; /* default is continue */
876
+			if (likely(v >= sjt->first && v <= sjt->last)){
877
+				if (likely(sjt->tbl[v - sjt->first])){
878
+					ret=run_actions(h, sjt->tbl[v - sjt->first], msg);
879
+					h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
880
+												   returns passthrough */
881
+				}
882
+				break; 
883
+			}else{
884
+				for(i=0; i<sjt->rest.n; i++)
885
+					if (sjt->rest.cond[i]==v){
886
+						if (likely(sjt->rest.jump[i])){
887
+							ret=run_actions(h, sjt->rest.jump[i], msg);
888
+							h->run_flags &= ~BREAK_R_F; /* catch breaks, but 
889
+														   let returns pass */
890
+						}
891
+						goto skip;
892
+					}
893
+			}
894
+			/* not found => try default */
895
+sw_jt_def:
896
+			if (sjt->rest.def){
897
+				ret=run_actions(h, sjt->rest.def, msg);
898
+				h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
899
+											   returns passthrough */
900
+			}
901
+			break;
902
+		case BLOCK_T:
903
+			if (likely(a->val[0].u.data)){
904
+				ret=run_actions(h, (struct action*)a->val[0].u.data, msg);
905
+				h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
906
+											   returns passthrough */
907
+			}
908
+			break;
811 909
 		case FORCE_RPORT_T:
812 910
 			msg->msg_flags|=FL_FORCE_RPORT;
813 911
 			ret=1; /* continue processing */
... ...
@@ -885,7 +983,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
885 983
 		default:
886 984
 			LOG(L_CRIT, "BUG: do_action: unknown type %d\n", a->type);
887 985
 	}
888
-/*skip:*/
986
+skip:
889 987
 	return ret;
890 988
 
891 989
 error_uri:
... ...
@@ -935,7 +1033,9 @@ int run_actions(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
935 1033
 
936 1034
 	for (t=a; t!=0; t=t->next){
937 1035
 		ret=do_action(h, t, msg);
938
-		if (h->run_flags & (RETURN_R_F|EXIT_R_F)){
1036
+		/* break, return or drop/exit stop execution of the current
1037
+		   block */
1038
+		if (h->run_flags & (BREAK_R_F|RETURN_R_F|EXIT_R_F)){
939 1039
 			if (h->run_flags & EXIT_R_F){
940 1040
 #ifdef USE_LONGJMP
941 1041
 				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