mirror of https://github.com/sipwise/kamailio.git
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.
1398 lines
35 KiB
1398 lines
35 KiB
/**
|
|
* dispatcher module - load balancing
|
|
*
|
|
* Copyright (C) 2004-2005 FhG Fokus
|
|
* Copyright (C) 2006 Voice Sistem SRL
|
|
* Copyright (C) 2015 Daniel-Constantin Mierla (asipto.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
|
|
*/
|
|
|
|
/*! \file
|
|
* \ingroup dispatcher
|
|
* \brief Dispatcher :: Dispatch
|
|
*/
|
|
|
|
/*! \defgroup dispatcher Dispatcher :: Load balancing and failover module
|
|
* The dispatcher module implements a set of functions for distributing SIP requests on a
|
|
* set of servers, but also grouping of server resources.
|
|
*
|
|
* - The module has an internal API exposed to other modules.
|
|
* - The module implements a couple of MI functions for managing the list of server resources
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include "../../lib/kmi/mi.h"
|
|
#include "../../sr_module.h"
|
|
#include "../../dprint.h"
|
|
#include "../../error.h"
|
|
#include "../../ut.h"
|
|
#include "../../route.h"
|
|
#include "../../timer_proc.h"
|
|
#include "../../mem/mem.h"
|
|
#include "../../mod_fix.h"
|
|
#include "../../rpc.h"
|
|
#include "../../rpc_lookup.h"
|
|
|
|
#include "ds_ht.h"
|
|
#include "dispatch.h"
|
|
#include "config.h"
|
|
#include "api.h"
|
|
|
|
MODULE_VERSION
|
|
|
|
#define DS_SET_ID_COL "setid"
|
|
#define DS_DEST_URI_COL "destination"
|
|
#define DS_DEST_FLAGS_COL "flags"
|
|
#define DS_DEST_PRIORITY_COL "priority"
|
|
#define DS_DEST_ATTRS_COL "attrs"
|
|
#define DS_TABLE_NAME "dispatcher"
|
|
|
|
/** parameters */
|
|
char *dslistfile = CFG_DIR"dispatcher.list";
|
|
int ds_force_dst = 1;
|
|
int ds_flags = 0;
|
|
int ds_use_default = 0;
|
|
static str dst_avp_param = {NULL, 0};
|
|
static str grp_avp_param = {NULL, 0};
|
|
static str cnt_avp_param = {NULL, 0};
|
|
static str dstid_avp_param = {NULL, 0};
|
|
static str attrs_avp_param = {NULL, 0};
|
|
static str sock_avp_param = {NULL, 0};
|
|
str hash_pvar_param = {NULL, 0};
|
|
|
|
int_str dst_avp_name;
|
|
unsigned short dst_avp_type;
|
|
int_str grp_avp_name;
|
|
unsigned short grp_avp_type;
|
|
int_str cnt_avp_name;
|
|
unsigned short cnt_avp_type;
|
|
int_str dstid_avp_name;
|
|
unsigned short dstid_avp_type;
|
|
int_str attrs_avp_name;
|
|
unsigned short attrs_avp_type;
|
|
int_str sock_avp_name;
|
|
unsigned short sock_avp_type;
|
|
|
|
pv_elem_t * hash_param_model = NULL;
|
|
|
|
int probing_threshold = 1; /* number of failed requests, before a destination
|
|
* is taken into probing */
|
|
int inactive_threshold = 1; /* number of replied requests, before a destination
|
|
* is taken into back in active state */
|
|
str ds_ping_method = str_init("OPTIONS");
|
|
str ds_ping_from = str_init("sip:dispatcher@localhost");
|
|
static int ds_ping_interval = 0;
|
|
int ds_probing_mode = DS_PROBE_NONE;
|
|
|
|
static str ds_ping_reply_codes_str= {NULL, 0};
|
|
static int** ds_ping_reply_codes = NULL;
|
|
static int* ds_ping_reply_codes_cnt;
|
|
|
|
str ds_default_socket = {NULL, 0};
|
|
struct socket_info * ds_default_sockinfo = NULL;
|
|
|
|
int ds_hash_size = 0;
|
|
int ds_hash_expire = 7200;
|
|
int ds_hash_initexpire = 7200;
|
|
int ds_hash_check_interval = 30;
|
|
int ds_timer_mode = 0;
|
|
|
|
str ds_outbound_proxy = {0, 0};
|
|
|
|
/* tm */
|
|
struct tm_binds tmb;
|
|
|
|
/*db */
|
|
str ds_db_url = {NULL, 0};
|
|
str ds_set_id_col = str_init(DS_SET_ID_COL);
|
|
str ds_dest_uri_col = str_init(DS_DEST_URI_COL);
|
|
str ds_dest_flags_col = str_init(DS_DEST_FLAGS_COL);
|
|
str ds_dest_priority_col = str_init(DS_DEST_PRIORITY_COL);
|
|
str ds_dest_attrs_col = str_init(DS_DEST_ATTRS_COL);
|
|
str ds_table_name = str_init(DS_TABLE_NAME);
|
|
|
|
str ds_setid_pvname = {NULL, 0};
|
|
pv_spec_t ds_setid_pv;
|
|
str ds_attrs_pvname = {NULL, 0};
|
|
pv_spec_t ds_attrs_pv;
|
|
|
|
/** module functions */
|
|
static int mod_init(void);
|
|
static int child_init(int);
|
|
|
|
static int ds_parse_reply_codes();
|
|
static int ds_init_rpc(void);
|
|
|
|
static int w_ds_select_dst(struct sip_msg*, char*, char*);
|
|
static int w_ds_select_dst_limit(struct sip_msg*, char*, char*, char*);
|
|
static int w_ds_select_domain(struct sip_msg*, char*, char*);
|
|
static int w_ds_select_domain_limit(struct sip_msg*, char*, char*, char*);
|
|
static int w_ds_next_dst(struct sip_msg*, char*, char*);
|
|
static int w_ds_next_domain(struct sip_msg*, char*, char*);
|
|
static int w_ds_mark_dst0(struct sip_msg*, char*, char*);
|
|
static int w_ds_mark_dst1(struct sip_msg*, char*, char*);
|
|
static int w_ds_load_unset(struct sip_msg*, char*, char*);
|
|
static int w_ds_load_update(struct sip_msg*, char*, char*);
|
|
|
|
static int w_ds_is_from_list0(struct sip_msg*, char*, char*);
|
|
static int w_ds_is_from_list1(struct sip_msg*, char*, char*);
|
|
static int w_ds_is_from_list2(struct sip_msg*, char*, char*);
|
|
static int w_ds_is_from_list3(struct sip_msg*, char*, char*, char*);
|
|
static int w_ds_list_exist(struct sip_msg*, char*);
|
|
|
|
static int fixup_ds_is_from_list(void** param, int param_no);
|
|
static int fixup_ds_list_exist(void** param,int param_no);
|
|
|
|
static void destroy(void);
|
|
|
|
static int ds_warn_fixup(void** param, int param_no);
|
|
|
|
static struct mi_root* ds_mi_set(struct mi_root* cmd, void* param);
|
|
static struct mi_root* ds_mi_list(struct mi_root* cmd, void* param);
|
|
static struct mi_root* ds_mi_reload(struct mi_root* cmd_tree, void* param);
|
|
static int mi_child_init(void);
|
|
|
|
static cmd_export_t cmds[]={
|
|
{"ds_select_dst", (cmd_function)w_ds_select_dst, 2,
|
|
fixup_igp_igp, 0, REQUEST_ROUTE|FAILURE_ROUTE},
|
|
{"ds_select_dst", (cmd_function)w_ds_select_dst_limit, 3,
|
|
fixup_igp_null, 0, REQUEST_ROUTE|FAILURE_ROUTE},
|
|
{"ds_select_domain", (cmd_function)w_ds_select_domain, 2,
|
|
fixup_igp_igp, 0, REQUEST_ROUTE|FAILURE_ROUTE},
|
|
{"ds_select_domain", (cmd_function)w_ds_select_domain_limit, 3,
|
|
fixup_igp_null, 0, REQUEST_ROUTE|FAILURE_ROUTE},
|
|
{"ds_next_dst", (cmd_function)w_ds_next_dst, 0,
|
|
ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE},
|
|
{"ds_next_domain", (cmd_function)w_ds_next_domain, 0,
|
|
ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE},
|
|
{"ds_mark_dst", (cmd_function)w_ds_mark_dst0, 0,
|
|
ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE},
|
|
{"ds_mark_dst", (cmd_function)w_ds_mark_dst1, 1,
|
|
ds_warn_fixup, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE},
|
|
{"ds_is_from_list", (cmd_function)w_ds_is_from_list0, 0,
|
|
0, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE},
|
|
{"ds_is_from_list", (cmd_function)w_ds_is_from_list1, 1,
|
|
fixup_igp_null, 0, ANY_ROUTE},
|
|
{"ds_is_from_list", (cmd_function)w_ds_is_from_list2, 2,
|
|
fixup_ds_is_from_list, 0, ANY_ROUTE},
|
|
{"ds_is_from_list", (cmd_function)w_ds_is_from_list3, 3,
|
|
fixup_ds_is_from_list, 0, ANY_ROUTE},
|
|
{"ds_list_exist", (cmd_function)w_ds_list_exist, 1,
|
|
fixup_ds_list_exist, 0, ANY_ROUTE},
|
|
{"ds_load_unset", (cmd_function)w_ds_load_unset, 0,
|
|
0, 0, ANY_ROUTE},
|
|
{"ds_load_update", (cmd_function)w_ds_load_update, 0,
|
|
0, 0, ANY_ROUTE},
|
|
{"bind_dispatcher", (cmd_function)bind_dispatcher, 0,
|
|
0, 0, 0},
|
|
{0,0,0,0,0,0}
|
|
};
|
|
|
|
|
|
static param_export_t params[]={
|
|
{"list_file", PARAM_STRING, &dslistfile},
|
|
{"db_url", PARAM_STR, &ds_db_url},
|
|
{"table_name", PARAM_STR, &ds_table_name},
|
|
{"setid_col", PARAM_STR, &ds_set_id_col},
|
|
{"destination_col", PARAM_STR, &ds_dest_uri_col},
|
|
{"flags_col", PARAM_STR, &ds_dest_flags_col},
|
|
{"priority_col", PARAM_STR, &ds_dest_priority_col},
|
|
{"attrs_col", PARAM_STR, &ds_dest_attrs_col},
|
|
{"force_dst", INT_PARAM, &ds_force_dst},
|
|
{"flags", INT_PARAM, &ds_flags},
|
|
{"use_default", INT_PARAM, &ds_use_default},
|
|
{"dst_avp", PARAM_STR, &dst_avp_param},
|
|
{"grp_avp", PARAM_STR, &grp_avp_param},
|
|
{"cnt_avp", PARAM_STR, &cnt_avp_param},
|
|
{"dstid_avp", PARAM_STR, &dstid_avp_param},
|
|
{"attrs_avp", PARAM_STR, &attrs_avp_param},
|
|
{"sock_avp", PARAM_STR, &sock_avp_param},
|
|
{"hash_pvar", PARAM_STR, &hash_pvar_param},
|
|
{"setid_pvname", PARAM_STR, &ds_setid_pvname},
|
|
{"attrs_pvname", PARAM_STR, &ds_attrs_pvname},
|
|
{"ds_probing_threshold", INT_PARAM, &probing_threshold},
|
|
{"ds_inactive_threshold", INT_PARAM, &inactive_threshold},
|
|
{"ds_ping_method", PARAM_STR, &ds_ping_method},
|
|
{"ds_ping_from", PARAM_STR, &ds_ping_from},
|
|
{"ds_ping_interval", INT_PARAM, &ds_ping_interval},
|
|
{"ds_ping_reply_codes", PARAM_STR, &ds_ping_reply_codes_str},
|
|
{"ds_probing_mode", INT_PARAM, &ds_probing_mode},
|
|
{"ds_hash_size", INT_PARAM, &ds_hash_size},
|
|
{"ds_hash_expire", INT_PARAM, &ds_hash_expire},
|
|
{"ds_hash_initexpire", INT_PARAM, &ds_hash_initexpire},
|
|
{"ds_hash_check_interval", INT_PARAM, &ds_hash_check_interval},
|
|
{"outbound_proxy", PARAM_STR, &ds_outbound_proxy},
|
|
{"ds_default_socket", PARAM_STR, &ds_default_socket},
|
|
{"ds_timer_mode", PARAM_INT, &ds_timer_mode},
|
|
{0,0,0}
|
|
};
|
|
|
|
|
|
static mi_export_t mi_cmds[] = {
|
|
{ "ds_set_state", ds_mi_set, 0, 0, 0 },
|
|
{ "ds_list", ds_mi_list, MI_NO_INPUT_FLAG, 0, 0 },
|
|
{ "ds_reload", ds_mi_reload, 0, 0, mi_child_init},
|
|
{ 0, 0, 0, 0, 0}
|
|
};
|
|
|
|
|
|
/** module exports */
|
|
struct module_exports exports= {
|
|
"dispatcher",
|
|
DEFAULT_DLFLAGS, /* dlopen flags */
|
|
cmds,
|
|
params,
|
|
0, /* exported statistics */
|
|
mi_cmds, /* exported MI functions */
|
|
0, /* exported pseudo-variables */
|
|
0, /* extra processes */
|
|
mod_init, /* module initialization function */
|
|
0,
|
|
(destroy_function) destroy,
|
|
child_init /* per-child init function */
|
|
};
|
|
|
|
/**
|
|
* init module function
|
|
*/
|
|
static int mod_init(void)
|
|
{
|
|
pv_spec_t avp_spec;
|
|
str host;
|
|
int port, proto;
|
|
|
|
if(register_mi_mod(exports.name, mi_cmds)!=0)
|
|
{
|
|
LM_ERR("failed to register MI commands\n");
|
|
return -1;
|
|
}
|
|
if(ds_ping_active_init()<0) {
|
|
return -1;
|
|
}
|
|
|
|
if(ds_init_rpc()<0)
|
|
{
|
|
LM_ERR("failed to register RPC commands\n");
|
|
return -1;
|
|
}
|
|
|
|
if(cfg_declare("dispatcher", dispatcher_cfg_def,
|
|
&default_dispatcher_cfg, cfg_sizeof(dispatcher),
|
|
&dispatcher_cfg)){
|
|
LM_ERR("Fail to declare the configuration\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Initialize the counter */
|
|
ds_ping_reply_codes = (int**)shm_malloc(sizeof(unsigned int*));
|
|
*ds_ping_reply_codes = 0;
|
|
ds_ping_reply_codes_cnt = (int*)shm_malloc(sizeof(int));
|
|
*ds_ping_reply_codes_cnt = 0;
|
|
if(ds_ping_reply_codes_str.s) {
|
|
cfg_get(dispatcher, dispatcher_cfg, ds_ping_reply_codes_str)
|
|
= ds_ping_reply_codes_str;
|
|
if(ds_parse_reply_codes()< 0)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
/* copy threshholds to config */
|
|
cfg_get(dispatcher, dispatcher_cfg, probing_threshold)
|
|
= probing_threshold;
|
|
cfg_get(dispatcher, dispatcher_cfg, inactive_threshold)
|
|
= inactive_threshold;
|
|
|
|
if (ds_default_socket.s && ds_default_socket.len > 0) {
|
|
if (parse_phostport( ds_default_socket.s, &host.s, &host.len,
|
|
&port, &proto)!=0) {
|
|
LM_ERR("bad socket <%.*s>\n", ds_default_socket.len, ds_default_socket.s);
|
|
return -1;
|
|
}
|
|
ds_default_sockinfo = grep_sock_info( &host, (unsigned short)port, proto);
|
|
if (ds_default_sockinfo==0) {
|
|
LM_WARN("non-local socket <%.*s>\n", ds_default_socket.len, ds_default_socket.s);
|
|
return -1;
|
|
}
|
|
LM_INFO("default dispatcher socket set to <%.*s>\n", ds_default_socket.len, ds_default_socket.s);
|
|
}
|
|
|
|
if(init_data()!= 0)
|
|
return -1;
|
|
|
|
if(ds_db_url.s)
|
|
{
|
|
if(init_ds_db()!= 0)
|
|
{
|
|
LM_ERR("could not initiate a connect to the database\n");
|
|
return -1;
|
|
}
|
|
} else {
|
|
if(ds_load_list(dslistfile)!=0) {
|
|
LM_ERR("no dispatching list loaded from file\n");
|
|
return -1;
|
|
} else {
|
|
LM_DBG("loaded dispatching list\n");
|
|
}
|
|
}
|
|
|
|
if (dst_avp_param.s && dst_avp_param.len > 0)
|
|
{
|
|
if (pv_parse_spec(&dst_avp_param, &avp_spec)==0
|
|
|| avp_spec.type!=PVT_AVP)
|
|
{
|
|
LM_ERR("malformed or non AVP %.*s AVP definition\n",
|
|
dst_avp_param.len, dst_avp_param.s);
|
|
return -1;
|
|
}
|
|
|
|
if(pv_get_avp_name(0, &(avp_spec.pvp), &dst_avp_name, &dst_avp_type)!=0)
|
|
{
|
|
LM_ERR("[%.*s]- invalid AVP definition\n", dst_avp_param.len,
|
|
dst_avp_param.s);
|
|
return -1;
|
|
}
|
|
} else {
|
|
dst_avp_name.n = 0;
|
|
dst_avp_type = 0;
|
|
}
|
|
if (grp_avp_param.s && grp_avp_param.len > 0)
|
|
{
|
|
if (pv_parse_spec(&grp_avp_param, &avp_spec)==0
|
|
|| avp_spec.type!=PVT_AVP)
|
|
{
|
|
LM_ERR("malformed or non AVP %.*s AVP definition\n",
|
|
grp_avp_param.len, grp_avp_param.s);
|
|
return -1;
|
|
}
|
|
|
|
if(pv_get_avp_name(0, &(avp_spec.pvp), &grp_avp_name, &grp_avp_type)!=0)
|
|
{
|
|
LM_ERR("[%.*s]- invalid AVP definition\n", grp_avp_param.len,
|
|
grp_avp_param.s);
|
|
return -1;
|
|
}
|
|
} else {
|
|
grp_avp_name.n = 0;
|
|
grp_avp_type = 0;
|
|
}
|
|
if (cnt_avp_param.s && cnt_avp_param.len > 0)
|
|
{
|
|
if (pv_parse_spec(&cnt_avp_param, &avp_spec)==0
|
|
|| avp_spec.type!=PVT_AVP)
|
|
{
|
|
LM_ERR("malformed or non AVP %.*s AVP definition\n",
|
|
cnt_avp_param.len, cnt_avp_param.s);
|
|
return -1;
|
|
}
|
|
|
|
if(pv_get_avp_name(0, &(avp_spec.pvp), &cnt_avp_name, &cnt_avp_type)!=0)
|
|
{
|
|
LM_ERR("[%.*s]- invalid AVP definition\n", cnt_avp_param.len,
|
|
cnt_avp_param.s);
|
|
return -1;
|
|
}
|
|
} else {
|
|
cnt_avp_name.n = 0;
|
|
cnt_avp_type = 0;
|
|
}
|
|
if (dstid_avp_param.s && dstid_avp_param.len > 0)
|
|
{
|
|
if (pv_parse_spec(&dstid_avp_param, &avp_spec)==0
|
|
|| avp_spec.type!=PVT_AVP)
|
|
{
|
|
LM_ERR("malformed or non AVP %.*s AVP definition\n",
|
|
dstid_avp_param.len, dstid_avp_param.s);
|
|
return -1;
|
|
}
|
|
|
|
if(pv_get_avp_name(0, &(avp_spec.pvp), &dstid_avp_name,
|
|
&dstid_avp_type)!=0)
|
|
{
|
|
LM_ERR("[%.*s]- invalid AVP definition\n", dstid_avp_param.len,
|
|
dstid_avp_param.s);
|
|
return -1;
|
|
}
|
|
} else {
|
|
dstid_avp_name.n = 0;
|
|
dstid_avp_type = 0;
|
|
}
|
|
|
|
if (attrs_avp_param.s && attrs_avp_param.len > 0)
|
|
{
|
|
if (pv_parse_spec(&attrs_avp_param, &avp_spec)==0
|
|
|| avp_spec.type!=PVT_AVP)
|
|
{
|
|
LM_ERR("malformed or non AVP %.*s AVP definition\n",
|
|
attrs_avp_param.len, attrs_avp_param.s);
|
|
return -1;
|
|
}
|
|
|
|
if(pv_get_avp_name(0, &(avp_spec.pvp), &attrs_avp_name,
|
|
&attrs_avp_type)!=0)
|
|
{
|
|
LM_ERR("[%.*s]- invalid AVP definition\n", attrs_avp_param.len,
|
|
attrs_avp_param.s);
|
|
return -1;
|
|
}
|
|
} else {
|
|
attrs_avp_name.n = 0;
|
|
attrs_avp_type = 0;
|
|
}
|
|
|
|
if (sock_avp_param.s && sock_avp_param.len > 0)
|
|
{
|
|
if (pv_parse_spec(&sock_avp_param, &avp_spec)==0
|
|
|| avp_spec.type!=PVT_AVP)
|
|
{
|
|
LM_ERR("malformed or non AVP %.*s AVP definition\n",
|
|
sock_avp_param.len, sock_avp_param.s);
|
|
return -1;
|
|
}
|
|
|
|
if(pv_get_avp_name(0, &(avp_spec.pvp), &sock_avp_name,
|
|
&sock_avp_type)!=0)
|
|
{
|
|
LM_ERR("[%.*s]- invalid AVP definition\n", sock_avp_param.len,
|
|
sock_avp_param.s);
|
|
return -1;
|
|
}
|
|
} else {
|
|
sock_avp_name.n = 0;
|
|
sock_avp_type = 0;
|
|
}
|
|
|
|
if (hash_pvar_param.s && *hash_pvar_param.s) {
|
|
if(pv_parse_format(&hash_pvar_param, &hash_param_model) < 0
|
|
|| hash_param_model==NULL) {
|
|
LM_ERR("malformed PV string: %s\n", hash_pvar_param.s);
|
|
return -1;
|
|
}
|
|
} else {
|
|
hash_param_model = NULL;
|
|
}
|
|
|
|
if(ds_setid_pvname.s!=0)
|
|
{
|
|
if(pv_parse_spec(&ds_setid_pvname, &ds_setid_pv)==NULL
|
|
|| !pv_is_w(&ds_setid_pv))
|
|
{
|
|
LM_ERR("[%s]- invalid setid_pvname\n", ds_setid_pvname.s);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if(ds_attrs_pvname.s!=0)
|
|
{
|
|
if(pv_parse_spec(&ds_attrs_pvname, &ds_attrs_pv)==NULL
|
|
|| !pv_is_w(&ds_attrs_pv))
|
|
{
|
|
LM_ERR("[%s]- invalid attrs_pvname\n", ds_attrs_pvname.s);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (dstid_avp_param.s && dstid_avp_param.len > 0)
|
|
{
|
|
if(ds_hash_size>0)
|
|
{
|
|
if(ds_hash_load_init(1<<ds_hash_size, ds_hash_expire,
|
|
ds_hash_initexpire)<0)
|
|
return -1;
|
|
if(ds_timer_mode==1) {
|
|
if(sr_wtimer_add(ds_ht_timer, NULL, ds_hash_check_interval)<0)
|
|
return -1;
|
|
} else {
|
|
if(register_timer(ds_ht_timer, NULL, ds_hash_check_interval)<0)
|
|
return -1;
|
|
}
|
|
} else {
|
|
LM_ERR("call load dispatching DSTID_AVP set but no size"
|
|
" for hash table (see ds_hash_size parameter)\n");
|
|
return -1;
|
|
}
|
|
}
|
|
/* Only, if the Probing-Timer is enabled the TM-API needs to be loaded: */
|
|
if (ds_ping_interval > 0)
|
|
{
|
|
/*****************************************************
|
|
* TM-Bindings
|
|
*****************************************************/
|
|
if (load_tm_api( &tmb ) == -1)
|
|
{
|
|
LM_ERR("could not load the TM-functions - disable DS ping\n");
|
|
return -1;
|
|
}
|
|
/*****************************************************
|
|
* Register the PING-Timer
|
|
*****************************************************/
|
|
if(ds_timer_mode==1) {
|
|
if(sr_wtimer_add(ds_check_timer, NULL, ds_ping_interval)<0)
|
|
return -1;
|
|
} else {
|
|
if(register_timer(ds_check_timer, NULL, ds_ping_interval)<0)
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*! \brief
|
|
* Initialize children
|
|
*/
|
|
static int child_init(int rank)
|
|
{
|
|
srand((11+rank)*getpid()*7);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mi_child_init(void)
|
|
{
|
|
|
|
if(ds_db_url.s)
|
|
return ds_connect_db();
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*! \brief
|
|
* destroy function
|
|
*/
|
|
static void destroy(void)
|
|
{
|
|
ds_destroy_list();
|
|
if(ds_db_url.s)
|
|
ds_disconnect_db();
|
|
ds_hash_load_destroy();
|
|
if(ds_ping_reply_codes)
|
|
shm_free(ds_ping_reply_codes);
|
|
if(ds_ping_reply_codes_cnt)
|
|
shm_free(ds_ping_reply_codes_cnt);
|
|
}
|
|
|
|
#define GET_VALUE(param_name,param,i_value,s_value,value_flags) do{ \
|
|
if(get_is_fparam(&(i_value), &(s_value), msg, (fparam_t*)(param), &(value_flags))!=0) { \
|
|
LM_ERR("no %s value\n", (param_name)); \
|
|
return -1; \
|
|
} \
|
|
}while(0)
|
|
|
|
/*! \brief
|
|
* parses string to dispatcher dst flags set
|
|
* returns <0 on failure or int with flag on success.
|
|
*/
|
|
int ds_parse_flags( char* flag_str, int flag_len )
|
|
{
|
|
int flag = 0;
|
|
int i;
|
|
|
|
for ( i=0; i<flag_len; i++)
|
|
{
|
|
if(flag_str[i]=='a' || flag_str[i]=='A') {
|
|
flag &= ~(DS_STATES_ALL);
|
|
} else if(flag_str[i]=='i' || flag_str[i]=='I') {
|
|
flag |= DS_INACTIVE_DST;
|
|
} else if(flag_str[i]=='d' || flag_str[i]=='D') {
|
|
flag |= DS_DISABLED_DST;
|
|
} else if(flag_str[i]=='t' || flag_str[i]=='T') {
|
|
flag |= DS_TRYING_DST;
|
|
} else if(flag_str[i]=='p' || flag_str[i]=='P') {
|
|
flag |= DS_PROBING_DST;
|
|
} else {
|
|
flag = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return flag;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static int w_ds_select(struct sip_msg* msg, char* set, char* alg, char* limit, int mode)
|
|
{
|
|
unsigned int algo_flags, set_flags, limit_flags;
|
|
str s_algo = STR_NULL;
|
|
str s_set = STR_NULL;
|
|
str s_limit = STR_NULL;
|
|
int a, s, l;
|
|
if(msg==NULL)
|
|
return -1;
|
|
|
|
GET_VALUE("destination set", set, s, s_set, set_flags);
|
|
if (!(set_flags&PARAM_INT)) {
|
|
if (set_flags&PARAM_STR)
|
|
LM_ERR("unable to get destination set from [%.*s]\n", s_set.len, s_set.s);
|
|
else
|
|
LM_ERR("unable to get destination set\n");
|
|
return -1;
|
|
}
|
|
GET_VALUE("algorithm", alg, a, s_algo, algo_flags);
|
|
if (!(algo_flags&PARAM_INT)) {
|
|
if (algo_flags&PARAM_STR)
|
|
LM_ERR("unable to get algorithm from [%.*s]\n", s_algo.len, s_algo.s);
|
|
else
|
|
LM_ERR("unable to get algorithm\n");
|
|
return -1;
|
|
}
|
|
|
|
if (limit) {
|
|
GET_VALUE("limit", limit, l, s_limit, limit_flags);
|
|
if (!(limit_flags&PARAM_INT)) {
|
|
if (limit_flags&PARAM_STR)
|
|
LM_ERR("unable to get dst number limit from [%.*s]\n", s_limit.len, s_limit.s);
|
|
else
|
|
LM_ERR("unable to get dst number limit\n");
|
|
return -1;
|
|
}
|
|
} else {
|
|
l = -1; /* will be casted to a rather big unsigned value */
|
|
}
|
|
|
|
return ds_select_dst_limit(msg, s, a, (unsigned int)l, mode);
|
|
}
|
|
/**
|
|
*
|
|
*/
|
|
static int w_ds_select_dst(struct sip_msg* msg, char* set, char* alg)
|
|
{
|
|
return w_ds_select(msg, set, alg, 0 /* limit number of dst*/, 0 /*set dst uri*/);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static int w_ds_select_dst_limit(struct sip_msg* msg, char* set, char* alg, char* limit)
|
|
{
|
|
return w_ds_select(msg, set, alg, limit /* limit number of dst*/, 0 /*set dst uri*/);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static int w_ds_select_domain(struct sip_msg* msg, char* set, char* alg)
|
|
{
|
|
return w_ds_select(msg, set, alg, 0 /* limit number of dst*/, 1 /*set host port*/);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static int w_ds_select_domain_limit(struct sip_msg* msg, char* set, char* alg, char* limit)
|
|
{
|
|
return w_ds_select(msg, set, alg, limit /* limit number of dst*/, 1 /*set host port*/);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static int w_ds_next_dst(struct sip_msg *msg, char *str1, char *str2)
|
|
{
|
|
return ds_next_dst(msg, 0/*set dst uri*/);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static int w_ds_next_domain(struct sip_msg *msg, char *str1, char *str2)
|
|
{
|
|
return ds_next_dst(msg, 1/*set host port*/);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static int w_ds_mark_dst0(struct sip_msg *msg, char *str1, char *str2)
|
|
{
|
|
int state;
|
|
|
|
state = DS_INACTIVE_DST;
|
|
if (ds_probing_mode==DS_PROBE_ALL)
|
|
state |= DS_PROBING_DST;
|
|
|
|
return ds_mark_dst(msg, state);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static int w_ds_mark_dst1(struct sip_msg *msg, char *str1, char *str2)
|
|
{
|
|
int state;
|
|
|
|
if(str1==NULL)
|
|
return w_ds_mark_dst0(msg, NULL, NULL);
|
|
|
|
state = ds_parse_flags( str1, strlen(str1) );
|
|
|
|
if ( state < 0 )
|
|
{
|
|
LM_WARN("Failed to parse flag: %s", str1 );
|
|
return -1;
|
|
}
|
|
|
|
return ds_mark_dst(msg, state);
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static int w_ds_load_unset(struct sip_msg *msg, char *str1, char *str2)
|
|
{
|
|
if(ds_load_unset(msg)<0)
|
|
return -1;
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static int w_ds_load_update(struct sip_msg *msg, char *str1, char *str2)
|
|
{
|
|
if(ds_load_update(msg)<0)
|
|
return -1;
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static int ds_warn_fixup(void** param, int param_no)
|
|
{
|
|
if(!dst_avp_param.s || !grp_avp_param.s || !cnt_avp_param.s || !sock_avp_param.s)
|
|
{
|
|
LM_ERR("failover functions used, but required AVP parameters"
|
|
" are NULL -- feature disabled\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/************************** MI STUFF ************************/
|
|
|
|
static struct mi_root* ds_mi_set(struct mi_root* cmd_tree, void* param)
|
|
{
|
|
str sp;
|
|
int ret;
|
|
unsigned int group;
|
|
int state;
|
|
struct mi_node* node;
|
|
|
|
node = cmd_tree->node.kids;
|
|
if(node == NULL)
|
|
return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
|
|
sp = node->value;
|
|
if(sp.len<=0 || !sp.s)
|
|
{
|
|
LM_ERR("bad state value\n");
|
|
return init_mi_tree(500, "bad state value", 15);
|
|
}
|
|
|
|
state = ds_parse_flags(sp.s, sp.len);
|
|
if( state < 0 )
|
|
{
|
|
LM_ERR("unknow state value\n");
|
|
return init_mi_tree(500, "unknown state value", 19);
|
|
}
|
|
node = node->next;
|
|
if(node == NULL)
|
|
return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
|
|
sp = node->value;
|
|
if(sp.s == NULL)
|
|
{
|
|
return init_mi_tree(500, "group not found", 15);
|
|
}
|
|
|
|
if(str2int(&sp, &group))
|
|
{
|
|
LM_ERR("bad group value\n");
|
|
return init_mi_tree( 500, "bad group value", 16);
|
|
}
|
|
|
|
node= node->next;
|
|
if(node == NULL)
|
|
return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
|
|
|
|
sp = node->value;
|
|
if(sp.s == NULL)
|
|
{
|
|
return init_mi_tree(500,"address not found", 18 );
|
|
}
|
|
|
|
ret = ds_reinit_state(group, &sp, state);
|
|
|
|
if(ret!=0)
|
|
{
|
|
return init_mi_tree(404, "destination not found", 21);
|
|
}
|
|
|
|
return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct mi_root* ds_mi_list(struct mi_root* cmd_tree, void* param)
|
|
{
|
|
struct mi_root* rpl_tree;
|
|
|
|
rpl_tree = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
|
|
if (rpl_tree==NULL)
|
|
return 0;
|
|
|
|
if( ds_print_mi_list(&rpl_tree->node)< 0 )
|
|
{
|
|
LM_ERR("failed to add node\n");
|
|
free_mi_tree(rpl_tree);
|
|
return 0;
|
|
}
|
|
|
|
return rpl_tree;
|
|
}
|
|
|
|
#define MI_ERR_RELOAD "ERROR Reloading data"
|
|
#define MI_ERR_RELOAD_LEN (sizeof(MI_ERR_RELOAD)-1)
|
|
#define MI_NOT_SUPPORTED "DB mode not configured"
|
|
#define MI_NOT_SUPPORTED_LEN (sizeof(MI_NOT_SUPPORTED)-1)
|
|
#define MI_ERR_DSLOAD "No reload support for call load dispatching"
|
|
#define MI_ERR_DSLOAD_LEN (sizeof(MI_ERR_DSLOAD)-1)
|
|
|
|
static struct mi_root* ds_mi_reload(struct mi_root* cmd_tree, void* param)
|
|
{
|
|
if(!ds_db_url.s) {
|
|
if (ds_load_list(dslistfile)!=0)
|
|
return init_mi_tree(500, MI_ERR_RELOAD, MI_ERR_RELOAD_LEN);
|
|
} else {
|
|
if(ds_reload_db()<0)
|
|
return init_mi_tree(500, MI_ERR_RELOAD, MI_ERR_RELOAD_LEN);
|
|
}
|
|
return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
|
|
}
|
|
|
|
|
|
static int w_ds_is_from_list0(struct sip_msg *msg, char *str1, char *str2)
|
|
{
|
|
return ds_is_from_list(msg, -1);
|
|
}
|
|
|
|
|
|
static int w_ds_is_from_list1(struct sip_msg *msg, char *set, char *str2)
|
|
{
|
|
int s;
|
|
if(fixup_get_ivalue(msg, (gparam_p)set, &s)!=0)
|
|
{
|
|
LM_ERR("cannot get set id value\n");
|
|
return -1;
|
|
}
|
|
return ds_is_from_list(msg, s);
|
|
}
|
|
|
|
static int w_ds_is_from_list2(struct sip_msg *msg, char *set, char *mode)
|
|
{
|
|
int vset;
|
|
int vmode;
|
|
|
|
if(fixup_get_ivalue(msg, (gparam_t*)set, &vset)!=0)
|
|
{
|
|
LM_ERR("cannot get set id value\n");
|
|
return -1;
|
|
}
|
|
if(fixup_get_ivalue(msg, (gparam_t*)mode, &vmode)!=0)
|
|
{
|
|
LM_ERR("cannot get mode value\n");
|
|
return -1;
|
|
}
|
|
|
|
return ds_is_addr_from_list(msg, vset, NULL, vmode);
|
|
}
|
|
|
|
static int w_ds_is_from_list3(struct sip_msg *msg, char *set, char *mode, char *uri)
|
|
{
|
|
int vset;
|
|
int vmode;
|
|
str suri;
|
|
|
|
if(fixup_get_ivalue(msg, (gparam_t*)set, &vset)!=0)
|
|
{
|
|
LM_ERR("cannot get set id value\n");
|
|
return -1;
|
|
}
|
|
if(fixup_get_ivalue(msg, (gparam_t*)mode, &vmode)!=0)
|
|
{
|
|
LM_ERR("cannot get mode value\n");
|
|
return -1;
|
|
}
|
|
if(fixup_get_svalue(msg, (gparam_t*)uri, &suri)!=0)
|
|
{
|
|
LM_ERR("cannot get uri value\n");
|
|
return -1;
|
|
}
|
|
|
|
return ds_is_addr_from_list(msg, vset, &suri, vmode);
|
|
}
|
|
|
|
|
|
static int fixup_ds_is_from_list(void** param, int param_no)
|
|
{
|
|
if(param_no==1 || param_no==2)
|
|
return fixup_igp_null(param, 1);
|
|
if(param_no==3)
|
|
return fixup_spve_null(param, 1);
|
|
return 0;
|
|
}
|
|
|
|
/* Check if a given set exist in memory */
|
|
static int w_ds_list_exist(struct sip_msg *msg, char *param)
|
|
{
|
|
int set;
|
|
|
|
if(fixup_get_ivalue(msg, (gparam_p)param, &set)!=0)
|
|
{
|
|
LM_ERR("cannot get set id param value\n");
|
|
return -1;
|
|
}
|
|
LM_DBG("--- Looking for dispatcher set %d\n", set);
|
|
return ds_list_exist(set);
|
|
}
|
|
|
|
static int fixup_ds_list_exist(void** param, int param_no)
|
|
{
|
|
return fixup_igp_null(param, param_no);
|
|
return 0;
|
|
}
|
|
|
|
static int ds_parse_reply_codes() {
|
|
param_t* params_list = NULL;
|
|
param_t *pit=NULL;
|
|
int list_size = 0;
|
|
int i = 0;
|
|
int pos = 0;
|
|
int code = 0;
|
|
str input = {0, 0};
|
|
int* ds_ping_reply_codes_new = NULL;
|
|
int* ds_ping_reply_codes_old = NULL;
|
|
|
|
/* Validate String: */
|
|
if (cfg_get(dispatcher, dispatcher_cfg, ds_ping_reply_codes_str).s == 0
|
|
|| cfg_get(dispatcher, dispatcher_cfg, ds_ping_reply_codes_str).len<=0)
|
|
return 0;
|
|
|
|
/* parse_params will modify the string pointer of .s, so we need to make a copy. */
|
|
input.s = cfg_get(dispatcher, dispatcher_cfg, ds_ping_reply_codes_str).s;
|
|
input.len = cfg_get(dispatcher, dispatcher_cfg, ds_ping_reply_codes_str).len;
|
|
|
|
/* Parse the parameters: */
|
|
if (parse_params(&input, CLASS_ANY, 0, ¶ms_list)<0)
|
|
return -1;
|
|
|
|
/* Get the number of entries in the list */
|
|
for (pit = params_list; pit; pit=pit->next)
|
|
{
|
|
if (pit->name.len==4
|
|
&& strncasecmp(pit->name.s, "code", 4)==0) {
|
|
str2sint(&pit->body, &code);
|
|
if ((code >= 100) && (code < 700))
|
|
list_size += 1;
|
|
} else if (pit->name.len==5
|
|
&& strncasecmp(pit->name.s, "class", 5)==0) {
|
|
str2sint(&pit->body, &code);
|
|
if ((code >= 1) && (code < 7))
|
|
list_size += 100;
|
|
}
|
|
}
|
|
LM_DBG("Should be %d Destinations.\n", list_size);
|
|
|
|
if (list_size > 0) {
|
|
/* Allocate Memory for the new list: */
|
|
ds_ping_reply_codes_new = (int*)shm_malloc(list_size * sizeof(int));
|
|
if(ds_ping_reply_codes_new== NULL)
|
|
{
|
|
free_params(params_list);
|
|
LM_ERR("no more memory\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Now create the list of valid reply-codes: */
|
|
for (pit = params_list; pit; pit=pit->next)
|
|
{
|
|
if (pit->name.len==4
|
|
&& strncasecmp(pit->name.s, "code", 4)==0) {
|
|
str2sint(&pit->body, &code);
|
|
if ((code >= 100) && (code < 700))
|
|
ds_ping_reply_codes_new[pos++] = code;
|
|
} else if (pit->name.len==5
|
|
&& strncasecmp(pit->name.s, "class", 5)==0) {
|
|
str2sint(&pit->body, &code);
|
|
if ((code >= 1) && (code < 7)) {
|
|
/* Add every code from this class, e.g. 100 to 199 */
|
|
for (i = (code*100); i <= ((code*100)+99); i++)
|
|
ds_ping_reply_codes_new[pos++] = i;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ds_ping_reply_codes_new = 0;
|
|
}
|
|
free_params(params_list);
|
|
|
|
/* More reply-codes? Change Pointer and then set number of codes. */
|
|
if (list_size > *ds_ping_reply_codes_cnt) {
|
|
// Copy Pointer
|
|
ds_ping_reply_codes_old = *ds_ping_reply_codes;
|
|
*ds_ping_reply_codes = ds_ping_reply_codes_new;
|
|
// Done: Set new Number of entries:
|
|
*ds_ping_reply_codes_cnt = list_size;
|
|
// Free the old memory area:
|
|
if(ds_ping_reply_codes_old)
|
|
shm_free(ds_ping_reply_codes_old);
|
|
/* Less or equal? Set the number of codes first. */
|
|
} else {
|
|
// Done:
|
|
*ds_ping_reply_codes_cnt = list_size;
|
|
// Copy Pointer
|
|
ds_ping_reply_codes_old = *ds_ping_reply_codes;
|
|
*ds_ping_reply_codes = ds_ping_reply_codes_new;
|
|
// Free the old memory area:
|
|
if(ds_ping_reply_codes_old)
|
|
shm_free(ds_ping_reply_codes_old);
|
|
}
|
|
/* Print the list as INFO: */
|
|
for (i =0; i< *ds_ping_reply_codes_cnt; i++)
|
|
{
|
|
LM_DBG("Dispatcher: Now accepting Reply-Code %d (%d/%d) as valid\n",
|
|
(*ds_ping_reply_codes)[i], (i+1), *ds_ping_reply_codes_cnt);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ds_ping_check_rplcode(int code)
|
|
{
|
|
int i;
|
|
|
|
for (i =0; i< *ds_ping_reply_codes_cnt; i++)
|
|
{
|
|
if((*ds_ping_reply_codes)[i] == code)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ds_ping_reply_codes_update(str* gname, str* name){
|
|
ds_parse_reply_codes();
|
|
}
|
|
|
|
/*** RPC implementation ***/
|
|
|
|
static const char* dispatcher_rpc_reload_doc[2] = {
|
|
"Reload dispatcher destination sets",
|
|
0
|
|
};
|
|
|
|
|
|
/*
|
|
* RPC command to reload dispatcher destination sets
|
|
*/
|
|
static void dispatcher_rpc_reload(rpc_t* rpc, void* ctx)
|
|
{
|
|
if(!ds_db_url.s) {
|
|
if (ds_load_list(dslistfile)!=0) {
|
|
rpc->fault(ctx, 500, "Reload Failed");
|
|
return;
|
|
}
|
|
} else {
|
|
if(ds_reload_db()<0) {
|
|
rpc->fault(ctx, 500, "Reload Failed");
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
static const char* dispatcher_rpc_list_doc[2] = {
|
|
"Return the content of dispatcher sets",
|
|
0
|
|
};
|
|
|
|
|
|
/*
|
|
* RPC command to print dispatcher destination sets
|
|
*/
|
|
static void dispatcher_rpc_list(rpc_t* rpc, void* ctx)
|
|
{
|
|
void* th;
|
|
void* ih;
|
|
void* rh;
|
|
void* sh;
|
|
void* vh;
|
|
void* wh;
|
|
int j;
|
|
char c[3];
|
|
str data = {"", 0};
|
|
ds_set_t *ds_list;
|
|
int ds_list_nr;
|
|
ds_set_t *list;
|
|
|
|
ds_list = ds_get_list();
|
|
ds_list_nr = ds_get_list_nr();
|
|
|
|
if(ds_list==NULL || ds_list_nr<=0)
|
|
{
|
|
LM_ERR("no destination sets\n");
|
|
rpc->fault(ctx, 500, "No Destination Sets");
|
|
return;
|
|
}
|
|
|
|
/* add entry node */
|
|
if (rpc->add(ctx, "{", &th) < 0)
|
|
{
|
|
rpc->fault(ctx, 500, "Internal error root reply");
|
|
return;
|
|
}
|
|
if(rpc->struct_add(th, "d[",
|
|
"NRSETS", ds_list_nr,
|
|
"RECORDS", &ih)<0)
|
|
{
|
|
rpc->fault(ctx, 500, "Internal error sets structure");
|
|
return;
|
|
}
|
|
|
|
for(list = ds_list; list!= NULL; list= list->next)
|
|
{
|
|
if (rpc->struct_add(ih, "{", "SET", &sh) < 0)
|
|
{
|
|
rpc->fault(ctx, 500, "Internal error set structure");
|
|
return;
|
|
}
|
|
if(rpc->struct_add(sh, "d[",
|
|
"ID", list->id,
|
|
"TARGETS", &rh)<0)
|
|
{
|
|
rpc->fault(ctx, 500, "Internal error creating set id");
|
|
return;
|
|
}
|
|
|
|
for(j=0; j<list->nr; j++)
|
|
{
|
|
if(rpc->struct_add(rh, "{",
|
|
"DEST", &vh)<0)
|
|
{
|
|
rpc->fault(ctx, 500, "Internal error creating dest");
|
|
return;
|
|
}
|
|
|
|
memset(&c, 0, sizeof(c));
|
|
if (list->dlist[j].flags & DS_INACTIVE_DST)
|
|
c[0] = 'I';
|
|
else if (list->dlist[j].flags & DS_DISABLED_DST)
|
|
c[0] = 'D';
|
|
else if (list->dlist[j].flags & DS_TRYING_DST)
|
|
c[0] = 'T';
|
|
else
|
|
c[0] = 'A';
|
|
|
|
if (list->dlist[j].flags & DS_PROBING_DST)
|
|
c[1] = 'P';
|
|
else
|
|
c[1] = 'X';
|
|
|
|
if (list->dlist[j].attrs.body.s)
|
|
{
|
|
if(rpc->struct_add(vh, "Ssd{",
|
|
"URI", &list->dlist[j].uri,
|
|
"FLAGS", c,
|
|
"PRIORITY", list->dlist[j].priority,
|
|
"ATTRS", &wh)<0)
|
|
{
|
|
rpc->fault(ctx, 500, "Internal error creating dest struct");
|
|
return;
|
|
}
|
|
if(rpc->struct_add(wh, "SSdddS",
|
|
"BODY", &(list->dlist[j].attrs.body),
|
|
"DUID", (list->dlist[j].attrs.duid.s)?
|
|
&(list->dlist[j].attrs.duid):&data,
|
|
"MAXLOAD", list->dlist[j].attrs.maxload,
|
|
"WEIGHT", list->dlist[j].attrs.weight,
|
|
"RWEIGHT", list->dlist[j].attrs.rweight,
|
|
"SOCKET", (list->dlist[j].attrs.socket.s)?
|
|
&(list->dlist[j].attrs.socket):&data)<0)
|
|
{
|
|
rpc->fault(ctx, 500, "Internal error creating attrs struct");
|
|
return;
|
|
}
|
|
} else {
|
|
if(rpc->struct_add(vh, "Ssd",
|
|
"URI", &list->dlist[j].uri,
|
|
"FLAGS", c,
|
|
"PRIORITY", list->dlist[j].priority)<0)
|
|
{
|
|
rpc->fault(ctx, 500, "Internal error creating dest struct");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
static const char* dispatcher_rpc_set_state_doc[2] = {
|
|
"Set the state of a destination address",
|
|
0
|
|
};
|
|
|
|
|
|
/*
|
|
* RPC command to set the state of a destination address
|
|
*/
|
|
static void dispatcher_rpc_set_state(rpc_t* rpc, void* ctx)
|
|
{
|
|
int group;
|
|
str dest;
|
|
str state;
|
|
int stval;
|
|
|
|
if(rpc->scan(ctx, ".SdS", &state, &group, &dest)<3)
|
|
{
|
|
rpc->fault(ctx, 500, "Invalid Parameters");
|
|
return;
|
|
}
|
|
if(state.len<=0 || state.s==NULL)
|
|
{
|
|
LM_ERR("bad state value\n");
|
|
rpc->fault(ctx, 500, "Invalid State Parameter");
|
|
return;
|
|
}
|
|
|
|
stval = 0;
|
|
if(state.s[0]=='0' || state.s[0]=='I' || state.s[0]=='i') {
|
|
/* set inactive */
|
|
stval |= DS_INACTIVE_DST;
|
|
if((state.len>1) && (state.s[1]=='P' || state.s[1]=='p'))
|
|
stval |= DS_PROBING_DST;
|
|
} else if(state.s[0]=='1' || state.s[0]=='A' || state.s[0]=='a') {
|
|
/* set active */
|
|
if((state.len>1) && (state.s[1]=='P' || state.s[1]=='p'))
|
|
stval |= DS_PROBING_DST;
|
|
} else if(state.s[0]=='2' || state.s[0]=='D' || state.s[0]=='d') {
|
|
/* set disabled */
|
|
stval |= DS_DISABLED_DST;
|
|
} else if(state.s[0]=='3' || state.s[0]=='T' || state.s[0]=='t') {
|
|
/* set trying */
|
|
stval |= DS_TRYING_DST;
|
|
if((state.len>1) && (state.s[1]=='P' || state.s[1]=='p'))
|
|
stval |= DS_PROBING_DST;
|
|
} else {
|
|
LM_ERR("unknow state value\n");
|
|
rpc->fault(ctx, 500, "Unknown State Value");
|
|
return;
|
|
}
|
|
|
|
if(ds_reinit_state(group, &dest, stval)<0)
|
|
{
|
|
rpc->fault(ctx, 500, "State Update Failed");
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
static const char* dispatcher_rpc_ping_active_doc[2] = {
|
|
"Manage setting on/off the pinging (keepalive) of destinations",
|
|
0
|
|
};
|
|
|
|
|
|
/*
|
|
* RPC command to set the state of a destination address
|
|
*/
|
|
static void dispatcher_rpc_ping_active(rpc_t* rpc, void* ctx)
|
|
{
|
|
int state;
|
|
int ostate;
|
|
void *th;
|
|
|
|
if(rpc->scan(ctx, "*d", &state)!=1) {
|
|
state = -1;
|
|
}
|
|
ostate = ds_ping_active_get();
|
|
/* add entry node */
|
|
if (rpc->add(ctx, "{", &th) < 0) {
|
|
rpc->fault(ctx, 500, "Internal error root reply");
|
|
return;
|
|
}
|
|
if(state==-1) {
|
|
if(rpc->struct_add(th, "d",
|
|
"OldPingState", ostate)<0)
|
|
{
|
|
rpc->fault(ctx, 500, "Internal error reply structure");
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(ds_ping_active_set(state)<0) {
|
|
rpc->fault(ctx, 500, "Ping State Update Failed");
|
|
return;
|
|
}
|
|
if(rpc->struct_add(th, "dd",
|
|
"NewPingState", state,
|
|
"OldPingState", ostate)<0)
|
|
{
|
|
rpc->fault(ctx, 500, "Internal error reply structure");
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
rpc_export_t dispatcher_rpc_cmds[] = {
|
|
{"dispatcher.reload", dispatcher_rpc_reload,
|
|
dispatcher_rpc_reload_doc, 0},
|
|
{"dispatcher.list", dispatcher_rpc_list,
|
|
dispatcher_rpc_list_doc, 0},
|
|
{"dispatcher.set_state", dispatcher_rpc_set_state,
|
|
dispatcher_rpc_set_state_doc, 0},
|
|
{"dispatcher.ping_active", dispatcher_rpc_ping_active,
|
|
dispatcher_rpc_ping_active_doc, 0},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
/**
|
|
* register RPC commands
|
|
*/
|
|
static int ds_init_rpc(void)
|
|
{
|
|
if (rpc_register_array(dispatcher_rpc_cmds)!=0)
|
|
{
|
|
LM_ERR("failed to register RPC commands\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|