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; d_entry->cnt++; LM_DBG("ref dlg %p with %d -> %d in h_entry %p - %d \n", dlg, n+1, dlg->ref, d_entry,dlg->h_entry); dlg_unlock( d_table, d_entry); return; }
/*! * \brief Release hash table slot by call-id * \param callid call-id value */ void dlg_hash_release(str *callid) { unsigned int he; struct dlg_entry *d_entry; he = core_hash(callid, 0, d_table->size); d_entry = &(d_table->entries[he]); dlg_unlock(d_table, d_entry); }
static int internal_mi_print_dlgs(struct mi_root *rpl_tree,struct mi_node *rpl, int with_context, unsigned int idx, unsigned int cnt) { struct dlg_cell *dlg; unsigned int i; unsigned int n; unsigned int total; char *p; total = 0; if (cnt) { for(i=0;i<d_table->size ; total+=d_table->entries[i++].cnt ); p = int2str((unsigned long)total, (int*)&n); if (add_mi_node_child(rpl,MI_DUP_VALUE,"dlg_counter",11,p,n)==0) return -1; } LM_DBG("printing %i dialogs, idx=%d, cnt=%d\n", total,idx,cnt); rpl->flags |= MI_NOT_COMPLETED; for( i=0,n=0 ; i<d_table->size ; i++ ) { dlg_lock( d_table, &(d_table->entries[i]) ); for( dlg=d_table->entries[i].first ; dlg ; dlg=dlg->next ) { if (cnt && n<idx) {n++;continue;} if (internal_mi_print_dlg(rpl, dlg, with_context)!=0) goto error; n++; if (cnt && n>=idx+cnt) { dlg_unlock( d_table, &(d_table->entries[i]) ); return 0; } if ( (n % 50) == 0 ) flush_mi_tree(rpl_tree); } dlg_unlock( d_table, &(d_table->entries[i]) ); } return 0; error: dlg_unlock( d_table, &(d_table->entries[i]) ); LM_ERR("failed to print dialog\n"); return -1; }
/*! * \brief Unreference a dialog with locking * \see unref_dlg_unsafe * \param dlg dialog * \param cnt decrement for the reference counter */ void dlg_unref(dlg_cell_t *dlg, unsigned int cnt) { dlg_entry_t *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 unref_dlg(struct dlg_cell *dlg, unsigned 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); }
int pv_get_dlg_variable(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { dlg_cell_t *dlg; str * value; str spv; if (param==NULL || param->pvn.type!=PV_NAME_INTSTR || param->pvn.u.isname.type!=AVP_NAME_STR || param->pvn.u.isname.name.s.s==NULL) { LM_CRIT("BUG - bad parameters\n"); return -1; } /* Retrieve the dialog for current message */ dlg=dlg_get_msg_dialog( msg); if (dlg) { /* Lock the dialog */ dlg_lock(d_table, &(d_table->entries[dlg->h_entry])); } else { /* Verify the local list */ get_local_varlist_pointer(msg, 0); } /* dcm: todo - the value should be cloned for safe usage */ value = get_dlg_variable_unsafe(dlg, ¶m->pvn.u.isname.name.s); spv.s = NULL; if(value) { spv.len = pv_get_buffer_size(); if(spv.len<value->len+1) { LM_ERR("pv buffer too small (%d) - needed %d\n", spv.len, value->len); } else { spv.s = pv_get_buffer(); strncpy(spv.s, value->s, value->len); spv.len = value->len; spv.s[spv.len] = '\0'; } } print_lists(dlg); /* unlock dialog */ if (dlg) { dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); dlg_release(dlg); } if (spv.s) return pv_get_strval(msg, param, res, &spv); return pv_get_null(msg, param, res); }
/*! * \brief Check if a dialog belongs to a profile * \param msg SIP message * \param profile dialog profile table * \param value value * \return 1 on success, -1 on failure */ int is_dlg_in_profile(struct sip_msg *msg, struct dlg_profile_table *profile, str *value) { struct dlg_cell *dlg; struct dlg_profile_link *linker; struct dlg_entry *d_entry; int ret; /* get current dialog */ dlg = dlg_get_msg_dialog(msg); if (dlg==NULL) return -1; ret = -1; /* check the dialog linkers */ d_entry = &d_table->entries[dlg->h_entry]; dlg_lock( d_table, d_entry); for( linker=dlg->profile_links ; linker ; linker=linker->next) { if (linker->profile==profile) { if (profile->has_value==0) { dlg_unlock( d_table, d_entry); ret = 1; goto done; } else if (value && value->len==linker->hash_linker.value.len && memcmp(value->s,linker->hash_linker.value.s,value->len)==0){ dlg_unlock( d_table, d_entry); ret = 1; goto done; } /* allow further search - maybe the dialog is inserted twice in * the same profile, but with different values -bogdan */ } } dlg_unlock( d_table, d_entry); done: dlg_release(dlg); return ret; }
/*! * \brief Unreference a dialog with locking * \see unref_dlg_unsafe * \param dlg dialog * \param cnt decrement for the reference counter */ void dlg_unref_helper(dlg_cell_t *dlg, unsigned int cnt, const char *fname, int fline) { dlg_entry_t *d_entry; LM_DBG("unref op on %p with %d from %s:%d\n", dlg, cnt, fname, fline); 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); }
/*! * \brief Update or set the Contact for an existing dialog * \param dlg dialog * \param leg must be either DLG_CALLER_LEG, or DLG_CALLEE_LEG * \param ct Contact of caller or callee * \return 0 on success, -1 on failure */ int dlg_update_contact(struct dlg_cell * dlg, unsigned int leg, str *ct) { dlg_entry_t *d_entry; d_entry = &(d_table->entries[dlg->h_entry]); dlg_lock(d_table, d_entry); if ( dlg->contact[leg].s ) { if(dlg->contact[leg].len == ct->len && memcmp(dlg->contact[leg].s, ct->s, ct->len)==0) { LM_DBG("same contact for leg[%d] - [%.*s]\n", leg, dlg->contact[leg].len, dlg->contact[leg].s); goto done; } if (dlg->contact[leg].len < ct->len) { shm_free(dlg->contact[leg].s); dlg->contact[leg].s = (char*)shm_malloc(ct->len); if (dlg->contact[leg].s==NULL) goto error; } } else { dlg->contact[leg].s = (char*)shm_malloc(ct->len); if (dlg->contact[leg].s==NULL) goto error; } memcpy( dlg->contact[leg].s, ct->s, ct->len ); dlg->contact[leg].len = ct->len; LM_DBG("contact of leg[%d] is %.*s\n", leg, dlg->contact[leg].len, dlg->contact[leg].s); done: dlg_unlock(d_table, d_entry); return 0; error: dlg_unlock(d_table, d_entry); LM_ERR("not more shm mem\n"); return -1; }
/*! * \brief Helper function that outputs all dialogs via the RPC interface * \see rpc_print_dlgs * \param rpc RPC node that should be filled * \param c RPC void pointer * \param with_context if 1 then the dialog context will be also printed */ static void internal_rpc_print_dlgs(rpc_t *rpc, void *c, int with_context) { dlg_cell_t *dlg; unsigned int i; for( i=0 ; i<d_table->size ; i++ ) { dlg_lock( d_table, &(d_table->entries[i]) ); for( dlg=d_table->entries[i].first ; dlg ; dlg=dlg->next ) { internal_rpc_print_dlg(rpc, c, dlg, with_context); } dlg_unlock( d_table, &(d_table->entries[i]) ); } }
/*! * \brief Helper function that output all dialogs via the MI interface * \see mi_print_dlgs * \param rpl MI node that should be filled * \param with_context if 1 then the dialog context will be also printed * \return 0 on success, -1 on failure */ static int internal_mi_print_dlgs(struct mi_node *rpl, int with_context) { struct dlg_cell *dlg; unsigned int i; LM_DBG("printing %i dialogs\n", d_table->size); for( i=0 ; i<d_table->size ; i++ ) { dlg_lock( d_table, &(d_table->entries[i]) ); for( dlg=d_table->entries[i].first ; dlg ; dlg=dlg->next ) { if (internal_mi_print_dlg(rpl, dlg, with_context)!=0) goto error; } dlg_unlock( d_table, &(d_table->entries[i]) ); } return 0; error: dlg_unlock( d_table, &(d_table->entries[i]) ); LM_ERR("failed to print dialog\n"); return -1; }
int set_dlg_variable(struct dlg_cell *dlg, str *key, str *val) { if( !dlg || !key || key->len > strlen(key->s) || (val && val->len > strlen(val->s))) { LM_ERR("BUG - bad parameters\n"); return -1; } dlg_lock(d_table, &(d_table->entries[dlg->h_entry])); if( !val) { if (set_dlg_variable_unsafe(dlg, key, NULL)!=0) { LM_ERR("failed to delete dialog variable <%.*s>\n", key->len,key->s); goto error; } } else { if (set_dlg_variable_unsafe(dlg, key, val)!=0) { LM_ERR("failed to store dialog values <%.*s>\n",key->len,key->s); goto error; } } dlg->dflags &= DLG_FLAG_CHANGED_VARS; dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); if ( dlg_db_mode==DB_MODE_REALTIME ) update_dialog_dbinfo(dlg); print_lists(dlg); return 0; error: dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); return -1; }
str * get_dlg_variable(struct dlg_cell *dlg, str *key) { str* var = NULL; if( !dlg || !key || key->len > strlen(key->s)) { LM_ERR("BUG - bad parameters\n"); return NULL; } dlg_lock(d_table, &(d_table->entries[dlg->h_entry])); var = get_dlg_variable_unsafe( dlg, key); dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); return var; }
/*! * \brief Helper function that outputs a dialog via the RPC interface * \see rpc_print_dlgs * \param rpc RPC node that should be filled * \param c RPC void pointer * \param with_context if 1 then the dialog context will be also printed */ static void internal_rpc_print_single_dlg(rpc_t *rpc, void *c, int with_context) { str callid, from_tag; dlg_entry_t *d_entry; dlg_cell_t *dlg; unsigned int h_entry; if (rpc->scan(c, ".S.S", &callid, &from_tag) < 2) return; h_entry = core_hash( &callid, 0, d_table->size); d_entry = &(d_table->entries[h_entry]); dlg_lock( d_table, d_entry); for( dlg = d_entry->first ; dlg ; dlg = dlg->next ) { if (match_downstream_dialog( dlg, &callid, &from_tag)==1) { internal_rpc_print_dlg(rpc, c, dlg, with_context); } } dlg_unlock( d_table, d_entry); }
/** * clean old unconfirmed dialogs * */ int dlg_clean_run(ticks_t ti) { unsigned int i; unsigned int tm; dlg_cell_t *dlg; dlg_cell_t *tdlg; tm = (unsigned int)time(NULL); for(i=0; i<d_table->size; i++) { dlg_lock(d_table, &d_table->entries[i]); dlg = d_table->entries[i].first; while (dlg) { tdlg = dlg; dlg = dlg->next; if(tdlg->state==DLG_STATE_UNCONFIRMED && tdlg->init_ts>0 && tdlg->init_ts<tm-dlg_early_timeout) { /* dialog in early state older than 5min */ LM_NOTICE("dialog in early state is too old (%p ref %d)\n", tdlg, tdlg->ref); unlink_unsafe_dlg(&d_table->entries[i], tdlg); destroy_dlg(tdlg); } if(tdlg->state==DLG_STATE_CONFIRMED_NA && tdlg->start_ts>0 && tdlg->start_ts<tm-dlg_noack_timeout) { if(update_dlg_timer(&tdlg->tl, 10)<0) { LM_ERR("failed to update dialog lifetime in long non-ack state\n"); } tdlg->lifetime = 10; tdlg->dflags |= DLG_FLAG_CHANGED; } if(tdlg->state==DLG_STATE_DELETED && tdlg->end_ts>0 && tdlg->end_ts<tm-dlg_end_timeout) { /* dialog in deleted state older than 5min */ LM_NOTICE("dialog in delete state is too old (%p ref %d)\n", tdlg, tdlg->ref); unlink_unsafe_dlg(&d_table->entries[i], tdlg); destroy_dlg(tdlg); } } dlg_unlock(d_table, &d_table->entries[i]); } return 0; }
/*! * \brief Link a dialog profile * \param linker dialog linker * \param dlg dialog cell */ static void link_dlg_profile(struct dlg_profile_link *linker, struct dlg_cell *dlg) { unsigned int hash; struct dlg_profile_entry *p_entry; struct dlg_entry *d_entry; /* add the linker to the dialog */ /* FIXME zero h_id is not 100% for testing if the dialog is inserted * into the hash table -> we need circular lists -bogdan */ if (dlg->h_id) { d_entry = &d_table->entries[dlg->h_entry]; dlg_lock( d_table, d_entry); linker->next = dlg->profile_links; dlg->profile_links =linker; linker->hash_linker.dlg = dlg; dlg_unlock( d_table, d_entry); } else { linker->next = dlg->profile_links; dlg->profile_links =linker; linker->hash_linker.dlg = dlg; } /* calculate the hash position */ hash = calc_hash_profile(&linker->hash_linker.value, dlg, linker->profile); linker->hash_linker.hash = hash; /* insert into profile hash table */ p_entry = &linker->profile->entries[hash]; lock_get( &linker->profile->lock ); if (p_entry->first) { linker->hash_linker.prev = p_entry->first->prev; linker->hash_linker.next = p_entry->first; p_entry->first->prev->next = &linker->hash_linker; p_entry->first->prev = &linker->hash_linker; } else { p_entry->first = linker->hash_linker.next = linker->hash_linker.prev = &linker->hash_linker; } p_entry->content ++; lock_release( &linker->profile->lock ); }
void print_all_dlgs() { //print all dialog information - this is just for testing and is set to happen every 10 seconds struct dlg_cell *dlg; unsigned int i; LM_DBG("********************"); LM_DBG("printing %i dialogs\n", d_table->size); for (i = 0; i < d_table->size; i++) { dlg_lock(d_table, &(d_table->entries[i])); for (dlg = d_table->entries[i].first; dlg; dlg = dlg->next) { internal_print_all_dlg(dlg); } dlg_unlock(d_table, &(d_table->entries[i])); } LM_DBG("********************"); }
int dmq_send_all_dlgs(dmq_node_t* dmq_node) { int index; dlg_entry_t entry; dlg_cell_t *dlg; LM_DBG("sending all dialogs \n"); for(index = 0; index< d_table->size; index++){ /* lock the whole entry */ entry = (d_table->entries)[index]; dlg_lock( d_table, &entry); for(dlg = entry.first; dlg != NULL; dlg = dlg->next){ dlg->dflags |= DLG_FLAG_CHANGED_PROF; dlg_dmq_replicate_action(DLG_DMQ_UPDATE, dlg, 0, dmq_node); } dlg_unlock( d_table, &entry); } return 0; }
/*! * \brief Link a dialog profile * \param linker dialog linker * \param dlg dialog cell */ static void link_dlg_profile(struct dlg_profile_link *linker, struct dlg_cell *dlg) { struct dlg_entry *d_entry; /* add the linker to the dialog */ /* FIXME zero h_id is not 100% for testing if the dialog is inserted * into the hash table -> we need circular lists -bogdan */ if (dlg->h_id) { d_entry = &d_table->entries[dlg->h_entry]; dlg_lock( d_table, d_entry); linker->next = dlg->profile_links; dlg->profile_links =linker; linker->hash_linker.dlg = dlg; dlg_unlock( d_table, d_entry); } else { linker->next = dlg->profile_links; dlg->profile_links =linker; linker->hash_linker.dlg = dlg; } atomic_or_int((volatile int*)&dlg->dflags, DLG_FLAG_CHANGED_PROF); link_profile(linker, &dlg->callid); }
/** * replicates the remote update of an ongoing dialog locally * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_update(void) { struct dlg_cell *dlg; str call_id, from_tag, to_tag, from_uri, to_uri, vars, profiles; unsigned int dir, dst_leg; int timeout, h_entry; str st; struct dlg_entry *d_entry; bin_pop_str(&call_id); bin_pop_str(&from_tag); bin_pop_str(&to_tag); bin_pop_str(&from_uri); bin_pop_str(&to_uri); LM_DBG("replicated update for ['%.*s' '%.*s' '%.*s' '%.*s' '%.*s']\n", call_id.len, call_id.s, from_tag.len, from_tag.s, to_tag.len, to_tag.s, from_uri.len, from_uri.s, to_uri.len, to_uri.s); dlg = get_dlg(&call_id, &from_tag, &to_tag, &dir, &dst_leg); h_entry = dlg_hash(&call_id); d_entry = &d_table->entries[h_entry]; dlg_lock(d_table, d_entry); if (!dlg) { /* TODO: change to LM_DBG */ LM_ERR("dialog not found, building new\n"); dlg = build_new_dlg(&call_id, &from_uri, &to_uri, &from_tag); if (!dlg) { LM_ERR("Failed to create replicated dialog!\n"); goto error; } return dlg_replicated_create(dlg, &from_tag, &to_tag, 0); } bin_skip_int(2); bin_pop_int(&dlg->state); bin_skip_str(2); bin_pop_str(&st); if (dlg_update_cseq(dlg, DLG_CALLER_LEG, &st, 0) != 0) { LM_ERR("failed to update caller cseq\n"); goto error; } bin_pop_str(&st); if (dlg_update_cseq(dlg, callee_idx(dlg), &st, 0) != 0) { LM_ERR("failed to update callee cseq\n"); goto error; } bin_skip_str(6); bin_pop_str(&vars); bin_pop_str(&profiles); bin_pop_int(&dlg->user_flags); bin_pop_int(&dlg->flags); bin_pop_int(&timeout); bin_skip_int(2); timeout -= time(0); LM_DBG("Received updated timeout of %d for dialog %.*s\n",timeout,call_id.len,call_id.s); if (dlg->lifetime != timeout) { dlg->lifetime = timeout; if (update_dlg_timer(&dlg->tl, dlg->lifetime) == -1) LM_ERR("failed to update dialog lifetime!\n"); } unref_dlg_unsafe(dlg, 1, d_entry); if (vars.s && vars.len != 0) read_dialog_vars(vars.s, vars.len, dlg); dlg_unlock(d_table, d_entry); if (profiles.s && profiles.len != 0) read_dialog_profiles(profiles.s, profiles.len, dlg, 1, 1); return 0; error: dlg_unlock(d_table, d_entry); return -1; }
/*! * \brief Update a dialog state according a event and the old state * * This functions implement the main state machine that update a dialog * state according a processed event and the current state. If necessary * it will delete the processed dialog. The old and new state are also * saved for reference. * \param dlg updated dialog * \param event current event * \param old_state old dialog state * \param new_state new dialog state * \param unref set to 1 when the dialog was deleted, 0 otherwise */ void next_state_dlg(dlg_cell_t *dlg, int event, int *old_state, int *new_state, int *unref) { dlg_entry_t *d_entry; d_entry = &(d_table->entries[dlg->h_entry]); *unref = 0; 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: unref_dlg_unsafe(dlg,1,d_entry); break; case DLG_STATE_DELETED: *unref = 1; break; default: log_next_state_dlg(event, dlg); } break; case DLG_EVENT_RPL1xx: switch (dlg->state) { case DLG_STATE_UNCONFIRMED: case DLG_STATE_EARLY: dlg->state = DLG_STATE_EARLY; break; default: log_next_state_dlg(event, dlg); } 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_next_state_dlg(event, dlg); } break; case DLG_EVENT_RPL2xx: switch (dlg->state) { case DLG_STATE_DELETED: if (dlg->dflags&DLG_FLAG_HASBYE) { LM_CRIT("bogus event %d in state %d (with BYE) " "for dlg %p [%u:%u] with clid '%.*s' and tags '%.*s' '%.*s'\n", event, dlg->state, dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->tag[DLG_CALLER_LEG].len, dlg->tag[DLG_CALLER_LEG].s, dlg->tag[DLG_CALLEE_LEG].len, dlg->tag[DLG_CALLEE_LEG].s); break; } 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_next_state_dlg(event, dlg); } break; case DLG_EVENT_REQACK: switch (dlg->state) { case DLG_STATE_CONFIRMED_NA: dlg->state = DLG_STATE_CONFIRMED; break; case DLG_STATE_CONFIRMED: break; case DLG_STATE_DELETED: break; default: log_next_state_dlg(event, dlg); } break; case DLG_EVENT_REQBYE: switch (dlg->state) { case DLG_STATE_CONFIRMED_NA: case DLG_STATE_CONFIRMED: dlg->dflags |= DLG_FLAG_HASBYE; dlg->state = DLG_STATE_DELETED; *unref = 1; break; case DLG_STATE_EARLY: case DLG_STATE_DELETED: break; default: log_next_state_dlg(event, dlg); } break; case DLG_EVENT_REQPRACK: switch (dlg->state) { case DLG_STATE_EARLY: case DLG_STATE_CONFIRMED_NA: case DLG_STATE_DELETED: break; default: log_next_state_dlg(event, dlg); } break; case DLG_EVENT_REQ: switch (dlg->state) { case DLG_STATE_EARLY: case DLG_STATE_CONFIRMED_NA: case DLG_STATE_CONFIRMED: case DLG_STATE_DELETED: break; default: log_next_state_dlg(event, dlg); } break; default: LM_CRIT("unknown event %d in state %d " "for dlg %p [%u:%u] with clid '%.*s' and tags '%.*s' '%.*s'\n", event, dlg->state, dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->tag[DLG_CALLER_LEG].len, dlg->tag[DLG_CALLER_LEG].s, dlg->tag[DLG_CALLEE_LEG].len, dlg->tag[DLG_CALLEE_LEG].s); } *new_state = dlg->state; /* remove the dialog from profiles when is not no longer active */ if(*new_state==DLG_STATE_DELETED && dlg->profile_links!=NULL && *old_state!=*new_state) { destroy_linkers(dlg->profile_links); dlg->profile_links = NULL; } dlg_unlock( d_table, d_entry); LM_DBG("dialog %p changed from state %d to " "state %d, due event %d (ref %d)\n", dlg, *old_state, *new_state, event, dlg->ref); }
void next_state_dlg(struct dlg_cell *dlg, int event, int dir, int *old_state, int *new_state, int *unref, int last_dst_leg, char is_replicated) { struct dlg_entry *d_entry; d_entry = &(d_table->entries[dlg->h_entry]); *unref = 0; 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 from TM CBs*/ *unref = 1; /* unref from hash -> t failed */ break; case DLG_STATE_CONFIRMED_NA: case DLG_STATE_CONFIRMED: unref_dlg_unsafe(dlg,1,d_entry); /* unref from TM CBs*/ break; case DLG_STATE_DELETED: /* as the dialog aleady is in DELETE state, it is dangerous to directly unref it from here as it might be last ref -> dialog will be destroied and we will end up with a dangling pointer :D - bogdan */ *unref = 1; /* unref from TM CBs*/ break; default: log_next_state_dlg(event, dlg); } break; case DLG_EVENT_RPL1xx: switch (dlg->state) { case DLG_STATE_UNCONFIRMED: case DLG_STATE_EARLY: dlg->state = DLG_STATE_EARLY; break; default: log_next_state_dlg(event, dlg); } break; case DLG_EVENT_RPL3xx: switch (dlg->state) { case DLG_STATE_UNCONFIRMED: case DLG_STATE_EARLY: dlg->state = DLG_STATE_DELETED; *unref = 1; /* unref from hash -> t failed */ break; default: log_next_state_dlg(event, dlg); } break; case DLG_EVENT_RPL2xx: switch (dlg->state) { case DLG_STATE_DELETED: if (dlg->flags&DLG_FLAG_HASBYE) { log_next_state_dlg(event, dlg); break; } ref_dlg_unsafe(dlg,1); /* back in hash */ 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_next_state_dlg(event, dlg); } break; case DLG_EVENT_REQACK: switch (dlg->state) { case DLG_STATE_CONFIRMED_NA: dlg->state = DLG_STATE_CONFIRMED; break; case DLG_STATE_CONFIRMED: break; case DLG_STATE_DELETED: break; default: log_next_state_dlg(event, dlg); } break; case DLG_EVENT_REQBYE: switch (dlg->state) { case DLG_STATE_CONFIRMED_NA: case DLG_STATE_CONFIRMED: if (dir == DLG_DIR_DOWNSTREAM && last_dst_leg!=dlg->legs_no[DLG_LEG_200OK] ) /* to end the call, the BYE must be received * on the same leg as the 200 OK for INVITE */ break; dlg->flags |= DLG_FLAG_HASBYE; dlg->state = DLG_STATE_DELETED; *unref = 1; /* unref from hash -> dialog ended */ break; case DLG_STATE_DELETED: break; default: /* only case for BYEs in early or unconfirmed states * is for requests generate by caller or callee. * We never internally generate BYEs for early dialogs * * RFC says caller may send BYEs for early dialogs, * while the callee side MUST not send such requests*/ if (last_dst_leg == 0) log_next_state_dlg(event, dlg); } break; case DLG_EVENT_REQPRACK: switch (dlg->state) { case DLG_STATE_EARLY: case DLG_STATE_CONFIRMED_NA: case DLG_STATE_CONFIRMED: break; default: log_next_state_dlg(event, dlg); } break; case DLG_EVENT_REQ: switch (dlg->state) { case DLG_STATE_EARLY: case DLG_STATE_CONFIRMED_NA: case DLG_STATE_CONFIRMED: break; default: log_next_state_dlg(event, dlg); } break; default: LM_INFO("unknown event %d in state %d " "for dlg %p [%u:%u] with clid '%.*s' and tags '%.*s' '%.*s'\n", event, dlg->state, dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg_leg_print_info( dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info( dlg, callee_idx(dlg), tag)); } *new_state = dlg->state; dlg_unlock( d_table, d_entry); if (*old_state != *new_state) raise_state_changed_event( dlg->h_entry, dlg->h_id, (unsigned int)(*old_state), (unsigned int)(*new_state) ); if ( !is_replicated && dialog_replicate_cluster && (*old_state==DLG_STATE_CONFIRMED_NA || *old_state==DLG_STATE_CONFIRMED) && dlg->state==DLG_STATE_DELETED ) replicate_dialog_deleted(dlg); LM_DBG("dialog %p changed from state %d to " "state %d, due event %d\n",dlg,*old_state,*new_state,event); }
void dialog_update_db(unsigned int ticks, void * param) { static db_ps_t my_ps_update = NULL; static db_ps_t my_ps_insert = NULL; static db_ps_t my_ps_update_vp = NULL; int index; db_val_t values[DIALOG_TABLE_TOTAL_COL_NO]; struct dlg_entry entry; struct dlg_cell * cell; unsigned char on_shutdown; int callee_leg,ins_done=0; static query_list_t *ins_list = NULL; db_key_t insert_keys[DIALOG_TABLE_TOTAL_COL_NO] = { &h_entry_column, &h_id_column, &call_id_column, &from_uri_column, &from_tag_column, &to_uri_column, &to_tag_column, &from_sock_column, &to_sock_column, &start_time_column, &from_route_column, &to_route_column, &from_contact_column, &to_contact_column, &mangled_fu_column, &mangled_tu_column, /*update chunk */ &state_column, &timeout_column, &from_cseq_column, &to_cseq_column, &from_ping_cseq_column, &to_ping_cseq_column, &vars_column, &profiles_column, &sflags_column, &flags_column}; if (dialog_db_handle==0 || use_dialog_table()!=0) return; on_shutdown = (ticks==0); /*save the current dialogs information*/ VAL_TYPE(values) = VAL_TYPE(values+1) = VAL_TYPE(values+9) = VAL_TYPE(values+16) = VAL_TYPE(values+17) = VAL_TYPE(values+20) = VAL_TYPE(values+21) = VAL_TYPE(values+24) = VAL_TYPE(values+25)= DB_INT; VAL_TYPE(values+2) = VAL_TYPE(values+3) = VAL_TYPE(values+4) = VAL_TYPE(values+5) = VAL_TYPE(values+6) = VAL_TYPE(values+7) = VAL_TYPE(values+8) = VAL_TYPE(values+10) = VAL_TYPE(values+11) = VAL_TYPE(values+12) = VAL_TYPE(values+13) = VAL_TYPE(values+14) = VAL_TYPE(values+15) = VAL_TYPE(values+18) = VAL_TYPE(values+19) = VAL_TYPE(values+22) = VAL_TYPE(values+23) = DB_STR; for(index = 0; index< d_table->size; index++){ /* lock the whole entry */ entry = (d_table->entries)[index]; dlg_lock( d_table, &entry); for(cell = entry.first; cell != NULL; cell = cell->next){ callee_leg = callee_idx(cell); if( (cell->flags & DLG_FLAG_NEW) != 0 ) { if ( cell->state == DLG_STATE_DELETED ) { /* don't need to insert dialogs already terminated */ continue; } LM_DBG("inserting new dialog %p\n",cell); SET_INT_VALUE(values, cell->h_entry); SET_INT_VALUE(values+1, cell->h_id); SET_STR_VALUE(values+2, cell->callid); SET_STR_VALUE(values+3, cell->from_uri); SET_STR_VALUE(values+4, cell->legs[DLG_CALLER_LEG].tag); SET_STR_VALUE(values+5, cell->to_uri); SET_STR_VALUE(values+6, cell->legs[callee_leg].tag); SET_STR_VALUE(values+7, cell->legs[DLG_CALLER_LEG].bind_addr->sock_str); if (cell->legs[callee_leg].bind_addr) { SET_STR_VALUE(values+8, cell->legs[callee_leg].bind_addr->sock_str); } else { VAL_NULL(values+8) = 1; } SET_INT_VALUE(values+9, cell->start_ts); SET_STR_VALUE(values+10, cell->legs[DLG_CALLER_LEG].route_set); SET_STR_VALUE(values+11, cell->legs[callee_leg].route_set); SET_STR_VALUE(values+12, cell->legs[DLG_CALLER_LEG].contact); SET_STR_VALUE(values+13, cell->legs[callee_leg].contact); SET_STR_VALUE(values+14,cell->legs[callee_leg].from_uri); SET_STR_VALUE(values+15,cell->legs[callee_leg].to_uri); SET_INT_VALUE(values+16, cell->state); SET_INT_VALUE(values+17, (unsigned int)((unsigned int)time(0) + cell->tl.timeout - get_ticks()) ); SET_STR_VALUE(values+18, cell->legs[DLG_CALLER_LEG].r_cseq); SET_STR_VALUE(values+19, cell->legs[callee_leg].r_cseq); SET_INT_VALUE(values+20, cell->legs[DLG_CALLER_LEG].last_gen_cseq); SET_INT_VALUE(values+21, cell->legs[callee_leg].last_gen_cseq); set_final_update_cols(values+22, cell, on_shutdown); SET_INT_VALUE(values+25, cell->flags & ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED)); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_insert; if (con_set_inslist(&dialog_dbf,dialog_db_handle, &ins_list,insert_keys,DIALOG_TABLE_TOTAL_COL_NO) < 0 ) CON_RESET_INSLIST(dialog_db_handle); if((dialog_dbf.insert(dialog_db_handle, insert_keys, values, DIALOG_TABLE_TOTAL_COL_NO)) !=0){ LM_ERR("could not add another dialog to db\n"); goto error; } if (ins_done==0) ins_done=1; /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~(DLG_FLAG_NEW |DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED); } else if ( (cell->flags & DLG_FLAG_CHANGED)!=0 || on_shutdown ){ LM_DBG("updating existing dialog %p\n",cell); SET_INT_VALUE(values, cell->h_entry); SET_INT_VALUE(values+1, cell->h_id); SET_INT_VALUE(values+16, cell->state); SET_INT_VALUE(values+17, (unsigned int)((unsigned int)time(0) + cell->tl.timeout - get_ticks()) ); SET_STR_VALUE(values+18, cell->legs[DLG_CALLER_LEG].r_cseq); SET_STR_VALUE(values+19, cell->legs[callee_leg].r_cseq); SET_INT_VALUE(values+20, cell->legs[DLG_CALLER_LEG].last_gen_cseq); SET_INT_VALUE(values+21, cell->legs[callee_leg].last_gen_cseq); set_final_update_cols(values+22, cell, 1); SET_INT_VALUE(values+25, cell->flags); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_update; if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, (values), (insert_keys+16), (values+16), 2, 10)) !=0) { LM_ERR("could not update database info\n"); goto error; } /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~(DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED); } else if (cell->flags & DLG_FLAG_VP_CHANGED) { SET_INT_VALUE(values, cell->h_entry); SET_INT_VALUE(values+1, cell->h_id); set_final_update_cols(values+22, cell, 0); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_update_vp; if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, (values), (insert_keys+22), (values+22), 2, 3)) !=0) { LM_ERR("could not update database info\n"); goto error; } run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~DLG_FLAG_VP_CHANGED; } } dlg_unlock( d_table, &entry); } if (ins_done) { LM_DBG("dlg timer attempting to flush rows to DB\n"); /* flush everything to DB * so that next-time timer fires * we are sure that DB updates will be succesful */ if (ql_flush_rows(&dialog_dbf,dialog_db_handle,ins_list) < 0) LM_ERR("failed to flush rows to DB\n"); } return; error: dlg_unlock( d_table, &entry); }
int update_dialog_dbinfo(struct dlg_cell * cell) { static db_ps_t my_ps_insert = NULL; static db_ps_t my_ps_update = NULL; static db_ps_t my_ps_update_vp = NULL; struct dlg_entry entry; db_val_t values[DIALOG_TABLE_TOTAL_COL_NO]; int callee_leg; db_key_t insert_keys[DIALOG_TABLE_TOTAL_COL_NO] = { &h_entry_column, &h_id_column, &call_id_column, &from_uri_column, &from_tag_column, &to_uri_column, &to_tag_column, &from_sock_column, &to_sock_column, &start_time_column, &mangled_fu_column, &mangled_tu_column, &state_column, &timeout_column, &from_cseq_column, &to_cseq_column, &from_ping_cseq_column, &to_ping_cseq_column,&flags_column, &vars_column, &profiles_column, &sflags_column, &from_route_column, &to_route_column, &from_contact_column,&to_contact_column}; if(use_dialog_table()!=0) return -1; callee_leg= callee_idx(cell); if((cell->flags & DLG_FLAG_NEW) != 0){ /* save all the current dialogs information*/ VAL_TYPE(values) = VAL_TYPE(values+1) = VAL_TYPE(values+9) = VAL_TYPE(values+12) = VAL_TYPE(values+13) = VAL_TYPE(values+16) = VAL_TYPE(values+17) = VAL_TYPE(values+18) = VAL_TYPE(values+21) = DB_INT; VAL_TYPE(values+2) = VAL_TYPE(values+3) = VAL_TYPE(values+4) = VAL_TYPE(values+5) = VAL_TYPE(values+6) = VAL_TYPE(values+7) = VAL_TYPE(values+8) = VAL_TYPE(values+10) = VAL_TYPE(values+11) = VAL_TYPE(values+14) = VAL_TYPE(values+15) = VAL_TYPE(values+19) = VAL_TYPE(values+20) = VAL_TYPE(values+22) = VAL_TYPE(values+23) = VAL_TYPE(values+24) = VAL_TYPE(values+25) = DB_STR; /* lock the entry */ entry = (d_table->entries)[cell->h_entry]; dlg_lock( d_table, &entry); SET_INT_VALUE(values, cell->h_entry); SET_INT_VALUE(values+1, cell->h_id); SET_STR_VALUE(values+2, cell->callid); SET_STR_VALUE(values+3, cell->from_uri); SET_STR_VALUE(values+4, cell->legs[DLG_CALLER_LEG].tag); SET_STR_VALUE(values+5, cell->to_uri); SET_STR_VALUE(values+6, cell->legs[callee_leg].tag); SET_STR_VALUE(values+7, cell->legs[DLG_CALLER_LEG].bind_addr->sock_str); if (cell->legs[callee_leg].bind_addr) { SET_STR_VALUE(values+8, cell->legs[callee_leg].bind_addr->sock_str); } else { VAL_NULL(values+8) = 1; } SET_INT_VALUE(values+9, cell->start_ts); SET_STR_VALUE(values+10,cell->legs[callee_leg].from_uri); SET_STR_VALUE(values+11,cell->legs[callee_leg].to_uri); SET_INT_VALUE(values+12, cell->state); SET_INT_VALUE(values+13, (unsigned int)( (unsigned int)time(0) + cell->tl.timeout - get_ticks()) ); SET_STR_VALUE(values+14, cell->legs[DLG_CALLER_LEG].r_cseq); SET_STR_VALUE(values+15, cell->legs[callee_leg].r_cseq); SET_INT_VALUE(values+16,cell->legs[DLG_CALLER_LEG].last_gen_cseq); SET_INT_VALUE(values+17,cell->legs[callee_leg].last_gen_cseq); SET_INT_VALUE(values+18, cell->flags & ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED)); set_final_update_cols(values+19, cell, 0); SET_STR_VALUE(values+22, cell->legs[DLG_CALLER_LEG].route_set); SET_STR_VALUE(values+23, cell->legs[callee_leg].route_set); SET_STR_VALUE(values+24, cell->legs[DLG_CALLER_LEG].contact); SET_STR_VALUE(values+25, cell->legs[callee_leg].contact); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_insert; if((dialog_dbf.insert(dialog_db_handle, insert_keys, values, DIALOG_TABLE_TOTAL_COL_NO)) !=0){ LM_ERR("could not add another dialog to db\n"); goto error; } /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED); } else if((cell->flags & DLG_FLAG_CHANGED) != 0) { /* save only dialog's state and timeout */ VAL_TYPE(values) = VAL_TYPE(values+1) = VAL_TYPE(values+12) = VAL_TYPE(values+13) = VAL_TYPE(values+16) = VAL_TYPE(values+17) = VAL_TYPE(values+18) = VAL_TYPE(values+21) =DB_INT; VAL_TYPE(values+14) = VAL_TYPE(values+15) = VAL_TYPE(values+19) = VAL_TYPE(values+20) = DB_STR; /* lock the entry */ entry = (d_table->entries)[cell->h_entry]; dlg_lock( d_table, &entry); SET_INT_VALUE(values, cell->h_entry); SET_INT_VALUE(values+1, cell->h_id); SET_INT_VALUE(values+12, cell->state); SET_INT_VALUE(values+13, (unsigned int)( (unsigned int)time(0) + cell->tl.timeout - get_ticks()) ); SET_STR_VALUE(values+14, cell->legs[DLG_CALLER_LEG].r_cseq); SET_STR_VALUE(values+15, cell->legs[callee_leg].r_cseq); SET_INT_VALUE(values+16,cell->legs[DLG_CALLER_LEG].last_gen_cseq); SET_INT_VALUE(values+17,cell->legs[callee_leg].last_gen_cseq); SET_INT_VALUE(values+18, cell->flags); set_final_update_cols(values+19, cell, 1); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_update; if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, (values), (insert_keys+12), (values+12), 2, 10)) !=0){ LM_ERR("could not update database info\n"); goto error; } /* dialog saved */ run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~(DLG_FLAG_CHANGED|DLG_FLAG_VP_CHANGED); } else if (cell->flags & DLG_FLAG_VP_CHANGED) { cell->flags |= DLG_FLAG_VP_CHANGED; VAL_TYPE(values) = VAL_TYPE(values+1) = VAL_TYPE(values+21) = DB_INT; VAL_TYPE(values+19) = VAL_TYPE(values+20) = DB_STR; /* lock the entry */ entry = (d_table->entries)[cell->h_entry]; dlg_lock( d_table, &entry); SET_INT_VALUE(values, cell->h_entry); SET_INT_VALUE(values+1, cell->h_id); set_final_update_cols(values+19, cell, 0); CON_PS_REFERENCE(dialog_db_handle) = &my_ps_update_vp; if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, (values), (insert_keys+19), (values+19), 2, 3)) !=0){ LM_ERR("could not update database info\n"); goto error; } run_dlg_callbacks( DLGCB_SAVED, cell, 0, DLG_DIR_NONE, 0); cell->flags &= ~DLG_FLAG_VP_CHANGED; } else { return 0; } dlg_unlock( d_table, &entry); return 0; error: dlg_unlock( d_table, &entry); return -1; }
int pv_set_dlg_variable(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) { dlg_cell_t *dlg = NULL; int ret = -1; if (param==NULL || param->pvn.type!=PV_NAME_INTSTR || param->pvn.u.isname.type!=AVP_NAME_STR || param->pvn.u.isname.name.s.s==NULL ) { LM_CRIT("BUG - bad parameters\n"); goto error; } /* Retrieve the dialog for current message */ dlg=dlg_get_msg_dialog( msg); if (dlg) { /* Lock the dialog */ dlg_lock(d_table, &(d_table->entries[dlg->h_entry])); } else { /* Verify the local list */ get_local_varlist_pointer(msg, 0); } if (val==NULL || val->flags&(PV_VAL_NONE|PV_VAL_NULL|PV_VAL_EMPTY)) { /* if NULL, remove the value */ ret = set_dlg_variable_unsafe(dlg, ¶m->pvn.u.isname.name.s, NULL); if(ret!= 0) { /* unlock dialog */ if (dlg) { dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); dlg_release(dlg); } return ret; } } else { /* if value, must be string */ if ( !(val->flags&PV_VAL_STR)) { LM_ERR("non-string values are not supported\n"); /* unlock dialog */ if (dlg) dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); goto error; } ret = set_dlg_variable_unsafe(dlg, ¶m->pvn.u.isname.name.s, &val->rs); if(ret!= 0) { /* unlock dialog */ if (dlg) dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); goto error; } } /* unlock dialog */ if (dlg) { dlg->dflags |= DLG_FLAG_CHANGED_VARS; dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); if ( dlg_db_mode==DB_MODE_REALTIME ) update_dialog_dbinfo(dlg); } print_lists(dlg); dlg_release(dlg); return 0; error: dlg_release(dlg); return -1; }
/** * replicates the remote update of an ongoing dialog locally * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_update(bin_packet_t *packet) { struct dlg_cell *dlg; str call_id, from_tag, to_tag, from_uri, to_uri, vars, profiles; unsigned int dir, dst_leg; int timeout, h_entry; str st; struct dlg_entry *d_entry; int rcv_flags, save_new_flag; bin_pop_str(packet, &call_id); bin_pop_str(packet, &from_tag); bin_pop_str(packet, &to_tag); bin_pop_str(packet, &from_uri); bin_pop_str(packet, &to_uri); LM_DBG("replicated update for ['%.*s' '%.*s' '%.*s' '%.*s' '%.*s']\n", call_id.len, call_id.s, from_tag.len, from_tag.s, to_tag.len, to_tag.s, from_uri.len, from_uri.s, to_uri.len, to_uri.s); dlg = get_dlg(&call_id, &from_tag, &to_tag, &dir, &dst_leg); h_entry = dlg_hash(&call_id); d_entry = &d_table->entries[h_entry]; dlg_lock(d_table, d_entry); if (!dlg) { LM_DBG("dialog not found, building new\n"); dlg = build_new_dlg(&call_id, &from_uri, &to_uri, &from_tag); if (!dlg) { LM_ERR("Failed to create replicated dialog!\n"); goto error; } return dlg_replicated_create(packet ,dlg, &from_tag, &to_tag, 0); } bin_skip_int(packet, 2); bin_pop_int(packet, &dlg->state); bin_skip_str(packet, 2); bin_pop_str(packet, &st); if (dlg_update_cseq(dlg, DLG_CALLER_LEG, &st, 0) != 0) { LM_ERR("failed to update caller cseq\n"); goto error; } bin_pop_str(packet, &st); if (dlg_update_cseq(dlg, callee_idx(dlg), &st, 0) != 0) { LM_ERR("failed to update callee cseq\n"); goto error; } bin_skip_str(packet, 6); bin_pop_str(packet, &vars); bin_pop_str(packet, &profiles); bin_pop_int(packet, &dlg->user_flags); bin_pop_int(packet, &dlg->mod_flags); bin_pop_int(packet, &rcv_flags); /* make sure an update received immediately after a create can't * incorrectly erase the DLG_FLAG_NEW before locally writing to DB */ save_new_flag = dlg->flags & DLG_FLAG_NEW; dlg->flags = rcv_flags; dlg->flags |= ((save_new_flag ? DLG_FLAG_NEW : 0) | DLG_FLAG_CHANGED); bin_pop_int(packet, &timeout); bin_skip_int(packet, 2); timeout -= time(0); LM_DBG("Received updated timeout of %d for dialog %.*s\n", timeout, call_id.len, call_id.s); if (dlg->lifetime != timeout) { dlg->lifetime = timeout; switch (update_dlg_timer(&dlg->tl, dlg->lifetime) ) { case -1: LM_ERR("failed to update dialog lifetime!\n"); /* continue */ case 0: /* timeout value was updated */ break; case 1: /* dlg inserted in timer list with new expire (reference it)*/ ref_dlg(dlg,1); } } unref_dlg_unsafe(dlg, 1, d_entry); if (vars.s && vars.len != 0) read_dialog_vars(vars.s, vars.len, dlg); dlg->flags |= DLG_FLAG_VP_CHANGED; dlg_unlock(d_table, d_entry); if (profiles.s && profiles.len != 0) read_dialog_profiles(profiles.s, profiles.len, dlg, 1, 1); return 0; error: dlg_unlock(d_table, d_entry); return -1; }
void rcv_cluster_event(enum clusterer_event ev, int node_id) { struct dlg_sharing_tag *tag; struct n_send_info *ni; int lock_old_flag; struct dlg_cell *dlg, *next_dlg; int i; int ret; int unref; if (ev == SYNC_REQ_RCV && receive_sync_request(node_id) < 0) LM_ERR("Failed to reply to sync request from node: %d\n", node_id); else if (ev == SYNC_DONE) { if (dlg_db_mode == DB_MODE_NONE) return; /* drop dialogs loaded from DB which have not been reconfirmed * through syncing or SIP(updates) */ for (i = 0; i < d_table->size; i++) { dlg_lock(d_table, &d_table->entries[i]); dlg = d_table->entries[i].first; while (dlg) { if (!(dlg->flags & DLG_FLAG_FROM_DB)) { dlg = dlg->next; continue; } LM_DBG("Drop DB loaded dialog ID=%lld\n", ((long long)dlg->h_entry << 32) | (dlg->h_id)); unref = 1; /* unref from hash */ dlg->state = DLG_STATE_DELETED; destroy_linkers(dlg->profile_links, 0); dlg->profile_links = NULL; /* remove from timer */ ret = remove_dlg_timer(&dlg->tl); if (ret < 0) { LM_ERR("unable to unlink the timer on dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg_leg_print_info(dlg, DLG_CALLER_LEG, tag), dlg_leg_print_info(dlg, callee_idx(dlg), tag)); } else if (ret == 0) /* successfully removed from timer list */ unref++; if (dlg_db_mode != DB_MODE_SHUTDOWN) { dlg->flags &= ~DLG_FLAG_NEW; remove_dialog_from_db(dlg); dlg->flags |= DLG_FLAG_DB_DELETED; } if (dlg_db_mode == DB_MODE_DELAYED) unref++; next_dlg = dlg->next; unref_dlg_unsafe(dlg, unref, &d_table->entries[i]); dlg = next_dlg; } dlg_unlock(d_table, &d_table->entries[i]); } } else if (ev == CLUSTER_NODE_UP) { lock_start_sw_read(shtags_lock); for (tag = *shtags_list; tag; tag = tag->next) { if (!tag->send_active_msg) continue; /* send sharing tag active msg to nodes to which we didn't already */ for (ni = tag->active_msgs_sent; ni && ni->node_id != node_id; ni = ni->next) ; if (!ni) { if (send_shtag_active_info(&tag->name, node_id) < 0) { LM_ERR("Failed to send info about sharing tag\n"); continue; } ni = shm_malloc(sizeof *ni); if (!ni) { LM_ERR("No more shm memory!\n"); return; } ni->node_id = node_id; ni->next = tag->active_msgs_sent; lock_switch_write(shtags_lock, lock_old_flag); tag->active_msgs_sent = ni; lock_switch_read(shtags_lock, lock_old_flag); } } lock_stop_sw_read(shtags_lock); } }
/** * replicates a confirmed dialog from another OpenSIPS instance * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_create(bin_packet_t *packet, struct dlg_cell *cell, str *ftag, str *ttag, int safe) { int h_entry; unsigned int dir, dst_leg; str callid = { NULL, 0 }, from_uri, to_uri, from_tag, to_tag; str cseq1, cseq2, contact1, contact2, rroute1, rroute2, mangled_fu, mangled_tu; str sock, vars, profiles; struct dlg_cell *dlg = NULL; struct socket_info *caller_sock, *callee_sock; struct dlg_entry *d_entry; LM_DBG("Received replicated dialog!\n"); if (!cell) { DLG_BIN_POP(str, packet, callid, malformed); DLG_BIN_POP(str, packet, from_tag, malformed); DLG_BIN_POP(str, packet, to_tag, malformed); DLG_BIN_POP(str, packet, from_uri, malformed); DLG_BIN_POP(str, packet, to_uri, malformed); dlg = get_dlg(&callid, &from_tag, &to_tag, &dir, &dst_leg); h_entry = dlg_hash(&callid); d_entry = &d_table->entries[h_entry]; if (safe) dlg_lock(d_table, d_entry); if (dlg) { LM_DBG("Dialog with ci '%.*s' is already created\n", callid.len, callid.s); unref_dlg_unsafe(dlg, 1, d_entry); /* unmark dlg as loaded from DB (otherwise it would have been * dropped later when syncing from cluster is done) */ dlg->flags &= ~DLG_FLAG_FROM_DB; dlg_unlock(d_table, d_entry); return 0; } dlg = build_new_dlg(&callid, &from_uri, &to_uri, &from_tag); if (!dlg) { LM_ERR("Failed to create replicated dialog!\n"); goto pre_linking_error; } } else { h_entry = dlg_hash(&cell->callid); d_entry = &d_table->entries[h_entry]; if (safe) dlg_lock(d_table, d_entry); from_tag = *ftag; to_tag = *ttag; dlg = cell; } if_update_stat(dlg_enable_stats, processed_dlgs, 1); DLG_BIN_POP(int, packet, dlg->h_id, pre_linking_error); DLG_BIN_POP(int, packet, dlg->start_ts, pre_linking_error); DLG_BIN_POP(int, packet, dlg->state, pre_linking_error); /* next_id follows the max value of all replicated ids */ if (d_table->entries[dlg->h_entry].next_id <= dlg->h_id) d_table->entries[dlg->h_entry].next_id = dlg->h_id + 1; DLG_BIN_POP(str, packet, sock, pre_linking_error); caller_sock = fetch_socket_info(&sock); DLG_BIN_POP(str, packet, sock, pre_linking_error); callee_sock = fetch_socket_info(&sock); if (!caller_sock || !callee_sock) { LM_ERR("Replicated dialog doesn't match any listening sockets\n"); goto pre_linking_error; } DLG_BIN_POP(str, packet, cseq1, pre_linking_error); DLG_BIN_POP(str, packet, cseq2, pre_linking_error); DLG_BIN_POP(str, packet, rroute1, pre_linking_error); DLG_BIN_POP(str, packet, rroute2, pre_linking_error); DLG_BIN_POP(str, packet, contact1, pre_linking_error); DLG_BIN_POP(str, packet, contact2, pre_linking_error); DLG_BIN_POP(str, packet, mangled_fu, pre_linking_error); DLG_BIN_POP(str, packet, mangled_tu, pre_linking_error); /* add the 2 legs */ /* TODO - sdp here */ if (dlg_update_leg_info(0, dlg, &from_tag, &rroute1, &contact1, &cseq1, caller_sock, 0, 0,0) != 0 || dlg_update_leg_info(1, dlg, &to_tag, &rroute2, &contact2, &cseq2, callee_sock, &mangled_fu, &mangled_tu,0) != 0) { LM_ERR("dlg_set_leg_info failed\n"); goto pre_linking_error; } dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG; /* link the dialog into the hash */ if (!d_entry->first) d_entry->first = d_entry->last = dlg; else { d_entry->last->next = dlg; dlg->prev = d_entry->last; d_entry->last = dlg; } dlg->ref++; d_entry->cnt++; DLG_BIN_POP(str, packet, vars, pre_linking_error); DLG_BIN_POP(str, packet, profiles, pre_linking_error); DLG_BIN_POP(int, packet, dlg->user_flags, pre_linking_error); DLG_BIN_POP(int, packet, dlg->mod_flags, pre_linking_error); DLG_BIN_POP(int, packet, dlg->flags, pre_linking_error); /* also save the dialog into the DB on this instance */ dlg->flags |= DLG_FLAG_NEW; DLG_BIN_POP(int, packet, dlg->tl.timeout, pre_linking_error); DLG_BIN_POP(int, packet, dlg->legs[DLG_CALLER_LEG].last_gen_cseq, pre_linking_error); DLG_BIN_POP(int, packet, dlg->legs[callee_idx(dlg)].last_gen_cseq, pre_linking_error); if (dlg->tl.timeout <= (unsigned int) time(0)) dlg->tl.timeout = 0; else dlg->tl.timeout -= (unsigned int) time(0); /* restore the timer values */ if (insert_dlg_timer(&dlg->tl, (int) dlg->tl.timeout) != 0) { LM_CRIT("Unable to insert dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); goto error; } if (dlg->state == DLG_STATE_CONFIRMED_NA || dlg->state == DLG_STATE_CONFIRMED) active_dlgs_cnt++; /* reference the dialog as kept in the timer list */ ref_dlg_unsafe(dlg, 1); LM_DBG("Received initial timeout of %d for dialog %.*s, safe = %d\n", dlg->tl.timeout, callid.len, callid.s, safe); dlg->lifetime = 0; if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { if (insert_ping_timer(dlg) != 0) LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg); else { ref_dlg_unsafe(dlg, 1); } } if (dlg_db_mode == DB_MODE_DELAYED) { /* to be later removed by timer */ ref_dlg_unsafe(dlg, 1); } if (vars.s && vars.len != 0) read_dialog_vars(vars.s, vars.len, dlg); dlg_unlock(d_table, d_entry); if (profiles.s && profiles.len != 0) read_dialog_profiles(profiles.s, profiles.len, dlg, 0, 1); if_update_stat(dlg_enable_stats, active_dlgs, 1); run_load_callback_per_dlg(dlg); return 0; pre_linking_error: dlg_unlock(d_table, d_entry); if (dlg) destroy_dlg(dlg); return -1; error: dlg_unlock(d_table, d_entry); if (dlg) unref_dlg(dlg, 1); malformed: return -1; }
int dlg_dmq_replicate_action(dlg_dmq_action_t action, dlg_cell_t* dlg, int needlock, dmq_node_t *node ) { srjson_doc_t jdoc, prof_jdoc; dlg_var_t *var; LM_DBG("replicating action [%d] on [%u:%u] to dmq peers\n", action, dlg->h_entry, dlg->h_id); if (action == DLG_DMQ_UPDATE) { if (!node && (dlg->iflags & DLG_IFLAG_DMQ_SYNC) && ((dlg->dflags & DLG_FLAG_CHANGED_PROF) == 0)) { LM_DBG("dlg not changed, no sync\n"); return 1; } } else if ( (dlg->iflags & DLG_IFLAG_DMQ_SYNC) == 0 ) { LM_DBG("dlg not synced, no sync\n"); return 1; } if (action == DLG_DMQ_STATE && (dlg->state != DLG_STATE_CONFIRMED && dlg->state != DLG_STATE_DELETED && dlg->state != DLG_STATE_EARLY)) { LM_DBG("not syncing state %u\n", dlg->state); return 1; } srjson_InitDoc(&jdoc, NULL); jdoc.root = srjson_CreateObject(&jdoc); if(jdoc.root==NULL) { LM_ERR("cannot create json root\n"); goto error; } if (needlock) dlg_lock(d_table, &(d_table->entries[dlg->h_entry])); srjson_AddNumberToObject(&jdoc, jdoc.root, "action", action); srjson_AddNumberToObject(&jdoc, jdoc.root, "h_entry", dlg->h_entry); srjson_AddNumberToObject(&jdoc, jdoc.root, "h_id", dlg->h_id); switch(action) { case DLG_DMQ_UPDATE: dlg->iflags |= DLG_IFLAG_DMQ_SYNC; dlg->dflags &= ~DLG_FLAG_CHANGED_PROF; srjson_AddNumberToObject(&jdoc, jdoc.root, "init_ts", dlg->init_ts); srjson_AddStrToObject(&jdoc, jdoc.root, "callid", dlg->callid.s, dlg->callid.len); srjson_AddStrToObject(&jdoc, jdoc.root, "from_uri", dlg->from_uri.s, dlg->from_uri.len); srjson_AddStrToObject(&jdoc, jdoc.root, "to_uri", dlg->to_uri.s, dlg->to_uri.len); srjson_AddStrToObject(&jdoc, jdoc.root, "req_uri", dlg->req_uri.s, dlg->req_uri.len); srjson_AddStrToObject(&jdoc, jdoc.root, "tag1", dlg->tag[0].s, dlg->tag[0].len); srjson_AddStrToObject(&jdoc, jdoc.root, "cseq1", dlg->cseq[0].s, dlg->cseq[0].len); srjson_AddStrToObject(&jdoc, jdoc.root, "route_set1", dlg->route_set[0].s, dlg->route_set[0].len); srjson_AddStrToObject(&jdoc, jdoc.root, "contact1", dlg->contact[0].s, dlg->contact[0].len); if (dlg->vars != NULL) { srjson_t *pj = NULL; pj = srjson_CreateObject(&jdoc); for(var=dlg->vars ; var ; var=var->next) { srjson_AddStrToObject(&jdoc, pj, var->key.s, var->value.s, var->value.len); } srjson_AddItemToObject(&jdoc, jdoc.root, "vars", pj); } if (dlg->profile_links) { srjson_InitDoc(&prof_jdoc, NULL); dlg_profiles_to_json(dlg, &prof_jdoc); if(prof_jdoc.buf.s!=NULL) { LM_DBG("adding profiles: [%.*s]\n", prof_jdoc.buf.len, prof_jdoc.buf.s); srjson_AddStrToObject(&jdoc, jdoc.root, "profiles", prof_jdoc.buf.s, prof_jdoc.buf.len); prof_jdoc.free_fn(prof_jdoc.buf.s); prof_jdoc.buf.s = NULL; } srjson_DestroyDoc(&prof_jdoc); } /* intentional fallthrough */ case DLG_DMQ_STATE: srjson_AddNumberToObject(&jdoc, jdoc.root, "state", dlg->state); switch (dlg->state) { case DLG_STATE_EARLY: srjson_AddNumberToObject(&jdoc, jdoc.root, "start_ts", dlg->start_ts); srjson_AddNumberToObject(&jdoc, jdoc.root, "lifetime", dlg->lifetime); srjson_AddStrToObject(&jdoc, jdoc.root, "tag1", dlg->tag[0].s, dlg->tag[0].len); srjson_AddStrToObject(&jdoc, jdoc.root, "cseq1", dlg->cseq[0].s, dlg->cseq[0].len); srjson_AddStrToObject(&jdoc, jdoc.root, "route_set1", dlg->route_set[0].s, dlg->route_set[0].len); srjson_AddStrToObject(&jdoc, jdoc.root, "contact1", dlg->contact[0].s, dlg->contact[0].len); break; case DLG_STATE_CONFIRMED: srjson_AddNumberToObject(&jdoc, jdoc.root, "start_ts", dlg->start_ts); srjson_AddNumberToObject(&jdoc, jdoc.root, "lifetime", dlg->lifetime); srjson_AddStrToObject(&jdoc, jdoc.root, "tag1", dlg->tag[0].s, dlg->tag[0].len); srjson_AddStrToObject(&jdoc, jdoc.root, "tag2", dlg->tag[1].s, dlg->tag[1].len); srjson_AddStrToObject(&jdoc, jdoc.root, "cseq1", dlg->cseq[0].s, dlg->cseq[0].len); srjson_AddStrToObject(&jdoc, jdoc.root, "cseq2", dlg->cseq[1].s, dlg->cseq[1].len); srjson_AddStrToObject(&jdoc, jdoc.root, "route_set1", dlg->route_set[0].s, dlg->route_set[0].len); srjson_AddStrToObject(&jdoc, jdoc.root, "route_set2", dlg->route_set[1].s, dlg->route_set[1].len); srjson_AddStrToObject(&jdoc, jdoc.root, "contact1", dlg->contact[0].s, dlg->contact[0].len); srjson_AddStrToObject(&jdoc, jdoc.root, "contact2", dlg->contact[1].s, dlg->contact[1].len); break; case DLG_STATE_DELETED: //dlg->iflags &= ~DLG_IFLAG_DMQ_SYNC; break; default: LM_DBG("not syncing state %u\n", dlg->state); } break; case DLG_DMQ_RM: srjson_AddNumberToObject(&jdoc, jdoc.root, "state", dlg->state); dlg->iflags &= ~DLG_IFLAG_DMQ_SYNC; break; case DLG_DMQ_NONE: case DLG_DMQ_SYNC: break; } if (needlock) dlg_unlock(d_table, &(d_table->entries[dlg->h_entry])); jdoc.buf.s = srjson_PrintUnformatted(&jdoc, jdoc.root); if(jdoc.buf.s==NULL) { LM_ERR("unable to serialize data\n"); goto error; } jdoc.buf.len = strlen(jdoc.buf.s); LM_DBG("sending serialized data %.*s\n", jdoc.buf.len, jdoc.buf.s); if (dlg_dmq_send(&jdoc.buf, node)!=0) { goto error; } jdoc.free_fn(jdoc.buf.s); jdoc.buf.s = NULL; srjson_DestroyDoc(&jdoc); return 0; error: if(jdoc.buf.s!=NULL) { jdoc.free_fn(jdoc.buf.s); jdoc.buf.s = NULL; } srjson_DestroyDoc(&jdoc); return -1; }
/** * replicates a confirmed dialog from another OpenSIPS instance * by reading the relevant information using the Binary Packet Interface */ int dlg_replicated_create(struct dlg_cell *cell, str *ftag, str *ttag, int safe) { int next_id, h_entry; unsigned int dir, dst_leg; str callid, from_uri, to_uri, from_tag, to_tag; str cseq1,cseq2,contact1,contact2,rroute1,rroute2,mangled_fu,mangled_tu; str sock, vars, profiles; struct dlg_cell *dlg = NULL; struct socket_info *caller_sock, *callee_sock; struct dlg_entry *d_entry; if_update_stat(dlg_enable_stats, processed_dlgs, 1); LM_DBG("Received replicated dialog!\n"); if (!cell) { bin_pop_str(&callid); bin_pop_str(&from_tag); bin_pop_str(&to_tag); bin_pop_str(&from_uri); bin_pop_str(&to_uri); dlg = get_dlg(&callid, &from_tag, &to_tag, &dir, &dst_leg); h_entry = dlg_hash(&callid); d_entry = &d_table->entries[h_entry]; if (safe) dlg_lock(d_table, d_entry); if (dlg) { LM_DBG("Dialog with ci '%.*s' is already created\n", callid.len, callid.s); unref_dlg_unsafe(dlg, 1, d_entry); dlg_unlock(d_table, d_entry); return 0; } dlg = build_new_dlg(&callid, &from_uri, &to_uri, &from_tag); if (!dlg) { LM_ERR("Failed to create replicated dialog!\n"); goto pre_linking_error; } } else { h_entry = dlg_hash(&cell->callid); d_entry = &d_table->entries[h_entry]; if (safe) dlg_lock(d_table, d_entry); from_tag = *ftag; to_tag = *ttag; dlg = cell; } bin_pop_int(&dlg->h_id); bin_pop_int(&dlg->start_ts); bin_pop_int(&dlg->state); next_id = d_table->entries[dlg->h_entry].next_id; d_table->entries[dlg->h_entry].next_id = (next_id <= dlg->h_id) ? (dlg->h_id + 1) : next_id; if (bin_pop_str(&sock)) goto pre_linking_error; caller_sock = fetch_socket_info(&sock); if (bin_pop_str(&sock)) goto pre_linking_error; callee_sock = fetch_socket_info(&sock); if (!caller_sock || !callee_sock) { LM_ERR("Dialog in DB doesn't match any listening sockets\n"); goto pre_linking_error; } bin_pop_str(&cseq1); bin_pop_str(&cseq2); bin_pop_str(&rroute1); bin_pop_str(&rroute2); bin_pop_str(&contact1); bin_pop_str(&contact2); bin_pop_str(&mangled_fu); bin_pop_str(&mangled_tu); /* add the 2 legs */ if (dlg_add_leg_info(dlg, &from_tag, &rroute1, &contact1, &cseq1, caller_sock, 0, 0) != 0 || dlg_add_leg_info(dlg, &to_tag, &rroute2, &contact2, &cseq2, callee_sock, &mangled_fu, &mangled_tu) != 0) { LM_ERR("dlg_set_leg_info failed\n"); goto pre_linking_error; } dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG; /* link the dialog into the hash */ dlg->h_id = d_entry->next_id++; if (!d_entry->first) d_entry->first = d_entry->last = dlg; else { d_entry->last->next = dlg; dlg->prev = d_entry->last; d_entry->last = dlg; } dlg->ref++; d_entry->cnt++; bin_pop_str(&vars); bin_pop_str(&profiles); bin_pop_int(&dlg->user_flags); bin_pop_int(&dlg->flags); bin_pop_int((void *)&dlg->tl.timeout); bin_pop_int(&dlg->legs[DLG_CALLER_LEG].last_gen_cseq); bin_pop_int(&dlg->legs[callee_idx(dlg)].last_gen_cseq); if (dlg->tl.timeout <= (unsigned int)time(0)) dlg->tl.timeout = 0; else dlg->tl.timeout -= (unsigned int)time(0); /* restore the timer values */ if (insert_dlg_timer(&dlg->tl, (int)dlg->tl.timeout) != 0) { LM_CRIT("Unable to insert dlg %p [%u:%u] " "with clid '%.*s' and tags '%.*s' '%.*s'\n", dlg, dlg->h_entry, dlg->h_id, dlg->callid.len, dlg->callid.s, dlg->legs[DLG_CALLER_LEG].tag.len, dlg->legs[DLG_CALLER_LEG].tag.s, dlg->legs[callee_idx(dlg)].tag.len, ZSW(dlg->legs[callee_idx(dlg)].tag.s)); goto error; } if (dlg->state == DLG_STATE_CONFIRMED_NA || dlg->state == DLG_STATE_CONFIRMED) active_dlgs_cnt++; /* reference the dialog as kept in the timer list */ ref_dlg_unsafe(dlg, 1); LM_DBG("Received initial timeout of %d for dialog %.*s, safe = %d\n",dlg->tl.timeout,callid.len,callid.s,safe); dlg->lifetime = 0; /* Do not replicate the pinging - we might terminate dialogs badly when running as backup if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) { if (insert_ping_timer(dlg) != 0) LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg); else { ref_dlg_unsafe(dlg, 1); } } */ if (dlg_db_mode == DB_MODE_DELAYED) { /* to be later removed by timer */ ref_dlg_unsafe(dlg, 1); } if (vars.s && vars.len != 0) read_dialog_vars(vars.s, vars.len, dlg); dlg_unlock(d_table, d_entry); if (profiles.s && profiles.len != 0) read_dialog_profiles(profiles.s, profiles.len, dlg, 0, 1); if_update_stat(dlg_enable_stats, active_dlgs, 1); run_load_callback_per_dlg(dlg); return 0; pre_linking_error: dlg_unlock(d_table, d_entry); if (dlg) destroy_dlg(dlg); return -1; error: dlg_unlock(d_table, d_entry); if (dlg) unref_dlg(dlg, 1); return -1; }