|
@@ -0,0 +1,324 @@
|
|
|
+/**
|
|
|
+ * Copyright (C) 2020 Julien Chavanton
|
|
|
+ *
|
|
|
+ * This file is part of Kamailio, a free SIP server.
|
|
|
+ *
|
|
|
+ * This file 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
|
|
|
+ *
|
|
|
+ *
|
|
|
+ * This file 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 "../../lib/srdb1/db.h"
|
|
|
+#include "mqueue_api.h"
|
|
|
+
|
|
|
+/** database connection */
|
|
|
+db1_con_t *mqueue_db_con = NULL;
|
|
|
+db_func_t mq_dbf;
|
|
|
+
|
|
|
+/** db parameters */
|
|
|
+str mqueue_db_url = {0, 0};
|
|
|
+str mq_db_key_column = str_init("key");
|
|
|
+str mq_db_val_column = str_init("val");
|
|
|
+str mq_db_id_column = str_init("id");
|
|
|
+
|
|
|
+/**
|
|
|
+ * initialize database connection
|
|
|
+ */
|
|
|
+int mqueue_db_init_con(void)
|
|
|
+{
|
|
|
+ if (mqueue_db_url.len<=0) {
|
|
|
+ LM_ERR("failed to connect to the database, no db url\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ /* binding to DB module */
|
|
|
+ if (db_bind_mod(&mqueue_db_url, &mq_dbf))
|
|
|
+ {
|
|
|
+ LM_ERR("database module not found\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!DB_CAPABILITY(mq_dbf, DB_CAP_ALL))
|
|
|
+ {
|
|
|
+ LM_ERR("database module does not "
|
|
|
+ "implement all functions needed by the module\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * open database connection
|
|
|
+ */
|
|
|
+int mqueue_db_open_con(void) {
|
|
|
+ if (mqueue_db_init_con()==0) {
|
|
|
+ mqueue_db_con = mq_dbf.init(&mqueue_db_url);
|
|
|
+ if (mqueue_db_con==NULL) {
|
|
|
+ LM_ERR("failed to connect to the database\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ LM_DBG("database connection opened successfully\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * close database connection
|
|
|
+ */
|
|
|
+int mqueue_db_close_con(void)
|
|
|
+{
|
|
|
+ if (mqueue_db_con!=NULL && mq_dbf.close!=NULL)
|
|
|
+ mq_dbf.close(mqueue_db_con);
|
|
|
+ mqueue_db_con=NULL;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int mqueue_db_load_queue(str *name)
|
|
|
+{
|
|
|
+ int ncols=2;
|
|
|
+ db1_res_t* db_res = NULL;
|
|
|
+ db_key_t db_cols[2] = {&mq_db_key_column, &mq_db_val_column};
|
|
|
+ db_key_t db_ord = &mq_db_id_column;
|
|
|
+ int mq_fetch_rows = 100;
|
|
|
+ int ret = 0;
|
|
|
+ str val = str_init("");
|
|
|
+ str key = str_init("");
|
|
|
+ int i;
|
|
|
+ int cnt=0;
|
|
|
+
|
|
|
+ if (mqueue_db_open_con() != 0) {
|
|
|
+ LM_ERR("no db connection\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mq_dbf.use_table(mqueue_db_con, name) < 0)
|
|
|
+ {
|
|
|
+ LM_ERR("failed to use_table\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ LM_INFO("=============== loading queue table [%.*s] from database\n",
|
|
|
+ name->len, name->s);
|
|
|
+
|
|
|
+ if (DB_CAPABILITY(mq_dbf, DB_CAP_FETCH)) {
|
|
|
+ if(mq_dbf.query(mqueue_db_con,0,0,0,db_cols,0,ncols,db_ord,0) < 0)
|
|
|
+ {
|
|
|
+ LM_ERR("Error while querying db\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ if(mq_dbf.fetch_result(mqueue_db_con, &db_res, mq_fetch_rows)<0)
|
|
|
+ {
|
|
|
+ LM_ERR("Error while fetching result\n");
|
|
|
+ if (db_res)
|
|
|
+ mq_dbf.free_result(mqueue_db_con, db_res);
|
|
|
+ goto error;
|
|
|
+ } else {
|
|
|
+ if(RES_ROW_N(db_res)==0)
|
|
|
+ {
|
|
|
+ mq_dbf.free_result(mqueue_db_con, db_res);
|
|
|
+ LM_DBG("Nothing to be loaded in queue\n");
|
|
|
+ mqueue_db_close_con();
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if((ret=mq_dbf.query(mqueue_db_con, NULL, NULL, NULL, db_cols,
|
|
|
+ 0, ncols, 0, &db_res))!=0
|
|
|
+ || RES_ROW_N(db_res)<=0 )
|
|
|
+ {
|
|
|
+ if(ret==0)
|
|
|
+ {
|
|
|
+ mq_dbf.free_result(mqueue_db_con, db_res);
|
|
|
+ mqueue_db_close_con();
|
|
|
+ return 0;
|
|
|
+ } else {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ do {
|
|
|
+ for(i=0; i<RES_ROW_N(db_res); i++)
|
|
|
+ {
|
|
|
+ if(VAL_NULL(&RES_ROWS(db_res)[i].values[0])) {
|
|
|
+ LM_ERR("mqueue [%.*s] row [%d] has NULL key string\n",
|
|
|
+ name->len, name->s, i);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ if(VAL_NULL(&RES_ROWS(db_res)[i].values[1])) {
|
|
|
+ LM_ERR("mqueue [%.*s] row [%d] has NULL value string\n",
|
|
|
+ name->len, name->s, i);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ switch(RES_ROWS(db_res)[i].values[0].type) {
|
|
|
+ case DB1_STR:
|
|
|
+ key.s = (RES_ROWS(db_res)[i].values[0].val.str_val.s);
|
|
|
+ if(key.s==NULL) {
|
|
|
+ LM_ERR("mqueue [%.*s] row [%d] has NULL key\n",
|
|
|
+ name->len, name->s, i);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ key.len = (RES_ROWS(db_res)[i].values[0].val.str_val.len);
|
|
|
+ break;
|
|
|
+ case DB1_BLOB:
|
|
|
+ key.s = (RES_ROWS(db_res)[i].values[0].val.blob_val.s);
|
|
|
+ if(key.s==NULL) {
|
|
|
+ LM_ERR("mqueue [%.*s] row [%d] has NULL key\n",
|
|
|
+ name->len, name->s, i);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ key.len = (RES_ROWS(db_res)[i].values[0].val.blob_val.len);
|
|
|
+ break;
|
|
|
+ case DB1_STRING:
|
|
|
+ key.s = (char*)(RES_ROWS(db_res)[i].values[0].val.string_val);
|
|
|
+ if(key.s==NULL) {
|
|
|
+ LM_ERR("mqueue [%.*s] row [%d] has NULL key\n",
|
|
|
+ name->len, name->s, i);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ key.len = strlen(key.s);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ LM_ERR("key type must be string (type=%d)\n",
|
|
|
+ RES_ROWS(db_res)[i].values[0].type);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ switch(RES_ROWS(db_res)[i].values[1].type) {
|
|
|
+ case DB1_STR:
|
|
|
+ val.s = (RES_ROWS(db_res)[i].values[1].val.str_val.s);
|
|
|
+ if(val.s==NULL) {
|
|
|
+ LM_ERR("mqueue [%.*s] row [%d] has NULL value\n",
|
|
|
+ name->len, name->s, i);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ val.len = (RES_ROWS(db_res)[i].values[1].val.str_val.len);
|
|
|
+ break;
|
|
|
+ case DB1_BLOB:
|
|
|
+ val.s = (RES_ROWS(db_res)[i].values[1].val.blob_val.s);
|
|
|
+ if(val.s==NULL) {
|
|
|
+ LM_ERR("mqueue [%.*s] row [%d] has NULL value\n",
|
|
|
+ name->len, name->s, i);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ val.len = (RES_ROWS(db_res)[i].values[1].val.blob_val.len);
|
|
|
+ break;
|
|
|
+ case DB1_STRING:
|
|
|
+ val.s = (char*)(RES_ROWS(db_res)[i].values[1].val.string_val);
|
|
|
+ if(val.s==NULL) {
|
|
|
+ LM_ERR("mqueue [%.*s] row [%d] has NULL value\n",
|
|
|
+ name->len, name->s, i);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ val.len = strlen(val.s);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ LM_ERR("key type must be string (type=%d)\n",
|
|
|
+ RES_ROWS(db_res)[i].values[1].type);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ cnt++;
|
|
|
+ LM_DBG("adding item[%d] key[%.*s] value[%.*s]\n", cnt, key.len, key.s, val.len, val.s);
|
|
|
+ mq_item_add(name, &key, &val);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (DB_CAPABILITY(mq_dbf, DB_CAP_FETCH)) {
|
|
|
+ if(mq_dbf.fetch_result(mqueue_db_con, &db_res, mq_fetch_rows)<0) {
|
|
|
+ LM_ERR("Error while fetching!\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } while(RES_ROW_N(db_res)>0);
|
|
|
+
|
|
|
+ mq_dbf.free_result(mqueue_db_con, db_res);
|
|
|
+
|
|
|
+ if (mq_dbf.delete(mqueue_db_con, 0, 0, 0, 0) < 0) {
|
|
|
+ LM_ERR("failed to clear table\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ LM_DBG("loaded %d values in queue\n", cnt);
|
|
|
+ mqueue_db_close_con();
|
|
|
+ return 0;
|
|
|
+error:
|
|
|
+ mqueue_db_close_con();
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+int mqueue_db_save_queue(str *name)
|
|
|
+{
|
|
|
+ int ncols=2;
|
|
|
+ db_key_t db_cols[2] = {&mq_db_key_column, &mq_db_val_column};
|
|
|
+ db_val_t db_vals[2];
|
|
|
+ int i;
|
|
|
+ int mqueue_sz = 0;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (mqueue_db_open_con() != 0) {
|
|
|
+ LM_ERR("no db connection\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mq_dbf.use_table(mqueue_db_con, name) < 0)
|
|
|
+ {
|
|
|
+ LM_ERR("failed to use_table\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (name->len <= 0 || name->s == NULL) {
|
|
|
+ LM_ERR("bad mqueue name\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ mqueue_sz = _mq_get_csize(name);
|
|
|
+
|
|
|
+ if (mqueue_sz < 0) {
|
|
|
+ LM_ERR("no such mqueue\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ for(i=0;i<mqueue_sz;i++) {
|
|
|
+ ret = mq_head_fetch(name);
|
|
|
+ if (ret != 0) break;
|
|
|
+ str *key = NULL;
|
|
|
+ str *val = NULL;
|
|
|
+ key = get_mqk(name);
|
|
|
+ val = get_mqv(name);
|
|
|
+ LM_DBG("inserting mqueue[%.*s] name[%.*s] value[%.*s]\n",
|
|
|
+ name->len, name->s, key->len, key->s, val->len, val->s);
|
|
|
+ db_vals[0].type = DB1_STR;
|
|
|
+ db_vals[0].nul = 0;
|
|
|
+ db_vals[0].val.str_val.s = key->s;
|
|
|
+ db_vals[0].val.str_val.len = key->len;
|
|
|
+ db_vals[1].type = DB1_STR;
|
|
|
+ db_vals[1].nul = 0;
|
|
|
+ db_vals[1].val.str_val.s = val->s;
|
|
|
+ db_vals[1].val.str_val.len = val->len;
|
|
|
+ if(mq_dbf.insert(mqueue_db_con, db_cols, db_vals, ncols) < 0)
|
|
|
+ {
|
|
|
+ LM_ERR("failed to store key [%.*s] val [%.*s]\n",
|
|
|
+ key->len, key->s,
|
|
|
+ val->len, val->s);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ LM_INFO("queue [%.*s] saved in db\n",
|
|
|
+ name->len, name->s);
|
|
|
+ mqueue_db_close_con();
|
|
|
+ return 0;
|
|
|
+error:
|
|
|
+ mqueue_db_close_con();
|
|
|
+ return -1;
|
|
|
+}
|