/* * $Id: dlg_hash.c 2514 2007-07-25 06:54:05Z bogdan_iancu $ * * Copyright (C) 2006 Voice System SRL * * This file is part of openser, a free SIP server. * * openser 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 * * openser 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * History: * -------- * 2006-04-14 initial version (bogdan) * 2007-03-06 syncronized state machine added for dialog state. New tranzition * design based on events; removed num_1xx and num_2xx (bogdan) */ #include #include #include "../../dprint.h" #include "../../ut.h" #include "../../hash_func.h" #include "../../mi/mi.h" #include "dlg_hash.h" #define MAX_LDG_LOCKS 2048 #define MIN_LDG_LOCKS 2 static struct dlg_table *d_table = 0; #define dlg_lock(_table, _entry) \ lock_set_get( (_table)->locks, (_entry)->lock_idx); #define dlg_unlock(_table, _entry) \ lock_set_release( (_table)->locks, (_entry)->lock_idx); int init_dlg_table(unsigned int size) { unsigned int n; unsigned int i; d_table = (struct dlg_table*)shm_malloc ( sizeof(struct dlg_table) + size*sizeof(struct dlg_entry)); if (d_table==0) { LOG(L_ERR, "ERROR:dialog:init_dlg_table: no more shm mem (1)\n"); goto error0; } memset( d_table, 0, sizeof(struct dlg_table) ); d_table->size = size; d_table->entries = (struct dlg_entry*)(d_table+1); n = (size=MIN_LDG_LOCKS ; n-- ) { d_table->locks = lock_set_alloc(n); if (d_table->locks==0) continue; if (lock_set_init(d_table->locks)==0) { lock_set_dealloc(d_table->locks); d_table->locks = 0; continue; } d_table->locks_no = n; break; } if (d_table->locks==0) { LOG(L_ERR,"ERROR:dialog:init_dlg_table: unable to allocted at least " "%d locks for the hash table\n",MIN_LDG_LOCKS); goto error1; } for( i=0 ; ientries[i]), 0, sizeof(struct dlg_entry) ); d_table->entries[i].next_id = rand(); d_table->entries[i].lock_idx = i % d_table->locks_no; } return 0; error1: shm_free( d_table ); error0: return -1; } static inline void destroy_dlg(struct dlg_cell *dlg) { DBG("DBUG:dialog:destroy_dlg: destroing dialog %p\n",dlg); if (dlg->to_tag.s && dlg->to_tag.len) shm_free(dlg->to_tag.s); if (dlg->cbs.first) destroy_dlg_callbacks_list(dlg->cbs.first); shm_free(dlg); } void destroy_dlg_table() { struct dlg_cell *dlg, *l_dlg; unsigned int i; if (d_table==0) return; if (d_table->locks) { lock_set_destroy(d_table->locks); lock_set_dealloc(d_table->locks); } for( i=0 ; isize; i++ ) { dlg = d_table->entries[i].first; while (dlg) { l_dlg = dlg; dlg = dlg->next; destroy_dlg(l_dlg); } } shm_free(d_table); d_table = 0; return; } struct dlg_cell* build_new_dlg( str *callid, str *from_uri, str *to_uri, str *from_tag) { struct dlg_cell *dlg; int len; char *p; len = sizeof(struct dlg_cell) + callid->len + from_uri->len + to_uri->len + from_tag->len; dlg = (struct dlg_cell*)shm_malloc( len ); if (dlg==0) { LOG(L_ERR,"ERROR:dialog:build_new_dlg: no more shm mem (%d)\n",len); return 0; } memset( dlg, 0, len); dlg->state = DLG_STATE_UNCONFIRMED; dlg->h_entry = core_hash( callid, from_tag->len?from_tag:0, d_table->size); DBG("DEBUG:dialog:build_new_dlg: new dialog on hash %u\n",dlg->h_entry); p = (char*)(dlg+1); dlg->callid.s = p; dlg->callid.len = callid->len; memcpy( p, callid->s, callid->len); p += callid->len; dlg->from_uri.s = p; dlg->from_uri.len = from_uri->len; memcpy( p, from_uri->s, from_uri->len); p += from_uri->len; dlg->to_uri.s = p; dlg->to_uri.len = to_uri->len; memcpy( p, to_uri->s, to_uri->len); p += to_uri->len; dlg->from_tag.s = p; dlg->from_tag.len = from_tag->len; memcpy( p, from_tag->s, from_tag->len); p += from_tag->len; if ( p!=(((char*)dlg)+len) ) { LOG(L_CRIT,"BUG:dialog:build_new_dlg: buffer overflow\n"); shm_free(dlg); return 0; } return dlg; } int dlg_set_totag(struct dlg_cell *dlg, str *tag) { dlg->to_tag.s = (char*)shm_malloc( tag->len ); if (dlg->to_tag.s==0) { LOG(L_ERR,"ERROR:dialog:dlg_set_totag: no more shm mem (%d)\n", tag->len); return -1; } memcpy( dlg->to_tag.s, tag->s, tag->len); dlg->to_tag.len = tag->len; return 0; } struct dlg_cell* lookup_dlg( unsigned int h_entry, unsigned int h_id) { struct dlg_cell *dlg; struct dlg_entry *d_entry; if (h_entry>=d_table->size) goto not_found; d_entry = &(d_table->entries[h_entry]); dlg_lock( d_table, d_entry); for( dlg=d_entry->first ; dlg ; dlg=dlg->next ) { if (dlg->h_id == h_id) { if (dlg->state==DLG_STATE_DELETED) { dlg_unlock( d_table, d_entry); goto not_found; } dlg->ref++; dlg_unlock( d_table, d_entry); DBG("DEBUG:dialog:lookup_dlg: dialog id=%u found on entry %u\n", h_id, h_entry); return dlg; } } dlg_unlock( d_table, d_entry); not_found: DBG("DEBUG:dialog:lookup_dlg: no dialog id=%u found on entry %u\n", h_id, h_entry); return 0; } void link_dlg(struct dlg_cell *dlg, int n) { struct dlg_entry *d_entry; d_entry = &(d_table->entries[dlg->h_entry]); dlg_lock( d_table, d_entry); dlg->h_id = d_entry->next_id++; if (d_entry->first==0) { d_entry->first = d_entry->last = dlg; } else { d_entry->last->next = dlg; dlg->prev = d_entry->last; d_entry->last = dlg; } dlg->ref += 1 + n; dlg_unlock( d_table, d_entry); return; } static inline void unlink_unsafe_dlg(struct dlg_entry *d_entry, struct dlg_cell *dlg) { if (dlg->next) dlg->next->prev = dlg->prev; else d_entry->last = dlg->prev; if (dlg->prev) dlg->prev->next = dlg->next; else d_entry->first = dlg->next; dlg->next = dlg->prev = 0; return; } #define ref_dlg_unsafe(_dlg,_cnt) \ (_dlg)->ref += (_cnt) #define unref_dlg_unsafe(_dlg,_cnt,_d_entry) \ do { \ (_dlg)->ref -= (_cnt); \ DBG("DBUG:dialog:unref_dlg: unref dlg %p with %d -> %d\n",\ (_dlg),(_cnt),(_dlg)->ref);\ if ((_dlg)->ref<=0) { \ unlink_unsafe_dlg( _d_entry, _dlg);\ destroy_dlg(_dlg);\ }\ }while(0) void unref_dlg(struct dlg_cell *dlg, int cnt) { struct dlg_entry *d_entry; d_entry = &(d_table->entries[dlg->h_entry]); dlg_lock( d_table, d_entry); unref_dlg_unsafe( dlg, cnt, d_entry); dlg_unlock( d_table, d_entry); } void next_state_dlg(struct dlg_cell *dlg, int event, int *old_state, int *new_state, int *unref) { struct dlg_entry *d_entry; d_entry = &(d_table->entries[dlg->h_entry]); dlg_lock( d_table, d_entry); *old_state = dlg->state; switch (event) { case DLG_EVENT_TDEL: switch (dlg->state) { case DLG_STATE_UNCONFIRMED: case DLG_STATE_EARLY: dlg->state = DLG_STATE_DELETED; unref_dlg_unsafe(dlg,1,d_entry); *unref = 1; break; case DLG_STATE_CONFIRMED_NA: case DLG_STATE_CONFIRMED: case DLG_STATE_DELETED: unref_dlg_unsafe(dlg,1,d_entry); break; default: LOG(L_CRIT,"BUG:next_state_dlg: bogus event %d in " "state %d\n",event,dlg->state); } break; case DLG_EVENT_RPL1xx: switch (dlg->state) { case DLG_STATE_UNCONFIRMED: case DLG_STATE_EARLY: dlg->state = DLG_STATE_EARLY; break; default: LOG(L_CRIT,"BUG:next_state_dlg: bogus event %d in " "state %d\n",event,dlg->state); } break; case DLG_EVENT_RPL3xx: switch (dlg->state) { case DLG_STATE_UNCONFIRMED: case DLG_STATE_EARLY: dlg->state = DLG_STATE_DELETED; *unref = 1; break; default: LOG(L_CRIT,"BUG:next_state_dlg: bogus event %d in " "state %d\n",event,dlg->state); } break; case DLG_EVENT_RPL2xx: switch (dlg->state) { case DLG_STATE_DELETED: ref_dlg_unsafe(dlg,1); case DLG_STATE_UNCONFIRMED: case DLG_STATE_EARLY: dlg->state = DLG_STATE_CONFIRMED_NA; break; case DLG_STATE_CONFIRMED_NA: case DLG_STATE_CONFIRMED: break; default: LOG(L_CRIT,"BUG:next_state_dlg: bogus event %d in " "state %d\n",event,dlg->state); } break; case DLG_EVENT_REQACK: switch (dlg->state) { case DLG_STATE_CONFIRMED_NA: dlg->state = DLG_STATE_CONFIRMED; break; case DLG_STATE_CONFIRMED: break; default: LOG(L_CRIT,"BUG:next_state_dlg: bogus event %d in " "state %d\n",event,dlg->state); } break; case DLG_EVENT_REQBYE: switch (dlg->state) { case DLG_STATE_CONFIRMED_NA: case DLG_STATE_CONFIRMED: dlg->state = DLG_STATE_DELETED; *unref = 1; break; default: LOG(L_CRIT,"BUG:next_state_dlg: bogus event %d in " "state %d\n",event,dlg->state); } break; case DLG_EVENT_REQPRACK: switch (dlg->state) { case DLG_STATE_EARLY: case DLG_STATE_CONFIRMED_NA: break; default: LOG(L_CRIT,"BUG:next_state_dlg: bogus event %d in " "state %d\n",event,dlg->state); } break; case DLG_EVENT_REQ: switch (dlg->state) { case DLG_STATE_CONFIRMED_NA: case DLG_STATE_CONFIRMED: break; default: LOG(L_CRIT,"BUG:next_state_dlg: bogus event %d in " "state %d\n",event,dlg->state); } break; default: LOG(L_CRIT,"BUG:next_state_dlg: unknown event %d\n", event); } *new_state = dlg->state; dlg_unlock( d_table, d_entry); DBG("DEBUG:dialog:next_state_dlg: dialog %p changed from state %d to " "state %d, due event %d\n",dlg,*old_state,*new_state,event); } struct mi_root * mi_print_dlgs(struct mi_root *cmd_tree, void *param ) { struct dlg_cell *dlg; struct mi_node* rpl = NULL, *node= NULL; struct mi_attr* attr= NULL; struct mi_root* rpl_tree= NULL; unsigned int i; int len; char* p; rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN); if (rpl_tree==0) return 0; rpl = &rpl_tree->node; for( i=0 ; isize ; i++ ) { dlg_lock( d_table, &(d_table->entries[i]) ); for( dlg=d_table->entries[i].first ; dlg ; dlg=dlg->next ) { node = add_mi_node_child(rpl, 0, "dialog",6 , 0, 0 ); if (node==0) goto error; attr = addf_mi_attr( node, 0, "hash", 4, "%u:%u", dlg->h_entry, dlg->h_id ); if (attr==0) goto error; p= int2str((unsigned long)dlg->state, &len); attr = add_mi_attr( node, MI_DUP_VALUE, "state", 5, p, len); if (attr==0) goto error; p= int2str((unsigned long)dlg->lifetime, &len); attr = add_mi_attr( node, MI_DUP_VALUE, "timeout", 7, p, len); if (attr==0) goto error; attr = add_mi_attr(node, MI_DUP_VALUE, "callid", 6, dlg->callid.s, dlg->callid.len); if(attr == 0) goto error; attr = add_mi_attr(node, MI_DUP_VALUE, "from_uri", 8, dlg->from_uri.s, dlg->from_uri.len); if(attr == 0) goto error; attr = add_mi_attr(node, MI_DUP_VALUE, "from_tag", 8, dlg->from_tag.s, dlg->from_tag.len); if(attr == 0) goto error; attr = add_mi_attr(node, MI_DUP_VALUE, "to_uri", 6, dlg->to_uri.s, dlg->to_uri.len); if(attr == 0) goto error; attr = add_mi_attr(node, MI_DUP_VALUE, "to_tag", 6, dlg->to_tag.s, dlg->to_tag.len); if(attr == 0) goto error; } dlg_unlock( d_table, &(d_table->entries[i]) ); } return rpl_tree; error: dlg_unlock( d_table, &(d_table->entries[i]) ); LOG(L_ERR,"ERROR:mi_ps: failed to add node\n"); free_mi_tree(rpl_tree); return 0; }