modules/tm/t_hooks.c
3881f12c
 /*
84d8e165
  * Copyright (C) 2001-2003 FhG Fokus
7dd0b342
  *
f1b15228
  * This file is part of Kamailio, a free SIP server.
7dd0b342
  *
f1b15228
  * Kamailio is free software; you can redistribute it and/or modify
7dd0b342
  * 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
  *
f1b15228
  * Kamailio is distributed in the hope that it will be useful,
7dd0b342
  * 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 
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
3881f12c
  */
 
ff979952
 #include "defs.h"
 
7dd0b342
 
caf80ae6
 #include "stdlib.h"
 #include "../../dprint.h"
 #include "../../error.h"
8710b7ee
 #include "../../mem/mem.h"
2be21e31
 #include "../../usr_avp.h"
620a704b
 #include "../../atomic_ops.h" /* membar_write() */
3881f12c
 #include "t_hooks.h"
a70eb298
 #include "t_lookup.h"
 #include "t_funcs.h"
3881f12c
 
 
a70eb298
 struct tmcb_head_list* req_in_tmcb_hl = 0;
5c953757
 struct tmcb_head_list* local_req_in_tmcb_hl = 0;
a70eb298
 
94942cba
 struct tm_early_cb {
 	unsigned int msgid;
 	struct tmcb_head_list cb_list;
 } tmcb_early_hl = { 0, {0, 0} };
a70eb298
 
94942cba
 struct tmcb_head_list* get_early_tmcb_list(struct sip_msg *msg)
 {
 	struct tm_callback *cbp, *cbp_tmp;
 	if (msg->id!=tmcb_early_hl.msgid) {
 		for( cbp=(struct tm_callback*)tmcb_early_hl.cb_list.first; cbp ; ) {
 			cbp_tmp = cbp;
 			cbp = cbp->next;
 			if (cbp_tmp->param && cbp_tmp->release)
 					cbp_tmp->release( cbp_tmp->param );
 			shm_free( cbp_tmp );
 		}
 		memset(&tmcb_early_hl.cb_list, 0, sizeof(struct tmcb_head_list));
 		tmcb_early_hl.msgid = msg->id;
 	}
 	return &tmcb_early_hl.cb_list;
 }
 
 void set_early_tmcb_list(struct sip_msg *msg, struct cell *t)
 {
a9c4a00b
 	if (msg->id==tmcb_early_hl.msgid) {
94942cba
 		t->tmcb_hl = tmcb_early_hl.cb_list;
 		memset(&tmcb_early_hl.cb_list, 0, sizeof(struct tmcb_head_list));
 		tmcb_early_hl.msgid = 0;
 	}
 }
a70eb298
 
 int init_tmcb_lists()
 {
 	req_in_tmcb_hl = (struct tmcb_head_list*)shm_malloc
 		( sizeof(struct tmcb_head_list) );
5c953757
 	local_req_in_tmcb_hl = (struct tmcb_head_list*)shm_malloc
 		( sizeof(struct tmcb_head_list) );
 	if ((req_in_tmcb_hl==0) || (local_req_in_tmcb_hl==0)) {
84d8e165
 		LOG(L_CRIT,"ERROR:tm:init_tmcb_lists: no more shared mem\n");
5c953757
 		goto error;
a70eb298
 	}
 	req_in_tmcb_hl->first = 0;
 	req_in_tmcb_hl->reg_types = 0;
5c953757
 	local_req_in_tmcb_hl->first = 0;
 	local_req_in_tmcb_hl->reg_types = 0;
a70eb298
 	return 1;
5c953757
 error:
 	if (req_in_tmcb_hl){
 		shm_free(req_in_tmcb_hl);
 		req_in_tmcb_hl=0;
 	}
 	if(local_req_in_tmcb_hl){
 		shm_free(local_req_in_tmcb_hl);
 		local_req_in_tmcb_hl=0;
 	}
 	return -1;
a70eb298
 }
 
 
 void destroy_tmcb_lists()
3881f12c
 {
a70eb298
 	struct tm_callback *cbp, *cbp_tmp;
3881f12c
 
5c953757
 	if (req_in_tmcb_hl){
6884496e
 		for( cbp=(struct tm_callback*)req_in_tmcb_hl->first; cbp ; ) {
5c953757
 			cbp_tmp = cbp;
 			cbp = cbp->next;
94942cba
 			if (cbp_tmp->param && cbp_tmp->release)
 					cbp_tmp->release( cbp_tmp->param );
5c953757
 			shm_free( cbp_tmp );
 		}
 		shm_free(req_in_tmcb_hl);
 		req_in_tmcb_hl=0;
 	}
 	if(local_req_in_tmcb_hl){
6884496e
 		for( cbp=(struct tm_callback*)local_req_in_tmcb_hl->first; cbp ; ) {
5c953757
 			cbp_tmp = cbp;
 			cbp = cbp->next;
94942cba
 			if (cbp_tmp->param && cbp_tmp->release)
 					cbp_tmp->release( cbp_tmp->param );
5c953757
 			shm_free( cbp_tmp );
 		}
 		shm_free(local_req_in_tmcb_hl);
 		local_req_in_tmcb_hl=0;
3881f12c
 	}
a70eb298
 }
 
 
6884496e
 
 /* lockless insert: should be always safe */
c4fa6c35
 int insert_tmcb(struct tmcb_head_list *cb_list, int types,
657f117b
 				transaction_cb f, void *param,
 				release_tmcb_param rel_func)
a70eb298
 {
 	struct tm_callback *cbp;
6884496e
 	struct tm_callback *old;
 
a70eb298
 
 	/* build a new callback structure */
 	if (!(cbp=shm_malloc( sizeof( struct tm_callback)))) {
1d4c5c00
 		LOG(L_ERR, "ERROR:tm:insert_tmcb: out of shm. mem\n");
3881f12c
 		return E_OUT_OF_MEM;
 	}
 
6884496e
 	atomic_or_int(&cb_list->reg_types, types);
a70eb298
 	/* ... and fill it up */
 	cbp->callback = f;
 	cbp->param = param;
657f117b
 	cbp->release = rel_func;
a70eb298
 	cbp->types = types;
91791c59
 	cbp->id=0;
6884496e
 	old=(struct tm_callback*)cb_list->first;
620a704b
 	/* link it into the proper place... */
6884496e
 	do{
 		cbp->next = old;
91791c59
 		/*
6884496e
 		if (cbp->next)
 			cbp->id = cbp->next->id+1;
 		else
 			cbp->id = 0;
91791c59
 		 -- callback ids are useless -- andrei */
 		membar_write_atomic_op();
 		old=(void*)atomic_cmpxchg_long((void*)&cb_list->first,
6884496e
 										(long)old, (long)cbp);
 	}while(old!=cbp->next);
a70eb298
 
 	return 1;
 }
 
 
 
 /* register a callback function 'f' for 'types' mask of events;
  * will be called back whenever one of the events occurs in transaction module
84d8e165
  * (global or per transaction, depending of event type)
620a704b
  * It _must_ be always called either with the REPLY_LOCK held, or before the
  *  branches are created.
5c953757
  *  Special cases: TMCB_REQUEST_IN & TMCB_LOCAL_REQUEST_IN - must be called 
  *                 from mod_init (before forking!).
a70eb298
 */
cb0a8be5
 int register_tmcb( struct sip_msg* p_msg, struct cell *t, int types,
657f117b
 				   transaction_cb f, void *param,
 				   release_tmcb_param rel_func)
a70eb298
 {
cb0a8be5
 	//struct cell* t;
a70eb298
 	struct tmcb_head_list *cb_list;
 
 	/* are the callback types valid?... */
f4aaeea3
 	if ( types<0 || types>TMCB_MAX ) {
a70eb298
 		LOG(L_CRIT, "BUG:tm:register_tmcb: invalid callback types: mask=%d\n",
 			types);
 		return E_BUG;
 	}
82679e88
 	/* we don't register null functions */
 	if (f==0) {
 		LOG(L_CRIT, "BUG:tm:register_tmcb: null callback function\n");
 		return E_BUG;
 	}
 
4f24ca9a
 	if ((types!=TMCB_MAX) && (types&TMCB_REQUEST_IN)) {
a70eb298
 		if (types!=TMCB_REQUEST_IN) {
 			LOG(L_CRIT, "BUG:tm:register_tmcb: callback type TMCB_REQUEST_IN "
 				"can't be register along with types\n");
 			return E_BUG;
 		}
 		cb_list = req_in_tmcb_hl;
4f24ca9a
 	}else if ((types!=TMCB_MAX) && (types & TMCB_LOCAL_REQUEST_IN)) {
5c953757
 		if (types!=TMCB_LOCAL_REQUEST_IN) {
 			LOG(L_CRIT, "BUG:tm:register_tmcb: callback type"
 					" TMCB_LOCAL_REQUEST_IN can't be register along with"
 					" other types\n");
 			return E_BUG;
 		}
 		cb_list = local_req_in_tmcb_hl;
a70eb298
 	} else {
cb0a8be5
 		if (!t) {
 			if (!p_msg) {
 				LOG(L_CRIT,"BUG:tm:register_tmcb: no sip_msg, nor transaction"
 					" given\n");
 				return E_BUG;
 			}
 			/* look for the transaction */
94942cba
 			t=get_t();
 			if ( t!=0 && t!=T_UNDEFINED) {
 				cb_list = &(t->tmcb_hl);
 			} else {
 				cb_list = get_early_tmcb_list(p_msg);
cb0a8be5
 			}
94942cba
 		} else {
 			cb_list = &(t->tmcb_hl);
a70eb298
 		}
 	}
 
657f117b
 	return insert_tmcb( cb_list, types, f, param, rel_func );
a70eb298
 }
3881f12c
 
a70eb298
 
0277f618
 void run_trans_callbacks_internal(struct tmcb_head_list* cb_lst, int type,
 									struct cell *trans, 
 									struct tmcb_params *params)
a70eb298
 {
 	struct tm_callback    *cbp;
51b8201f
 	avp_list_t* backup_from, *backup_to, *backup_dom_from, *backup_dom_to, *backup_uri_from, *backup_uri_to;
074517db
 #ifdef WITH_XAVP
 	sr_xavp_t **backup_xavps;
 #endif
a70eb298
 
5c953757
 	backup_uri_from = set_avp_list(AVP_CLASS_URI | AVP_TRACK_FROM,
 			&trans->uri_avps_from );
 	backup_uri_to = set_avp_list(AVP_CLASS_URI | AVP_TRACK_TO, 
 			&trans->uri_avps_to );
 	backup_from = set_avp_list(AVP_CLASS_USER | AVP_TRACK_FROM, 
 			&trans->user_avps_from );
 	backup_to = set_avp_list(AVP_CLASS_USER | AVP_TRACK_TO, 
 			&trans->user_avps_to );
 	backup_dom_from = set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_FROM, 
 			&trans->domain_avps_from);
 	backup_dom_to = set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_TO, 
 			&trans->domain_avps_to);
074517db
 #ifdef WITH_XAVP
 	backup_xavps = xavp_set_list(&trans->xavps_list);
 #endif
 
91791c59
 	cbp=(struct tm_callback*)cb_lst->first;
 	while(cbp){
 		membar_depends(); /* make sure the cache has the correct cbp 
 							 contents */
a70eb298
 		if ( (cbp->types)&type ) {
 			DBG("DBG: trans=%p, callback type %d, id %d entered\n",
 				trans, type, cbp->id );
9217a640
 			params->param = &(cbp->param);
 			cbp->callback( trans, type, params );
a70eb298
 		}
91791c59
 		cbp=cbp->next;
a70eb298
 	}
d002d02d
 	set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_TO, backup_dom_to );
 	set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_FROM, backup_dom_from );
 	set_avp_list(AVP_CLASS_USER | AVP_TRACK_TO, backup_to );
 	set_avp_list(AVP_CLASS_USER | AVP_TRACK_FROM, backup_from );
51b8201f
 	set_avp_list(AVP_CLASS_URI | AVP_TRACK_TO, backup_uri_to );
 	set_avp_list(AVP_CLASS_URI | AVP_TRACK_FROM, backup_uri_from );
074517db
 #ifdef WITH_XAVP
 	xavp_set_list(backup_xavps);
 #endif
3881f12c
 }
 
a70eb298
 
 
9217a640
 void run_trans_callbacks( int type , struct cell *trans,
 						struct sip_msg *req, struct sip_msg *rpl, int code )
 {
 	struct tmcb_params params;
 	if (trans->tmcb_hl.first==0 || ((trans->tmcb_hl.reg_types)&type)==0 )
 		return;
 	memset (&params, 0, sizeof(params));
 	params.req = req;
 	params.rpl = rpl;
 	params.code = code;
0277f618
 	run_trans_callbacks_internal(&trans->tmcb_hl, type, trans, &params);
9217a640
 }
 
 
 
68017b5d
 void run_trans_callbacks_with_buf(int type, struct retr_buf* rbuf,
                                   struct sip_msg* req, struct sip_msg* repl,
                                   short flags)
9217a640
 {
 	struct tmcb_params params;
 	struct cell * trans;
 
 	trans=rbuf->my_T;
 	if ( trans==0 || trans->tmcb_hl.first==0 || 
 			((trans->tmcb_hl.reg_types)&type)==0 )
 		return;
6aa46ec4
 	INIT_TMCB_ONSEND_PARAMS(params, req, repl, rbuf, &rbuf->dst, rbuf->buffer,
 					rbuf->buffer_len, flags, rbuf->branch, rbuf->activ_type);
9217a640
 	/* req, rpl */
0277f618
 	run_trans_callbacks_internal(&trans->tmcb_hl, type, trans, &params);
9217a640
 }
 
 
68017b5d
 void run_trans_callbacks_off_params(int type, struct cell* trans,
                                     struct tmcb_params* p)
9217a640
 {
 
6aa46ec4
 	if (p->t_rbuf==0) return;
9217a640
 	if ( trans==0 || trans->tmcb_hl.first==0 || 
 			((trans->tmcb_hl.reg_types)&type)==0 )
 		return;
66298eb4
 	run_trans_callbacks_internal(&trans->tmcb_hl, type, p->t_rbuf->my_T, p);
9217a640
 }
 
 
5c953757
 static void run_reqin_callbacks_internal(struct tmcb_head_list* hl,
 							struct cell *trans, struct tmcb_params* params)
3881f12c
 {
a70eb298
 	struct tm_callback    *cbp;
5c953757
 	avp_list_t* backup_from, *backup_to, *backup_dom_from, *backup_dom_to,
 				*backup_uri_from, *backup_uri_to;
e1f96b11
 #ifdef WITH_XAVP
 	sr_xavp_t **backup_xavps;
 #endif
5c953757
 
 	if (hl==0 || hl->first==0) return;
 	backup_uri_from = set_avp_list(AVP_CLASS_URI | AVP_TRACK_FROM,
 			&trans->uri_avps_from );
 	backup_uri_to = set_avp_list(AVP_CLASS_URI | AVP_TRACK_TO, 
 			&trans->uri_avps_to );
 	backup_from = set_avp_list(AVP_CLASS_USER | AVP_TRACK_FROM, 
 			&trans->user_avps_from );
 	backup_to = set_avp_list(AVP_CLASS_USER | AVP_TRACK_TO, 
 			&trans->user_avps_to );
 	backup_dom_from = set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_FROM, 
 			&trans->domain_avps_from);
 	backup_dom_to = set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_TO, 
 			&trans->domain_avps_to);
e1f96b11
 #ifdef WITH_XAVP
 	backup_xavps = xavp_set_list(&trans->xavps_list);
 #endif
6884496e
 	for (cbp=(struct tm_callback*)hl->first; cbp; cbp=cbp->next)  {
a70eb298
 		DBG("DBG: trans=%p, callback type %d, id %d entered\n",
 			trans, cbp->types, cbp->id );
5c953757
 		params->param = &(cbp->param);
 		cbp->callback( trans, cbp->types, params );
3881f12c
 	}
51b8201f
 	set_avp_list(AVP_CLASS_URI | AVP_TRACK_TO, backup_uri_to );
 	set_avp_list(AVP_CLASS_URI | AVP_TRACK_FROM, backup_uri_from );
d002d02d
 	set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_TO, backup_dom_to );
 	set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_FROM, backup_dom_from );
 	set_avp_list(AVP_CLASS_USER | AVP_TRACK_TO, backup_to );
 	set_avp_list(AVP_CLASS_USER | AVP_TRACK_FROM, backup_from );
e1f96b11
 #ifdef WITH_XAVP
 	xavp_set_list(backup_xavps);
 #endif
3881f12c
 }
5c953757
 
 
 
 void run_reqin_callbacks( struct cell *trans, struct sip_msg *req, int code )
 {
 	static struct tmcb_params params;
 
 	if (req_in_tmcb_hl->first==0)
 		return;
 	memset (&params, 0, sizeof(params));
 	params.req = req;
 	params.code = code;
 	
 	run_reqin_callbacks_internal(req_in_tmcb_hl, trans, &params);
 }
 
 
 void run_local_reqin_callbacks( struct cell *trans, struct sip_msg *req,
 								int code )
 {
 	static struct tmcb_params params;
 
 	if (local_req_in_tmcb_hl->first==0)
 		return;
 	memset (&params, 0, sizeof(params));
 	params.req = req;
 	params.code = code;
 	
 	run_reqin_callbacks_internal(local_req_in_tmcb_hl, trans, &params);
 }