modules_k/p_usrloc/udomain.c

Go to the documentation of this file.
00001 /*
00002  * $Id: udomain.c 5272 2008-11-27 12:32:26Z henningw $ 
00003  *
00004  * Copyright (C) 2001-2003 FhG Fokus
00005  *
00006  * This file is part of Kamailio, a free SIP server.
00007  *
00008  * Kamailio is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version
00012  *
00013  * Kamailio is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License 
00019  * along with this program; if not, write to the Free Software 
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  *
00022  * History:
00023  * ---------
00024  * 2003-03-11 changed to the new locking scheme: locking.h (andrei)
00025  * 2003-03-12 added replication mark and zombie state (nils)
00026  * 2004-06-07 updated to the new DB api (andrei)
00027  * 2004-08-23  hash function changed to process characters as unsigned
00028  *             -> no negative results occur (jku)
00029  */
00030 
00038 #include "udomain.h"
00039 #include <string.h>
00040 #include "../../parser/parse_methods.h"
00041 #include "../../mem/shm_mem.h"
00042 #include "../../dprint.h"
00043 #include "../../lib/srdb1/db.h"
00044 #include "../../socket_info.h"
00045 #include "../../ut.h"
00046 #include "../../hashes.h"
00047 #include "p_usrloc_mod.h"            /* usrloc module parameters */
00048 #include "utime.h"
00049 #include "ul_db_layer.h"
00050 
00051 
00052 #ifdef STATISTICS
00053 static char *build_stat_name( str* domain, char *var_name)
00054 {
00055         int n;
00056         char *s;
00057         char *p;
00058 
00059         n = domain->len + 1 + strlen(var_name) + 1;
00060         s = (char*)shm_malloc( n );
00061         if (s==0) {
00062                 LM_ERR("no more shm mem\n");
00063                 return 0;
00064         }
00065         memcpy( s, domain->s, domain->len);
00066         p = s + domain->len;
00067         *(p++) = '-';
00068         memcpy( p , var_name, strlen(var_name));
00069         p += strlen(var_name);
00070         *(p++) = 0;
00071         return s;
00072 }
00073 #endif
00074 
00075 
00084 int new_udomain(str* _n, int _s, udomain_t** _d)
00085 {
00086         int i;
00087 #ifdef STATISTICS
00088         char *name;
00089 #endif
00090         
00091         /* Must be always in shared memory, since
00092          * the cache is accessed from timer which
00093          * lives in a separate process
00094          */
00095         *_d = (udomain_t*)shm_malloc(sizeof(udomain_t));
00096         if (!(*_d)) {
00097                 LM_ERR("new_udomain(): No memory left\n");
00098                 goto error0;
00099         }
00100         memset(*_d, 0, sizeof(udomain_t));
00101         
00102         (*_d)->table = (hslot_t*)shm_malloc(sizeof(hslot_t) * _s);
00103         if (!(*_d)->table) {
00104                 LM_ERR("no memory left 2\n");
00105                 goto error1;
00106         }
00107 
00108         (*_d)->name = _n;
00109         
00110         for(i = 0; i < _s; i++) {
00111                 init_slot(*_d, &((*_d)->table[i]), i);
00112         }
00113 
00114         (*_d)->size = _s;
00115 
00116 #ifdef STATISTICS
00117         /* register the statistics */
00118         if ( (name=build_stat_name(_n,"users"))==0 || register_stat("usrloc",
00119         name, &(*_d)->users, STAT_NO_RESET|STAT_SHM_NAME)!=0 ) {
00120                 LM_ERR("failed to add stat variable\n");
00121                 goto error2;
00122         }
00123         if ( (name=build_stat_name(_n,"contacts"))==0 || register_stat("usrloc",
00124         name, &(*_d)->contacts, STAT_NO_RESET|STAT_SHM_NAME)!=0 ) {
00125                 LM_ERR("failed to add stat variable\n");
00126                 goto error2;
00127         }
00128         if ( (name=build_stat_name(_n,"expires"))==0 || register_stat("usrloc",
00129         name, &(*_d)->expires, STAT_SHM_NAME)!=0 ) {
00130                 LM_ERR("failed to add stat variable\n");
00131                 goto error2;
00132         }
00133 #endif
00134 
00135         return 0;
00136 
00137 #ifdef STATISTICS
00138 error2:
00139         shm_free((*_d)->table);
00140 #endif
00141 error1:
00142         shm_free(*_d);
00143 error0:
00144         return -1;
00145 }
00146 
00147 
00152 void free_udomain(udomain_t* _d)
00153 {
00154         int i;
00155         
00156         if (_d->table) {
00157                 for(i = 0; i < _d->size; i++) {
00158                         lock_ulslot(_d, i);
00159                         deinit_slot(_d->table + i);
00160                         unlock_ulslot(_d, i);
00161                 }
00162                 shm_free(_d->table);
00163         }
00164         shm_free(_d);
00165 }
00166 
00167 
00174 static inline void get_static_urecord(udomain_t* _d, str* _aor,
00175                                                                                                                 struct urecord** _r)
00176 {
00177         static struct urecord r;
00178 
00179         memset( &r, 0, sizeof(struct urecord) );
00180         r.aor = *_aor;
00181         r.domain = _d->name;
00182         *_r = &r;
00183 }
00184 
00185 
00189 void print_udomain(FILE* _f, udomain_t* _d)
00190 {
00191         int i;
00192         int max=0, slot=0, n=0;
00193         struct urecord* r;
00194         fprintf(_f, "---Domain---\n");
00195         fprintf(_f, "name : '%.*s'\n", _d->name->len, ZSW(_d->name->s));
00196         fprintf(_f, "size : %d\n", _d->size);
00197         fprintf(_f, "table: %p\n", _d->table);
00198         /*fprintf(_f, "lock : %d\n", _d->lock); -- can be a structure --andrei*/
00199         fprintf(_f, "\n");
00200         for(i=0; i<_d->size; i++)
00201         {
00202                 r = _d->table[i].first;
00203                 n += _d->table[i].n;
00204                 if(max<_d->table[i].n){
00205                         max= _d->table[i].n;
00206                         slot = i;
00207                 }
00208                 while(r) {
00209                         print_urecord(_f, r);
00210                         r = r->next;
00211                 }
00212         }
00213         fprintf(_f, "\nMax slot: %d (%d/%d)\n", max, slot, n);
00214         fprintf(_f, "\n---/Domain---\n");
00215 }
00216 
00217 
00228 static inline ucontact_info_t* dbrow2info( db_val_t *vals, str *contact)
00229 {
00230         static ucontact_info_t ci;
00231         static str callid, ua, received, host, path;
00232         int port, proto;
00233         char *p;
00234 
00235         memset( &ci, 0, sizeof(ucontact_info_t));
00236 
00237         contact->s = (char*)VAL_STRING(vals);
00238         if (VAL_NULL(vals) || contact->s==0 || contact->s[0]==0) {
00239                 LM_CRIT("bad contact\n");
00240                 return 0;
00241         }
00242         contact->len = strlen(contact->s);
00243 
00244         if (VAL_NULL(vals+1)) {
00245                 LM_CRIT("empty expire\n");
00246                 return 0;
00247         }
00248         ci.expires = VAL_TIME(vals+1);
00249 
00250         if (VAL_NULL(vals+2)) {
00251                 LM_CRIT("empty q\n");
00252                 return 0;
00253         }
00254         ci.q = double2q(VAL_DOUBLE(vals+2));
00255 
00256         if (VAL_NULL(vals+4)) {
00257                 LM_CRIT("empty cseq_nr\n");
00258                 return 0;
00259         }
00260         ci.cseq = VAL_INT(vals+4);
00261 
00262         callid.s = (char*)VAL_STRING(vals+3);
00263         if (VAL_NULL(vals+3) || !callid.s || !callid.s[0]) {
00264                 LM_CRIT("bad callid\n");
00265                 return 0;
00266         }
00267         callid.len  = strlen(callid.s);
00268         ci.callid = &callid;
00269 
00270         if (VAL_NULL(vals+5)) {
00271                 LM_CRIT("empty flag\n");
00272                 return 0;
00273         }
00274         ci.flags  = VAL_BITMAP(vals+5);
00275 
00276         if (VAL_NULL(vals+6)) {
00277                 LM_CRIT("empty cflag\n");
00278                 return 0;
00279         }
00280         ci.cflags  = VAL_BITMAP(vals+6);
00281 
00282         ua.s  = (char*)VAL_STRING(vals+7);
00283         if (VAL_NULL(vals+7) || !ua.s || !ua.s[0]) {
00284                 ua.s = 0;
00285                 ua.len = 0;
00286         } else {
00287                 ua.len = strlen(ua.s);
00288         }
00289         ci.user_agent = &ua;
00290 
00291         received.s  = (char*)VAL_STRING(vals+8);
00292         if (VAL_NULL(vals+8) || !received.s || !received.s[0]) {
00293                 received.len = 0;
00294                 received.s = 0;
00295         } else {
00296                 received.len = strlen(received.s);
00297         }
00298         ci.received = received;
00299         
00300         path.s  = (char*)VAL_STRING(vals+9);
00301                 if (VAL_NULL(vals+9) || !path.s || !path.s[0]) {
00302                         path.len = 0;
00303                         path.s = 0;
00304                 } else {
00305                         path.len = strlen(path.s);
00306                 }
00307         ci.path= &path;
00308 
00309         /* socket name */
00310         p  = (char*)VAL_STRING(vals+10);
00311         if (VAL_NULL(vals+10) || p==0 || p[0]==0){
00312                 ci.sock = 0;
00313         } else {
00314                 if (parse_phostport( p, &host.s, &host.len,
00315                                 &port, &proto)!=0){
00316                         LM_ERR("bad socket <%s>\n", p);
00317                         return 0;
00318                 }
00319                 ci.sock = grep_sock_info( &host, (unsigned short)port, proto);
00320                 if (ci.sock==0) {
00321                         LM_INFO("non-local socket <%s>...ignoring\n", p);
00322                 }
00323         }
00324 
00325         /* supported methods */
00326         if (VAL_NULL(vals+11)) {
00327                 ci.methods = ALL_METHODS;
00328         } else {
00329                 ci.methods = VAL_BITMAP(vals+11);
00330         }
00331 
00332         /* last modified time */
00333         if (!VAL_NULL(vals+12)) {
00334                 ci.last_modified = VAL_TIME(vals+12);
00335         }
00336 
00337         return &ci;
00338 }
00339 
00340 
00341 
00349 urecord_t* db_load_urecord(udomain_t* _d, str *_aor)
00350 {
00351         ucontact_info_t *ci;
00352         db_key_t columns[13];
00353         db_key_t keys[2];
00354         db_key_t order;
00355         db_val_t vals[2];
00356         db1_res_t* res = NULL;
00357         str contact;
00358         char *domain;
00359         int i;
00360 
00361         urecord_t* r;
00362         ucontact_t* c;
00363 
00364         keys[0] = &user_col;
00365         vals[0].type = DB1_STR;
00366         vals[0].nul = 0;
00367         if (use_domain) {
00368                 keys[1] = &domain_col;
00369                 vals[1].type = DB1_STR;
00370                 vals[1].nul = 0;
00371                 domain = memchr(_aor->s, '@', _aor->len);
00372                 vals[0].val.str_val.s   = _aor->s;
00373                 if (domain==0) {
00374                         vals[0].val.str_val.len = 0;
00375                         vals[1].val.str_val = *_aor;
00376                 } else {
00377                         vals[0].val.str_val.len = domain - _aor->s;
00378                         vals[1].val.str_val.s   = domain+1;
00379                         vals[1].val.str_val.len = _aor->s + _aor->len - domain - 1;
00380                 }
00381         } else {
00382                 vals[0].val.str_val = *_aor;
00383         }
00384 
00385         columns[0] = &contact_col;
00386         columns[1] = &expires_col;
00387         columns[2] = &q_col;
00388         columns[3] = &callid_col;
00389         columns[4] = &cseq_col;
00390         columns[5] = &flags_col;
00391         columns[6] = &cflags_col;
00392         columns[7] = &user_agent_col;
00393         columns[8] = &received_col;
00394         columns[9] = &path_col;
00395         columns[10] = &sock_col;
00396         columns[11] = &methods_col;
00397         columns[12] = &last_mod_col;
00398 
00399         if (desc_time_order)
00400                 order = &last_mod_col;
00401         else
00402                 order = &q_col;
00403 
00404         if (ul_db_layer_query(_d,  &vals[0].val.str_val,  &vals[1].val.str_val, keys, 0, vals, columns, (use_domain)?2:1, 13, order,
00405                                 &res) < 0) {
00406                 LM_ERR("db_query failed\n");
00407                 return 0;
00408         }
00409 
00410         if (RES_ROW_N(res) == 0) {
00411                 LM_DBG("aor %.*s not found in table %.*s\n",_aor->len, _aor->s, _d->name->len, _d->name->s);
00412 
00413                 ul_db_layer_free_result(_d, res);
00414                 return 0;
00415         }
00416 
00417         r = 0;
00418 
00419         for(i = 0; i < RES_ROW_N(res); i++) {
00420                 ci = dbrow2info(  ROW_VALUES(RES_ROWS(res) + i), &contact);
00421                 if (ci==0) {
00422                         LM_ERR("skipping record for %.*s in table %s\n",
00423                                         _aor->len, _aor->s, _d->name->s);
00424                         continue;
00425                 }
00426                 
00427                 if ( r==0 )
00428                         get_static_urecord( _d, _aor, &r);
00429 
00430                 if ( (c=mem_insert_ucontact(r, &contact, ci)) == 0) {
00431                         LM_ERR("mem_insert failed\n");
00432                         free_urecord(r);
00433                         ul_db_layer_free_result(_d, res);
00434                         return 0;
00435                 }
00436 
00437                 /* We have to do this, because insert_ucontact sets state to CS_NEW
00438                  * and we have the contact in the database already */
00439                 c->state = CS_SYNC;
00440         }
00441 
00442         ul_db_layer_free_result(_d, res);
00443         return r;
00444 }
00445 
00446 
00452 int db_timer_udomain(udomain_t* _d)
00453 {
00454         db_key_t keys[2];
00455         db_op_t  ops[2];
00456         db_val_t vals[2];
00457 
00458         keys[0] = &expires_col;
00459         ops[0] = "<";
00460         vals[0].type = DB1_DATETIME;
00461         vals[0].nul = 0;
00462         vals[0].val.time_val = act_time + 1;
00463 
00464         keys[1] = &expires_col;
00465         ops[1] = "!=";
00466         vals[1].type = DB1_DATETIME;
00467         vals[1].nul = 0;
00468         vals[1].val.time_val = 0;
00469         // we're using a local cron job to delete expired entries
00470         LM_INFO("using sp-ul_db database interface, expires is not implemented");
00471         //if (ul_db_layer_delete(_d, NULL, NULL, keys, ops, vals, 2) < 0) { //FIXME
00472         return 0;
00473 }
00474 
00475 
00476 
00484 int mem_insert_urecord(udomain_t* _d, str* _aor, struct urecord** _r)
00485 {
00486         int sl;
00487         
00488         if (new_urecord(_d->name, _aor, _r) < 0) {
00489                 LM_ERR("creating urecord failed\n");
00490                 return -1;
00491         }
00492 
00493         sl = ((*_r)->aorhash)&(_d->size-1);
00494         slot_add(&_d->table[sl], *_r);
00495         update_stat( _d->users, 1);
00496         return 0;
00497 }
00498 
00499 
00505 void mem_delete_urecord(udomain_t* _d, struct urecord* _r)
00506 {
00507         slot_rem(_r->slot, _r);
00508         free_urecord(_r);
00509         update_stat( _d->users, -1);
00510 }
00511 
00512 
00517 void mem_timer_udomain(udomain_t* _d)
00518 {
00519         struct urecord* ptr, *t;
00520         int i;
00521 
00522         for(i=0; i<_d->size; i++)
00523         {
00524                 lock_ulslot(_d, i);
00525 
00526                 ptr = _d->table[i].first;
00527 
00528                 while(ptr) {
00529                         timer_urecord(ptr);
00530                         /* Remove the entire record if it is empty */
00531                         if (ptr->contacts == 0) {
00532                                 t = ptr;
00533                                 ptr = ptr->next;
00534                                 mem_delete_urecord(_d, t);
00535                         } else {
00536                                 ptr = ptr->next;
00537                         }
00538                 }
00539                 unlock_ulslot(_d, i);
00540         }
00541 }
00542 
00543 
00549 void lock_udomain(udomain_t* _d, str* _aor)
00550 {
00551         unsigned int sl;
00552         if (db_mode!=DB_ONLY)
00553         {
00554                 sl = core_hash(_aor, 0, _d->size);
00555 
00556 #ifdef GEN_LOCK_T_PREFERED
00557                 lock_get(_d->table[sl].lock);
00558 #else
00559                 ul_lock_idx(_d->table[sl].lockidx);
00560 #endif
00561         }
00562 }
00563 
00564 
00570 void unlock_udomain(udomain_t* _d, str* _aor)
00571 {
00572         unsigned int sl;
00573         if (db_mode!=DB_ONLY)
00574         {
00575                 sl = core_hash(_aor, 0, _d->size);
00576 #ifdef GEN_LOCK_T_PREFERED
00577                 lock_release(_d->table[sl].lock);
00578 #else
00579                 ul_release_idx(_d->table[sl].lockidx);
00580 #endif
00581         }
00582 }
00583 
00589 void lock_ulslot(udomain_t* _d, int i)
00590 {
00591         if (db_mode!=DB_ONLY)
00592 #ifdef GEN_LOCK_T_PREFERED
00593                 lock_get(_d->table[i].lock);
00594 #else
00595                 ul_lock_idx(_d->table[i].lockidx);
00596 #endif
00597 }
00598 
00599 
00605 void unlock_ulslot(udomain_t* _d, int i)
00606 {
00607         if (db_mode!=DB_ONLY)
00608 #ifdef GEN_LOCK_T_PREFERED
00609                 lock_release(_d->table[i].lock);
00610 #else
00611                 ul_release_idx(_d->table[i].lockidx);
00612 #endif
00613 }
00614 
00615 
00616 
00624 int insert_urecord(udomain_t* _d, str* _aor, struct urecord** _r)
00625 {
00626         if (db_mode!=DB_ONLY) {
00627                 if (mem_insert_urecord(_d, _aor, _r) < 0) {
00628                         LM_ERR("inserting record failed\n");
00629                         return -1;
00630                 }
00631         } else {
00632                 get_static_urecord( _d, _aor, _r);
00633         }
00634         return 0;
00635 }
00636 
00637 
00645 int get_urecord(udomain_t* _d, str* _aor, struct urecord** _r)
00646 {
00647         unsigned int sl, i, aorhash;
00648         urecord_t* r;
00649         if (db_mode!=DB_ONLY) {
00650                 /* search in cache */
00651                 aorhash = core_hash(_aor, 0, 0);
00652                 sl = aorhash&(_d->size-1);
00653                 r = _d->table[sl].first;
00654 
00655                 for(i = 0; i < _d->table[sl].n; i++) {
00656                         if((r->aorhash==aorhash) && (r->aor.len==_aor->len)
00657                                                 && !memcmp(r->aor.s,_aor->s,_aor->len)){
00658                                 *_r = r;
00659                                 return 0;
00660                         }
00661 
00662                         r = r->next;
00663                 }
00664         } else {
00665                 /* search in DB */
00666                 r = db_load_urecord(_d, _aor);
00667                 if (r) {
00668                         *_r = r;
00669                         return 0;
00670                 }
00671         }
00672 
00673         return 1;   /* Nothing found */
00674 }
00675 
00676 
00684 int delete_urecord(udomain_t* _d, str* _aor, struct urecord* _r)
00685 {
00686         struct ucontact* c, *t;
00687 
00688         if (db_mode==DB_ONLY) {
00689                 if (_r==0)
00690                         get_static_urecord( _d, _aor, &_r);
00691                 if (db_delete_urecord(_d, _r)<0) {
00692                         LM_ERR("DB delete failed\n");
00693                         return -1;
00694                 }
00695                 free_urecord(_r);
00696                 return 0;
00697         }
00698 
00699         if (_r==0) {
00700                 if (get_urecord(_d, _aor, &_r) > 0) {
00701                         return 0;
00702                 }
00703         }
00704 
00705         c = _r->contacts;
00706         while(c) {
00707                 t = c;
00708                 c = c->next;
00709                 if (delete_ucontact(_r, t) < 0) {
00710                         LM_ERR("deleting contact failed\n");
00711                         return -1;
00712                 }
00713         }
00714         release_urecord(_r);
00715         return 0;
00716 }