From a8661f7507f5a68ef3c971dd75fb13108e812aa1 Mon Sep 17 00:00:00 2001 From: Vlad Patrascu Date: Thu, 28 Feb 2019 18:55:47 +0200 Subject: [PATCH] Rework interface for script functions parameters * basic parameter fixup (type validation and variables evaluation) is now transparently done by the core for all script functions * pass variables and integers to functions without quotes --- aaa/aaa.c | 2 +- action.c | 75 +++++++--- async.c | 7 +- async.h | 9 +- cfg.y | 163 ++++++++++++++++------ db/db.c | 28 ++-- mod_fix.h | 2 + modules/tm/async.c | 8 +- net/proto_tcp/proto_tcp.c | 4 +- net/proto_udp/proto_udp.c | 4 +- route.c | 38 ++--- route_struct.c | 285 +++++++++++++++++++++++++++++++++++++- route_struct.h | 6 +- sr_module.c | 93 +++++++++---- sr_module.h | 54 +++++--- trace_api.c | 2 +- 16 files changed, 606 insertions(+), 174 deletions(-) diff --git a/aaa/aaa.c b/aaa/aaa.c index c2abec12d6e..7349112f994 100644 --- a/aaa/aaa.c +++ b/aaa/aaa.c @@ -94,7 +94,7 @@ int aaa_prot_bind(str* aaa_url, aaa_prot* prot) { sprintf(module_name, "aaa_%.*s", pc.prot_name->len,pc.prot_name->s); bind_f = (aaa_bind_api_f) find_mod_export(module_name, - "aaa_bind_api", 0, 0); + "aaa_bind_api", 0); if (bind_f) { LM_DBG("using aaa bind api for %s\n", module_name); diff --git a/action.c b/action.c index 7f2530c511b..03659638f83 100644 --- a/action.c +++ b/action.c @@ -438,6 +438,9 @@ int do_action(struct action* a, struct sip_msg* msg) struct timeval start; int end_time; int aux_counter; + cmd_export_t *cmd = NULL; + acmd_export_t *acmd; + void* cmdp[MAX_CMD_PARAMS]; /* reset the value of error to E_UNSPEC so avoid unknowledgable functions to return with error (status<0) and not setting it @@ -1860,31 +1863,58 @@ int do_action(struct action* a, struct sip_msg* msg) ret=return_code; break; case MODULE_T: - script_trace("module", ((cmd_export_t*)(a->elem[0].u.data))->name, - msg, a->file, a->line) ; - if ( (a->elem[0].type==CMD_ST) && a->elem[0].u.data ) { - ret=((cmd_export_t*)(a->elem[0].u.data))->function(msg, - (char*)a->elem[1].u.data, (char*)a->elem[2].u.data, - (char*)a->elem[3].u.data, (char*)a->elem[4].u.data, - (char*)a->elem[5].u.data, (char*)a->elem[6].u.data); - }else{ + if (a->elem[0].type != CMD_ST || + ((cmd = (cmd_export_t*)a->elem[0].u.data) == NULL)) { LM_ALERT("BUG in module call\n"); + break; + } + + script_trace("module", cmd->name, msg, a->file, a->line); + + if ((ret = get_cmd_fixups(msg, cmd->params, a->elem, cmdp)) < 0) { + LM_ERR("Failed to get fixups for command <%s>\n", + cmd->name); + break; + } + + ret = cmd->function(msg, + cmdp[0],cmdp[1],cmdp[2], + cmdp[3],cmdp[4],cmdp[5]); + + if ((ret = free_cmd_fixups(cmd->params, a->elem, cmdp)) < 0) { + LM_ERR("Failed to free fixups for command <%s>\n", + cmd->name); + break; } + break; case ASYNC_T: /* first param - an ACTIONS_ST containing an ACMD_ST * second param - a NUMBER_ST pointing to resume route */ aitem = (struct action *)(a->elem[0].u.data); + acmd = (acmd_export_t *)aitem->elem[0].u.data; + if (async_script_start_f==NULL || a->elem[0].type!=ACTIONS_ST || a->elem[1].type!=NUMBER_ST || aitem->type!=AMODULE_T) { LM_ALERT("BUG in async expression\n"); } else { - script_trace("async", - ((acmd_export_t*)(aitem->elem[0].u.data))->name, - msg, a->file, a->line) ; - ret = async_script_start_f( msg, aitem, a->elem[1].u.number); + script_trace("async", acmd->name, msg, a->file, a->line); + + if ((ret = get_cmd_fixups(msg, acmd->params, aitem->elem, cmdp)) < 0) { + LM_ERR("Failed to get fixups for async command <%s>\n", + acmd->name); + break; + } + + ret = async_script_start_f(msg, aitem, a->elem[1].u.number, cmdp); if (ret>=0) action_flags |= ACT_FL_TBCONT; + + if ((ret = free_cmd_fixups(acmd->params, aitem->elem, cmdp)) < 0) { + LM_ERR("Failed to free fixups for command <%s>\n", + cmd->name); + break; + } } ret = 0; break; @@ -1892,16 +1922,29 @@ int do_action(struct action* a, struct sip_msg* msg) /* first param - an ACTIONS_ST containing an ACMD_ST * second param - an optional NUMBER_ST pointing to an end route */ aitem = (struct action *)(a->elem[0].u.data); + acmd = (acmd_export_t *)aitem->elem[0].u.data; + if (async_script_start_f==NULL || a->elem[0].type!=ACTIONS_ST || a->elem[1].type!=NUMBER_ST || aitem->type!=AMODULE_T) { LM_ALERT("BUG in launch expression\n"); } else { - script_trace("launch", - ((acmd_export_t*)(aitem->elem[0].u.data))->name, - msg, a->file, a->line) ; + script_trace("launch", acmd->name, msg, a->file, a->line); /* NOTE that the routeID (a->elem[1].u.number) is set to * -1 if no reporting route is set */ - ret = async_script_launch( msg, aitem, a->elem[1].u.number); + + if ((ret = get_cmd_fixups(msg, acmd->params, aitem->elem, cmdp)) < 0) { + LM_ERR("Failed to get fixups for async command <%s>\n", + acmd->name); + break; + } + + ret = async_script_launch( msg, aitem, a->elem[1].u.number, cmdp); + + if ((ret = free_cmd_fixups(acmd->params, aitem->elem, cmdp)) < 0) { + LM_ERR("Failed to free fixups for command <%s>\n", + cmd->name); + break; + } } break; case FORCE_RPORT_T: diff --git a/async.c b/async.c index 221a18e16f4..39a6e3f6a88 100644 --- a/async.c +++ b/async.c @@ -249,7 +249,7 @@ int async_launch_resume(int fd, void *param) int async_script_launch(struct sip_msg *msg, struct action* a, - int report_route) + int report_route, void **params) { struct sip_msg req; async_launch_ctx *ctx; @@ -273,9 +273,8 @@ int async_script_launch(struct sip_msg *msg, struct action* a, return_code = ((acmd_export_t*)(a->elem[0].u.data))->function(msg, (async_ctx*)ctx, - (char*)a->elem[1].u.data, (char*)a->elem[2].u.data, - (char*)a->elem[3].u.data, (char*)a->elem[4].u.data, - (char*)a->elem[5].u.data, (char*)a->elem[6].u.data ); + params[0], params[1], params[2], + params[3], params[4], params[5]); /* what to do now ? */ if (async_status>=0) { /* async I/O was successfully launched */ diff --git a/async.h b/async.h index 075781e2982..c7e908f7f28 100644 --- a/async.h +++ b/async.h @@ -64,14 +64,15 @@ extern int async_status; /******** functions related to script async ops *******/ /* function to handle script function in async mode. - Input: the sip message, the function/action (MODULE_T) and the ID of - the resume route (where to continue after the I/O is done). + Input: the sip message, the function/action (MODULE_T), the ID of + the resume route (where to continue after the I/O is done) and the + array of parameters for the function. Output: 0 if the async call was successfully done and script execution must be terminated. -1 some error happened and the async call did not happened. */ typedef int (async_script_start_function) - (struct sip_msg *msg, struct action* a , int resume_route); + (struct sip_msg *msg, struct action* a , int resume_route, void **params); /* Handles periodic progress (data arrival) on behalf of the contained, * module-specific resume function, which it must also call @@ -136,7 +137,7 @@ int async_fd_resume(int fd, void *param); /******** functions related to async launch *******/ int async_script_launch(struct sip_msg *msg, struct action* a, - int report_route); + int report_route, void **params); /* @fd is always valid */ int async_launch_resume(int fd, void *param); diff --git a/cfg.y b/cfg.y index d3f85c2985a..51f0a79691a 100644 --- a/cfg.y +++ b/cfg.y @@ -1907,7 +1907,7 @@ default_stm: DEFAULT COLON actions { mk_action2( $$, DEFAULT_T, ; module_func_param: STRING { - elems[1].type = STRING_ST; + elems[1].type = STR_ST; elems[1].u.data = $1; $$=1; } @@ -1917,7 +1917,7 @@ module_func_param: STRING { "in function\n"); $$=0; } - elems[$1+1].type = STRING_ST; + elems[$1+1].type = STR_ST; elems[$1+1].u.data = $3; $$=$1+1; } @@ -1931,7 +1931,7 @@ module_func_param: STRING { | COMMA STRING { elems[1].type = NULLV_ST; elems[1].u.data = NULL; - elems[2].type = STRING_ST; + elems[2].type = STR_ST; elems[2].u.data = $2; $$=2; } @@ -1946,19 +1946,48 @@ module_func_param: STRING { $$=$1+1; } | NUMBER { - $$=0; - yyerror("numbers used as parameters -" - " they should be quoted"); + elems[1].type = NUMBER_ST; + elems[1].u.number = $1; + $$=1; } | COMMA NUMBER { - $$=0; - yyerror("numbers used as parameters -" - " they should be quoted"); + elems[1].type = NULLV_ST; + elems[1].u.data = NULL; + elems[2].type = NUMBER_ST; + elems[2].u.number = $2; + $$=2; } | module_func_param COMMA NUMBER { - $$=0; - yyerror("numbers used as parameters -" - " they should be quoted"); + if ($1+1>=MAX_ACTION_ELEMS) { + yyerror("too many arguments " + "in function\n"); + $$=0; + } + elems[$1+1].type = NUMBER_ST; + elems[$1+1].u.number = $3; + $$=$1+1; + } + | script_var { + elems[1].type = SCRIPTVAR_ST; + elems[1].u.data = $1; + $$=1; + } + | COMMA script_var { + elems[1].type = NULLV_ST; + elems[1].u.data = NULL; + elems[2].type = SCRIPTVAR_ST; + elems[2].u.data = $2; + $$=2; + } + | module_func_param COMMA script_var { + if ($1+1>=MAX_ACTION_ELEMS) { + yyerror("too many arguments " + "in function\n"); + $$=0; + } + elems[$1+1].type = SCRIPTVAR_ST; + elems[$1+1].u.data = $3; + $$=$1+1; } ; @@ -2025,27 +2054,52 @@ route_param: STRING { ; async_func: ID LPAREN RPAREN { - cmd_tmp=(void*)find_acmd_export_t($1, 0); + cmd_tmp=(void*)find_acmd_export_t($1); if (cmd_tmp==0){ yyerrorf("unknown async command <%s>, " "missing loadmodule?", $1); $$=0; }else{ - elems[0].type = ACMD_ST; - elems[0].u.data = cmd_tmp; - mk_action_($$, AMODULE_T, 1, elems); + if (check_acmd_call_params(cmd_tmp,elems,0)<0) { + yyerrorf("too few parameters " + "for command <%s>\n", $1); + $$=0; + } else { + elems[0].type = ACMD_ST; + elems[0].u.data = cmd_tmp; + mk_action_($$, AMODULE_T, 1, elems); + } } } | ID LPAREN module_func_param RPAREN { - cmd_tmp=(void*)find_acmd_export_t($1, $3); + cmd_tmp=(void*)find_acmd_export_t($1); if (cmd_tmp==0){ yyerrorf("unknown async command <%s>, " "missing loadmodule?", $1); $$=0; }else{ - elems[0].type = ACMD_ST; - elems[0].u.data = cmd_tmp; - mk_action_($$, AMODULE_T, $3+1, elems); + rc = check_acmd_call_params(cmd_tmp,elems,$3); + switch (rc) { + case -1: + yyerrorf("too few parameters " + "for async command <%s>\n", $1); + $$=0; + break; + case -2: + yyerrorf("too many parameters " + "for async command <%s>\n", $1); + $$=0; + break; + case -3: + yyerrorf("mandatory parameter " + " omitted for async command <%s>\n", $1); + $$=0; + break; + default: + elems[0].type = ACMD_ST; + elems[0].u.data = cmd_tmp; + mk_action_($$, AMODULE_T, $3+1, elems); + } } } | ID LPAREN error RPAREN { @@ -2621,40 +2675,65 @@ cmd: FORWARD LPAREN STRING RPAREN { mk_action2( $$, FORWARD_T, elems[1].u.data = $5; mk_action_($$, CACHE_RAW_QUERY_T, 2, elems); } - | ID LPAREN RPAREN { - cmd_tmp=(void*)find_cmd_export_t($1, 0, rt); - if (cmd_tmp==0){ - if (find_cmd_export_t($1, 0, 0)) { - yyerror("Command cannot be " - "used in the block\n"); - } else { - yyerrorf("unknown command <%s>, " - "missing loadmodule?", $1); - } + | ID LPAREN RPAREN { + cmd_tmp=(void*)find_cmd_export_t($1, rt); + if (cmd_tmp==0){ + if (find_cmd_export_t($1, 0)) { + yyerrorf("Command <%s> cannot be " + "used in the block\n", $1); + } else { + yyerrorf("unknown command <%s>, " + "missing loadmodule?", $1); + } + $$=0; + }else{ + if (check_cmd_call_params(cmd_tmp,elems,0)<0) { + yyerrorf("too few parameters " + "for command <%s>\n", $1); $$=0; - }else{ + } else { elems[0].type = CMD_ST; elems[0].u.data = cmd_tmp; mk_action_($$, MODULE_T, 1, elems); } } - | ID LPAREN module_func_param RPAREN { - cmd_tmp=(void*)find_cmd_export_t($1,$3,rt); - if (cmd_tmp==0){ - if (find_cmd_export_t($1, $3, 0)) { - yyerror("Command cannot be " - "used in the block\n"); - } else { - yyerrorf("unknown command <%s>, " - "missing loadmodule?", $1); - } + } + | ID LPAREN module_func_param RPAREN { + cmd_tmp=(void*)find_cmd_export_t($1, rt); + if (cmd_tmp==0){ + if (find_cmd_export_t($1, 0)) { + yyerrorf("Command <%s> cannot be " + "used in the block\n", $1); + } else { + yyerrorf("unknown command <%s>, " + "missing loadmodule?", $1); + } + $$=0; + }else{ + rc = check_cmd_call_params(cmd_tmp,elems,$3); + switch (rc) { + case -1: + yyerrorf("too few parameters " + "for command <%s>\n", $1); + $$=0; + break; + case -2: + yyerrorf("too many parameters " + "for command <%s>\n", $1); $$=0; - }else{ + break; + case -3: + yyerrorf("mandatory parameter " + "omitted for command <%s>\n", $1); + $$=0; + break; + default: elems[0].type = CMD_ST; elems[0].u.data = cmd_tmp; mk_action_($$, MODULE_T, $3+1, elems); } } + } | ID LPAREN error RPAREN { $$=0; yyerrorf("bad arguments for " "command <%s>", $1); } | ID error { $$=0; diff --git a/db/db.c b/db/db.c index 0da86b7c5e5..f166b67e4c7 100644 --- a/db/db.c +++ b/db/db.c @@ -212,7 +212,7 @@ int db_bind_mod(const str* mod, db_func_t* mydbf) tmp = name; } - dbind = (db_bind_api_f)find_mod_export(tmp, "db_bind_api", 0, 0); + dbind = (db_bind_api_f)find_mod_export(tmp, "db_bind_api", 0); if(dbind != NULL) { LM_DBG("using db bind api for %s\n", tmp); @@ -225,24 +225,24 @@ int db_bind_mod(const str* mod, db_func_t* mydbf) memset(&dbf, 0, sizeof(db_func_t)); LM_DBG("using export interface to bind %s\n", tmp); dbf.use_table = (db_use_table_f)find_mod_export(tmp, - "db_use_table", 2, 0); - dbf.init = (db_init_f)find_mod_export(tmp, "db_init", 1, 0); - dbf.close = (db_close_f)find_mod_export(tmp, "db_close", 2, 0); - dbf.query = (db_query_f)find_mod_export(tmp, "db_query", 2, 0); + "db_use_table", 0); + dbf.init = (db_init_f)find_mod_export(tmp, "db_init", 0); + dbf.close = (db_close_f)find_mod_export(tmp, "db_close", 0); + dbf.query = (db_query_f)find_mod_export(tmp, "db_query", 0); dbf.fetch_result = (db_fetch_result_f)find_mod_export(tmp, - "db_fetch_result", 2, 0); + "db_fetch_result", 0); dbf.raw_query = (db_raw_query_f)find_mod_export(tmp, - "db_raw_query", 2, 0); + "db_raw_query", 0); dbf.free_result = (db_free_result_f)find_mod_export(tmp, - "db_free_result", 2, 0); - dbf.insert = (db_insert_f)find_mod_export(tmp, "db_insert", 2, 0); - dbf.delete = (db_delete_f)find_mod_export(tmp, "db_delete", 2, 0); - dbf.update = (db_update_f)find_mod_export(tmp, "db_update", 2, 0); - dbf.replace = (db_replace_f)find_mod_export(tmp, "db_replace", 2, 0); + "db_free_result", 0); + dbf.insert = (db_insert_f)find_mod_export(tmp, "db_insert", 0); + dbf.delete = (db_delete_f)find_mod_export(tmp, "db_delete", 0); + dbf.update = (db_update_f)find_mod_export(tmp, "db_update", 0); + dbf.replace = (db_replace_f)find_mod_export(tmp, "db_replace", 0); dbf.last_inserted_id= (db_last_inserted_id_f)find_mod_export(tmp, - "db_last_inserted_id", 1, 0); + "db_last_inserted_id", 0); dbf.insert_update = (db_insert_update_f)find_mod_export(tmp, - "db_insert_update", 2, 0); + "db_insert_update", 0); } /* check if the module pre-populated the capabilities, or we need to * compute them ourselves - we check for the INSERT capability, because diff --git a/mod_fix.h b/mod_fix.h index d96d81357de..6f0928bafbb 100644 --- a/mod_fix.h +++ b/mod_fix.h @@ -35,6 +35,7 @@ #define GPARAM_TYPE_PVE 3 #define GPARAM_TYPE_FLAGS 4 #define GPARAM_TYPE_REGEX 5 +#define GPARAM_TYPE_FIXUP 6 #define GPARAM_INT_VALUE_FLAG (1U<<0) #define GPARAM_STR_VALUE_FLAG (1U<<1) @@ -52,6 +53,7 @@ typedef struct _gparam pv_spec_t *pvs; pv_elem_t *pve; regex_t *re; + void *val; } v; } gparam_t, *gparam_p; diff --git a/modules/tm/async.c b/modules/tm/async.c index 7a7e61287d8..579e93faa18 100644 --- a/modules/tm/async.c +++ b/modules/tm/async.c @@ -207,7 +207,8 @@ int t_resume_async(int fd, void *param) } -int t_handle_async(struct sip_msg *msg, struct action* a , int resume_route) +int t_handle_async(struct sip_msg *msg, struct action* a , int resume_route, + void **params) { async_tm_ctx *ctx = NULL; struct cell *t; @@ -253,9 +254,8 @@ int t_handle_async(struct sip_msg *msg, struct action* a , int resume_route) async_status = ASYNC_NO_IO; /*assume default status "no IO done" */ return_code = ((acmd_export_t*)(a->elem[0].u.data))->function(msg, (async_ctx*)ctx, - (char*)a->elem[1].u.data, (char*)a->elem[2].u.data, - (char*)a->elem[3].u.data, (char*)a->elem[4].u.data, - (char*)a->elem[5].u.data, (char*)a->elem[6].u.data ); + params[0], params[1], params[2], + params[3], params[4], params[5]); /* what to do now ? */ if (async_status>=0) { /* async I/O was successfully launched */ diff --git a/net/proto_tcp/proto_tcp.c b/net/proto_tcp/proto_tcp.c index 58aad378025..d0c1cd8e958 100644 --- a/net/proto_tcp/proto_tcp.c +++ b/net/proto_tcp/proto_tcp.c @@ -141,8 +141,8 @@ struct tcp_data { static cmd_export_t cmds[] = { - {"proto_init", (cmd_function)proto_tcp_init, 0, 0, 0, 0}, - {0,0,0,0,0,0} + {"proto_init", (cmd_function)proto_tcp_init, {{0, 0, 0}}, 0}, + {0,0,{{0,0,0}},0} }; diff --git a/net/proto_udp/proto_udp.c b/net/proto_udp/proto_udp.c index 381741373ab..7d73cb5d22b 100644 --- a/net/proto_udp/proto_udp.c +++ b/net/proto_udp/proto_udp.c @@ -54,8 +54,8 @@ static int udp_port = SIP_PORT; static cmd_export_t cmds[] = { - {"proto_init", (cmd_function)proto_udp_init, 0, 0, 0, 0}, - {0,0,0,0,0,0} + {"proto_init", (cmd_function)proto_udp_init, {{0,0,0}}, 0}, + {0,0,{{0,0,0}},0} }; diff --git a/route.c b/route.c index c1e358df3df..8e701d860f1 100644 --- a/route.c +++ b/route.c @@ -446,21 +446,10 @@ static int fix_actions(struct action* a) case MODULE_T: cmd = (cmd_export_t*)t->elem[0].u.data; LM_DBG("fixing %s, %s:%d\n", cmd->name, t->file, t->line); - if (cmd->fixup){ - if (cmd->param_no==0){ - ret=cmd->fixup( 0, 0); - if (ret<0) goto error; - } - else { - for (i=1; i<=cmd->param_no; i++) { - /* we only call the fixup for non-null arguments */ - if (t->elem[i].type != NULLV_ST) { - ret=cmd->fixup(&t->elem[i].u.data, i); - t->elem[i].type=MODFIXUP_ST; - if (ret<0) goto error; - } - } - } + + if ((ret = fix_cmd(cmd->params, t->elem)) < 0) { + LM_ERR("Failed to fix command <%s>\n", cmd->name); + goto error; } break; case ASYNC_T: @@ -473,21 +462,10 @@ static int fix_actions(struct action* a) case AMODULE_T: acmd = (acmd_export_t*)t->elem[0].u.data; LM_DBG("fixing async %s, %s:%d\n", acmd->name, t->file, t->line); - if (acmd->fixup){ - if (acmd->param_no==0){ - ret=acmd->fixup( 0, 0); - if (ret<0) goto error; - } - else { - for (i=1; i<=acmd->param_no; i++) { - /* we only call the fixup for non-null arguments */ - if (t->elem[i].type != NULLV_ST) { - ret=acmd->fixup(&t->elem[i].u.data, i); - t->elem[i].type=MODFIXUP_ST; - if (ret<0) goto error; - } - } - } + + if ((ret = fix_cmd(acmd->params, t->elem)) < 0) { + LM_ERR("Failed to fix command <%s>\n", acmd->name); + goto error; } break; case FORCE_SEND_SOCKET_T: diff --git a/route_struct.c b/route_struct.c index f8992a93040..1d2bd284955 100644 --- a/route_struct.c +++ b/route_struct.c @@ -46,7 +46,7 @@ #include "ip_addr.h" #include "mem/mem.h" #include "ut.h" /* ZSW() */ - +#include "mod_fix.h" struct expr* mk_exp(int op, struct expr* left, struct expr* right) { @@ -623,8 +623,7 @@ int is_mod_func_used(struct action *a, char *name, int param_no) if (a->type==MODULE_T) { /* first param is the name of the function */ cmd = (cmd_export_t*)a->elem[0].u.data; - if (strcasecmp(cmd->name, name)==0 && - (param_no==cmd->param_no || param_no==-1) ) { + if (strcasecmp(cmd->name, name)==0 || param_no==-1) { LM_DBG("function %s found to be used in script\n",name); return 1; } @@ -674,8 +673,7 @@ int is_mod_async_func_used(struct action *a, char *name, int param_no) acmd = ((struct action *)(a->elem[0].u.data))->elem[0].u.data; LM_DBG("checking %s against %s\n", name, acmd->name); - if (strcasecmp(acmd->name, name) == 0 - && (param_no == acmd->param_no || param_no == -1)) + if (strcasecmp(acmd->name, name) == 0 || param_no == -1) return 1; } @@ -699,3 +697,280 @@ int is_mod_async_func_used(struct action *a, char *name, int param_no) return 0; } + +int fix_cmd(struct cmd_param *params, action_elem_t *elems) +{ + int i; + struct cmd_param *param; + gparam_p gp = NULL; + int ret; + pv_elem_t *pve; + + for (param=params, i=1; param->flags; param++, i++) { + if ((elems[i].type == NOSUBTYPE) || + (elems[i].type == NULLV_ST)) { + if (param->flags & CMD_PARAM_OPT) + continue; + else { + LM_BUG("Mandatory parameter missing\n"); + ret = E_BUG; + goto error; + } + } + + gp = pkg_malloc(sizeof *gp); + if (!gp) { + LM_ERR("no more pkg memory\n"); + ret = E_OUT_OF_MEM; + goto error; + } + memset(gp, 0, sizeof *gp); + + if (param->flags & CMD_PARAM_INT) { + + if (elems[i].type == NUMBER_ST) { + if (param->fixup) { + gp->v.val = (void *)&elems[i].u.number; + if (param->fixup(&gp->v.val) < 0) { + LM_ERR("Fixup failed for param [%d]\n", i); + ret = E_UNSPEC; + goto error; + } + gp->type = GPARAM_TYPE_FIXUP; + } else { + gp->v.ival = elems[i].u.number; + gp->type = GPARAM_TYPE_INT; + } + } else if (elems[i].type == SCRIPTVAR_ST) { + gp->v.pvs = elems[i].u.data; + gp->type = GPARAM_TYPE_PVS; + } else { + LM_ERR("Param [%d] expected to be an integer " + "or variable\n", i); + return E_CFG; + } + + } else if (param->flags & CMD_PARAM_STR) { + + if (elems[i].type == STR_ST) { + if (pv_parse_format(&elems[i].u.s, &pve) < 0) { + LM_ERR("Failed to parse formatted string in param " + "[%d]\n",i); + ret = E_UNSPEC; + goto error; + } + if (!pve->next && pve->spec.type == PVT_NONE) { + /* no variables in the provided string */ + pv_elem_free_all(pve); + + if (param->fixup) { + gp->v.val = (void *)&elems[i].u.s; + if (param->fixup(&gp->v.val) < 0) { + LM_ERR("Fixup failed for param [%d]\n", i); + ret = E_UNSPEC; + goto error; + } + gp->type = GPARAM_TYPE_FIXUP; + } else { + gp->v.sval = elems[i].u.s; + gp->type = GPARAM_TYPE_STR; + } + } else { + gp->v.pve = pve; + gp->type = GPARAM_TYPE_PVE; + } + } else if (elems[i].type == SCRIPTVAR_ST) { + gp->v.pvs = elems[i].u.data; + gp->type = GPARAM_TYPE_PVS; + } else { + LM_ERR("Param [%d] expected to be a string " + "or variable\n", i); + ret = E_CFG; + goto error; + } + + } else if (param->flags & CMD_PARAM_VAR) { + + if (elems[i].type != SCRIPTVAR_ST) { + LM_ERR("Param [%d] expected to be a variable\n",i); + ret = E_CFG; + goto error; + } + + gp->v.pvs = elems[i].u.data; + gp->type = GPARAM_TYPE_PVS; + + } else { + LM_BUG("Bad command parameter type\n"); + ret = E_BUG; + goto error; + } + + elems[i].u.data = (void*)gp; + } + + return 0; +error: + if (gp) + pkg_free(gp); + return ret; +} + +int get_cmd_fixups(struct sip_msg* msg, struct cmd_param *params, + action_elem_t *elems, void **cmdp) +{ + int i; + struct cmd_param *param; + gparam_p gp; + pv_value_t pv_val; + str sval; + + for (param=params, i=1; param->flags; param++, i++) { + gp = (gparam_p)elems[i].u.data; + if (!gp) { + cmdp[i-1] = NULL; + continue; + } + + if (param->flags & CMD_PARAM_INT) { + + switch (gp->type) { + case GPARAM_TYPE_INT: + cmdp[i-1] = (void*)&gp->v.ival; + break; + case GPARAM_TYPE_PVS: + if (pv_get_spec_value(msg, gp->v.pvs, &pv_val) != 0) { + LM_ERR("Failed to get spec value in param [%d]\n", i); + return E_UNSPEC; + } + if (pv_val.flags & PV_VAL_NULL || + !(pv_val.flags & PV_VAL_INT)) { + LM_ERR("Variable in param [%d] is not an integer\n", i); + return E_UNSPEC; + } + + cmdp[i-1] = &pv_val.ri; + + /* run fixup as we now have the value of the variable */ + if (param->fixup && param->fixup(&cmdp[i-1]) < 0) { + LM_ERR("Fixup failed for param [%d]\n", i); + return E_UNSPEC; + } + + break; + case GPARAM_TYPE_FIXUP: + /* fixup was possible at startup */ + cmdp[i-1] = gp->v.val; + break; + default: + LM_BUG("Bad type for generic parameter\n"); + return E_BUG; + } + + } else if (param->flags & CMD_PARAM_STR) { + + switch (gp->type) { + case GPARAM_TYPE_STR: + cmdp[i-1] = (void*)&gp->v.sval; + break; + case GPARAM_TYPE_PVE: + if (pv_printf_s(msg, gp->v.pve, &sval) != 0) { + LM_ERR("Failed to print formatted string in param [%d]\n", i); + return E_UNSPEC; + } + + cmdp[i-1] = &sval; + + if (param->fixup && param->fixup(&cmdp[i-1]) < 0) { + LM_ERR("Fixup failed for param [%d]\n", i); + return E_UNSPEC; + } + + break; + case GPARAM_TYPE_PVS: + if (pv_get_spec_value(msg, gp->v.pvs, &pv_val) != 0) { + LM_ERR("Failed to get spec value in param [%d]\n", i); + return E_UNSPEC; + } + if (pv_val.flags & PV_VAL_NULL || + !(pv_val.flags & PV_VAL_STR)) { + LM_ERR("Variable in param [%d] is not a string\n", i); + return E_UNSPEC; + } + + cmdp[i-1] = &pv_val.rs; + + if (param->fixup && param->fixup(&cmdp[i-1]) < 0) { + LM_ERR("Fixup failed for param [%d]\n", i); + return E_UNSPEC; + } + + break; + case GPARAM_TYPE_FIXUP: + cmdp[i-1] = gp->v.val; + break; + default: + LM_BUG("Bad type for generic parameter\n"); + return E_BUG; + } + + } else if (param->flags & CMD_PARAM_VAR) { + if (gp->type != GPARAM_TYPE_PVS) { + LM_BUG("Bad type for generic parameter\n"); + return E_BUG; + } + + cmdp[i-1] = gp->v.pvs; + + if (param->fixup && param->fixup(&cmdp[i-1]) < 0) { + LM_ERR("Fixup failed for param [%d]\n", i); + return E_UNSPEC; + } + + } else { + LM_BUG("Bad command parameter type\n"); + return E_BUG; + } + } + + return 0; +} + +int free_cmd_fixups(struct cmd_param *params, action_elem_t *elems, void **cmdp) +{ + int i; + struct cmd_param *param; + gparam_p gp; + + for (param=params, i=1; param->flags; param++, i++) { + gp = (gparam_p)elems[i].u.data; + if (!gp) + continue; + + if (param->flags & CMD_PARAM_INT) { + if (param->free_fixup && gp->type == GPARAM_TYPE_PVS) + if (param->free_fixup(&cmdp[i-1]) < 0) { + LM_ERR("Failed to free fixup for param [%d]\n", i); + return E_UNSPEC; + } + } else if (param->flags & CMD_PARAM_STR) { + if (param->free_fixup && (gp->type == GPARAM_TYPE_PVS || + gp->type == GPARAM_TYPE_PVE)) + if (param->free_fixup(&cmdp[i-1]) < 0) { + LM_ERR("Failed to free fixup for param [%d]\n", i); + return E_UNSPEC; + } + } else if (param->flags & CMD_PARAM_VAR) { + if (param->free_fixup) + if (param->free_fixup(&cmdp[i-1]) < 0) { + LM_ERR("Failed to free fixup for param [%d]\n", i); + return E_UNSPEC; + } + } else { + LM_BUG("Bad command parameter type\n"); + return E_BUG; + } + } + + return 0; +} diff --git a/route_struct.h b/route_struct.h index 5dc9f7194d4..a3dd867f13a 100644 --- a/route_struct.h +++ b/route_struct.h @@ -157,8 +157,12 @@ void print_actions(struct action* a); int is_mod_func_used(struct action *a, char *name, int param_no); int is_mod_async_func_used(struct action *a, char *name, int param_no); +struct cmd_param; - +int fix_cmd(struct cmd_param *params, action_elem_t *elems); +int get_cmd_fixups(struct sip_msg* msg, struct cmd_param *params, + action_elem_t *elems, void **cmdp); +int free_cmd_fixups(struct cmd_param *params, action_elem_t *elems, void **cmdp); #endif diff --git a/sr_module.c b/sr_module.c index c4ea1f67c7a..7505c772115 100644 --- a/sr_module.c +++ b/sr_module.c @@ -417,63 +417,52 @@ int load_module(char* name) * 0 if not found * flags parameter is OR value of all flags that must match */ -cmd_function find_export(char* name, int param_no, int flags) +cmd_function find_export(char* name, int flags) { cmd_export_t* cmd; - cmd = find_cmd_export_t(name, param_no, flags); + cmd = find_cmd_export_t(name, flags); if (cmd==0) return 0; + return cmd->function; } -/* searches the module list and returns pointer to the "name" cmd_export_t - * structure or 0 if not found - * In order to find the module the name, flags parameter number and type and - * the value of all flags in the config must match to the module export +/* Searches the module list for the "name" cmd_export_t structure. */ -cmd_export_t* find_cmd_export_t(char* name, int param_no, int flags) +cmd_export_t* find_cmd_export_t(char* name, int flags) { struct sr_module* t; cmd_export_t* cmd; for(t=modules;t;t=t->next){ for(cmd=t->exports->cmds; cmd && cmd->name; cmd++){ - if((strcmp(name, cmd->name)==0)&& - (cmd->param_no==param_no) && - ((cmd->flags & flags) == flags) - ){ - LM_DBG("found <%s>(%d) in module %s [%s]\n", - name, param_no, t->exports->name, t->path); + if((strcmp(name, cmd->name)==0)&&((cmd->flags & flags) == flags)){ + LM_DBG("found <%s> in module %s [%s]\n", + name, t->exports->name, t->path); return cmd; } } } + LM_DBG("<%s> not found \n", name); return 0; } - - -/* searches the module list and returns pointer to the async "name" cmd_export_t - * structure or 0 if not found - * In order to find the module the name, flags parameter number in the config - * must match to the module export +/* Searches the module list for the "name" acmd_export_t structure. */ -acmd_export_t* find_acmd_export_t(char* name, int param_no) +acmd_export_t* find_acmd_export_t(char* name) { struct sr_module* t; acmd_export_t* cmd; for(t=modules;t;t=t->next){ for(cmd=t->exports->acmds; cmd && cmd->name; cmd++){ - if((strcmp(name, cmd->name)==0)&& - (cmd->param_no==param_no) - ){ - LM_DBG("found async <%s>(%d) in module %s [%s]\n", - name, param_no, t->exports->name, t->path); + if((strcmp(name, cmd->name)==0)){ + LM_DBG("found <%s> in module %s [%s]\n", + name, t->exports->name, t->path); return cmd; } } @@ -482,14 +471,65 @@ acmd_export_t* find_acmd_export_t(char* name, int param_no) return 0; } +/* Checks if the module function is called with the right number of parameters + * and all mandatory parameters are given + * Return: + * 0 - correct call + * -1 - too few parameters + * -2 - too many parameters + * -3 - mandatory parameter omitted + */ +int check_cmd_call_params(cmd_export_t *cmd, action_elem_t *elems, int no_params) +{ + struct cmd_param *param; + int n=0, m=0, i; + + for (param=cmd->params; param->flags; param++, n++) + if (!(param->flags & CMD_PARAM_OPT)) + m = n+1; + + if (no_params < m) /* check the minimum number of arguments for the call, + * including optional params that must be explicitly omitted */ + return -1; + else if (no_params > n) + return -2; + + for (i=1, param=cmd->params; i<=no_params; i++, param++) + if (!(param->flags & CMD_PARAM_OPT) && elems[i].type == NULLV_ST) + return -3; + return 0; +} + +/* simillar function to check_cmd_call_params but for async cmds */ +int check_acmd_call_params(acmd_export_t *acmd, action_elem_t *elems, int no_params) +{ + struct cmd_param *param; + int n=0, m=0, i; + + for (param=acmd->params; param->flags; param++, n++) + if (!(param->flags & CMD_PARAM_OPT)) + m = n+1; + + if (no_params < m) /* check the minimum number of arguments for the call, + * including optional params that must be explicitly omitted */ + return -1; + else if (no_params > n) + return -2; + + for (i=1, param=acmd->params; i<=no_params; i++, param++) + if (!(param->flags & CMD_PARAM_OPT) && elems[i].type == NULLV_ST) + return -3; + + return 0; +} /* * searches the module list and returns pointer to "name" function in module * "mod" or 0 if not found * flags parameter is OR value of all flags that must match */ -cmd_function find_mod_export(char* mod, char* name, int param_no, int flags) +cmd_function find_mod_export(char* mod, char* name, int flags) { struct sr_module* t; cmd_export_t* cmd; @@ -498,7 +538,6 @@ cmd_function find_mod_export(char* mod, char* name, int param_no, int flags) if (strcmp(t->exports->name, mod) == 0) { for (cmd = t->exports->cmds; cmd && cmd->name; cmd++) { if ((strcmp(name, cmd->name) == 0) && - (cmd->param_no == param_no) && ((cmd->flags & flags) == flags) ){ LM_DBG("found <%s> in module %s [%s]\n", diff --git a/sr_module.h b/sr_module.h index 7dad1678eb4..46ae096c5d8 100644 --- a/sr_module.h +++ b/sr_module.h @@ -57,12 +57,12 @@ #include "sr_module_deps.h" typedef struct module_exports* (*module_register)(); -typedef int (*cmd_function)(struct sip_msg*, char*, char*, char*, char*, - char*, char*); +typedef int (*cmd_function)(struct sip_msg*, void*, void*, void*, void*, + void*, void*); typedef int (*acmd_function)(struct sip_msg*, async_ctx *ctx, - char*, char*, char*, char*, char*, char*); -typedef int (*fixup_function)(void** param, int param_no); -typedef int (*free_fixup_function)(void** param, int param_no); + void*, void*, void*, void*, void*, void*); +typedef int (*fixup_function)(void** param); +typedef int (*free_fixup_function)(void** param); typedef int (*response_function)(struct sip_msg*); typedef void (*destroy_function)(); typedef int (*init_function)(void); @@ -106,26 +106,36 @@ typedef int (*mod_proc_wrapper)(); #define PROC_FLAG_INITCHILD (1<<0) #define PROC_FLAG_HAS_IPC (1<<1) +#define MAX_CMD_PARAMS (MAX_ACTION_ELEMS-1) -struct cmd_export_ { - char* name; /* null terminated command name */ - cmd_function function; /* pointer to the corresponding function */ - int param_no; /* number of parameters used by the function */ +#define CMD_PARAM_INT (1<<0) /* integer parameter */ +#define CMD_PARAM_STR (1<<1) /* string parameter */ +#define CMD_PARAM_VAR (1<<2) /* PV spec parameter */ +#define CMD_PARAM_OPT (1<<3) /* optional parameter */ + +struct cmd_param { + int flags; /* parameter flags */ fixup_function fixup; /* pointer to the function called to "fix" the - parameters */ + parameter */ free_fixup_function free_fixup; /* pointer to the function called to free the - "fixed" parameters */ - int flags; /* Function flags */ + "fixed" parameter */ +}; + +struct cmd_export_ { + char* name; /* null terminated command name */ + cmd_function function; /* pointer to the corresponding function */ + struct cmd_param + params[MAX_CMD_PARAMS]; /* array of parameters */ + int flags; /* Function flags */ }; struct acmd_export_ { - char* name; /* null terminated command name */ - acmd_function function; /* pointer to the corresponding function */ - int param_no; /* number of parameters used by the function */ - fixup_function fixup; /* pointer to the function called to "fix" the - parameters */ + char* name; /* null terminated command name */ + acmd_function function; /* pointer to the corresponding function */ + struct cmd_param + params[MAX_CMD_PARAMS]; /* array of parameters */ }; @@ -215,10 +225,12 @@ struct sr_module* modules; /*!< global module list*/ int register_builtin_modules(); int register_module(struct module_exports*, char*, void*); int load_module(char* name); -cmd_export_t* find_cmd_export_t(char* name, int param_no, int flags); -acmd_export_t* find_acmd_export_t(char* name, int param_no); -cmd_function find_export(char* name, int param_no, int flags); -cmd_function find_mod_export(char* mod, char* name, int param_no, int flags); +cmd_export_t* find_cmd_export_t(char* name, int flags); +acmd_export_t* find_acmd_export_t(char* name); +int check_cmd_call_params(cmd_export_t *cmd, action_elem_t *elems, int no_params); +int check_acmd_call_params(acmd_export_t *acmd, action_elem_t *elems, int no_params); +cmd_function find_export(char* name, int flags); +cmd_function find_mod_export(char* mod, char* name, int flags); void destroy_modules(); int init_child(int rank); int init_modules(void); diff --git a/trace_api.c b/trace_api.c index d0bdada9ba9..beb69281722 100644 --- a/trace_api.c +++ b/trace_api.c @@ -42,7 +42,7 @@ int trace_prot_bind(char* module_name, trace_proto_t* prot) } bind_f = (trace_bind_api_f) find_mod_export(module_name, - "trace_bind_api", 1, 0); + "trace_bind_api", 0); if (bind_f) { LM_DBG("using trace bind api for %s\n", module_name);