3301f5447a4f1e0e5f776fd98eb9031698234b65
[sip-router] / modules / tm / h_table.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  * History
28  * -------
29  * 2003-03-06  200/INV to-tag list deallocation added;
30  *             setting "kill_reason" moved in here -- it is moved
31  *             from transaction state to a static var(jiri)
32  * 2003-03-16  removed _TOTAG (jiri)
33  * 2003-03-30  set_kr for requests only (jiri)
34  * 2003-04-04  bug_fix: REQ_IN callback not called for local 
35  *             UAC transactions (jiri)
36  * 2003-09-12  timer_link->tg will be set only if EXTRA_DEBUG (andrei)
37  * 2003-12-04  global callbacks replaceed with callbacks per transaction;
38  *             completion callback merged into them as LOCAL_COMPETED (bogdan)
39  * 2004-02-11  FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri)
40  * 2004-02-13  t->is_invite and t->local replaced with flags;
41  *             timer_link.payload removed (bogdan)
42  */
43
44 #include <stdlib.h>
45
46
47 #include "../../mem/shm_mem.h"
48 #include "../../hash_func.h"
49 #include "../../dprint.h"
50 #include "../../md5utils.h"
51 #include "../../ut.h"
52 #include "../../globals.h"
53 #include "../../error.h"
54 #include "../../fifo_server.h"
55 #include "../../unixsock_server.h"
56 #include "defs.h"
57 #include "t_reply.h"
58 #include "t_cancel.h"
59 #include "t_stats.h"
60 #include "h_table.h"
61
62 static enum kill_reason kr;
63
64 /* pointer to the big table where all the transaction data
65    lives */
66 static struct s_table*  tm_table;
67
68
69
70 void set_kr( enum kill_reason _kr )
71 {
72         kr|=_kr;
73 }
74
75
76 enum kill_reason get_kr() {
77         return kr;
78 }
79
80
81 void lock_hash(int i) 
82 {
83         lock(&tm_table->entrys[i].mutex);
84 }
85
86
87 void unlock_hash(int i) 
88 {
89         unlock(&tm_table->entrys[i].mutex);
90 }
91
92
93 struct s_table* get_tm_table()
94 {
95         return tm_table;
96 }
97
98
99 unsigned int transaction_count( void )
100 {
101         unsigned int i;
102         unsigned int count;
103
104         count=0;        
105         for (i=0; i<TABLE_ENTRIES; i++) 
106                 count+=tm_table->entrys[i].cur_entries;
107         return count;
108 }
109
110
111
112 void free_cell( struct cell* dead_cell )
113 {
114         char *b;
115         int i;
116         struct sip_msg *rpl;
117         struct totag_elem *tt, *foo;
118         struct tm_callback *cbs, *cbs_tmp;
119
120         release_cell_lock( dead_cell );
121         shm_lock();
122
123         /* UA Server */
124         if ( dead_cell->uas.request )
125                 sip_msg_free_unsafe( dead_cell->uas.request );
126         if ( dead_cell->uas.response.buffer )
127                 shm_free_unsafe( dead_cell->uas.response.buffer );
128
129         /* callbacks */
130         for( cbs=dead_cell->tmcb_hl.first ; cbs ; ) {
131                 cbs_tmp = cbs;
132                 cbs = cbs->next;
133                 shm_free_unsafe( cbs_tmp );
134         }
135
136         /* UA Clients */
137         for ( i =0 ; i<dead_cell->nr_of_outgoings;  i++ )
138         {
139                 /* retransmission buffer */
140                 if ( (b=dead_cell->uac[i].request.buffer) )
141                         shm_free_unsafe( b );
142                 b=dead_cell->uac[i].local_cancel.buffer;
143                 if (b!=0 && b!=BUSY_BUFFER)
144                         shm_free_unsafe( b );
145                 rpl=dead_cell->uac[i].reply;
146                 if (rpl && rpl!=FAKED_REPLY) {
147                         sip_msg_free_unsafe( rpl );
148                 }
149         }
150
151         /* collected to tags */
152         tt=dead_cell->fwded_totags;
153         while(tt) {
154                 foo=tt->next;
155                 shm_free_unsafe(tt->tag.s);
156                 shm_free_unsafe(tt);
157                 tt=foo;
158         }
159
160         /* the cell's body */
161         shm_free_unsafe( dead_cell );
162
163         shm_unlock();
164 }
165
166
167
168 static inline void init_synonym_id( struct cell *t )
169 {
170         struct sip_msg *p_msg;
171         int size;
172         char *c;
173         unsigned int myrand;
174
175         if (!syn_branch) {
176                 p_msg=t->uas.request;
177                 if (p_msg) {
178                         /* char value of a proxied transaction is
179                            calculated out of header-fileds forming
180                            transaction key
181                         */
182                         char_msg_val( p_msg, t->md5 );
183                 } else {
184                         /* char value for a UAC transaction is created
185                            randomly -- UAC is an originating stateful element 
186                            which cannot be refreshed, so the value can be
187                            anything
188                         */
189                         /* HACK : not long enough */
190                         myrand=rand();
191                         c=t->md5;
192                         size=MD5_LEN;
193                         memset(c, '0', size );
194                         int2reverse_hex( &c, &size, myrand );
195                 }
196         }
197 }
198
199 static void inline init_branches(struct cell *t)
200 {
201         unsigned int i;
202         struct ua_client *uac;
203
204         for(i=0;i<MAX_BRANCHES;i++)
205         {
206                 uac=&t->uac[i];
207                 uac->request.my_T = t;
208                 uac->request.branch = i;
209 #ifdef EXTRA_DEBUG
210                 uac->request.fr_timer.tg = TG_FR;
211                 uac->request.retr_timer.tg = TG_RT;
212 #endif
213                 uac->local_cancel=uac->request;
214         }
215 }
216
217
218 struct cell*  build_cell( struct sip_msg* p_msg )
219 {
220         struct cell* new_cell;
221         int          sip_msg_len;
222
223         /* allocs a new cell */
224         new_cell = (struct cell*)shm_malloc( sizeof( struct cell ) );
225         if  ( !new_cell ) {
226                 ser_error=E_OUT_OF_MEM;
227                 return NULL;
228         }
229
230         /* filling with 0 */
231         memset( new_cell, 0, sizeof( struct cell ) );
232
233         /* UAS */
234 #ifdef EXTRA_DEBUG
235         new_cell->uas.response.retr_timer.tg=TG_RT;
236         new_cell->uas.response.fr_timer.tg=TG_FR;
237 #endif
238         new_cell->uas.response.my_T=new_cell;
239
240         /* enter callback, which may potentially want to parse some stuff,
241          * before the request is shmem-ized */
242         if ( p_msg && has_reqin_tmcbs() )
243                 run_reqin_callbacks( new_cell, p_msg, p_msg->REQ_METHOD);
244
245         if (p_msg) {
246                 new_cell->uas.request = sip_msg_cloner(p_msg,&sip_msg_len);
247                 if (!new_cell->uas.request)
248                         goto error;
249                 new_cell->uas.end_request=((char*)new_cell->uas.request)+sip_msg_len;
250         }
251
252         /* UAC */
253         init_branches(new_cell);
254
255         new_cell->relaied_reply_branch   = -1;
256         /* new_cell->T_canceled = T_UNDEFINED; */
257 #ifdef EXTRA_DEBUG
258         new_cell->wait_tl.tg=TG_WT;
259         new_cell->dele_tl.tg=TG_DEL;
260 #endif
261
262         init_synonym_id(new_cell);
263         init_cell_lock(  new_cell );
264         return new_cell;
265
266 error:
267         shm_free(new_cell);
268         return NULL;
269 }
270
271
272
273 /* Release all the data contained by the hash table. All the aux. structures
274  *  as sems, lists, etc, are also released */
275 void free_hash_table(  )
276 {
277         struct cell* p_cell;
278         struct cell* tmp_cell;
279         int    i;
280
281         if (tm_table)
282         {
283                 /* remove the data contained by each entry */
284                 for( i = 0 ; i<TABLE_ENTRIES; i++)
285                 {
286                         release_entry_lock( (tm_table->entrys)+i );
287                         /* delete all synonyms at hash-collision-slot i */
288                         p_cell=tm_table->entrys[i].first_cell;
289                         for( ; p_cell; p_cell = tmp_cell )
290                         {
291                                 tmp_cell = p_cell->next_cell;
292                                 free_cell( p_cell );
293                         }
294                 }
295                 shm_free(tm_table);
296         }
297 }
298
299
300
301
302 /*
303  */
304 struct s_table* init_hash_table()
305 {
306         int              i;
307
308         /*allocs the table*/
309         tm_table= (struct s_table*)shm_malloc( sizeof( struct s_table ) );
310         if ( !tm_table) {
311                 LOG(L_ERR, "ERROR: init_hash_table: no shmem for TM table\n");
312                 goto error0;
313         }
314
315         memset( tm_table, 0, sizeof (struct s_table ) );
316
317         /* try first allocating all the structures needed for syncing */
318         if (lock_initialize()==-1)
319                 goto error1;
320
321         /* inits the entrys */
322         for(  i=0 ; i<TABLE_ENTRIES; i++ )
323         {
324                 init_entry_lock( tm_table, (tm_table->entrys)+i );
325                 tm_table->entrys[i].next_label = rand();
326         }
327
328         return  tm_table;
329
330 error1:
331         free_hash_table( );
332 error0:
333         return 0;
334 }
335
336
337
338
339 /*  Takes an already created cell and links it into hash table on the
340  *  appropiate entry. */
341 void insert_into_hash_table_unsafe( struct cell * p_cell, unsigned int _hash )
342 {
343         struct entry* p_entry;
344
345         p_cell->hash_index=_hash;
346
347         /* locates the apropiate entry */
348         p_entry = &tm_table->entrys[ _hash ];
349
350         p_cell->label = p_entry->next_label++;
351         if ( p_entry->last_cell )
352         {
353                 p_entry->last_cell->next_cell = p_cell;
354                 p_cell->prev_cell = p_entry->last_cell;
355         } else p_entry->first_cell = p_cell;
356
357         p_entry->last_cell = p_cell;
358
359         /* update stats */
360         p_entry->cur_entries++;
361         p_entry->acc_entries++;
362         t_stats_new( is_local(p_cell) );
363 }
364
365
366
367 #ifdef _OBSOLETED
368 void insert_into_hash_table( struct cell * p_cell)
369 {
370         LOCK_HASH(p_cell->hash_index);
371         insert_into_hash_table_unsafe(  p_cell );
372         UNLOCK_HASH(p_cell->hash_index);
373 }
374 #endif
375
376
377
378
379 /*  Un-link a  cell from hash_table, but the cell itself is not released */
380 void remove_from_hash_table_unsafe( struct cell * p_cell)
381 {
382         struct entry*  p_entry  = &(tm_table->entrys[p_cell->hash_index]);
383
384         /* unlink the cell from entry list */
385         /* lock( &(p_entry->mutex) ); */
386
387         if ( p_cell->prev_cell )
388                 p_cell->prev_cell->next_cell = p_cell->next_cell;
389         else
390                 p_entry->first_cell = p_cell->next_cell;
391
392         if ( p_cell->next_cell )
393                 p_cell->next_cell->prev_cell = p_cell->prev_cell;
394         else
395                 p_entry->last_cell = p_cell->prev_cell;
396         /* update stats */
397 #       ifdef EXTRA_DEBUG
398         if (p_entry->cur_entries==0) {
399                 LOG(L_CRIT, "BUG: bad things happened: cur_entries=0\n");
400                 abort();
401         }
402 #       endif
403         p_entry->cur_entries--;
404         t_stats_deleted( is_local(p_cell) );
405
406         /* unlock( &(p_entry->mutex) ); */
407 }
408
409 /* print accumulated distribution of the hash table */
410 int fifo_hash( FILE *stream, char *response_file )
411 {
412         FILE *reply_file;
413         unsigned int i;
414
415         reply_file=open_reply_pipe(response_file);
416         if (reply_file==0) {
417                 LOG(L_ERR, "ERROR: fifo_hash: file '%s' not opened\n", 
418                         response_file);
419                 return -1;
420         }
421         fputs( "200 ok\n\tcurrent\ttotal\n", reply_file);
422         for (i=0; i<TABLE_ENTRIES; i++) {
423                 fprintf(reply_file, "%d.\t%lu\t%lu\n", 
424                         i, tm_table->entrys[i].cur_entries ,
425                         tm_table->entrys[i].acc_entries );
426         }
427         fclose(reply_file);
428         return 1;
429 }
430
431
432 int unixsock_hash(str* msg)
433 {
434         unsigned int i, ret;
435
436         ret = 0;
437         unixsock_reply_asciiz( "200 OK\n\tcurrent\ttotal\n");
438
439         for (i = 0; i < TABLE_ENTRIES; i++) {
440                 if (unixsock_reply_printf("%d.\t%lu\t%lu\n", 
441                                           i, tm_table->entrys[i].cur_entries,
442                                           tm_table->entrys[i].acc_entries
443                                           ) < 0) {
444                         unixsock_reply_reset();
445                         unixsock_reply_asciiz("500 Error while creating reply\n");
446                         ret = -1;
447                         break;
448                 }
449         }
450
451         if (unixsock_reply_send() < 0) {
452                 ret = -1;
453         }
454         return ret;
455 }