73f82eb745b2e5c32187e4d1226862e83315664f
[sip-router] / modules / tm / t_hooks.c
1 /*
2  * $Id$
3  *
4  * Copyright (C) 2001-2003 FhG Fokus
5  *
6  * This file is part of ser, a free SIP server.
7  *
8  * ser is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * For a license to use the ser software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * ser is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License 
24  * along with this program; if not, write to the Free Software 
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 /*
28  * History:
29  * --------
30  *  2003-03-19  replaced all the mallocs/frees w/ pkg_malloc/pkg_free (andrei)
31  *  2003-12-04  global callbacks moved into transaction callbacks;
32  *              multiple events per callback added; single list per
33  *              transaction for all its callbacks (bogdan)
34  *  2004-08-23  user avp(attribute value pair) added -> making avp list
35  *              available in callbacks (bogdan)
36  * 2007-03-08  membar_write() used in insert_tmcb(...) (andrei)
37  * 2007-03-14  added *_SENT callbacks (andrei)
38  * 2007-03-23  added local_req_in callbacks support (andrei)
39  */
40
41 #include "defs.h"
42
43
44 #include "stdlib.h"
45 #include "../../dprint.h"
46 #include "../../error.h"
47 #include "../../mem/mem.h"
48 #include "../../usr_avp.h"
49 #include "../../atomic_ops.h" /* membar_write() */
50 #include "t_hooks.h"
51 #include "t_lookup.h"
52 #include "t_funcs.h"
53
54
55 struct tmcb_head_list* req_in_tmcb_hl = 0;
56 struct tmcb_head_list* local_req_in_tmcb_hl = 0;
57
58
59
60 int init_tmcb_lists()
61 {
62         req_in_tmcb_hl = (struct tmcb_head_list*)shm_malloc
63                 ( sizeof(struct tmcb_head_list) );
64         local_req_in_tmcb_hl = (struct tmcb_head_list*)shm_malloc
65                 ( sizeof(struct tmcb_head_list) );
66         if ((req_in_tmcb_hl==0) || (local_req_in_tmcb_hl==0)) {
67                 LOG(L_CRIT,"ERROR:tm:init_tmcb_lists: no more shared mem\n");
68                 goto error;
69         }
70         req_in_tmcb_hl->first = 0;
71         req_in_tmcb_hl->reg_types = 0;
72         local_req_in_tmcb_hl->first = 0;
73         local_req_in_tmcb_hl->reg_types = 0;
74         return 1;
75 error:
76         if (req_in_tmcb_hl){
77                 shm_free(req_in_tmcb_hl);
78                 req_in_tmcb_hl=0;
79         }
80         if(local_req_in_tmcb_hl){
81                 shm_free(local_req_in_tmcb_hl);
82                 local_req_in_tmcb_hl=0;
83         }
84         return -1;
85 }
86
87
88 void destroy_tmcb_lists()
89 {
90         struct tm_callback *cbp, *cbp_tmp;
91
92         if (req_in_tmcb_hl){
93                 for( cbp=req_in_tmcb_hl->first; cbp ; ) {
94                         cbp_tmp = cbp;
95                         cbp = cbp->next;
96                         if (cbp_tmp->param) shm_free( cbp_tmp->param );
97                         shm_free( cbp_tmp );
98                 }
99                 shm_free(req_in_tmcb_hl);
100                 req_in_tmcb_hl=0;
101         }
102         if(local_req_in_tmcb_hl){
103                 for( cbp=local_req_in_tmcb_hl->first; cbp ; ) {
104                         cbp_tmp = cbp;
105                         cbp = cbp->next;
106                         if (cbp_tmp->param) shm_free( cbp_tmp->param );
107                         shm_free( cbp_tmp );
108                 }
109                 shm_free(local_req_in_tmcb_hl);
110                 local_req_in_tmcb_hl=0;
111         }
112 }
113
114
115 int insert_tmcb(struct tmcb_head_list *cb_list, int types,
116                                                                         transaction_cb f, void *param )
117 {
118         struct tm_callback *cbp;
119
120         /* build a new callback structure */
121         if (!(cbp=shm_malloc( sizeof( struct tm_callback)))) {
122                 LOG(L_ERR, "ERROR:tm:insert_tmcb: out of shm. mem\n");
123                 return E_OUT_OF_MEM;
124         }
125
126         cb_list->reg_types |= types;
127         /* ... and fill it up */
128         cbp->callback = f;
129         cbp->param = param;
130         cbp->types = types;
131         /* link it into the proper place... */
132         cbp->next = cb_list->first;
133         if (cbp->next)
134                 cbp->id = cbp->next->id+1;
135         else
136                 cbp->id = 0;
137         membar_write(); /* make sure all the changes to cbp are visible on all cpus
138                                            before we update cb_list->first. This is needed for
139                                            three reasons: the compiler might reorder some of the 
140                                            writes, the cpu/cache could also reorder them with
141                                            respect to the visibility on other cpus
142                                            (e.g. some of the changes to cbp could be visible on
143                                             another cpu _after_ seeing cb_list->first=cbp) and
144                                            the "readers" (callbacks callers) do not use locks and
145                                            could be called simultaneously on another cpu.*/
146         cb_list->first = cbp;
147
148         return 1;
149 }
150
151
152
153 /* register a callback function 'f' for 'types' mask of events;
154  * will be called back whenever one of the events occurs in transaction module
155  * (global or per transaction, depending of event type)
156  * It _must_ be always called either with the REPLY_LOCK held, or before the
157  *  branches are created.
158  *  Special cases: TMCB_REQUEST_IN & TMCB_LOCAL_REQUEST_IN - must be called 
159  *                 from mod_init (before forking!).
160 */
161 int register_tmcb( struct sip_msg* p_msg, struct cell *t, int types,
162                                                                                         transaction_cb f, void *param )
163 {
164         //struct cell* t;
165         struct tmcb_head_list *cb_list;
166
167         /* are the callback types valid?... */
168         if ( types<0 || types>TMCB_MAX ) {
169                 LOG(L_CRIT, "BUG:tm:register_tmcb: invalid callback types: mask=%d\n",
170                         types);
171                 return E_BUG;
172         }
173         /* we don't register null functions */
174         if (f==0) {
175                 LOG(L_CRIT, "BUG:tm:register_tmcb: null callback function\n");
176                 return E_BUG;
177         }
178
179         if (types&TMCB_REQUEST_IN) {
180                 if (types!=TMCB_REQUEST_IN) {
181                         LOG(L_CRIT, "BUG:tm:register_tmcb: callback type TMCB_REQUEST_IN "
182                                 "can't be register along with types\n");
183                         return E_BUG;
184                 }
185                 cb_list = req_in_tmcb_hl;
186         }else if (types & TMCB_LOCAL_REQUEST_IN) {
187                 if (types!=TMCB_LOCAL_REQUEST_IN) {
188                         LOG(L_CRIT, "BUG:tm:register_tmcb: callback type"
189                                         " TMCB_LOCAL_REQUEST_IN can't be register along with"
190                                         " other types\n");
191                         return E_BUG;
192                 }
193                 cb_list = local_req_in_tmcb_hl;
194         } else {
195                 if (!t) {
196                         if (!p_msg) {
197                                 LOG(L_CRIT,"BUG:tm:register_tmcb: no sip_msg, nor transaction"
198                                         " given\n");
199                                 return E_BUG;
200                         }
201                         /* look for the transaction */
202                         if ( t_check(p_msg,0)!=1 ){
203                                 LOG(L_CRIT,"BUG:tm:register_tmcb: no transaction found\n");
204                                 return E_BUG;
205                         }
206                         if ( (t=get_t())==0 ) {
207                                 LOG(L_CRIT,"BUG:tm:register_tmcb: transaction found "
208                                         "is NULL\n");
209                                 return E_BUG;
210                         }
211                 }
212                 cb_list = &(t->tmcb_hl);
213         }
214
215         return insert_tmcb( cb_list, types, f, param );
216 }
217
218
219 static void run_trans_callbacks_internal(int type, struct cell *trans, 
220                                                                                         struct tmcb_params *params)
221 {
222         struct tm_callback    *cbp;
223         avp_list_t* backup_from, *backup_to, *backup_dom_from, *backup_dom_to, *backup_uri_from, *backup_uri_to;
224
225         backup_uri_from = set_avp_list(AVP_CLASS_URI | AVP_TRACK_FROM,
226                         &trans->uri_avps_from );
227         backup_uri_to = set_avp_list(AVP_CLASS_URI | AVP_TRACK_TO, 
228                         &trans->uri_avps_to );
229         backup_from = set_avp_list(AVP_CLASS_USER | AVP_TRACK_FROM, 
230                         &trans->user_avps_from );
231         backup_to = set_avp_list(AVP_CLASS_USER | AVP_TRACK_TO, 
232                         &trans->user_avps_to );
233         backup_dom_from = set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_FROM, 
234                         &trans->domain_avps_from);
235         backup_dom_to = set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_TO, 
236                         &trans->domain_avps_to);
237         for (cbp=trans->tmcb_hl.first; cbp; cbp=cbp->next)  {
238                 if ( (cbp->types)&type ) {
239                         DBG("DBG: trans=%p, callback type %d, id %d entered\n",
240                                 trans, type, cbp->id );
241                         params->param = &(cbp->param);
242                         cbp->callback( trans, type, params );
243                 }
244         }
245         set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_TO, backup_dom_to );
246         set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_FROM, backup_dom_from );
247         set_avp_list(AVP_CLASS_USER | AVP_TRACK_TO, backup_to );
248         set_avp_list(AVP_CLASS_USER | AVP_TRACK_FROM, backup_from );
249         set_avp_list(AVP_CLASS_URI | AVP_TRACK_TO, backup_uri_to );
250         set_avp_list(AVP_CLASS_URI | AVP_TRACK_FROM, backup_uri_from );
251 }
252
253
254
255 void run_trans_callbacks( int type , struct cell *trans,
256                                                 struct sip_msg *req, struct sip_msg *rpl, int code )
257 {
258         struct tmcb_params params;
259         if (trans->tmcb_hl.first==0 || ((trans->tmcb_hl.reg_types)&type)==0 )
260                 return;
261         memset (&params, 0, sizeof(params));
262         params.req = req;
263         params.rpl = rpl;
264         params.code = code;
265         run_trans_callbacks_internal(type, trans, &params);
266 }
267
268
269
270 #ifdef TMCB_ONSEND
271 void run_onsend_callbacks(int type, struct retr_buf* rbuf, int retr)
272 {
273         struct tmcb_params params;
274         struct cell * trans;
275
276         trans=rbuf->my_T;
277         if ( trans==0 || trans->tmcb_hl.first==0 || 
278                         ((trans->tmcb_hl.reg_types)&type)==0 )
279                 return;
280         memset (&params, 0, sizeof(params));
281         params.send_buf.s=rbuf->buffer;
282         params.send_buf.len=rbuf->buffer_len;
283         params.dst=&rbuf->dst;
284         params.is_retr=retr;
285         params.branch=rbuf->branch;
286         params.t_rbuf=rbuf;
287         params.code=rbuf->activ_type;
288         /* req, rpl */
289         run_trans_callbacks_internal(type, trans, &params);
290 }
291
292
293 void run_onsend_callbacks2(int type , struct retr_buf* rbuf, char* buf,
294                                                         int buf_len, struct dest_info* dst, int code)
295 {
296         struct tmcb_params params;
297         struct cell * trans;
298
299         trans=rbuf->my_T;
300         if ( trans==0 || trans->tmcb_hl.first==0 || 
301                         ((trans->tmcb_hl.reg_types)&type)==0 )
302                 return;
303         memset (&params, 0, sizeof(params));
304         params.send_buf.s=buf;
305         params.send_buf.len=buf_len;
306         params.dst=dst;
307         params.is_retr=0;
308         params.branch=rbuf->branch;
309         params.t_rbuf=rbuf;
310         params.code=code;
311         /* req, rpl */
312         run_trans_callbacks_internal(type, trans, &params);
313 }
314
315 #endif
316
317 static void run_reqin_callbacks_internal(struct tmcb_head_list* hl,
318                                                         struct cell *trans, struct tmcb_params* params)
319 {
320         struct tm_callback    *cbp;
321         avp_list_t* backup_from, *backup_to, *backup_dom_from, *backup_dom_to,
322                                 *backup_uri_from, *backup_uri_to;
323
324         if (hl==0 || hl->first==0) return;
325         backup_uri_from = set_avp_list(AVP_CLASS_URI | AVP_TRACK_FROM,
326                         &trans->uri_avps_from );
327         backup_uri_to = set_avp_list(AVP_CLASS_URI | AVP_TRACK_TO, 
328                         &trans->uri_avps_to );
329         backup_from = set_avp_list(AVP_CLASS_USER | AVP_TRACK_FROM, 
330                         &trans->user_avps_from );
331         backup_to = set_avp_list(AVP_CLASS_USER | AVP_TRACK_TO, 
332                         &trans->user_avps_to );
333         backup_dom_from = set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_FROM, 
334                         &trans->domain_avps_from);
335         backup_dom_to = set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_TO, 
336                         &trans->domain_avps_to);
337         for (cbp=hl->first; cbp; cbp=cbp->next)  {
338                 DBG("DBG: trans=%p, callback type %d, id %d entered\n",
339                         trans, cbp->types, cbp->id );
340                 params->param = &(cbp->param);
341                 cbp->callback( trans, cbp->types, params );
342         }
343         set_avp_list(AVP_CLASS_URI | AVP_TRACK_TO, backup_uri_to );
344         set_avp_list(AVP_CLASS_URI | AVP_TRACK_FROM, backup_uri_from );
345         set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_TO, backup_dom_to );
346         set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_FROM, backup_dom_from );
347         set_avp_list(AVP_CLASS_USER | AVP_TRACK_TO, backup_to );
348         set_avp_list(AVP_CLASS_USER | AVP_TRACK_FROM, backup_from );
349 }
350
351
352
353 void run_reqin_callbacks( struct cell *trans, struct sip_msg *req, int code )
354 {
355         static struct tmcb_params params;
356
357         if (req_in_tmcb_hl->first==0)
358                 return;
359         memset (&params, 0, sizeof(params));
360         params.req = req;
361         params.code = code;
362         
363         run_reqin_callbacks_internal(req_in_tmcb_hl, trans, &params);
364 }
365
366
367 void run_local_reqin_callbacks( struct cell *trans, struct sip_msg *req,
368                                                                 int code )
369 {
370         static struct tmcb_params params;
371
372         if (local_req_in_tmcb_hl->first==0)
373                 return;
374         memset (&params, 0, sizeof(params));
375         params.req = req;
376         params.code = code;
377         
378         run_reqin_callbacks_internal(local_req_in_tmcb_hl, trans, &params);
379 }