浏览代码

usrloc: store per-contact attributes in database

- attributes table is the name of table used for save/lookup plus suffix
  '_attrs' (e.g., location_attrs)
- feature enable only if xavp_contact parameter is set
Daniel-Constantin Mierla 12 年之前
父节点
当前提交
5463e46f9c
共有 6 个文件被更改,包括 438 次插入1 次删除
  1. 190 1
      modules_k/usrloc/ucontact.c
  2. 27 0
      modules_k/usrloc/ucontact.h
  3. 182 0
      modules_k/usrloc/udomain.c
  4. 12 0
      modules_k/usrloc/udomain.h
  5. 18 0
      modules_k/usrloc/ul_mod.c
  6. 9 0
      modules_k/usrloc/ul_mod.h

+ 190 - 1
modules_k/usrloc/ucontact.c

@@ -75,6 +75,7 @@ void ucontact_xavp_store(ucontact_t *_c)
 	if(xavp==NULL)
 		return;
 	/* clone the xavp found in core */
+	LM_DBG("trying to clone per contact xavps\n");
 	_c->xavp = xavp_clone_level_nodata(xavp);
 	return;
 }
@@ -615,6 +616,9 @@ int db_insert_ucontact(ucontact_t* _c)
 		return -1;
 	}
 
+	uldb_insert_attrs(_c->domain, &vals[0].val.str_val, &vals[nr_cols-1].val.str_val,
+		&_c->ruid, _c->xavp);
+
 	return 0;
 }
 
@@ -786,9 +790,15 @@ int db_update_ucontact(ucontact_t* _c)
 		 * to do an INSERT */
 		if(ul_dbf.affected_rows(ul_dbh)==0) {
 			LM_DBG("affected rows by UPDATE was 0, doing an INSERT\n");
-			return db_insert_ucontact(_c);
+			if(db_insert_ucontact(_c)<0)
+				return -1;
 		}
 	}
+	/* delete old db attrs and add the current list */
+	uldb_delete_attrs(_c->domain, &vals1[0].val.str_val,
+			&vals1[3].val.str_val, &_c->ruid);
+	uldb_insert_attrs(_c->domain, &vals1[0].val.str_val, &vals1[3].val.str_val,
+		&_c->ruid, _c->xavp);
 
 	return 0;
 }
@@ -840,6 +850,9 @@ int db_delete_ucontact(ucontact_t* _c)
 		}
 	}
 
+	uldb_delete_attrs(_c->domain, &vals[0].val.str_val,
+			&vals[3].val.str_val, &_c->ruid);
+
 	if (ul_dbf.use_table(ul_dbh, _c->domain) < 0) {
 		LM_ERR("sql use_table failed\n");
 		return -1;
@@ -969,3 +982,179 @@ int update_ucontact(struct urecord* _r, ucontact_t* _c, ucontact_info_t* _ci)
 	}
 	return 0;
 }
+
+/*!
+ * \brief Load all location attributes from a udomain
+ *
+ * Load all location attributes from a udomain, useful to populate the
+ * memory cache on startup.
+ * \param _dname loaded domain name
+ * \param _user sip username
+ * \param _domain sip domain
+ * \param _ruid usrloc record unique id
+ * \return 0 on success, -1 on failure
+ */
+int uldb_delete_attrs(str* _dname, str *_user, str *_domain, str *_ruid)
+{
+	char tname_buf[64];
+	str tname;
+	db_key_t keys[3];
+	db_val_t vals[3];
+
+	LM_DBG("trying to delete location attributes\n");
+
+	if(ul_xavp_contact_name.s==NULL) {
+		/* feature disabled by mod param */
+		return 0;
+	}
+
+	if(_dname->len+6>=64) {
+		LM_ERR("attributes table name is too big\n");
+		return -1;
+	}
+	strncpy(tname_buf, _dname->s, _dname->len);
+	tname_buf[_dname->len] = '\0';
+	strcat(tname_buf, "_attrs");
+	tname.s = tname_buf;
+	tname.len = _dname->len + 6;
+
+	keys[0] = &ulattrs_user_col;
+	keys[1] = &ulattrs_ruid_col;
+	keys[2] = &ulattrs_domain_col;
+
+	vals[0].type = DB1_STR;
+	vals[0].nul = 0;
+	vals[0].val.str_val = *_user;
+
+	vals[1].type = DB1_STR;
+	vals[1].nul = 0;
+	vals[1].val.str_val = *_ruid;
+
+	if (use_domain) {
+		vals[2].type = DB1_STR;
+		vals[2].nul = 0;
+		vals[2].val.str_val = *_domain;
+	}
+
+	if (ul_dbf.use_table(ul_dbh, &tname) < 0) {
+		LM_ERR("sql use_table failed\n");
+		return -1;
+	}
+
+	if (ul_dbf.delete(ul_dbh, keys, 0, vals, (use_domain) ? (3) : (2)) < 0) {
+		LM_ERR("deleting from database failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief Insert contact attributes into the database
+ * \param _dname loaded domain name
+ * \param _user sip username
+ * \param _domain sip domain
+ * \param _ruid record unique id
+ * \param _xhead head of xavp list
+ * \return 0 on success, -1 on failure
+ */
+int uldb_insert_attrs(str *_dname, str *_user, str *_domain,
+		str *_ruid, sr_xavp_t *_xhead)
+{
+	char tname_buf[64];
+	str tname;
+	str avalue;
+	sr_xavp_t *xavp;
+	db_key_t keys[7];
+	db_val_t vals[7];
+	int nr_cols;
+
+	LM_DBG("trying to insert location attributes\n");
+
+	if(ul_xavp_contact_name.s==NULL) {
+		/* feature disabled by mod param */
+		LM_DBG("location attributes disabled\n");
+		return 0;
+	}
+
+	if(_xhead==NULL || _xhead->val.type!=SR_XTYPE_XAVP
+			|| _xhead->val.v.xavp==NULL) {
+		/* nothing to write */
+		LM_DBG("no location attributes\n");
+		return 0;
+	}
+
+	if(_dname->len+6>=64) {
+		LM_ERR("attributes table name is too big\n");
+		return -1;
+	}
+	strncpy(tname_buf, _dname->s, _dname->len);
+	tname_buf[_dname->len] = '\0';
+	strcat(tname_buf, "_attrs");
+	tname.s = tname_buf;
+	tname.len = _dname->len + 6;
+
+	if (ul_dbf.use_table(ul_dbh, &tname) < 0) {
+		LM_ERR("sql use_table failed for %.*s\n", tname.len, tname.s);
+		return -1;
+	}
+
+	keys[0] = &ulattrs_user_col;
+	keys[1] = &ulattrs_ruid_col;
+	keys[2] = &ulattrs_last_mod_col;
+	keys[3] = &ulattrs_aname_col;
+	keys[4] = &ulattrs_atype_col;
+	keys[5] = &ulattrs_avalue_col;
+	keys[6] = &ulattrs_domain_col;
+
+	vals[0].type = DB1_STR;
+	vals[0].nul = 0;
+	vals[0].val.str_val = *_user;
+
+	vals[1].type = DB1_STR;
+	vals[1].nul = 0;
+	vals[1].val.str_val = *_ruid;
+
+	vals[2].type = DB1_DATETIME;
+	vals[2].nul = 0;
+	vals[2].val.time_val = time(NULL);
+
+	if (use_domain && _domain!=NULL && _domain->s!=NULL) {
+		nr_cols = 7;
+		vals[6].type = DB1_STR;
+		vals[6].nul = 0;
+		vals[6].val.str_val = *_domain;
+
+	} else {
+		nr_cols = 6;
+	}
+
+	for(xavp=_xhead->val.v.xavp; xavp; xavp=xavp->next) {
+		vals[3].type = DB1_STR;
+		vals[3].nul = 0;
+		vals[3].val.str_val = xavp->name;
+
+		vals[4].type = DB1_INT;
+		vals[4].nul = 0;
+		if(xavp->val.type==SR_XTYPE_STR) {
+			vals[4].val.int_val = 0;
+			avalue = xavp->val.v.s;
+		} else if(xavp->val.type==SR_XTYPE_INT) {
+			vals[4].val.int_val = 1;
+			avalue.s = sint2str((long)xavp->val.v.i, &avalue.len);
+		} else {
+			continue;
+		}
+
+		vals[5].type = DB1_STR;
+		vals[5].nul = 0;
+		vals[5].val.str_val = avalue;
+
+		if (ul_dbf.insert(ul_dbh, keys, vals, nr_cols) < 0) {
+			LM_ERR("inserting contact in db failed\n");
+			return -1;
+		}
+
+	}
+	return 0;
+}

+ 27 - 0
modules_k/usrloc/ucontact.h

@@ -37,6 +37,7 @@
 
 
 #include <stdio.h>
+#include "../../xavp.h"
 #include "usrloc.h"
 
 
@@ -149,4 +150,30 @@ int db_delete_ucontact(ucontact_t* _c);
  */
 int update_ucontact(struct urecord* _r, ucontact_t* _c, ucontact_info_t* _ci);
 
+/* ====== per contact attributes ====== */
+
+/*!
+ * \brief Load all location attributes from a udomain
+ *
+ * Load all location attributes from a udomain, useful to populate the
+ * memory cache on startup.
+ * \param _dname loaded domain name
+ * \param _user sip username
+ * \param _domain sip domain
+ * \param _ruid usrloc record unique id
+ * \return 0 on success, -1 on failure
+ */
+int uldb_delete_attrs(str* _dname, str *_user, str *_domain, str *_ruid);
+
+/*!
+ * \brief Insert contact attributes into the database
+ * \param _dname loaded domain name
+ * \param _user sip username
+ * \param _domain sip domain
+ * \param _ruid record unique id
+ * \param _xhead head of xavp list
+ * \return 0 on success, -1 on failure
+ */
+int uldb_insert_attrs(str *_dname, str *_user, str *_domain,
+        str *_ruid, sr_xavp_t *_xhead);
 #endif

+ 182 - 0
modules_k/usrloc/udomain.c

@@ -1123,3 +1123,185 @@ int delete_urecord(udomain_t* _d, str* _aor, struct urecord* _r)
 	release_urecord(_r);
 	return 0;
 }
+
+
+/*!
+ * \brief Load all location attributes from an udomain
+ *
+ * Load all location attributes from a udomain, useful to populate the
+ * memory cache on startup.
+ * \param _d loaded domain
+ * \return 0 on success, -1 on failure
+ */
+int uldb_preload_attrs(udomain_t *_d)
+{
+	char uri[MAX_URI_SIZE];
+	str  suri;
+	char tname_buf[64];
+	str tname;
+	db_row_t *row;
+	db_key_t columns[6];
+	db1_res_t* res = NULL;
+	str user = {0};
+	str domain = {0};
+	str ruid;
+	str aname;
+	str avalue;
+	sr_xval_t aval;
+	int i;
+	int n;
+
+	urecord_t* r;
+	ucontact_t* c;
+
+	if(ul_xavp_contact_name.s==NULL) {
+		/* feature disabled by mod param */
+		return 0;
+	}
+
+	if(_d->name->len + 6>=64) {
+		LM_ERR("attributes table name is too big\n");
+		return -1;
+	}
+	strncpy(tname_buf, _d->name->s, _d->name->len);
+	tname_buf[_d->name->len] = '\0';
+	strcat(tname_buf, "_attrs");
+	tname.s = tname_buf;
+	tname.len = _d->name->len + 6;
+
+	columns[0] = &ulattrs_user_col;
+	columns[1] = &ulattrs_ruid_col;
+	columns[2] = &ulattrs_aname_col;
+	columns[3] = &ulattrs_atype_col;
+	columns[4] = &ulattrs_avalue_col;
+	columns[5] = &ulattrs_domain_col;
+
+	if (ul_dbf.use_table(ul_dbh, &tname) < 0) {
+		LM_ERR("sql use_table failed for %.*s\n", tname.len, tname.s);
+		return -1;
+	}
+
+#ifdef EXTRA_DEBUG
+	LM_NOTICE("load start time [%d]\n", (int)time(NULL));
+#endif
+
+	if (DB_CAPABILITY(ul_dbf, DB_CAP_FETCH)) {
+		if (ul_dbf.query(ul_dbh, 0, 0, 0, columns, 0, (use_domain)?(6):(5), 0,
+					0) < 0) {
+			LM_ERR("db_query (1) failed\n");
+			return -1;
+		}
+		if(ul_dbf.fetch_result(ul_dbh, &res, ul_fetch_rows)<0) {
+			LM_ERR("fetching rows failed\n");
+			return -1;
+		}
+	} else {
+		if (ul_dbf.query(ul_dbh, 0, 0, 0, columns, 0, (use_domain)?(6):(5), 0,
+		&res) < 0) {
+			LM_ERR("db_query failed\n");
+			return -1;
+		}
+	}
+
+	if (RES_ROW_N(res) == 0) {
+		LM_DBG("location attrs table is empty\n");
+		ul_dbf.free_result(ul_dbh, res);
+		return 0;
+	}
+
+
+	n = 0;
+	do {
+		LM_DBG("loading records - cycle [%d]\n", ++n);
+		for(i = 0; i < RES_ROW_N(res); i++) {
+			row = RES_ROWS(res) + i;
+
+			user.s = (char*)VAL_STRING(ROW_VALUES(row));
+			if (VAL_NULL(ROW_VALUES(row)) || user.s==0 || user.s[0]==0) {
+				LM_CRIT("empty username record in table %s...skipping\n",
+						_d->name->s);
+				continue;
+			}
+			user.len = strlen(user.s);
+
+			ruid.s = (char*)VAL_STRING(ROW_VALUES(row) + 1);
+			ruid.len = strlen(ruid.s);
+			aname.s = (char*)VAL_STRING(ROW_VALUES(row) + 2);
+			aname.len = strlen(aname.s);
+			avalue.s = (char*)VAL_STRING(ROW_VALUES(row) + 4);
+			avalue.len = strlen(avalue.s);
+			memset(&aval, 0, sizeof(sr_xval_t));
+			if(VAL_INT(ROW_VALUES(row)+3)==0) {
+				/* string value */
+				aval.v.s = avalue;
+				aval.type = SR_XTYPE_STR;
+			} else if(VAL_INT(ROW_VALUES(row)+3)==1) {
+				/* int value */
+				str2sint(&avalue, &aval.v.i);
+				aval.type = SR_XTYPE_INT;
+			} else {
+				/* unknown type - ignore */
+				continue;
+			}
+
+			if (use_domain) {
+				domain.s = (char*)VAL_STRING(ROW_VALUES(row) + 6);
+				if (VAL_NULL(ROW_VALUES(row)+6) || domain.s==0 || domain.s[0]==0){
+					LM_CRIT("empty domain record for user %.*s...skipping\n",
+							user.len, user.s);
+					continue;
+				}
+				domain.len = strlen(domain.s);
+				/* user.s cannot be NULL - checked previosly */
+				suri.len = snprintf(uri, MAX_URI_SIZE, "%.*s@%s",
+					user.len, user.s, domain.s);
+				suri.s = uri;
+				if (suri.s[suri.len]!=0) {
+					LM_CRIT("URI '%.*s@%s' longer than %d\n", user.len, user.s,
+							domain.s, MAX_URI_SIZE);
+					continue;
+				}
+			} else {
+				suri = user;
+			}
+
+			lock_udomain(_d, &suri);
+			if (get_urecord_by_ruid(_d, ul_get_aorhash(&suri), &ruid, &r, &c) > 0) {
+				/* delete attrs records from db table */
+				LM_INFO("no contact record for this ruid\n");
+				uldb_delete_attrs(_d->name, &user, &domain, &ruid);
+			} else {
+				/* add xavp to contact */
+				if(c->xavp==NULL) {
+					if(xavp_add_xavp_value(&ul_xavp_contact_name, &aname,
+								&aval, &c->xavp)==NULL)
+						LM_INFO("cannot add first xavp to contact - ignoring\n");
+				} else {
+					if(c->xavp->val.type==SR_XTYPE_XAVP) {
+						if(xavp_add_value(&aname, &aval, &c->xavp->val.v.xavp)==NULL)
+							LM_INFO("cannot add values to contact xavp\n");
+					}
+				}
+			}
+			unlock_udomain(_d, &user);
+		}
+
+		if (DB_CAPABILITY(ul_dbf, DB_CAP_FETCH)) {
+			if(ul_dbf.fetch_result(ul_dbh, &res, ul_fetch_rows)<0) {
+				LM_ERR("fetching rows (1) failed\n");
+				ul_dbf.free_result(ul_dbh, res);
+				return -1;
+			}
+		} else {
+			break;
+		}
+	} while(RES_ROW_N(res)>0);
+
+	ul_dbf.free_result(ul_dbh, res);
+
+#ifdef EXTRA_DEBUG
+	LM_NOTICE("load end time [%d]\n", (int)time(NULL));
+#endif
+
+	return 0;
+}

+ 12 - 0
modules_k/usrloc/udomain.h

@@ -213,4 +213,16 @@ int get_urecord_by_ruid(udomain_t* _d, unsigned int _aorhash,
 int delete_urecord(udomain_t* _d, str* _aor, struct urecord* _r);
 
 
+/* ===== per-contact attributes ======= */
+
+/*!
+ * \brief Load all location attributes from an udomain
+ *
+ * Load all location attributes from a udomain, useful to populate the
+ * memory cache on startup.
+ * \param _d loaded domain
+ * \return 0 on success, -1 on failure
+ */
+int uldb_preload_attrs(udomain_t *_d);
+
 #endif

+ 18 - 0
modules_k/usrloc/ul_mod.c

@@ -91,6 +91,14 @@ MODULE_VERSION
 #define REG_ID_COL     "reg_id"
 #define LAST_MOD_COL   "last_modified"
 
+#define ULATTRS_USER_COL       "username"
+#define ULATTRS_DOMAIN_COL     "domain"
+#define ULATTRS_RUID_COL       "ruid"
+#define ULATTRS_ANAME_COL      "aname"
+#define ULATTRS_ATYPE_COL      "atype"
+#define ULATTRS_AVALUE_COL     "avalue"
+#define ULATTRS_LAST_MOD_COL   "last_modified"
+
 static int mod_init(void);                          /*!< Module initialization function */
 static void destroy(void);                          /*!< Module destroy function */
 static void ul_core_timer(unsigned int ticks, void* param);  /*!< Core timer handler */
@@ -137,6 +145,15 @@ str methods_col     = str_init(METHODS_COL);	/*!< Name of column containing the
 str instance_col    = str_init(INSTANCE_COL);	/*!< Name of column containing the SIP instance value */
 str reg_id_col      = str_init(REG_ID_COL);		/*!< Name of column containing the reg-id value */
 str last_mod_col    = str_init(LAST_MOD_COL);	/*!< Name of column containing the last modified date */
+
+str ulattrs_user_col   = str_init(ULATTRS_USER_COL);   /*!< Name of column containing username */
+str ulattrs_domain_col = str_init(ULATTRS_DOMAIN_COL); /*!< Name of column containing domain */
+str ulattrs_ruid_col   = str_init(ULATTRS_RUID_COL);   /*!< Name of column containing record unique id */
+str ulattrs_aname_col  = str_init(ULATTRS_ANAME_COL);  /*!< Name of column containing attribute name */
+str ulattrs_atype_col  = str_init(ULATTRS_ATYPE_COL);  /*!< Name of column containing attribute type */
+str ulattrs_avalue_col = str_init(ULATTRS_AVALUE_COL); /*!< Name of column containing attribute value */
+str ulattrs_last_mod_col = str_init(ULATTRS_LAST_MOD_COL);	/*!< Name of column containing the last modified date */
+
 str db_url          = str_init(DEFAULT_DB_URL);	/*!< Database URL */
 int timer_interval  = 60;				/*!< Timer interval in seconds */
 int db_mode         = 0;				/*!< Database sync scheme: 0-no db, 1-write through, 2-write back, 3-only db */
@@ -423,6 +440,7 @@ static int child_init(int _rank)
 						_rank, ptr->name.len, ZSW(ptr->name.s));
 				return -1;
 			}
+			uldb_preload_attrs(ptr->d);
 		}
 	}
 

+ 9 - 0
modules_k/usrloc/ul_mod.h

@@ -64,6 +64,15 @@ extern str instance_col;
 extern str reg_id_col;
 extern str last_mod_col;
 
+extern str ulattrs_user_col;
+extern str ulattrs_domain_col;
+extern str ulattrs_ruid_col;
+extern str ulattrs_aname_col;
+extern str ulattrs_atype_col;
+extern str ulattrs_avalue_col;
+extern str ulattrs_last_mod_col;
+
+
 extern str db_url;
 extern int timer_interval;
 extern int db_mode;