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.
482 lines
10 KiB
482 lines
10 KiB
/*
|
|
* Copyright (C) 2013-16 Robert Boisvert
|
|
*
|
|
* This file is part of the mohqueue module for Kamailio, a free SIP server.
|
|
*
|
|
* The mohqueue module 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
|
|
*
|
|
* The mohqueue module 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 <sys/types.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "mohq.h"
|
|
#include "mohq_db.h"
|
|
#include "mohq_funcs.h"
|
|
|
|
MODULE_VERSION
|
|
|
|
/**********
|
|
* local function declarations
|
|
**********/
|
|
|
|
int fixup_count (void **, int);
|
|
static int mod_child_init (int);
|
|
static void mod_destroy (void);
|
|
static int mod_init (void);
|
|
|
|
/**********
|
|
* global varbs
|
|
**********/
|
|
|
|
mod_data *pmod_data;
|
|
pv_spec_t *prtp_pv;
|
|
|
|
/**********
|
|
* module exports
|
|
**********/
|
|
|
|
/* COMMANDS */
|
|
static cmd_export_t mod_cmds [] = {
|
|
{ "mohq_count", (cmd_function) mohq_count, 2, fixup_count, 0,
|
|
REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE },
|
|
{ "mohq_process", (cmd_function) mohq_process, 0, NULL, 0, REQUEST_ROUTE },
|
|
{ "mohq_retrieve", (cmd_function) mohq_retrieve, 2, fixup_spve_spve, 0,
|
|
REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE },
|
|
{ "mohq_send", (cmd_function) mohq_send, 1, fixup_spve_spve, 0, REQUEST_ROUTE },
|
|
{ NULL, NULL, -1, 0, 0 },
|
|
};
|
|
|
|
/* PARAMETERS */
|
|
str db_url = str_init (DEFAULT_DB_URL);
|
|
str db_ctable = str_init ("mohqcalls");
|
|
str db_qtable = str_init ("mohqueues");
|
|
char *mohdir = "";
|
|
int moh_maxcalls = 50;
|
|
|
|
static param_export_t mod_parms [] = {
|
|
{ "db_url", PARAM_STR, &db_url },
|
|
{ "db_ctable", PARAM_STR, &db_ctable },
|
|
{ "db_qtable", PARAM_STR, &db_qtable },
|
|
{ "mohdir", PARAM_STRING, &mohdir },
|
|
{ "moh_maxcalls", INT_PARAM, &moh_maxcalls },
|
|
{ NULL, 0, NULL },
|
|
};
|
|
|
|
/* MI COMMANDS */
|
|
static mi_export_t mi_cmds [] = {
|
|
{ "debug", mi_debug, 0, 0, 0 },
|
|
{ "drop_call", mi_drop_call, 0, 0, 0 },
|
|
{ 0, 0, 0, 0, 0 }
|
|
};
|
|
|
|
/* MODULE EXPORTS */
|
|
struct module_exports exports = {
|
|
"mohqueue", /* module name */
|
|
DEFAULT_DLFLAGS, /* dlopen flags */
|
|
mod_cmds, /* exported functions */
|
|
mod_parms, /* exported parameters */
|
|
0, /* statistics */
|
|
mi_cmds, /* MI functions */
|
|
0, /* exported pseudo-variables */
|
|
0, /* extra processes */
|
|
mod_init, /* module initialization function */
|
|
0, /* response handling function */
|
|
mod_destroy, /* destructor function */
|
|
mod_child_init, /* per-child initialization function */
|
|
};
|
|
|
|
/**********
|
|
* local constants
|
|
**********/
|
|
|
|
str prtpstat [1] = {STR_STATIC_INIT ("$rtpstat")};
|
|
|
|
/**********
|
|
* local functions
|
|
**********/
|
|
|
|
/**********
|
|
* Fixup Count
|
|
*
|
|
* INPUT:
|
|
* Arg (1) = parameter array pointer
|
|
* Arg (1) = parameter number
|
|
* OUTPUT: -1 if failed; 0 if saved as pv_elem_t
|
|
**********/
|
|
|
|
int fixup_count (void **param, int param_no)
|
|
|
|
{
|
|
if (param_no == 1)
|
|
{ return fixup_spve_spve (param, 1); }
|
|
if (param_no == 2)
|
|
{ return fixup_pvar_null (param, 1); }
|
|
return 0;
|
|
}
|
|
|
|
/**********
|
|
* Configuration Initialization
|
|
*
|
|
* INPUT:
|
|
* pmod_data memory allocated
|
|
* configuration values set
|
|
* OUTPUT: 0 if failed; else pmod_data has config values
|
|
**********/
|
|
|
|
static int init_cfg (void)
|
|
|
|
{
|
|
int bfnd = 0;
|
|
int berror = 0;
|
|
struct stat psb [1];
|
|
|
|
/**********
|
|
* db_url, db_ctable, db_qtable exist?
|
|
**********/
|
|
|
|
if (!db_url.s || db_url.len <= 0)
|
|
{
|
|
LM_ERR ("db_url parameter not set!\n");
|
|
berror = 1;
|
|
}
|
|
if (!db_ctable.s || db_ctable.len <= 0)
|
|
{
|
|
LM_ERR ("db_ctable parameter not set!\n");
|
|
berror = 1;
|
|
}
|
|
if (!db_qtable.s || db_qtable.len <= 0)
|
|
{
|
|
LM_ERR ("db_qtable parameter not set!\n");
|
|
berror = 1;
|
|
}
|
|
|
|
/**********
|
|
* mohdir
|
|
* o exists?
|
|
* o directory?
|
|
**********/
|
|
|
|
if (!*mohdir)
|
|
{
|
|
LM_ERR ("mohdir parameter not set!\n");
|
|
berror = 1;
|
|
}
|
|
else if (strlen (mohdir) > MOHDIRLEN)
|
|
{
|
|
LM_ERR ("mohdir too long!\n");
|
|
berror = 1;
|
|
}
|
|
else
|
|
{
|
|
if (!lstat (mohdir, psb))
|
|
{
|
|
if ((psb->st_mode & S_IFMT) == S_IFDIR)
|
|
{ bfnd = 1; }
|
|
}
|
|
if (!bfnd)
|
|
{
|
|
LM_ERR ("mohdir is not a directory!\n");
|
|
berror = 1;
|
|
}
|
|
}
|
|
|
|
/**********
|
|
* o max calls valid?
|
|
* o alloc memory
|
|
* o save data
|
|
**********/
|
|
|
|
if (moh_maxcalls < 1 || moh_maxcalls > 5000)
|
|
{
|
|
LM_ERR ("moh_maxcalls not in range of 1-5000!\n");
|
|
berror = 1;
|
|
}
|
|
if (berror)
|
|
{ return 0; }
|
|
pmod_data->pcall_lst =
|
|
(call_lst *) shm_malloc (sizeof (call_lst) * moh_maxcalls);
|
|
if (!pmod_data->pcall_lst)
|
|
{
|
|
LM_ERR ("Unable to allocate shared memory!\n");
|
|
return 0;
|
|
}
|
|
pmod_data->pcfg->db_url = db_url;
|
|
pmod_data->pcfg->db_ctable = db_ctable;
|
|
pmod_data->pcfg->db_qtable = db_qtable;
|
|
pmod_data->pcfg->mohdir = mohdir;
|
|
memset (pmod_data->pcall_lst, 0, sizeof (call_lst) * moh_maxcalls);
|
|
pmod_data->call_cnt = moh_maxcalls;
|
|
return -1;
|
|
}
|
|
|
|
/**********
|
|
* DB Initialization
|
|
*
|
|
* INPUT:
|
|
* pmod_data memory allocated and cfg values set
|
|
* OUTPUT: 0 if failed; else pmod_data has db_api
|
|
**********/
|
|
|
|
static int init_db (void)
|
|
|
|
{
|
|
/**********
|
|
* o bind to DB
|
|
* o check capabilities
|
|
* o init DB
|
|
**********/
|
|
|
|
str *pdb_url = &pmod_data->pcfg->db_url;
|
|
if (db_bind_mod (pdb_url, pmod_data->pdb))
|
|
{
|
|
LM_ERR ("Unable to bind DB API using %s!\n", pdb_url->s);
|
|
return 0;
|
|
}
|
|
db_func_t *pdb = pmod_data->pdb;
|
|
if (!DB_CAPABILITY ((*pdb), DB_CAP_ALL))
|
|
{
|
|
LM_ERR ("Selected database %s lacks required capabilities!\n", pdb_url->s);
|
|
return 0;
|
|
}
|
|
db1_con_t *pconn = mohq_dbconnect ();
|
|
if (!pconn)
|
|
{ return 0; }
|
|
|
|
/**********
|
|
* o check schema
|
|
* o remove all call recs
|
|
* o load queue list
|
|
**********/
|
|
|
|
if (db_check_table_version (pdb, pconn,
|
|
&pmod_data->pcfg->db_ctable, MOHQ_CTABLE_VERSION) < 0)
|
|
{
|
|
LM_ERR ("%s table in DB %s not at version %d!\n",
|
|
pmod_data->pcfg->db_ctable.s, pdb_url->s, MOHQ_CTABLE_VERSION);
|
|
goto dberr;
|
|
}
|
|
if (db_check_table_version (pdb, pconn,
|
|
&pmod_data->pcfg->db_qtable, MOHQ_QTABLE_VERSION) < 0)
|
|
{
|
|
LM_ERR ("%s table in DB %s not at version %d!\n",
|
|
pmod_data->pcfg->db_qtable.s, pdb_url->s, MOHQ_QTABLE_VERSION);
|
|
goto dberr;
|
|
}
|
|
clear_calls (pconn);
|
|
update_mohq_lst (pconn);
|
|
pmod_data->mohq_update = time (0);
|
|
mohq_dbdisconnect (pconn);
|
|
return -1;
|
|
|
|
/**********
|
|
* close DB
|
|
**********/
|
|
|
|
dberr:
|
|
pdb->close (pconn);
|
|
return 0;
|
|
}
|
|
|
|
/**********
|
|
* Child Module Initialization
|
|
*
|
|
* INPUT:
|
|
* Arg (1) = child type
|
|
* OUTPUT: -1 if db_api not ready; else 0
|
|
**********/
|
|
|
|
int mod_child_init (int rank)
|
|
|
|
{
|
|
/**********
|
|
* o seed random number generator
|
|
* o make sure DB initialized
|
|
**********/
|
|
|
|
srand (getpid () + time (0));
|
|
if (rank == PROC_INIT || rank == PROC_TCP_MAIN || rank == PROC_MAIN)
|
|
{ return 0; }
|
|
if (!pmod_data->pdb->init)
|
|
{
|
|
LM_CRIT ("DB API not loaded!\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**********
|
|
* Module Teardown
|
|
*
|
|
* INPUT: none
|
|
* OUTPUT: none
|
|
**********/
|
|
|
|
void mod_destroy (void)
|
|
|
|
{
|
|
/**********
|
|
* o destroy MOH can call queue locks
|
|
* o deallocate shared mem
|
|
**********/
|
|
|
|
if (!pmod_data)
|
|
{ return; }
|
|
if (pmod_data->pmohq_lock->plock)
|
|
{ mohq_lock_destroy (pmod_data->pmohq_lock); }
|
|
if (pmod_data->pcall_lock->plock)
|
|
{ mohq_lock_destroy (pmod_data->pcall_lock); }
|
|
if (pmod_data->pmohq_lst)
|
|
{ shm_free (pmod_data->pmohq_lst); }
|
|
if (pmod_data->pcall_lst)
|
|
{ shm_free (pmod_data->pcall_lst); }
|
|
shm_free (pmod_data);
|
|
return;
|
|
}
|
|
|
|
/**********
|
|
* Module Initialization
|
|
*
|
|
* INPUT: none
|
|
* OUTPUT: -1 if failed; 0 if success
|
|
**********/
|
|
|
|
int mod_init (void)
|
|
|
|
{
|
|
int rtplen;
|
|
|
|
/**********
|
|
* o allocate shared mem and init
|
|
* o init configuration data
|
|
* o init DB
|
|
**********/
|
|
|
|
pmod_data = (mod_data *) shm_malloc (sizeof (mod_data));
|
|
if (!pmod_data)
|
|
{
|
|
LM_ERR ("Unable to allocate shared memory!\n");
|
|
return -1;
|
|
}
|
|
memset (pmod_data, 0, sizeof (mod_data));
|
|
if (!init_cfg ())
|
|
{ goto initerr; }
|
|
if (!init_db ())
|
|
{ goto initerr; }
|
|
|
|
/**********
|
|
* o bind to SL/TM/RR modules
|
|
* o bind to RTPPROXY functions
|
|
**********/
|
|
|
|
if (sl_load_api (pmod_data->psl))
|
|
{
|
|
LM_ERR ("Unable to load SL module!\n");
|
|
goto initerr;
|
|
}
|
|
if (load_tm_api (pmod_data->ptm))
|
|
{
|
|
LM_ERR ("Unable to load TM module!\n");
|
|
goto initerr;
|
|
}
|
|
if (load_rr_api (pmod_data->prr))
|
|
{
|
|
LM_ERR ("Unable to load RR module!\n");
|
|
goto initerr;
|
|
}
|
|
pmod_data->fn_rtp_answer = find_export ("rtpproxy_answer", 0, 0);
|
|
if (!pmod_data->fn_rtp_answer)
|
|
{
|
|
LM_ERR ("Unable to load rtpproxy_answer!\n");
|
|
goto initerr;
|
|
}
|
|
pmod_data->fn_rtp_offer = find_export ("rtpproxy_offer", 0, 0);
|
|
if (!pmod_data->fn_rtp_offer)
|
|
{
|
|
LM_ERR ("Unable to load rtpproxy_offer!\n");
|
|
goto initerr;
|
|
}
|
|
pmod_data->fn_rtp_stream_c = find_export ("rtpproxy_stream2uac", 2, 0);
|
|
if (!pmod_data->fn_rtp_stream_c)
|
|
{
|
|
LM_ERR ("Unable to load rtpproxy_stream2uac!\n");
|
|
goto initerr;
|
|
}
|
|
pmod_data->fn_rtp_stream_s = find_export ("rtpproxy_stream2uas", 2, 0);
|
|
if (!pmod_data->fn_rtp_stream_s)
|
|
{
|
|
LM_ERR ("Unable to load rtpproxy_stream2uas!\n");
|
|
goto initerr;
|
|
}
|
|
pmod_data->fn_rtp_stop_c = find_export ("rtpproxy_stop_stream2uac", 0, 0);
|
|
if (!pmod_data->fn_rtp_stop_c)
|
|
{
|
|
LM_ERR ("Unable to load rtpproxy_stop_stream2uac!\n");
|
|
goto initerr;
|
|
}
|
|
pmod_data->fn_rtp_stop_s = find_export ("rtpproxy_stop_stream2uas", 0, 0);
|
|
if (!pmod_data->fn_rtp_stop_s)
|
|
{
|
|
LM_ERR ("Unable to load rtpproxy_stop_stream2uas!\n");
|
|
goto initerr;
|
|
}
|
|
pmod_data->fn_rtp_destroy = find_export ("rtpproxy_destroy", 0, 0);
|
|
if (!pmod_data->fn_rtp_destroy)
|
|
{
|
|
LM_ERR ("Unable to load rtpproxy_destroy!\n");
|
|
goto initerr;
|
|
}
|
|
|
|
/**********
|
|
* get RTPSTAT pv spec
|
|
**********/
|
|
|
|
rtplen = pv_locate_name (prtpstat);
|
|
if(rtplen != prtpstat->len)
|
|
{
|
|
LM_ERR ("Unable to find RTPSTAT pv!\n");
|
|
goto initerr;
|
|
}
|
|
prtp_pv = pv_cache_get (&prtpstat);
|
|
if(!prtp_pv)
|
|
{
|
|
LM_ERR ("Unable to find pv spec for RTPSTAT!\n");
|
|
goto initerr;
|
|
}
|
|
|
|
/**********
|
|
* init MOH and call queue locks
|
|
**********/
|
|
|
|
if (!mohq_lock_init (pmod_data->pmohq_lock))
|
|
{ goto initerr; }
|
|
if (!mohq_lock_init (pmod_data->pcall_lock))
|
|
{ goto initerr; }
|
|
return 0;
|
|
|
|
/**********
|
|
* o release shared mem
|
|
* o exit with error
|
|
**********/
|
|
|
|
initerr:
|
|
if (pmod_data->mohq_cnt)
|
|
{ shm_free (pmod_data->pmohq_lst); }
|
|
if (pmod_data->pcall_lock->plock)
|
|
{ mohq_lock_destroy (pmod_data->pcall_lock); }
|
|
shm_free (pmod_data);
|
|
pmod_data = NULL;
|
|
return -1;
|
|
} |