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/mohqueue/mohq_db.c

624 lines
14 KiB

/*
* Copyright (C) 2013-15 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 "mohq.h"
#include "mohq_db.h"
#include "mohq_funcs.h"
/**********
* mohqueue definitions
**********/
str MOHQCSTR_ID = STR_STATIC_INIT ("id");
str MOHQCSTR_URI = STR_STATIC_INIT ("uri");
str MOHQCSTR_MDIR = STR_STATIC_INIT ("mohdir");
str MOHQCSTR_MFILE = STR_STATIC_INIT ("mohfile");
str MOHQCSTR_NAME = STR_STATIC_INIT ("name");
str MOHQCSTR_DEBUG = STR_STATIC_INIT ("debug");
static str *mohq_columns [] =
{
&MOHQCSTR_ID,
&MOHQCSTR_URI,
&MOHQCSTR_MDIR,
&MOHQCSTR_MFILE,
&MOHQCSTR_NAME,
&MOHQCSTR_DEBUG,
NULL
};
/**********
* mohqcalls definitions
**********/
str CALLCSTR_CALL = STR_STATIC_INIT ("call_id");
str CALLCSTR_CNTCT = STR_STATIC_INIT ("call_contact");
str CALLCSTR_FROM = STR_STATIC_INIT ("call_from");
str CALLCSTR_MOHQ = STR_STATIC_INIT ("mohq_id");
str CALLCSTR_STATE = STR_STATIC_INIT ("call_status");
str CALLCSTR_TIME = STR_STATIC_INIT ("call_time");
static str *call_columns [] =
{
&CALLCSTR_STATE,
&CALLCSTR_CALL,
&CALLCSTR_MOHQ,
&CALLCSTR_FROM,
&CALLCSTR_CNTCT,
&CALLCSTR_TIME,
NULL
};
/**********
* local function declarations
**********/
void set_call_key (db_key_t *, int, int);
void set_call_val (db_val_t *, int, int, void *);
/**********
* local functions
**********/
/**********
* Fill Call Keys
*
* INPUT:
* Arg (1) = row pointer
* Arg (2) = column count
* OUTPUT: none
**********/
void fill_call_keys (db_key_t *prkeys, int ncnt)
{
int nidx;
for (nidx = 0; nidx < ncnt; nidx++)
{ set_call_key (prkeys, nidx, nidx); }
return;
}
/**********
* Fill Call Values
*
* INPUT:
* Arg (1) = row pointer
* Arg (2) = call struct pointer
* Arg (3) = column count
* OUTPUT: none
**********/
void fill_call_vals (db_val_t *prvals, call_lst *pcall, int ncnt)
{
int nstate = pcall->call_state / 100;
set_call_val (prvals, CALLCOL_STATE, CALLCOL_STATE, &nstate);
if (!ncnt)
{ return; }
set_call_val (prvals, CALLCOL_MOHQ, CALLCOL_MOHQ, &pcall->pmohq->mohq_id);
set_call_val (prvals, CALLCOL_CALL, CALLCOL_CALL, pcall->call_id);
set_call_val (prvals, CALLCOL_FROM, CALLCOL_FROM, pcall->call_from);
set_call_val (prvals, CALLCOL_CNTCT, CALLCOL_CNTCT, pcall->call_contact);
set_call_val (prvals, CALLCOL_TIME, CALLCOL_TIME, &pcall->call_time);
return;
}
/**********
* Set Call Column Key
*
* INPUT:
* Arg (1) = row pointer
* Arg (2) = column number
* Arg (3) = column id
* OUTPUT: none
**********/
void set_call_key (db_key_t *prkeys, int ncol, int ncolid)
{
prkeys [ncol] = call_columns [ncolid];
return;
}
/**********
* Set Call Column Value
*
* INPUT:
* Arg (1) = row pointer
* Arg (2) = column number
* Arg (3) = column id
* Arg (4) = value pointer
* OUTPUT: none
**********/
void set_call_val (db_val_t *prvals, int ncol, int ncolid, void *pdata)
{
/**********
* fill based on column
**********/
switch (ncolid)
{
case CALLCOL_MOHQ:
case CALLCOL_STATE:
prvals [ncol].val.int_val = *((int *)pdata);
prvals [ncol].type = DB1_INT;
prvals [ncol].nul = 0;
break;
case CALLCOL_CALL:
case CALLCOL_CNTCT:
case CALLCOL_FROM:
prvals [ncol].val.string_val = (char *)pdata;
prvals [ncol].type = DB1_STRING;
prvals [ncol].nul = 0;
break;
case CALLCOL_TIME:
prvals [ncol].val.time_val = *((time_t *)pdata);
prvals [ncol].type = DB1_DATETIME;
prvals [ncol].nul = 0;
break;
}
return;
}
/**********
* external functions
**********/
/**********
* Add Call Record
*
* INPUT:
* Arg (1) = call index
* OUTPUT: none
**********/
void add_call_rec (int ncall_idx)
{
/**********
* o fill column names and values
* o insert new record
**********/
char *pfncname = "add_call_rec: ";
db1_con_t *pconn = mohq_dbconnect ();
if (!pconn)
{ return; }
db_func_t *pdb = pmod_data->pdb;
pdb->use_table (pconn, &pmod_data->pcfg->db_ctable);
db_key_t prkeys [CALL_COLCNT];
fill_call_keys (prkeys, CALL_COLCNT);
db_val_t prvals [CALL_COLCNT];
call_lst *pcall = &pmod_data->pcall_lst [ncall_idx];
pcall->call_time = time (0);
fill_call_vals (prvals, pcall, CALL_COLCNT);
if (pdb->insert (pconn, prkeys, prvals, CALL_COLCNT) < 0)
{
LM_WARN ("%sUnable to add new row to %s\n", pfncname,
pmod_data->pcfg->db_ctable.s);
}
mohq_dbdisconnect (pconn);
return;
}
/**********
* Clear Call Records
*
* INPUT:
* Arg (1) = connection pointer
* OUTPUT: none
**********/
void clear_calls (db1_con_t *pconn)
{
/**********
* delete all records
**********/
char *pfncname = "clear_calls: ";
db_func_t *pdb = pmod_data->pdb;
pdb->use_table (pconn, &pmod_data->pcfg->db_ctable);
if (pdb->delete (pconn, 0, 0, 0, 0) < 0)
{
LM_WARN ("%sUnable to delete all rows from %s\n", pfncname,
pmod_data->pcfg->db_ctable.s);
}
return;
}
/**********
* Delete Call Record
*
* INPUT:
* Arg (1) = call pointer
* OUTPUT: none
**********/
void delete_call_rec (call_lst *pcall)
{
/**********
* o setup to delete based on call ID
* o delete record
**********/
char *pfncname = "delete_call_rec: ";
db1_con_t *pconn = mohq_dbconnect ();
if (!pconn)
{ return; }
db_func_t *pdb = pmod_data->pdb;
pdb->use_table (pconn, &pmod_data->pcfg->db_ctable);
db_key_t prkeys [1];
set_call_key (prkeys, 0, CALLCOL_CALL);
db_val_t prvals [1];
set_call_val (prvals, 0, CALLCOL_CALL, pcall->call_id);
if (pdb->delete (pconn, prkeys, 0, prvals, 1) < 0)
{
LM_WARN ("%sUnable to delete row from %s\n", pfncname,
pmod_data->pcfg->db_ctable.s);
}
mohq_dbdisconnect (pconn);
return;
}
/**********
* Connect to DB
*
* INPUT: none
* OUTPUT: DB connection pointer; NULL=failed
**********/
db1_con_t *mohq_dbconnect (void)
{
str *pdb_url = &pmod_data->pcfg->db_url;
db1_con_t *pconn = pmod_data->pdb->init (pdb_url);
if (!pconn)
{ LM_ERR ("Unable to connect to DB %s!\n", pdb_url->s); }
return pconn;
}
/**********
* Disconnect from DB
*
* INPUT:
* Arg (1) = connection pointer
* OUTPUT: none
**********/
void mohq_dbdisconnect (db1_con_t *pconn)
{
pmod_data->pdb->close (pconn);
return;
}
/**********
* Update Call Record
*
* INPUT:
* Arg (1) = call pointer
* OUTPUT: none
**********/
void update_call_rec (call_lst *pcall)
{
/**********
* o setup to update based on call ID
* o update record
**********/
char *pfncname = "update_call_rec: ";
db1_con_t *pconn = mohq_dbconnect ();
if (!pconn)
{ return; }
db_func_t *pdb = pmod_data->pdb;
pdb->use_table (pconn, &pmod_data->pcfg->db_ctable);
db_key_t pqkeys [1];
set_call_key (pqkeys, 0, CALLCOL_CALL);
db_val_t pqvals [1];
set_call_val (pqvals, 0, CALLCOL_CALL, pcall->call_id);
db_key_t pukeys [1];
set_call_key (pukeys, 0, CALLCOL_STATE);
db_val_t puvals [1];
fill_call_vals (puvals, pcall, CALLCOL_STATE);
if (pdb->update (pconn, pqkeys, 0, pqvals, pukeys, puvals, 1, 1) < 0)
{
LM_WARN ("%sUnable to update row in %s\n", pfncname,
pmod_data->pcfg->db_ctable.s);
}
mohq_dbdisconnect (pconn);
return;
}
/**********
* Update Debug Record
*
* INPUT:
* Arg (1) = MOH queue pointer
* Arg (2) = debug flag
* OUTPUT: none
**********/
void update_debug (mohq_lst *pqueue, int bdebug)
{
/**********
* o setup to update based on queue name
* o update record
**********/
char *pfncname = "update_debug: ";
db1_con_t *pconn = mohq_dbconnect ();
if (!pconn)
{ return; }
db_func_t *pdb = pmod_data->pdb;
pdb->use_table (pconn, &pmod_data->pcfg->db_qtable);
db_key_t pqkeys [1] = { mohq_columns [MOHQCOL_NAME] };
db_val_t pqvals [1];
pqvals->val.string_val = pqueue->mohq_name;
pqvals->type = DB1_STRING;
pqvals->nul = 0;
db_key_t pukeys [1] = { mohq_columns [MOHQCOL_DEBUG] };
db_val_t puvals [1];
puvals->val.int_val = bdebug;
puvals->type = DB1_INT;
puvals->nul = 0;
if (pdb->update (pconn, pqkeys, 0, pqvals, pukeys, puvals, 1, 1) < 0)
{
LM_WARN ("%sUnable to update row in %s\n", pfncname,
pmod_data->pcfg->db_qtable.s);
}
mohq_dbdisconnect (pconn);
return;
}
/**********
* Update Message Queue List
*
* INPUT:
* Arg (1) = connection pointer
* OUTPUT: none
**********/
void update_mohq_lst (db1_con_t *pconn)
{
/**********
* o reset checked flag on all queues
* o read queues from table
**********/
char *pfncname = "update_mohq_lst: ";
if (!pconn)
{ return; }
db_func_t *pdb = pmod_data->pdb;
mohq_lst *pqlst = pmod_data->pmohq_lst;
int nidx;
for (nidx = 0; nidx < pmod_data->mohq_cnt; nidx++)
{ pqlst [nidx].mohq_flags &= ~MOHQF_CHK; }
pdb->use_table (pconn, &pmod_data->pcfg->db_qtable);
db_key_t prkeys [MOHQ_COLCNT];
for (nidx = 0; nidx < MOHQ_COLCNT; nidx++)
{ prkeys [nidx] = mohq_columns [nidx]; }
db1_res_t *presult = NULL;
if (pdb->query (pconn, 0, 0, 0, prkeys, 0, MOHQ_COLCNT, 0, &presult))
{
LM_ERR ("%stable query (%s) failed!\n", pfncname,
pmod_data->pcfg->db_qtable.s);
return;
}
db_row_t *prows = RES_ROWS (presult);
int nrows = RES_ROW_N (presult);
db_val_t *prowvals = NULL;
char *ptext, *puri;
mohq_lst *pnewlst;
for (nidx = 0; nidx < nrows; nidx++)
{
/**********
* check URI
**********/
prowvals = ROW_VALUES (prows + nidx);
char *pqname = (char *)VAL_STRING (prowvals + MOHQCOL_NAME);
puri = (char *)VAL_STRING (prowvals + MOHQCOL_URI);
struct sip_uri puri_parsed [1];
if (parse_uri (puri, strlen (puri), puri_parsed))
{
LM_ERR ("Queue,Field (%s,%.*s): %s is not a valid URI!\n", pqname,
STR_FMT (&MOHQCSTR_URI), puri);
continue;
}
/**********
* check MOHDIR
**********/
char *pmohdir;
if (VAL_NULL (prowvals + MOHQCOL_MDIR))
{ pmohdir = pmod_data->pcfg->mohdir; }
else
{
pmohdir = (char *)VAL_STRING (prowvals + MOHQCOL_MDIR);
if (!*pmohdir)
{ pmohdir = pmod_data->pcfg->mohdir; }
else
{
/**********
* mohdir
* o exists?
* o directory?
**********/
struct stat psb [1];
if (lstat (pmohdir, psb))
{
LM_ERR ("Queue,Field (%s,%.*s): Unable to find %s!\n", pqname,
STR_FMT (&MOHQCSTR_MDIR), pmohdir);
continue;
}
else
{
if ((psb->st_mode & S_IFMT) != S_IFDIR)
{
LM_ERR ("Queue,Field (%s,%.*s): %s is not a directory!\n", pqname,
STR_FMT (&MOHQCSTR_MDIR), pmohdir);
continue;
}
}
}
}
/**********
* check for MOH files
**********/
rtpmap **pmohfiles = find_MOH (pmohdir,
(char *)VAL_STRING (prowvals + MOHQCOL_MFILE));
if (!pmohfiles [0])
{
LM_ERR ("Queue,Field (%s,%.*s): Unable to find MOH files (%s/%s.*)!\n",
pqname, STR_FMT (&MOHQCSTR_MDIR), pmohdir,
(char *)VAL_STRING (prowvals + MOHQCOL_MFILE));
continue;
}
/**********
* find matching queues
**********/
int bfnd = 0;
int nidx2;
for (nidx2 = 0; nidx2 < pmod_data->mohq_cnt; nidx2++)
{
if (!strcasecmp (pqlst [nidx2].mohq_uri, puri))
{
/**********
* o data the same?
* o mark as found
**********/
if (strcmp (pqlst [nidx2].mohq_mohdir, pmohdir))
{
strcpy (pqlst [nidx2].mohq_mohdir, pmohdir);
LM_INFO ("Queue,Field (%s,%.*s): Changed\n", pqname,
STR_FMT (&MOHQCSTR_MDIR));
}
ptext = (char *)VAL_STRING (prowvals + MOHQCOL_MFILE);
if (strcmp (pqlst [nidx2].mohq_mohfile, ptext))
{
strcpy (pqlst [nidx2].mohq_mohfile, ptext);
LM_INFO ("Queue,Field (%s,%.*s): Changed\n", pqname,
STR_FMT (&MOHQCSTR_MFILE));
}
ptext = (char *)VAL_STRING (prowvals + MOHQCOL_NAME);
if (strcmp (pqlst [nidx2].mohq_name, ptext))
{
strcpy (pqlst [nidx2].mohq_name, ptext);
LM_INFO ("Queue,Field (%s,%.*s): Changed\n", pqname,
STR_FMT (&MOHQCSTR_NAME));
}
int bdebug = VAL_INT (prowvals + MOHQCOL_DEBUG) ? MOHQF_DBG : 0;
if ((pqlst [nidx2].mohq_flags & MOHQF_DBG) != bdebug)
{
if (bdebug)
{ pqlst [nidx2].mohq_flags |= MOHQF_DBG; }
else
{ pqlst [nidx2].mohq_flags &= ~MOHQF_DBG; }
LM_INFO ("Queue,Field (%s,%.*s): Changed\n", pqname,
STR_FMT (&MOHQCSTR_DEBUG));
}
bfnd = -1;
pqlst [nidx2].mohq_flags |= MOHQF_CHK;
break;
}
}
/**********
* add new queue
**********/
if (!bfnd)
{
/**********
* o allocate new list
* o copy old list
* o add new row
* o release old list
* o adjust pointers to new list
**********/
int nsize = pmod_data->mohq_cnt + 1;
pnewlst = (mohq_lst *) shm_malloc (sizeof (mohq_lst) * nsize);
if (!pnewlst)
{
LM_ERR ("%sUnable to allocate shared memory!\n", pfncname);
return;
}
pmod_data->mohq_cnt = nsize;
if (--nsize)
{ memcpy (pnewlst, pqlst, sizeof (mohq_lst) * nsize); }
pnewlst [nsize].mohq_id = prowvals [MOHQCOL_ID].val.int_val;
pnewlst [nsize].mohq_flags = MOHQF_CHK;
strcpy (pnewlst [nsize].mohq_uri, puri);
strcpy (pnewlst [nsize].mohq_mohdir, pmohdir);
strcpy (pnewlst [nsize].mohq_mohfile,
(char *)VAL_STRING (prowvals + MOHQCOL_MFILE));
strcpy (pnewlst [nsize].mohq_name,
(char *)VAL_STRING (prowvals + MOHQCOL_NAME));
if (VAL_INT (prowvals + MOHQCOL_DEBUG))
{ pnewlst [nsize].mohq_flags |= MOHQF_DBG; }
LM_INFO ("Added new queue (%s)\n", pnewlst [nsize].mohq_name);
if (nsize)
{ shm_free (pmod_data->pmohq_lst); }
pmod_data->pmohq_lst = pnewlst;
pqlst = pnewlst;
}
}
/**********
* find deleted queues
**********/
for (nidx = 0; nidx < pmod_data->mohq_cnt; nidx++)
{
/**********
* o exists?
* o if not last, replace current with last queue
**********/
if (pqlst [nidx].mohq_flags & MOHQF_CHK)
{ continue; }
LM_INFO ("Removed queue (%s)\n", pqlst [nidx].mohq_name);
if (nidx != (pmod_data->mohq_cnt - 1))
{
memcpy (&pqlst [nidx], &pqlst [pmod_data->mohq_cnt - 1],
sizeof (mohq_lst));
}
--pmod_data->mohq_cnt;
--nidx;
}
return;
}