b7f11238454947e2a78fa31e6ff529b575b36838
[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 void run_trans_callbacks_internal(struct tmcb_head_list* cb_lst, int type,
220                                                                         struct cell *trans, 
221                                                                         struct tmcb_params *params)
222 {
223         struct tm_callback    *cbp;
224         avp_list_t* backup_from, *backup_to, *backup_dom_from, *backup_dom_to, *backup_uri_from, *backup_uri_to;
225
226         backup_uri_from = set_avp_list(AVP_CLASS_URI | AVP_TRACK_FROM,
227                         &trans->uri_avps_from );
228         backup_uri_to = set_avp_list(AVP_CLASS_URI | AVP_TRACK_TO, 
229                         &trans->uri_avps_to );
230         backup_from = set_avp_list(AVP_CLASS_USER | AVP_TRACK_FROM, 
231                         &trans->user_avps_from );
232         backup_to = set_avp_list(AVP_CLASS_USER | AVP_TRACK_TO, 
233                         &trans->user_avps_to );
234         backup_dom_from = set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_FROM, 
235                         &trans->domain_avps_from);
236         backup_dom_to = set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_TO, 
237                         &trans->domain_avps_to);
238         for (cbp=cb_lst->first; cbp; cbp=cbp->next)  {
239                 if ( (cbp->types)&type ) {
240                         DBG("DBG: trans=%p, callback type %d, id %d entered\n",
241                                 trans, type, cbp->id );
242                         params->param = &(cbp->param);
243                         cbp->callback( trans, type, params );
244                 }
245         }
246         set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_TO, backup_dom_to );
247         set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_FROM, backup_dom_from );
248         set_avp_list(AVP_CLASS_USER | AVP_TRACK_TO, backup_to );
249         set_avp_list(AVP_CLASS_USER | AVP_TRACK_FROM, backup_from );
250         set_avp_list(AVP_CLASS_URI | AVP_TRACK_TO, backup_uri_to );
251         set_avp_list(AVP_CLASS_URI | AVP_TRACK_FROM, backup_uri_from );
252 }
253
254
255
256 void run_trans_callbacks( int type , struct cell *trans,
257                                                 struct sip_msg *req, struct sip_msg *rpl, int code )
258 {
259         struct tmcb_params params;
260         if (trans->tmcb_hl.first==0 || ((trans->tmcb_hl.reg_types)&type)==0 )
261                 return;
262         memset (&params, 0, sizeof(params));
263         params.req = req;
264         params.rpl = rpl;
265         params.code = code;
266         run_trans_callbacks_internal(&trans->tmcb_hl, type, trans, &params);
267 }
268
269
270
271 #ifdef TMCB_ONSEND
272 void run_onsend_callbacks(int type, struct retr_buf* rbuf, int retr)
273 {
274         struct tmcb_params params;
275         struct cell * trans;
276
277         trans=rbuf->my_T;
278         if ( trans==0 || trans->tmcb_hl.first==0 || 
279                         ((trans->tmcb_hl.reg_types)&type)==0 )
280                 return;
281         memset (&params, 0, sizeof(params));
282         params.send_buf.s=rbuf->buffer;
283         params.send_buf.len=rbuf->buffer_len;
284         params.dst=&rbuf->dst;
285         params.is_retr=retr;
286         params.branch=rbuf->branch;
287         params.t_rbuf=rbuf;
288         params.code=rbuf->activ_type;
289         /* req, rpl */
290         run_trans_callbacks_internal(&trans->tmcb_hl, type, trans, &params);
291 }
292
293
294 void run_onsend_callbacks2(int type , struct retr_buf* rbuf, char* buf,
295                                                         int buf_len, struct dest_info* dst, int code)
296 {
297         struct tmcb_params params;
298         struct cell * trans;
299
300         trans=rbuf->my_T;
301         if ( trans==0 || trans->tmcb_hl.first==0 || 
302                         ((trans->tmcb_hl.reg_types)&type)==0 )
303                 return;
304         memset (&params, 0, sizeof(params));
305         params.send_buf.s=buf;
306         params.send_buf.len=buf_len;
307         params.dst=dst;
308         params.is_retr=0;
309         params.branch=rbuf->branch;
310         params.t_rbuf=rbuf;
311         params.code=code;
312         /* req, rpl */
313         run_trans_callbacks_internal(&trans->tmcb_hl, type, trans, &params);
314 }
315
316 #endif
317
318 static void run_reqin_callbacks_internal(struct tmcb_head_list* hl,
319                                                         struct cell *trans, struct tmcb_params* params)
320 {
321         struct tm_callback    *cbp;
322         avp_list_t* backup_from, *backup_to, *backup_dom_from, *backup_dom_to,
323                                 *backup_uri_from, *backup_uri_to;
324
325         if (hl==0 || hl->first==0) return;
326         backup_uri_from = set_avp_list(AVP_CLASS_URI | AVP_TRACK_FROM,
327                         &trans->uri_avps_from );
328         backup_uri_to = set_avp_list(AVP_CLASS_URI | AVP_TRACK_TO, 
329                         &trans->uri_avps_to );
330         backup_from = set_avp_list(AVP_CLASS_USER | AVP_TRACK_FROM, 
331                         &trans->user_avps_from );
332         backup_to = set_avp_list(AVP_CLASS_USER | AVP_TRACK_TO, 
333                         &trans->user_avps_to );
334         backup_dom_from = set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_FROM, 
335                         &trans->domain_avps_from);
336         backup_dom_to = set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_TO, 
337                         &trans->domain_avps_to);
338         for (cbp=hl->first; cbp; cbp=cbp->next)  {
339                 DBG("DBG: trans=%p, callback type %d, id %d entered\n",
340                         trans, cbp->types, cbp->id );
341                 params->param = &(cbp->param);
342                 cbp->callback( trans, cbp->types, params );
343         }
344         set_avp_list(AVP_CLASS_URI | AVP_TRACK_TO, backup_uri_to );
345         set_avp_list(AVP_CLASS_URI | AVP_TRACK_FROM, backup_uri_from );
346         set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_TO, backup_dom_to );
347         set_avp_list(AVP_CLASS_DOMAIN | AVP_TRACK_FROM, backup_dom_from );
348         set_avp_list(AVP_CLASS_USER | AVP_TRACK_TO, backup_to );
349         set_avp_list(AVP_CLASS_USER | AVP_TRACK_FROM, backup_from );
350 }
351
352
353
354 void run_reqin_callbacks( struct cell *trans, struct sip_msg *req, int code )
355 {
356         static struct tmcb_params params;
357
358         if (req_in_tmcb_hl->first==0)
359                 return;
360         memset (&params, 0, sizeof(params));
361         params.req = req;
362         params.code = code;
363         
364         run_reqin_callbacks_internal(req_in_tmcb_hl, trans, &params);
365 }
366
367
368 void run_local_reqin_callbacks( struct cell *trans, struct sip_msg *req,
369                                                                 int code )
370 {
371         static struct tmcb_params params;
372
373         if (local_req_in_tmcb_hl->first==0)
374                 return;
375         memset (&params, 0, sizeof(params));
376         params.req = req;
377         params.code = code;
378         
379         run_reqin_callbacks_internal(local_req_in_tmcb_hl, trans, &params);
380 }