Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

udomain.c

Go to the documentation of this file.
00001 /* 
00002  * $Id: udomain.c,v 1.59 2008/04/16 15:50:39 janakj Exp $ 
00003  *
00004  * Copyright (C) 2001-2003 FhG Fokus
00005  *
00006  * This file is part of ser, a free SIP server.
00007  *
00008  * ser 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  * For a license to use the ser software under conditions
00014  * other than those described here, or to purchase support for this
00015  * software, please contact iptel.org by e-mail at the following addresses:
00016  *    info@iptel.org
00017  *
00018  * ser is distributed in the hope that it will be useful,
00019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00021  * GNU General Public License for more details.
00022  *
00023  * You should have received a copy of the GNU General Public License 
00024  * along with this program; if not, write to the Free Software 
00025  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00026  *
00027  * History:
00028  * ---------
00029  * 2003-03-11 changed to the new locking scheme: locking.h (andrei)
00030  * 2003-03-12 added replication mark and zombie state (nils)
00031  * 2004-06-07 updated to the new DB api (andrei)
00032  * 2004-08-23  hash function changed to process characters as unsigned
00033  *             -> no negative results occur (jku)
00034  * 2005-02-25 incoming socket is saved in ucontact record (bogdan)
00035  * 2006-11-23 switched to better hash functions and fixed hash size (andrei)
00036  *   
00037  */
00038 
00039 #include "udomain.h"
00040 #include <string.h>
00041 #include "../../mem/shm_mem.h"
00042 #include "../../dprint.h"
00043 #include "../../db/db.h"
00044 #include "../../ut.h"
00045 #include "../../parser/parse_param.h"
00046 #include "../../parser/parse_uri.h"
00047 #include "../../resolve.h"
00048 #include "../../socket_info.h"
00049 #include "ul_mod.h"            /* usrloc module parameters */
00050 #include "notify.h"
00051 #include "reg_avps.h"
00052 #include "reg_avps_db.h"
00053 #include "utime.h"
00054 #include "../../hashes.h"
00055 
00056 /* #define HASH_STRING_OPTIMIZE */
00057 
00058 
00059 
00060 
00061 /*
00062  * Hash function
00063  */
00064 static inline int hash_func(udomain_t* _d, unsigned char* _s, int _l)
00065 {
00066 #ifdef HASH_STRING_OPTIMIZE
00067         return get_hash1_raw((char*)_s, _l) % UDOMAIN_HASH_SIZE;
00068 #else
00069         return get_hash1_raw2((char*)_s, _l) % UDOMAIN_HASH_SIZE;
00070 #endif
00071 }
00072 
00073 
00074 /*
00075  * Add a record to list of all records in a domain
00076  */
00077 static inline void udomain_add(udomain_t* _d, urecord_t* _r)
00078 {
00079         if (_d->d_ll.n == 0) {
00080                 _d->d_ll.first = _r;
00081                 _d->d_ll.last = _r;
00082         } else {
00083                 _r->d_ll.prev = _d->d_ll.last;
00084                 _d->d_ll.last->d_ll.next = _r;
00085                 _d->d_ll.last = _r;
00086         }
00087         _d->d_ll.n++;
00088 }
00089 
00090 
00091 /*
00092  * Remove a record from list of all records in a domain
00093  */
00094 static inline void udomain_remove(udomain_t* _d, urecord_t* _r)
00095 {
00096         if (_d->d_ll.n == 0) return;
00097 
00098         if (_r->d_ll.prev) {
00099                 _r->d_ll.prev->d_ll.next = _r->d_ll.next;
00100         } else {
00101                 _d->d_ll.first = _r->d_ll.next;
00102         }
00103 
00104         if (_r->d_ll.next) {
00105                 _r->d_ll.next->d_ll.prev = _r->d_ll.prev;
00106         } else {
00107                 _d->d_ll.last = _r->d_ll.prev;
00108         }
00109 
00110         _r->d_ll.prev = _r->d_ll.next = 0;
00111         _d->d_ll.n--;
00112 }
00113 
00114 
00115 /*
00116  * Create a new domain structure
00117  * _n is pointer to str representing
00118  * name of the domain, the string is
00119  * not copied, it should point to str
00120  * structure stored in domain list
00121  */
00122 int new_udomain(str* _n, udomain_t** _d)
00123 {
00124         int i;
00125         
00126              /* Must be always in shared memory, since
00127               * the cache is accessed from timer which
00128               * lives in a separate process
00129               */
00130         *_d = (udomain_t*)shm_malloc(sizeof(udomain_t));
00131         if (!(*_d)) {
00132                 LOG(L_ERR, "new_udomain(): No memory left\n");
00133                 return -1;
00134         }
00135         memset(*_d, 0, sizeof(udomain_t));
00136         
00137         (*_d)->table = (hslot_t*)shm_malloc(sizeof(hslot_t) * UDOMAIN_HASH_SIZE);
00138         if (!(*_d)->table) {
00139                 LOG(L_ERR, "new_udomain(): No memory left 2\n");
00140                 shm_free(*_d);
00141                 return -2;
00142         }
00143 
00144         (*_d)->name = _n;
00145         
00146         for(i = 0; i < UDOMAIN_HASH_SIZE; i++) {
00147                 if (init_slot(*_d, &((*_d)->table[i])) < 0) {
00148                         LOG(L_ERR, "new_udomain(): Error while initializing hash table\n");
00149                         shm_free((*_d)->table);
00150                         shm_free(*_d);
00151                         return -3;
00152                 }
00153         }
00154 
00155         lock_init(&(*_d)->lock);
00156         (*_d)->users = 0;
00157         (*_d)->expired = 0;
00158         
00159         return 0;
00160 }
00161 
00162 
00163 /*
00164  * Free all memory allocated for
00165  * the domain
00166  */
00167 void free_udomain(udomain_t* _d)
00168 {
00169         int i;
00170         
00171         lock_udomain(_d);
00172         if (_d->table) {
00173                 for(i = 0; i < UDOMAIN_HASH_SIZE; i++) {
00174                         deinit_slot(_d->table + i);
00175                 }
00176                 shm_free(_d->table);
00177         }
00178         unlock_udomain(_d);
00179         lock_destroy(&_d->lock);/* destroy the lock (required for SYSV sems!)*/
00180 
00181         shm_free(_d);
00182 }
00183 
00184 
00185 /*
00186  * Just for debugging
00187  */
00188 void print_udomain(FILE* _f, udomain_t* _d)
00189 {
00190         struct urecord* r;
00191         fprintf(_f, "---Domain---\n");
00192         fprintf(_f, "name : '%.*s'\n", _d->name->len, ZSW(_d->name->s));
00193         fprintf(_f, "size : %d\n", UDOMAIN_HASH_SIZE);
00194         fprintf(_f, "table: %p\n", _d->table);
00195         fprintf(_f, "d_ll {\n");
00196         fprintf(_f, "    n    : %d\n", _d->d_ll.n);
00197         fprintf(_f, "    first: %p\n", _d->d_ll.first);
00198         fprintf(_f, "    last : %p\n", _d->d_ll.last);
00199         fprintf(_f, "}\n");
00200         /*fprintf(_f, "lock : %d\n", _d->lock); -- can be a structure --andrei*/
00201         if (_d->d_ll.n > 0) {
00202                 fprintf(_f, "\n");
00203                 r = _d->d_ll.first;
00204                 while(r) {
00205                         print_urecord(_f, r);
00206                         r = r->d_ll.next;
00207                 }
00208 
00209 
00210                 fprintf(_f, "\n");
00211         }
00212         fprintf(_f, "---/Domain---\n");
00213 }
00214 
00215 
00216 static struct socket_info* find_socket(str* received)
00217 {
00218         struct sip_uri puri;
00219         param_hooks_t hooks;
00220         struct hostent* he;
00221         struct ip_addr ip;
00222         struct socket_info* si;
00223         param_t* params;
00224         unsigned short port;
00225         char* buf;
00226         int error;
00227 
00228         if (!received) return 0;
00229 
00230         si = 0;
00231         if (parse_uri(received->s, received->len, &puri) < 0) {
00232                 LOG(L_ERR, "find_socket: Error while parsing received URI\n");
00233                 return 0;
00234         }
00235         
00236         if (parse_params(&puri.params, CLASS_URI, &hooks, &params) < 0) {
00237                 LOG(L_ERR, "find_socket: Error while parsing received URI parameters\n");
00238                 return 0;
00239         }
00240 
00241         if (!hooks.uri.dstip || !hooks.uri.dstip->body.s || !hooks.uri.dstip->body.len) goto end;
00242 
00243         buf = (char*)pkg_malloc(hooks.uri.dstip->body.len + 1);
00244         if (!buf) {
00245                 LOG(L_ERR, "find_socket: No memory left\n");
00246                 goto end;
00247         }
00248         memcpy(buf, hooks.uri.dstip->body.s, hooks.uri.dstip->body.len);
00249         buf[hooks.uri.dstip->body.len] = '\0';
00250 
00251         he = resolvehost(buf);
00252         if (he == 0) {
00253                 LOG(L_ERR, "find_socket: Unable to resolve '%s'\n", buf);
00254                 pkg_free(buf);
00255                 goto end;
00256         }
00257         pkg_free(buf);
00258 
00259         if (hooks.uri.dstport && hooks.uri.dstport->body.s && hooks.uri.dstport->body.len) {
00260                 port = str2s(hooks.uri.dstport->body.s, hooks.uri.dstport->body.len, &error);
00261                 if (error != 0) {
00262                         LOG(L_ERR, "find_socket: Unable to convert port number\n");             
00263                         goto end;
00264                 }
00265         } else {
00266                 port = 0;
00267         }
00268 
00269         hostent2ip_addr(&ip, he, 0);
00270         si = find_si(&ip, port, puri.proto);
00271         if (si == 0) {
00272                 LOG(L_ERR, "find_socket: Unable to find socket, using the default one\n");
00273                 goto end;
00274         }
00275         
00276  end:
00277         if (params) free_params(params);
00278         return si;
00279 }
00280 
00281 
00282 
00283 int preload_udomain(udomain_t* _d)
00284 {
00285         db_fld_t columns[] = {
00286                 {.name = uid_col.s,        .type = DB_STR},
00287                 {.name = contact_col.s,    .type = DB_STR},
00288                 {.name = expires_col.s,    .type = DB_DATETIME},
00289                 {.name = q_col.s,          .type = DB_DOUBLE},
00290                 {.name = callid_col.s,     .type = DB_STR},
00291                 {.name = cseq_col.s,       .type = DB_INT},
00292                 {.name = flags_col.s,      .type = DB_BITMAP},
00293                 {.name = user_agent_col.s, .type = DB_STR},
00294                 {.name = received_col.s,   .type = DB_STR},
00295                 {.name = instance_col.s,   .type = DB_STR},
00296                 {.name = aor_col.s,        .type = DB_STR},
00297                 {.name = server_id_col.s,  .type = DB_INT},
00298                 {.name = avp_column,       .type = DB_STR}, /* Must be the last element in the array */
00299                 {.name = NULL}
00300         };
00301 
00302         db_res_t* res = NULL;
00303         db_rec_t* rec;
00304         db_cmd_t* get_all = NULL;
00305 
00306         struct socket_info* sock;
00307         str callid, ua, instance, aor;
00308         str* receivedp;
00309         qvalue_t q;
00310         unsigned int flags;
00311         urecord_t* r;
00312         ucontact_t* c;
00313 
00314         get_all = db_cmd(DB_GET, db, _d->name->s, columns, NULL, NULL);
00315         if (get_all == NULL) {
00316                 ERR("usrloc: Error while compiling DB_GET command\n");
00317                 return -1;
00318         }
00319         if (db_setopt(get_all, "fetch_all", 0) < 0) {
00320                 ERR("usrloc: Error while disabling 'fetch_all' database option\n");
00321         }
00322 
00323         if (db_exec(&res, get_all) < 0) goto error;
00324 
00325         rec = db_first(res);
00326         if (rec == NULL) {
00327                 DBG("preload_udomain(): Table is empty\n");
00328                 db_res_free(res);
00329                 db_cmd_free(get_all);
00330                 return 0;
00331         }
00332 
00333         lock_udomain(_d);
00334         get_act_time();
00335 
00336         for(; rec != NULL; rec = db_next(res)) {
00337                 /* UID column must never be NULL */
00338                 if (rec->fld[0].flags & DB_NULL) {
00339                         LOG(L_CRIT, "preload_udomain: ERROR: bad uid "
00340                                 "record in table %.*s, skipping...\n", 
00341                                 _d->name->len, _d->name->s);
00342                         continue;
00343                 }
00344 
00345                 /* Contact column must never be NULL */
00346                 if (rec->fld[1].flags & DB_NULL) {
00347                         LOG(L_CRIT, "ERROR: Bad contact for uid %.*s in table %.*s, skipping\n",
00348                                 rec->fld[0].v.lstr.len, rec->fld[0].v.lstr.s,
00349                                 _d->name->len, _d->name->s);
00350                         continue;
00351                 }
00352 
00353                 /* We only skip expired contacts if db_skip_delete is enabled. If
00354                  * db_skip_delete is disabled then we must load expired contacts
00355                  * in memory so that the timer can delete them later.
00356                  */
00357                 if (db_skip_delete && (rec->fld[2].v.time < act_time)) {
00358                         DBG("preload_udomain: Skipping expired contact\n");
00359                         continue;
00360                 }
00361 
00362                 q = double2q(rec->fld[3].v.dbl);
00363 
00364                 if (rec->fld[4].flags & DB_NULL) {
00365                         callid.s = NULL;
00366                         callid.len = 0;
00367                 } else {
00368                         callid = rec->fld[4].v.lstr;
00369                 }
00370 
00371                 if (rec->fld[7].flags & DB_NULL) {
00372                         ua.s = NULL;
00373                         ua.len = 0;
00374                 } else {
00375                         ua = rec->fld[7].v.lstr;
00376                 }
00377 
00378                 if (rec->fld[8].flags & DB_NULL) {
00379                         receivedp = 0;
00380                         sock = 0;
00381                 } else {
00382                         receivedp = &rec->fld[8].v.lstr;
00383                         sock = find_socket(receivedp);
00384                 }
00385 
00386                 if (rec->fld[9].flags & DB_NULL) {
00387                         instance.s = NULL;
00388                         instance.len = 0;
00389                 } else {
00390                         instance = rec->fld[9].v.lstr;
00391                 }
00392 
00393                 if (rec->fld[10].flags & DB_NULL) {
00394                         aor.s = NULL;
00395                         aor.len = 0;
00396                 } else {
00397                         aor = rec->fld[10].v.lstr;
00398                 }
00399 
00400                 if (get_urecord(_d, &rec->fld[0].v.lstr, &r) > 0) {
00401                         if (mem_insert_urecord(_d, &rec->fld[0].v.lstr, &r) < 0) {
00402                                 LOG(L_ERR, "preload_udomain(): Can't create a record\n");
00403                                 unlock_udomain(_d);
00404                                 goto error;
00405                         }
00406                 }
00407 
00408                 flags = rec->fld[6].v.bitmap;
00409                 if (rec->fld[11].v.int4 != server_id) {
00410                         /* FIXME: this should not be hardcoded here this way */
00411                         /* This is a records from another SIP server instance, mark
00412                          * it as in memory only because the other SIP server is responsible
00413                          * for updating the record in database
00414                          */
00415                         flags |= FL_MEM;
00416                 }
00417 
00418                 if (mem_insert_ucontact(r, &aor, &rec->fld[1].v.lstr, rec->fld[2].v.int4, 
00419                                                                 q, &callid, rec->fld[5].v.int4, flags, &c, &ua, receivedp, 
00420                                                                 sock, &instance, rec->fld[11].v.int4) < 0) {
00421                         LOG(L_ERR, "preload_udomain(): Error while inserting contact\n");
00422                         unlock_udomain(_d);
00423                         goto error;
00424                 }
00425 
00426                 if (use_reg_avps() && ((rec->fld[12].flags & DB_NULL) != DB_NULL)) {
00427                         c->avps = deserialize_avps(&rec->fld[12].v.lstr);
00428                                 
00429                 }
00430 
00431                      /* We have to do this, because insert_ucontact sets state to CS_NEW
00432                       * and we have the contact in the database already
00433                           * we also store zombies in database so we have to restore
00434                           * the correct state
00435                       */
00436                 c->state = CS_SYNC;
00437         }
00438 
00439         unlock_udomain(_d);
00440         db_res_free(res);
00441         db_cmd_free(get_all);
00442         return 0;
00443 
00444  error:
00445         if (res) db_res_free(res);
00446         if (get_all) db_cmd_free(get_all);
00447         return -1;
00448 }
00449 
00450 
00451 /*
00452  * Insert a new record into domain
00453  */
00454 int mem_insert_urecord(udomain_t* _d, str* _uid, struct urecord** _r)
00455 {
00456         int sl;
00457         
00458         if (new_urecord(_d->name, _uid, _r) < 0) {
00459                 LOG(L_ERR, "insert_urecord(): Error while creating urecord\n");
00460                 return -1;
00461         }
00462 
00463         sl = hash_func(_d, (unsigned char*)_uid->s, _uid->len);
00464         slot_add(&_d->table[sl], *_r);
00465         udomain_add(_d, *_r);
00466         _d->users++;
00467         return 0;
00468 }
00469 
00470 
00471 /*
00472  * Remove a record from domain
00473  */
00474 void mem_delete_urecord(udomain_t* _d, struct urecord* _r)
00475 {
00476         if (_r->watchers == 0) {
00477                 udomain_remove(_d, _r);
00478                 slot_rem(_r->slot, _r);
00479                 free_urecord(_r);
00480                 _d->users--; /* FIXME */
00481         }
00482                 
00483 }
00484 
00485 
00486 int timer_udomain(udomain_t* _d)
00487 {
00488         struct urecord* ptr, *t;
00489 
00490         lock_udomain(_d);
00491 
00492         ptr = _d->d_ll.first;
00493 
00494         while(ptr) {
00495                 if (timer_urecord(ptr) < 0) {
00496                         LOG(L_ERR, "timer_udomain(): Error in timer_urecord\n");
00497                         unlock_udomain(_d);
00498                         return -1;
00499                 }
00500                 
00501                      /* Remove the entire record
00502                       * if it is empty
00503                       */
00504                 if (ptr->contacts == 0) {
00505                         t = ptr;
00506                         ptr = ptr->d_ll.next;
00507                         mem_delete_urecord(_d, t);
00508                 } else {
00509                         ptr = ptr->d_ll.next;
00510                 }
00511         }
00512         
00513         unlock_udomain(_d);
00514 /*      process_del_list(_d->name); */
00515 /*      process_ins_list(_d->name); */
00516         return 0;
00517 }
00518 
00519 
00520 /*
00521  * Get lock
00522  */
00523 void lock_udomain(udomain_t* _d)
00524 {
00525         lock_get(&_d->lock);
00526         cur_cmd = _d->db_cmd_idx;
00527 }
00528 
00529 
00530 /*
00531  * Release lock
00532  */
00533 void unlock_udomain(udomain_t* _d)
00534 {
00535         lock_release(&_d->lock);
00536 }
00537 
00538 
00539 /*
00540  * Create and insert a new record
00541  */
00542 int insert_urecord(udomain_t* _d, str* _uid, struct urecord** _r)
00543 {
00544         if (mem_insert_urecord(_d, _uid, _r) < 0) {
00545                 LOG(L_ERR, "insert_urecord(): Error while inserting record\n");
00546                 return -1;
00547         }
00548         return 0;
00549 }
00550 
00551 
00552 /*
00553  * Obtain a urecord pointer if the urecord exists in domain
00554  */
00555 int get_urecord(udomain_t* _d, str* _uid, struct urecord** _r)
00556 {
00557         int sl, i;
00558         urecord_t* r;
00559 
00560         sl = hash_func(_d, (unsigned char*)_uid->s, _uid->len);
00561 
00562         r = _d->table[sl].first;
00563 
00564         for(i = 0; i < _d->table[sl].n; i++) {
00565                 if ((r->uid.len == _uid->len) && !memcmp(r->uid.s, _uid->s, _uid->len)) {
00566                         *_r = r;
00567                         return 0;
00568                 }
00569 
00570                 r = r->s_ll.next;
00571         }
00572 
00573         return 1;   /* Nothing found */
00574 }
00575 
00576 
00577 /*
00578  * Delete a urecord from domain
00579  */
00580 int delete_urecord(udomain_t* _d, str* _uid)
00581 {
00582         struct ucontact* c, *t;
00583         struct urecord* r;
00584 
00585         if (get_urecord(_d, _uid, &r) > 0) {
00586                 return 0;
00587         }
00588 
00589         c = r->contacts;
00590         while(c) {
00591                 t = c;
00592                 c = c->next;
00593                 if (delete_ucontact(r, t) < 0) {
00594                         LOG(L_ERR, "delete_urecord(): Error while deleting contact\n");
00595                         return -1;
00596                 }
00597         }
00598         release_urecord(r);
00599         return 0;
00600 }

Generated on Thu Sep 9 04:16:01 2010 for SIPExpressRouter by  doxygen 1.3.9.1