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); }
/*! * \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 *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); }