You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/modules/erlang/mod_erlang.c

1448 lines
32 KiB

/**
* Copyright (C) 2015 Bicom Systems Ltd, (bicomsystems.com)
*
* Author: Seudin Kasumovic (seudin.kasumovic@gmail.com)
*
* This file is part of Kamailio, a free SIP server.
*
* Kamailio is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* Kamailio is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "../../ver.h"
#include "../../sr_module.h"
#include "../../pt.h"
#include "../../cfg/cfg_struct.h"
#include "../../mod_fix.h"
#include "../../script_cb.h"
#ifndef USE_TCP
#error "USE_TCP must be enabled for this module"
#endif
#include "../../pass_fd.h"
#include "mod_erlang.h"
#include "erl_helpers.h"
#include "cnode.h"
#include "handle_rpc.h"
#include "pv_xbuff.h"
#include "pv_tuple.h"
#include "pv_atom.h"
#include "pv_list.h"
#include "pv_pid.h"
#include "pv_ref.h"
MODULE_VERSION
/* module exports */
static int child_init(int rank);
static int mod_init(void);
static void mod_destroy(void);
static int postprocess_request(struct sip_msg *msg, unsigned int flags, void *_param);
/* exported functions */
static int erl_rpc(struct sip_msg *msg, char *module, char *function, char *args, char *reply);
static int erl_reg_send_k(struct sip_msg *msg, char *_server, char *_emsg);
static int erl_send_k(struct sip_msg *msg, char *_server, char *_emsg);
static int erl_reply_k(struct sip_msg *msg, char *_emsg);
/* fix-ups */
static int fixup_rpc(void** param, int param_no);
static int fixup_send(void** param, int param_no);
static int fixup_reg(void** param, int param_no);
static int fixup_reply(void** param, int param_no);
int fixup_free_rpc(void** param, int param_no);
int fixup_free_reg(void** param, int param_no);
int fixup_free_send(void** param, int param_no);
int fixup_free_reply(void** param, int param_no);
/* initialize common vars */
str cookie = STR_NULL;
int trace_level = 0;
str cnode_alivename = STR_NULL;
str cnode_host = STR_NULL;
int no_cnodes=1;
int rpc_reply_with_struct = 0;
str erlang_nodename = STR_NULL;
str erlang_node_sname = STR_NULL;
int rex_call_in_progress = 0;
int *usocks[2];
int csockfd;
handler_common_t* io_handlers = NULL;
erl_api_t erl_api;
static pv_export_t pvs[] = {
{
{ "erl_tuple", (sizeof("erl_tuple")-1) },
PVT_OTHER,
pv_tuple_get,
pv_tuple_set,
pv_xbuff_parse_name,
0,
0,
0
},
{
{ "erl_atom", (sizeof("erl_atom")-1) },
PVT_OTHER,
pv_atom_get,
pv_atom_set,
pv_atom_parse_name,
0,
0,
0
},
{
{ "erl_list", (sizeof("erl_list")-1) },
PVT_OTHER,
pv_list_get,
pv_list_set,
pv_xbuff_parse_name,
0,
0,
0
},
{
{ "erl_xbuff", (sizeof("erl_xbuff")-1) },
PVT_OTHER,
pv_xbuff_get,
pv_xbuff_set,
pv_xbuff_parse_name,
0,
0,
0
},
{
{ "erl_pid", (sizeof("erl_pid")-1) },
PVT_OTHER,
pv_pid_get,
pv_pid_set,
pv_pid_parse_name,
0,
0,
0
},
{
{ "erl_ref", (sizeof("erl_ref")-1) },
PVT_OTHER,
pv_ref_get,
pv_ref_set,
pv_ref_parse_name,
0,
0,
0
},
{{0,0}, 0, 0, 0, 0, 0, 0, 0}
};
/* exported parameters */
static param_export_t parameters[] =
{
/* Kamailo C node parameters */
{ "no_cnodes", PARAM_INT, &no_cnodes },
{ "cnode_alivename", PARAM_STR, &cnode_alivename },
{ "cnode_host", PARAM_STR, &cnode_host },
/* Erlang node parameters */
{ "erlang_nodename", PARAM_STR, &erlang_nodename },
{ "erlang_node_sname", PARAM_STR, &erlang_node_sname },
{ "cookie", PARAM_STR, &cookie },
{ "trace_level", PARAM_INT, &trace_level }, /* tracing level on the distribution */
{ "rpc_reply_with_struct", PARAM_INT, &rpc_reply_with_struct},
{ 0, 0, 0 }
};
/* exported commands */
static cmd_export_t commands[] =
{
{"erl_rpc", (cmd_function)erl_rpc, 4, fixup_rpc, fixup_free_rpc, ANY_ROUTE},
{"erl_send", (cmd_function)erl_send_k, 2, fixup_send, fixup_free_send, ANY_ROUTE},
{"erl_reg_send", (cmd_function)erl_reg_send_k, 2, fixup_reg, fixup_free_reg, ANY_ROUTE},
{"erl_reply", (cmd_function)erl_reply_k, 1, fixup_reply, fixup_free_reply, EVENT_ROUTE},
{"load_erl",(cmd_function)load_erl,0, 0, 0, 0}, /* API loader */
{ 0, 0, 0, 0, 0, 0 }
};
struct module_exports exports = {
"erlang",
DEFAULT_DLFLAGS,
commands,
parameters,
NULL,
NULL,
pvs,
NULL,
mod_init,
NULL,
mod_destroy,
child_init
};
/**
* \brief Initialize Erlang module
*/
static int mod_init(void)
{
/* check required parameters */
if (!cookie.s || cookie.len == 0)
{
LM_CRIT("Erlang cookie parameter is required\n");
return -1;
}
cookie.s[cookie.len]=0;
if ((!erlang_nodename.s || erlang_nodename.len == 0)
&& (!erlang_node_sname.s || erlang_node_sname.len == 0)) {
LM_CRIT("Erlang node name is required\n");
return -1;
}
if (erlang_nodename.s) {
erlang_nodename.s[erlang_nodename.len]=0;
}
if (erlang_node_sname.s) {
erlang_node_sname.s[erlang_node_sname.len]=0;
}
if (!cnode_alivename.s || !cnode_alivename.len) {
LM_CRIT("Kamailio C node alive name is required\n");
return -1;
}
cnode_alivename.s[cnode_alivename.len]=0;
if (!cnode_host.s || !cnode_host.len) {
LM_WARN("Kamailio host name is not set, trying with gethostname...\n");
return -1;
}
if (erl_load_api(&erl_api)) {
LM_CRIT("failed to load erl API\n");
return -1;
}
if (compile_xbuff_re()) {
return -1;
}
if (register_script_cb(postprocess_request, POST_SCRIPT_CB | REQUEST_CB, 0)
!= 0)
{
LOG(L_CRIT, "could not register request post processing call back.\n");
return -1;
}
/* init RPC handler for Erlang calls */
init_rpc_handlers();
/* add space for extra processes */
register_procs(no_cnodes);
/* add child to update local config framework structures */
cfg_register_child(no_cnodes);
return 0;
}
#define MAX_CNODE_DESC_LEN (MAXNODELEN + sizeof("Erlang C node "))
/**
* \brief Initialize Erlang module children
*/
static int child_init(int rank)
{
char _cnode_desc[MAX_CNODE_DESC_LEN];
int pair[2], data[2];
int i,j,pid;
if (rank == PROC_INIT) {
#ifdef SHM_MEM
usocks[KSOCKET]=(int*)shm_malloc(sizeof(int)*no_cnodes);
if (!usocks[KSOCKET]) {
LM_ERR("Not enough memory\n");
return -1;
}
usocks[CSOCKET]=(int*)shm_malloc(sizeof(int)*no_cnodes);
if (!usocks[CSOCKET]) {
LM_ERR("Not enough memory\n");
return -1;
}
#else
usocks[KSOCKET]=(int*)pkg_malloc(sizeof(int)*no_cnodes);
if (!usocks[KSOCKET]) {
LM_ERR("Not enough memory\n");
return -1;
}
usocks[CSOCKET]=(int*)pkg_malloc(sizeof(int)*no_cnodes);
if (!usocks[CSOCKET]) {
LM_ERR("Not enough memory\n");
return -1;
}
#endif
for(i=0;i<no_cnodes;i++) {
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair)) {
LOG(L_CRIT,"failed create socket pair: %s. -- don't start -- \n", strerror(errno));
return -1;
}
usocks[KSOCKET][i]=pair[KSOCKET];
usocks[CSOCKET][i]=pair[CSOCKET];
}
return 0;
}
if (rank == PROC_MAIN) {
for (j=0; j<no_cnodes; j++) {
snprintf(_cnode_desc, MAX_CNODE_DESC_LEN, "%s%.*s%d@%.*s", "Erlang C Node ", STR_FMT(&cnode_alivename), j+1, STR_FMT(&cnode_host));
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair)) {
LOG(L_CRIT,"failed create socket pair: %s. -- don't start -- \n", strerror(errno));
return -1;
}
if ((pid = fork_process(PROC_NOCHLDINIT, _cnode_desc, 0)) == -1) {
return -1; /* error -- don't start -- */
} else if (pid == 0) {
/* child */
if(cfg_child_init()) {
LM_CRIT("failed cfg_child_init\n");
return -1;
}
for (i=0;i<no_cnodes;i++)
{
close(usocks[KSOCKET][i]);
if (i!=j) close(usocks[CSOCKET][i]);
}
csockfd = usocks[CSOCKET][j];
/* enter Erlang C Node main loop (cnode process) */
cnode_main_loop(j);
LM_CRIT("failed to start Erlang C node main loop!\n");
return -1;
}
/* parent */
}
return 0;
}
for (i=0;i<no_cnodes;i++) {
close(usocks[CSOCKET][i]);
}
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair)) {
LOG(L_CRIT,"failed create socket pair: %s. -- don't start -- \n", strerror(errno));
return -1;
}
data[0] = pair[CSOCKET];
data[1] = my_pid();
if (send_fd(usocks[KSOCKET][process_no % no_cnodes],(void*)&data,sizeof(data),pair[CSOCKET])==-1) {
LM_CRIT("failed to send socket %d over socket %d to cnode\n",pair[CSOCKET],usocks[KSOCKET][process_no % no_cnodes]);
return -1;
}
csockfd = pair[KSOCKET];
erl_set_nonblock(csockfd);
erl_init_common();
for (i=0;i<no_cnodes;i++) {
close(usocks[KSOCKET][i]);
}
return 0;
}
/**
* @brief Destroy module allocated resources
*/
static void mod_destroy(void)
{
#ifdef SHM_MEM
shm_free(usocks[0]);
shm_free(usocks[1]);
#else
pkg_free(usocks[0]);
pkg_free(usocks[1]);
#endif
free_tuple_fmt_buff();
free_atom_fmt_buff();
free_list_fmt_buff();
free_xbuff_fmt_buff();
free_pid_fmt_buff();
free_ref_fmt_buff();
}
static int postprocess_request(struct sip_msg *msg, unsigned int flags, void *_param)
{
free_tuple_fmt_buff();
free_atom_fmt_buff();
free_list_fmt_buff();
free_xbuff_fmt_buff();
free_pid_fmt_buff();
free_ref_fmt_buff();
return 0;
}
/**
* Erlang RPC.
*/
static int erl_rpc(struct sip_msg *msg, char *_m, char *_f, char *_a, char *_r)
{
erl_param_t *m=(erl_param_t*)_m;
erl_param_t *f=(erl_param_t*)_f;
erl_param_t *a=(erl_param_t*)_a;
erl_param_t *r=(erl_param_t*)_r;
str module;
str function;
str vname;
sr_xavp_t *xreq=NULL;
sr_xavp_t *xrepl=NULL;
pv_spec_t sp;
pv_spec_t *nsp;
pv_param_t pvp;
pv_name_t *pvn;
pv_index_t *pvi;
int idx;
int idxf;
int attr;
ei_x_buff ei_req;
ei_x_buff ei_rep;
switch (m->type) {
case ERL_PARAM_FPARAM:
if(get_str_fparam(&module,msg,m->value.fp)) {
LM_ERR("can't get module name\n");
}
break;
default:
LM_ERR("unexpected type for module name parameter\n");
return -1;
}
switch (f->type) {
case ERL_PARAM_FPARAM:
if(get_str_fparam(&function,msg,f->value.fp)) {
LM_ERR("can't get function name\n");
}
break;
default:
LM_ERR("unexpected type for function name parameter\n");
return -1;
}
switch(a->type){
case ERL_PARAM_FPARAM:
if(get_str_fparam(&vname,msg,a->value.fp)){
LM_ERR("can't get name of arguments parameter\n");
return -1;
}
xreq = pv_list_get_list(&vname);
if (!xreq){
xreq = pv_xbuff_get_xbuff(&vname);
}
if (!xreq) {
LM_ERR("can't find variable $list(%.*s) nor $xbuff(%.*s)",STR_FMT(&vname),STR_FMT(&vname));
return -1;
}
break;
case ERL_PARAM_XBUFF_SPEC:
nsp = NULL;
pvp = a->value.sp.pvp; /* work on copy */
if( pvp.pvn.type == PV_NAME_PVAR) {
nsp = pvp.pvn.u.dname;
}
if (nsp) {
pvi = &nsp->pvp.pvi;
pvn = &nsp->pvp.pvn;
sp = *nsp;
} else {
pvi = &pvp.pvi;
pvn = &pvp.pvn;
sp = a->value.sp;
}
if (sp.setf == pv_list_set ) {
xreq = pv_list_get_list(&pvn->u.isname.name.s);
} else if (sp.setf == pv_xbuff_set) {
xreq = pv_xbuff_get_xbuff(&pvn->u.isname.name.s);
} else if (sp.setf == pv_tuple_set) {
xreq = pv_tuple_get_tuple(&pvn->u.isname.name.s);
}
/* fix index */
attr = xbuff_get_attr_flags(pvi->type);
/* get the index */
if(pv_get_spec_index(msg, &pvp, &idx, &idxf))
{
LM_ERR("invalid index\n");
return -1;
}
if (xbuff_is_attr_set(attr)) {
LM_WARN("attribute is not expected here!\n");
}
if (!xreq) {
LM_ERR("undefined variable '%.*s'\n",STR_FMT(&pvn->u.isname.name.s));
return -1;
}
xreq = xreq->val.v.xavp;
if ((idxf != PV_IDX_ALL) && !xbuff_is_no_index(attr) ) {
xreq = xavp_get_nth(&xreq->val.v.xavp,idx,NULL);
}
if (!xreq) {
LM_ERR("undefined value in '%.*s' at index %d\n",STR_FMT(&pvn->u.isname.name.s),idx);
return -1;
}
if (xreq->val.type != SR_XTYPE_XAVP || xreq->name.s[0] != 'l') {
LM_ERR("given value in parameter args is not list\n");
return -1;
}
break;
default:
LM_ERR("unexpected type for arguments parameter\n");
return -1;
}
switch(r->type){
case ERL_PARAM_FPARAM:
if(get_str_fparam(&vname,msg,r->value.fp)){
LM_ERR("can't get name of arguments parameter\n");
return -1;
}
xrepl = pv_xbuff_get_xbuff(&vname);
break;
case ERL_PARAM_XBUFF_SPEC:
nsp = NULL;
pvp = r->value.sp.pvp; /* work on copy */
if( pvp.pvn.type == PV_NAME_PVAR) {
nsp = pvp.pvn.u.dname;
}
if (nsp) {
pvi = &nsp->pvp.pvi;
pvn = &nsp->pvp.pvn;
sp = *nsp;
} else {
pvi = &pvp.pvi;
pvn = &pvp.pvn;
sp = a->value.sp;
}
if (sp.setf == pv_xbuff_set ) {
xrepl = pv_xbuff_get_xbuff(&pvn->u.isname.name.s);
} else {
LM_ERR("unsupported variable type, xbuff only\n");
return -1;
}
/* fix index */
attr = xbuff_get_attr_flags(pvi->type);
/* get the index */
if(pv_get_spec_index(msg, &pvp, &idx, &idxf))
{
LM_ERR("invalid index\n");
return -1;
}
if (xbuff_is_attr_set(attr)) {
LM_WARN("attribute is not expected here!\n");
}
if ((idxf != PV_IDX_ALL) && !xbuff_is_no_index(attr) ) {
LM_ERR("index is not expected here!\n");
return -1;
}
break;
default:
LM_ERR("unexpected type for arguments parameter\n");
return -1;
}
/* note: new without version byte */
ei_x_new(&ei_req);
/* ei_x_buff <- XAVP */
if (erl_api.xavp2xbuff(&ei_req,xreq)) {
LM_ERR("failed to encode\n");
ei_x_free(&ei_req);
return -1;
}
memset((void*)&ei_rep,0,sizeof(ei_x_buff));
erl_api.rpc(&ei_rep,&module,&function,&ei_req);
if (xrepl) {
xavp_destroy_list(&xrepl->val.v.xavp);
} else {
xrepl = xbuff_new(&pvn->u.isname.name.s);
}
/* must be XAVP */
xrepl->val.type = SR_XTYPE_XAVP;
/* XAVP <- ei_x_buff */
if (erl_api.xbuff2xavp(&xrepl->val.v.xavp,&ei_rep)){
LM_ERR("failed to decode\n");
ei_x_free(&ei_req);
ei_x_free(&ei_rep);
return -1;
}
ei_x_free(&ei_req);
ei_x_free(&ei_rep);
return 1;
}
static int fixup_rpc(void** param, int param_no)
{
erl_param_t *erl_param;
pv_spec_p psp;
str s;
erl_param=(erl_param_t*)pkg_malloc(sizeof(erl_param_t));
if(!erl_param) {
LM_ERR("no more memory\n");
return -1;
}
memset(erl_param,0,sizeof(erl_param_t));
if(param_no==1 || param_no==2) {
if (fix_param_types(FPARAM_STR|FPARAM_STRING|FPARAM_AVP|FPARAM_PVS|FPARAM_PVE,param)){
LM_ERR("wrong parameter #%d\n",param_no);
return -1;
}
erl_param->type = ERL_PARAM_FPARAM;
erl_param->value.fp = (fparam_t*)*param;
}
if (param_no==3 || param_no==4) {
s.s = (char*)*param; s.len = strlen(s.s);
if (pv_parse_avp_name(&erl_param->value.sp,&s)) {
LM_ERR("failed to parse parameter #%d\n",param_no);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
if (erl_param->value.sp.pvp.pvn.type == PV_NAME_INTSTR) {
if (fix_param_types(FPARAM_STR|FPARAM_STRING,param)) {
LM_ERR("wrong parameter #%d\n",param_no);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
erl_param->type = ERL_PARAM_FPARAM;
erl_param->value.fp = (fparam_t*)*param;
} else {
/* lets check what is acceptable */
psp = (pv_spec_p)erl_param->value.sp.pvp.pvn.u.dname;
if (psp->setf != pv_list_set && psp->setf != pv_xbuff_set) {
LM_ERR("wrong parameter #%d: accepted types are list or xbuff\n",param_no);
pv_spec_free(&erl_param->value.sp);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
erl_param->type = ERL_PARAM_XBUFF_SPEC;
LM_ERR("erl_param->value.sp.type=%d\n",erl_param->value.sp.type);
}
}
*param = (void*)erl_param;
return 0;
}
int fixup_free_rpc(void** param, int param_no) {
erl_param_t *erl_param;
erl_param = (erl_param_t*)*param;
if(param_no==1 || param_no==2) {
return fixup_free_fparam_2((void**)&erl_param->value.fp,param_no);
}
if (param_no==3 || param_no==4) {
LM_ERR("erl_param->value.sp.type=%d\n",erl_param->value.sp.type);
if (erl_param->value.sp.type == PVT_OTHER) {
pv_spec_free((pv_spec_p)erl_param->value.sp.pvp.pvn.u.dname);
} else if (erl_param->value.sp.pvp.pvn.type == PV_NAME_INTSTR) {
return fixup_free_fparam_2((void**)&erl_param->value.fp,param_no);
}
}
return 0;
}
static int erl_reg_send_k(struct sip_msg *msg, char *_server, char *_emsg)
{
erl_param_t *param_server=(erl_param_t*)_server;
erl_param_t *param_emsg=(erl_param_t*)_emsg;
str server;
str str_msg;
sr_xavp_t *xmsg=NULL;
pv_spec_t sp;
pv_spec_t *nsp = NULL;
pv_param_t pvp;
pv_name_t *pvn;
pv_index_t *pvi;
int idx;
int idxf;
int attr;
ei_x_buff ei_msg;
switch (param_server->type) {
case ERL_PARAM_FPARAM:
if(get_str_fparam(&server,msg,param_server->value.fp)) {
LM_ERR("can't get server process name\n");
}
break;
default:
LM_ERR("unexpected type for server name parameter\n");
return -1;
}
ei_x_new_with_version(&ei_msg);
switch(param_emsg->type){
case ERL_PARAM_FPARAM:
if(get_str_fparam(&str_msg,msg,param_emsg->value.fp)){
LM_ERR("can't get emsg parameter\n");
goto err;
}
ei_x_encode_string_len(&ei_msg,str_msg.s,str_msg.len);
break;
case ERL_PARAM_XBUFF_SPEC:
pvp = param_emsg->value.sp.pvp; /* work on copy */
if( pvp.pvn.type == PV_NAME_PVAR) {
nsp = pvp.pvn.u.dname;
}
if (nsp) {
pvi = &nsp->pvp.pvi;
pvn = &nsp->pvp.pvn;
sp = *nsp;
} else {
pvi = &pvp.pvi;
pvn = &pvp.pvn;
sp = param_emsg->value.sp;
}
if (sp.setf == pv_list_set ) {
xmsg = pv_list_get_list(&pvn->u.isname.name.s);
} else if (sp.setf == pv_xbuff_set) {
xmsg = pv_xbuff_get_xbuff(&pvn->u.isname.name.s);
} else if (sp.setf == pv_tuple_set) {
xmsg = pv_tuple_get_tuple(&pvn->u.isname.name.s);
}
/* fix index */
attr = xbuff_get_attr_flags(pvi->type);
/* get the index */
if(pv_get_spec_index(msg, &pvp, &idx, &idxf))
{
LM_ERR("invalid index\n");
return -1;
}
if (xbuff_is_attr_set(attr)) {
LM_WARN("attribute is not expected here!\n");
}
if (!xmsg) {
LM_ERR("undefined variable '%.*s'\n",STR_FMT(&pvn->u.isname.name.s));
return -1;
}
xmsg = xmsg->val.v.xavp;
if ((idxf != PV_IDX_ALL) && !xbuff_is_no_index(attr) ) {
xmsg = xavp_get_nth(&xmsg->val.v.xavp,idx,NULL);
}
if (!xmsg) {
LM_ERR("undefined value in '%.*s' at index %d\n",STR_FMT(&pvn->u.isname.name.s),idx);
goto err;
}
/* ei_x_buff <- XAVP */
if (erl_api.xavp2xbuff(&ei_msg,xmsg)) {
LM_ERR("failed to encode %.*s\n",STR_FMT(&pvn->u.isname.name.s));
goto err;
}
break;
default:
LM_ERR("unexpected type for emsg parameter\n");
return -1;
}
if (erl_api.reg_send(&server,&ei_msg)) {
goto err;
}
ei_x_free(&ei_msg);
return 1;
err:
ei_x_free(&ei_msg);
return -1;
}
static int fixup_reg(void** param, int param_no)
{
erl_param_t *erl_param;
pv_spec_p psp;
str s;
erl_param=(erl_param_t*)pkg_malloc(sizeof(erl_param_t));
if(!erl_param) {
LM_ERR("no more memory\n");
return -1;
}
memset(erl_param,0,sizeof(erl_param_t));
if(param_no==1) {
if (fix_param_types(FPARAM_STR|FPARAM_STRING|FPARAM_AVP|FPARAM_PVS|FPARAM_PVE|FPARAM_INT,param)){
LM_ERR("wrong parameter #%d\n",param_no);
return -1;
}
erl_param->type = ERL_PARAM_FPARAM;
erl_param->value.fp = (fparam_t*)*param;
}
if (param_no==2) {
s.s = (char*)*param; s.len = strlen(s.s);
if (pv_parse_avp_name(&erl_param->value.sp,&s)) {
LM_ERR("failed to parse parameter #%d\n",param_no);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
if (erl_param->value.sp.pvp.pvn.type == PV_NAME_INTSTR) {
if (fix_param_types(FPARAM_STR|FPARAM_STRING,param)) {
LM_ERR("wrong parameter #%d\n",param_no);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
erl_param->type = ERL_PARAM_FPARAM;
erl_param->value.fp = (fparam_t*)*param;
} else {
if (erl_param->value.sp.type == PVT_XAVP) {
LM_ERR("XAVP not acceptable for parameter #%d\n",param_no);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
psp = (pv_spec_p)erl_param->value.sp.pvp.pvn.u.dname;
if (psp->setf == pv_list_set
|| psp->setf == pv_xbuff_set
|| psp->setf == pv_tuple_set
|| psp->setf == pv_atom_set) {
erl_param->type = ERL_PARAM_XBUFF_SPEC;
} else {
LM_ERR("wrong parameter #%d\n",param_no);
pv_spec_free(&erl_param->value.sp);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
}
}
*param = (void*)erl_param;
return 0;
}
int fixup_free_reg(void** param, int param_no) {
erl_param_t *erl_param;
erl_param = (erl_param_t*)*param;
if(param_no==1) {
return fixup_free_fparam_1((void**)&erl_param->value.fp,param_no);
}
if (param_no==2) {
LM_ERR("erl_param->value.sp.type=%d\n",erl_param->value.sp.type);
if (erl_param->value.sp.type == PVT_OTHER) {
pv_spec_free((pv_spec_p)erl_param->value.sp.pvp.pvn.u.dname);
} else if (erl_param->value.sp.pvp.pvn.type == PV_NAME_INTSTR) {
return fixup_free_fparam_2((void**)&erl_param->value.fp,param_no);
}
}
return 0;
}
static int erl_reply_k(struct sip_msg *msg, char *_emsg)
{
erl_param_t *param_emsg=(erl_param_t*)_emsg;
str str_msg;
sr_xavp_t *xmsg=NULL;
pv_spec_t sp;
pv_spec_t *nsp = NULL;
pv_param_t pvp;
pv_name_t *pvn;
pv_index_t *pvi;
int idx;
int idxf;
int attr;
ei_x_buff ei_msg;
ei_x_new_with_version(&ei_msg);
switch(param_emsg->type){
case ERL_PARAM_FPARAM:
if(get_str_fparam(&str_msg,msg,param_emsg->value.fp)){
LM_ERR("can't get emsg parameter\n");
goto err;
}
ei_x_encode_string_len(&ei_msg,str_msg.s,str_msg.len);
break;
case ERL_PARAM_XBUFF_SPEC:
pvp = param_emsg->value.sp.pvp; /* work on copy */
if( pvp.pvn.type == PV_NAME_PVAR) {
nsp = pvp.pvn.u.dname;
}
if (nsp) {
pvi = &nsp->pvp.pvi;
pvn = &nsp->pvp.pvn;
sp = *nsp;
} else {
pvi = &pvp.pvi;
pvn = &pvp.pvn;
sp = param_emsg->value.sp;
}
if (sp.setf == pv_list_set ) {
xmsg = pv_list_get_list(&pvn->u.isname.name.s);
} else if (sp.setf == pv_xbuff_set) {
xmsg = pv_xbuff_get_xbuff(&pvn->u.isname.name.s);
} else if (sp.setf == pv_tuple_set) {
xmsg = pv_tuple_get_tuple(&pvn->u.isname.name.s);
}
/* fix index */
attr = xbuff_get_attr_flags(pvi->type);
pvi->type = xbuff_fix_index(pvi->type);
/* get the index */
if(pv_get_spec_index(msg, &pvp, &idx, &idxf))
{
LM_ERR("invalid index\n");
return -1;
}
if (xbuff_is_attr_set(attr)) {
LM_WARN("attribute is not expected here!\n");
}
if (!xmsg) {
LM_ERR("undefined variable '%.*s'\n",STR_FMT(&pvn->u.isname.name.s));
return -1;
}
xmsg = xmsg->val.v.xavp;
if ((idxf != PV_IDX_ALL) && !xbuff_is_no_index(attr) ) {
xmsg = xavp_get_nth(&xmsg->val.v.xavp,idx,NULL);
}
if (!xmsg) {
LM_ERR("undefined value in '%.*s' at index %d\n",STR_FMT(&pvn->u.isname.name.s),idx);
goto err;
}
/* ei_x_buff <- XAVP */
if (erl_api.xavp2xbuff(&ei_msg,xmsg)) {
LM_ERR("failed to encode %.*s\n",STR_FMT(&pvn->u.isname.name.s));
goto err;
}
break;
default:
LM_ERR("unexpected type for emsg parameter\n");
return -1;
}
if (erl_api.reply(&ei_msg)) {
goto err;
}
ei_x_free(&ei_msg);
return 1;
err:
ei_x_free(&ei_msg);
return -1;
}
static int fixup_reply(void** param, int param_no)
{
erl_param_t *erl_param;
pv_spec_p psp;
str s;
erl_param=(erl_param_t*)pkg_malloc(sizeof(erl_param_t));
if(!erl_param) {
LM_ERR("no more memory\n");
return -1;
}
memset(erl_param,0,sizeof(erl_param_t));
if (param_no==1) {
s.s = (char*)*param; s.len = strlen(s.s);
if (pv_parse_avp_name(&erl_param->value.sp,&s)) {
LM_ERR("failed to parse parameter #%d\n",param_no);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
if (erl_param->value.sp.pvp.pvn.type == PV_NAME_INTSTR) {
if (fix_param_types(FPARAM_STR|FPARAM_STRING,param)) {
LM_ERR("wrong parameter #%d\n",param_no);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
erl_param->type = ERL_PARAM_FPARAM;
erl_param->value.fp = (fparam_t*)*param;
} else {
if (erl_param->value.sp.type ==PVT_XAVP) {
LM_ERR("XAVP not acceptable for parameter #%d\n",param_no);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
psp = (pv_spec_p)erl_param->value.sp.pvp.pvn.u.dname;
if (psp->setf == pv_list_set
|| psp->setf == pv_xbuff_set
|| psp->setf == pv_tuple_set
|| psp->setf == pv_atom_set) {
erl_param->type = ERL_PARAM_XBUFF_SPEC;
} else {
LM_ERR("wrong parameter #%d\n",param_no);
pv_spec_free(&erl_param->value.sp);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
}
}
*param = (void*)erl_param;
return 0;
}
int fixup_free_reply(void** param, int param_no) {
erl_param_t *erl_param;
erl_param = (erl_param_t*)*param;
if (param_no==1) {
LM_ERR("erl_param->value.sp.type=%d\n",erl_param->value.sp.type);
if (erl_param->value.sp.type == PVT_OTHER) {
pv_spec_free((pv_spec_p)erl_param->value.sp.pvp.pvn.u.dname);
} else if (erl_param->value.sp.pvp.pvn.type == PV_NAME_INTSTR) {
return fixup_free_fparam_2((void**)&erl_param->value.fp,param_no);
}
}
return 0;
}
static int erl_send_k(struct sip_msg *msg, char *_pid, char *_emsg)
{
erl_param_t *param_pid=(erl_param_t*)_pid;
erl_param_t *param_emsg=(erl_param_t*)_emsg;
str str_msg;
sr_xavp_t *xmsg=NULL;
pv_spec_t sp;
pv_spec_t *nsp;
pv_param_t pvp;
pv_name_t *pvn;
pv_index_t *pvi;
int idx;
int idxf;
int attr;
ei_x_buff ei_msg;
erlang_pid *pid;
switch (param_pid->type) {
case ERL_PARAM_XBUFF_SPEC:
nsp = NULL;
pvp = param_pid->value.sp.pvp; /* work on copy */
if( pvp.pvn.type == PV_NAME_PVAR) {
nsp = pvp.pvn.u.dname;
}
if (nsp) {
pvi = &nsp->pvp.pvi;
pvn = &nsp->pvp.pvn;
sp = *nsp;
} else {
pvi = &pvp.pvi;
pvn = &pvp.pvn;
sp = param_pid->value.sp;
}
if (sp.getf == pv_pid_get ) {
xmsg = pv_pid_get_pid(&pvn->u.isname.name.s);
} else if (sp.getf == pv_xbuff_get) {
xmsg = pv_xbuff_get_xbuff(&pvn->u.isname.name.s);
} else {
LM_ERR("BUG: unexpected type for pid parameter\n");
return -1;
}
/* fix index */
attr = xbuff_get_attr_flags(pvi->type);
/* get the index */
if(pv_get_spec_index(msg, &pvp, &idx, &idxf))
{
LM_ERR("invalid index\n");
return -1;
}
if (xbuff_is_attr_set(attr)) {
LM_WARN("attribute is not expected here!\n");
}
if (!xmsg) {
LM_ERR("undefined variable '%.*s'\n",STR_FMT(&pvn->u.isname.name.s));
return -1;
}
xmsg = xmsg->val.v.xavp;
if ((idxf != PV_IDX_ALL) && !xbuff_is_no_index(attr) ) {
xmsg = xavp_get_nth(&xmsg->val.v.xavp,idx,NULL);
}
if (!xmsg) {
LM_ERR("undefined value in '%.*s' at index %d\n",STR_FMT(&pvn->u.isname.name.s),idx);
goto err;
}
/* erlang_pid <- XAVP */
if (xmsg->name.s[0] == 'p' && xmsg->val.type == SR_XTYPE_DATA && xmsg->val.v.data) {
pid = xmsg->val.v.data->p;
} else {
LM_ERR("invalid value for pid parameter\n");
return -1;
}
break;
default:
LM_ERR("unexpected type for pid parameter\n");
return -1;
}
ei_x_new_with_version(&ei_msg);
switch(param_emsg->type){
case ERL_PARAM_FPARAM:
if(get_str_fparam(&str_msg,msg,param_emsg->value.fp)){
LM_ERR("can't get emsg parameter\n");
goto err;
}
ei_x_encode_string_len(&ei_msg,str_msg.s,str_msg.len);
break;
case ERL_PARAM_XBUFF_SPEC:
nsp = NULL;
pvp = param_emsg->value.sp.pvp; /* work on copy */
if( pvp.pvn.type == PV_NAME_PVAR) {
nsp = pvp.pvn.u.dname;
}
if (nsp) {
pvi = &nsp->pvp.pvi;
pvn = &nsp->pvp.pvn;
sp = *nsp;
} else {
pvi = &pvp.pvi;
pvn = &pvp.pvn;
sp = param_emsg->value.sp;
}
if (sp.getf == pv_list_get ) {
xmsg = pv_list_get_list(&pvn->u.isname.name.s);
} else if (sp.getf == pv_xbuff_get) {
xmsg = pv_xbuff_get_xbuff(&pvn->u.isname.name.s);
} else if (sp.getf == pv_tuple_get) {
xmsg = pv_tuple_get_tuple(&pvn->u.isname.name.s);
} else if (sp.getf == pv_atom_get) {
xmsg = pv_atom_get_atom(&pvn->u.isname.name.s);
} else if (sp.getf == pv_pid_get) {
xmsg = pv_pid_get_pid(&pvn->u.isname.name.s);
}
/* fix index */
attr = xbuff_get_attr_flags(pvi->type);
/* get the index */
if(pv_get_spec_index(msg, &pvp, &idx, &idxf))
{
LM_ERR("invalid index\n");
return -1;
}
if (xbuff_is_attr_set(attr)) {
LM_WARN("attribute is not expected here!\n");
}
if (!xmsg) {
LM_ERR("undefined variable '%.*s'\n",STR_FMT(&pvn->u.isname.name.s));
return -1;
}
xmsg = xmsg->val.v.xavp;
if ((idxf != PV_IDX_ALL) && !xbuff_is_no_index(attr) ) {
xmsg = xavp_get_nth(&xmsg->val.v.xavp,idx,NULL);
}
if (!xmsg) {
LM_ERR("undefined value in '%.*s' at index %d\n",STR_FMT(&pvn->u.isname.name.s),idx);
goto err;
}
/* ei_x_buff <- XAVP */
if (erl_api.xavp2xbuff(&ei_msg,xmsg)) {
LM_ERR("failed to encode %.*s\n",STR_FMT(&pvn->u.isname.name.s));
goto err;
}
break;
default:
LM_ERR("unexpected type for emsg parameter\n");
return -1;
}
if (erl_api.send(pid,&ei_msg)) {
goto err;
}
ei_x_free(&ei_msg);
return 1;
err:
ei_x_free(&ei_msg);
return -1;
}
static int fixup_send(void** param, int param_no)
{
erl_param_t *erl_param;
pv_spec_p psp;
str s;
erl_param=(erl_param_t*)pkg_malloc(sizeof(erl_param_t));
if(!erl_param) {
LM_ERR("no more memory\n");
return -1;
}
memset(erl_param,0,sizeof(erl_param_t));
if (param_no==1) {
s.s = (char*)*param; s.len = strlen(s.s);
if (pv_parse_avp_name(&erl_param->value.sp,&s)) {
LM_ERR("failed to parse parameter #%d\n",param_no);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
if (erl_param->value.sp.pvp.pvn.type == PV_NAME_INTSTR) {
if (fix_param_types(FPARAM_STR|FPARAM_STRING,param)) {
LM_ERR("wrong parameter #%d\n",param_no);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
erl_param->type = ERL_PARAM_FPARAM;
erl_param->value.fp = (fparam_t*)*param;
}
else {
if (erl_param->value.sp.type == PVT_XAVP) {
LM_ERR("XAVP not acceptable for parameter #%d\n",param_no);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
psp = (pv_spec_p)erl_param->value.sp.pvp.pvn.u.dname;
if (psp->getf == pv_pid_get || psp->getf == pv_xbuff_get) {
erl_param->type = ERL_PARAM_XBUFF_SPEC;
} else {
LM_ERR("wrong parameter #%d\n",param_no);
pv_spec_free(&erl_param->value.sp);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
}
}
if (param_no==2) {
s.s = (char*)*param; s.len = strlen(s.s);
if (pv_parse_avp_name(&erl_param->value.sp,&s)) {
LM_ERR("failed to parse parameter #%d\n",param_no);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
if (erl_param->value.sp.pvp.pvn.type == PV_NAME_INTSTR) {
if (fix_param_types(FPARAM_STR|FPARAM_STRING,param)) {
LM_ERR("wrong parameter #%d\n",param_no);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
erl_param->type = ERL_PARAM_FPARAM;
erl_param->value.fp = (fparam_t*)*param;
} else {
if (erl_param->value.sp.type ==PVT_XAVP) {
LM_ERR("XAVP not acceptable for parameter #%d\n",param_no);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
psp = (pv_spec_p)erl_param->value.sp.pvp.pvn.u.dname;
if (psp->getf == pv_list_get
|| psp->getf == pv_xbuff_get
|| psp->getf == pv_tuple_get
|| psp->getf == pv_atom_get
|| psp->getf == pv_pid_get) {
erl_param->type = ERL_PARAM_XBUFF_SPEC;
} else {
LM_ERR("wrong parameter #%d\n",param_no);
pv_spec_free(&erl_param->value.sp);
pkg_free((void*)erl_param);
return E_UNSPEC;
}
}
}
*param = (void*)erl_param;
return 0;
}
int fixup_free_send(void** param, int param_no) {
erl_param_t *erl_param;
erl_param = (erl_param_t*)*param;
if (param_no==1 || param_no==2) {
if (erl_param->value.sp.type == PVT_OTHER) {
pv_spec_free((pv_spec_p)erl_param->value.sp.pvp.pvn.u.dname);
} else if (erl_param->value.sp.pvp.pvn.type == PV_NAME_INTSTR) {
return fixup_free_fparam_2((void**)&erl_param->value.fp,param_no);
}
}
return 0;
}