void destroy_dlg(struct dlg_cell *dlg) { int ret = 0; LM_DBG("destroying dialog %p\n",dlg); ret = remove_dlg_timer(&dlg->tl); if (ret < 0) { LM_CRIT("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) { LM_DBG("dlg expired or not in list - 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)); } run_dlg_callbacks( DLGCB_DESTROY , dlg, 0, DLG_DIR_NONE, NULL, 0); free_dlg_dlg(dlg); }
/** * Small logging helper functions for next_state_dlg. * \param event logged event * \param dlg dialog data * \see next_state_dlg */ static inline void log_next_state_dlg(const int event, const struct dlg_cell *dlg) { LM_WARN("bogus 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)); }
static inline void log_bogus_dst_leg(struct dlg_cell *dlg) { if (last_dst_leg>=dlg->legs_no[DLG_LEGS_USED]) LM_CRIT("bogus dst leg %d in state %d for dlg %p [%u:%u] with " "clid '%.*s' and tags '%.*s' '%.*s'. legs used %d\n", last_dst_leg,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),dlg->legs_no[DLG_LEGS_USED]); }
static void dual_bye_event(struct dlg_cell* dlg, struct sip_msg *req, int extra_unref) { int event, old_state, new_state, unref, ret; struct dlg_cell *curr; event = DLG_EVENT_REQBYE; last_dst_leg = dlg->legs_no[DLG_LEG_200OK]; next_state_dlg(dlg, event, DLG_DIR_DOWNSTREAM, &old_state, &new_state, &unref, 0); unref += extra_unref; if(new_state == DLG_STATE_DELETED && old_state != DLG_STATE_DELETED){ LM_DBG("removing dialog with h_entry %u and h_id %u\n", dlg->h_entry, dlg->h_id); /*destroy linkers */ dlg_lock_dlg(dlg); destroy_linkers(dlg->profile_links, 0); dlg->profile_links = NULL; dlg_unlock_dlg(dlg); /* remove from timer */ ret = remove_dlg_timer(&dlg->tl); if (ret < 0) { LM_CRIT("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) { LM_DBG("dlg already expired (not in timer list) %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 { /* successfully removed from timer list */ unref++; } curr = current_dlg_pointer; current_dlg_pointer = dlg; /* dialog terminated (BYE) */ run_dlg_callbacks( DLGCB_TERMINATED, dlg, req, DLG_DIR_NONE, 0); current_dlg_pointer = curr; LM_DBG("first final reply\n"); /* derefering the dialog */ unref_dlg(dlg, unref); if_update_stat( dlg_enable_stats, active_dlgs, -1); } if(new_state == DLG_STATE_DELETED && old_state == DLG_STATE_DELETED ) { /* trash the dialog from DB and memory */ LM_DBG("second final reply\n"); /* delete the dialog from DB */ if (should_remove_dlg_db()) remove_dialog_from_db(dlg); /* force delete from mem */ unref_dlg(dlg, unref); } }
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); } }
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); }
static void dual_bye_event(struct dlg_cell* dlg, struct sip_msg *req, int extra_unref) { int event, old_state, new_state, unref, ret; struct sip_msg *fake_msg=NULL; context_p old_ctx; context_p *new_ctx; event = DLG_EVENT_REQBYE; next_state_dlg(dlg, event, DLG_DIR_DOWNSTREAM, &old_state, &new_state, &unref, dlg->legs_no[DLG_LEG_200OK], 0); unref += extra_unref; if(new_state == DLG_STATE_DELETED && old_state != DLG_STATE_DELETED){ LM_DBG("removing dialog with h_entry %u and h_id %u\n", dlg->h_entry, dlg->h_id); /*destroy linkers */ dlg_lock_dlg(dlg); destroy_linkers(dlg->profile_links, 0); dlg->profile_links = NULL; dlg_unlock_dlg(dlg); /* remove from timer */ ret = remove_dlg_timer(&dlg->tl); if (ret < 0) { LM_CRIT("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) { LM_DBG("dlg already expired (not in timer list) %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 { /* successfully removed from timer list */ unref++; } if (req==NULL) { /* set new msg & processing context */ if (push_new_processing_context( dlg, &old_ctx, &new_ctx, &fake_msg)==0) { /* dialog terminated (BYE) */ run_dlg_callbacks( DLGCB_TERMINATED, dlg, fake_msg, DLG_DIR_NONE, NULL, 0); /* reset the processing context */ if (current_processing_ctx == NULL) *new_ctx = NULL; else context_destroy(CONTEXT_GLOBAL, *new_ctx); current_processing_ctx = old_ctx; } /* no CB run in case of failure FIXME */ } else { /* we should have the msg and context from upper levels */ /* dialog terminated (BYE) */ run_dlg_callbacks( DLGCB_TERMINATED, dlg, req, DLG_DIR_NONE, NULL, 0); } LM_DBG("first final reply\n"); /* derefering the dialog */ unref_dlg(dlg, unref); if_update_stat( dlg_enable_stats, active_dlgs, -1); } if(new_state == DLG_STATE_DELETED && old_state == DLG_STATE_DELETED ) { /* trash the dialog from DB and memory */ LM_DBG("second final reply\n"); /* delete the dialog from DB */ if (should_remove_dlg_db()) remove_dialog_from_db(dlg); /* force delete from mem */ unref_dlg(dlg, unref); } }
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]); *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: dlg->flags |= DLG_FLAG_HASBYE; dlg->state = DLG_STATE_DELETED; *unref = 1; /* unref from hash -> dialog ended */ break; 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: 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_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_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); LM_DBG("dialog %p changed from state %d to " "state %d, due event %d\n",dlg,*old_state,*new_state,event); }