backup
[lcr.git] / main.c
1 /*****************************************************************************\
2 **                                                                           **
3 ** Linux Call Router                                                         **
4 **                                                                           **
5 **---------------------------------------------------------------------------**
6 ** Copyright: Andreas Eversberg                                              **
7 **                                                                           **
8 ** Main function                                                             **
9 **                                                                           **
10 \*****************************************************************************/ 
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <sys/wait.h>
17 #include <sys/stat.h>
18 #include <unistd.h>
19 #include <signal.h>
20 #include <stdarg.h>
21 #include <fcntl.h>
22 #include <sys/ioctl.h>
23 #include <sys/file.h>
24 #include <errno.h>
25 #include <sys/mman.h>
26 #include <sys/resource.h>
27 #include "main.h"
28
29 MESSAGES
30
31 double now_d;
32 time_t now;
33 struct tm *now_tm;
34 struct timeval now_tv;
35 struct timezone now_tz;
36 #define GET_NOW() \
37         { \
38                 gettimeofday(&now_tv, &now_tz); \
39                 now_d = ((double)(now_tv.tv_usec))/1000000 + now_tv.tv_sec; \
40                 now = now_tv.tv_sec; \
41                 now_tm = localtime(&now); \
42         }
43 //#define DEBUG_DURATION
44
45 int global_debug = 0;
46 int quit=0;
47
48 pthread_mutex_t mutexd; // debug output mutex
49 //pthread_mutex_t mutext; // trace output mutex
50 pthread_mutex_t mutexe; // error output mutex
51
52 int memuse = 0;
53 int mmemuse = 0;
54 int cmemuse = 0;
55 int ememuse = 0;
56 int pmemuse = 0;
57 int amemuse = 0;
58 int rmemuse = 0;
59 int classuse = 0;
60 int fduse = 0;
61 int fhuse = 0;
62
63 char *debug_prefix = 0;
64 int debug_count = 0;
65 int last_debug = 0;
66 int debug_newline = 1;
67 int nooutput = 0;
68
69 static void debug(const char *function, int line, char *prefix, char *buffer)
70 {
71         /* if we have a new debug count, we add a mark */
72         if (last_debug != debug_count)
73         {
74                 last_debug = debug_count;
75                 if (!nooutput)
76                         printf("\033[34m--------------------- %04d.%02d.%02d %02d:%02d:%02d %06d\033[36m\n", now_tm->tm_year+1900, now_tm->tm_mon+1, now_tm->tm_mday, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec, debug_count%1000000);
77                 if (options.deb&DEBUG_LOG && global_debug)
78                         dprint(DBGM_MAN, 0, "--------------------- %04d.%02d.%02d %02d:%02d:%02d %06d\n", now_tm->tm_year+1900, now_tm->tm_mon+1, now_tm->tm_mday, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec, debug_count%1000000);
79         }
80
81         if (!nooutput)
82         {
83                 if (debug_newline)
84                         printf("\033[32m%06d %s\033[37m%s", debug_count%1000000, prefix?prefix:"", prefix?" ":"");
85                 if (function)
86                         printf("(in %s() line %d): %s", function, line, buffer);
87                 else
88                         printf("%s", buffer);
89         }
90
91         if (options.deb&DEBUG_LOG && global_debug)
92         {
93                 if (debug_newline)
94                 {
95                         if (function)
96                                 dprint(DBGM_MAN, 0, "%s%s(in %s() line %d): %s", prefix?prefix:"", prefix?" ":"", function, line, buffer);
97                         else
98                                 dprint(DBGM_MAN, 0, "%s%s: %s", prefix?prefix:"", prefix?" ":"", buffer);
99                 }
100         }
101
102         debug_newline = 0;
103         if (buffer[0])
104                 if (buffer[strlen(buffer)-1] == '\n')
105                         debug_newline = 1;
106 }
107
108
109 void _printdebug(const char *function, int line, unsigned long mask, const char *fmt, ...)
110 {
111         char buffer[4096];
112         va_list args;
113
114         if (!(options.deb & mask))
115                 return;
116         pthread_mutex_lock(&mutexd);
117
118         va_start(args,fmt);
119         VUNPRINT(buffer,sizeof(buffer)-1,fmt,args);
120         buffer[sizeof(buffer)-1]=0;
121         va_end(args);
122
123         debug(function, line, debug_prefix, buffer);
124
125         pthread_mutex_unlock(&mutexd);
126 }
127
128 void _printerror(const char *function, int line, const char *fmt, ...)
129 {
130         char buffer[4096];
131         va_list args;
132
133         pthread_mutex_lock(&mutexe);
134
135         va_start(args,fmt);
136         VUNPRINT(buffer,sizeof(buffer)-1,fmt,args);
137         buffer[sizeof(buffer)-1]=0;
138         va_end(args);
139
140         if (options.deb)
141                 debug(function, line, "ERROR", buffer);
142         else /* only if we do not debug */
143         {
144                 if (function)
145                         fprintf(stderr, "ERROR (in %s() line %d) %s", function, line, buffer);
146                 else
147                         fprintf(stderr, "ERROR %s", buffer);
148         }
149
150         pthread_mutex_unlock(&mutexe);
151 }
152
153
154 void sighandler(int sigset)
155 {
156         struct sched_param schedp;
157
158         if (sigset == SIGHUP)
159                 return;
160         if (sigset == SIGPIPE)
161                 return;
162         if (!quit)
163         {
164                 quit=1;
165                 /* set scheduler & priority */
166                 if (options.schedule > 1)
167                 {
168                         memset(&schedp, 0, sizeof(schedp));
169                         schedp.sched_priority = 0;
170                         sched_setscheduler(0, SCHED_OTHER, &schedp);
171                 }
172                 fprintf(stderr, "PBX: Signal received: %d\n", sigset);
173                 PERROR("Signal received: %d\n", sigset);
174         }
175 }
176
177
178 /*
179  * the main
180  */
181 #ifdef VOIP
182 #define ARGC (args.GetCount()+1)
183 #define ARGV(a) (args[a-1])
184 void PBXMain::Main(void)
185 {
186         PArgList &args = GetArguments();
187 #else
188 #define ARGC (argc)
189 #define ARGV(a) (argv[a])
190 int main(int argc, char *argv[])
191 {
192 #endif
193         int                     ret = -1;
194         int                     lockfd = -1; /* file lock */
195         struct message          *message;
196         class Port              *port;
197         class Endpoint          *epoint;
198         class Call              *call;
199         int                     i;
200         int                     all_idle;
201         char                    prefix_string[64];
202         struct sched_param      schedp;
203         char                    *debug_prefix = "alloc";
204         int                     created_mutexd = 0,/* created_mutext = 0,*/ created_mutexe = 0,
205                                 created_lock = 0, created_signal = 0, created_debug = 0;
206 #ifdef DEBUG_DURATION
207         time_t                  durationupdate;
208         double                  idle_duration, isdn_duration, port_duration, epoint_duration, call_duration, message_duration, admin_duration;
209         double                  start_d;
210 #endif
211         int                     idletime = 0, idlecheck = 0;
212         char                    debug_log[128];
213
214         /* current time */
215         GET_NOW();
216
217         /* show version */
218         printf("\n** %s  Version %s\n\n", NAME, VERSION_STRING);
219
220         /* show options */
221         if (ARGC <= 1)
222         {
223                 usage:
224                 printf("\n");
225                 printf("Usage: pbx (query | start | fork | rules | route)\n");
226                 printf("query     = Show available isdn ports.\n");
227                 printf("start     = Run pbx normally, abort with CTRL+C.\n");
228                 printf("fork      = Do daemon fork and run as background process.\n");
229                 printf("interface = Get help of available interface syntax.\n");
230                 printf("rules     = Get help of available routing rule syntax.\n");
231                 printf("rules [action] = Get individual help for given action.\n");
232 //              printf("route = Show current routing as it is parsed.\n");
233                 printf("\n");
234                 ret = 0;
235                 goto free;
236         }
237
238         /* init crc */
239         crc_init();
240
241         /* check for root (real or effective) */
242         if (getuid() && geteuid())
243         {
244                 fprintf(stderr, "Please run %s as super-user.\n", NAME);
245                 goto free;
246         }
247
248         /* the mutex init */
249         if (pthread_mutex_init(&mutexd, NULL))
250         {
251                 fprintf(stderr, "cannot create 'PDEBUG' mutex\n");
252                 goto free;
253         }
254         created_mutexd = 1;
255 //      if (pthread_mutex_init(&mutext, NULL))
256 //      {
257 //              fprintf(stderr, "cannot create 'trace' mutex\n");
258 //              goto free;
259 //      }
260 //      created_mutext = 1;
261         if (pthread_mutex_init(&mutexe, NULL))
262         {
263                 fprintf(stderr, "cannot create 'PERROR' mutex\n");
264                 goto free;
265         }
266         created_mutexe = 1;
267
268         /* show interface */
269         if (!(strcasecmp(ARGV(1),"interface")))
270         {
271                 doc_interface();
272                 ret = 0;
273                 goto free;
274         }
275
276         /* show rules */
277         if (!(strcasecmp(ARGV(1),"rules")))
278         {
279                 if (ARGC <= 2)
280                         doc_rules(NULL);
281                 else
282                         doc_rules(ARGV(2));
283                 ret = 0;
284                 goto free;
285         }
286
287         /* query available isdn ports */
288         if (!(strcasecmp(ARGV(1),"query")))
289         {
290                 mISDN_port_info();
291                 ret = 0;
292                 goto free;
293         }
294
295         /* read options */
296         if (read_options() == 0)
297                 goto free;
298
299         /* initialize stuff of the NT lib */
300         if (options.deb & DEBUG_STACK)
301         {
302                 global_debug = 0xffffffff & ~DBGM_MSG;
303 //              global_debug = DBGM_L3DATA;
304         } else
305                 global_debug = DBGM_MAN;
306         SPRINT(debug_log, "%s/debug.log", INSTALL_DATA);
307         if (options.deb & DEBUG_LOG)
308                 debug_init(global_debug, debug_log, debug_log, debug_log);
309         else
310                 debug_init(global_debug, NULL, NULL, NULL);
311         created_debug = 1;
312
313         msg_init();
314
315         /* read ruleset(s) */
316         if (!(ruleset_first = ruleset_parse()))
317                 goto free;
318
319         /* set pointer to main ruleset */
320         ruleset_main = getrulesetbyname("main");
321         if (!ruleset_main)
322         {
323                 fprintf(stderr, "\n***\n -> Missing 'main' ruleset, causing ALL calls to be disconnected.\n***\n\n");
324                 PDEBUG(DEBUG_LOG, "Missing 'main' ruleset, causing ALL calls to be disconnected.\n");
325                 sleep(2);
326         }
327
328 #if 0
329         /* query available isdn ports */
330         if (!(strcasecmp(ARGV(1),"route")))
331         {
332                 ruleset_debug(ruleset_first);
333                 ret = 0;
334                 goto free;
335         }
336 #endif
337
338         /* do fork in special cases */
339         if (!(strcasecmp(ARGV(1),"fork")))
340         {
341                 pid_t pid;
342
343                 /* do daemon fork */
344                 pid = fork();
345
346                 if (pid < 0)
347                 {
348                         fprintf(stderr, "Cannot fork!\n");
349                         goto free;
350                 }
351                 if (pid != 0)
352                 {
353                         exit(0);
354                 }
355                 usleep(200000);
356                 printf("\n");
357                 
358                 /* do second fork */
359                 pid = fork();
360
361                 if (pid < 0)
362                 {
363                         fprintf(stderr, "Cannot fork!\n");
364                         goto free;
365                 }
366                 if (pid != 0)
367                 {
368                         printf("PBX: Starting daemon.\n");
369                         exit(0);
370                 }
371                 nooutput = 1;
372         } else
373         /* if not start */
374         if (!!strcasecmp(ARGV(1),"start"))
375         {
376                 goto usage;
377         }
378
379         /* create lock and lock! */
380         if ((lockfd = open("/var/run/pbx.lock", O_CREAT, 0)) < 0)
381         {
382                 fprintf(stderr, "Cannot create lock file: /var/run/pbx.lock\n");
383                 goto free;
384         }
385         if (flock(lockfd, LOCK_EX|LOCK_NB) < 0)
386         {
387                 if (errno == EWOULDBLOCK)
388                         fprintf(stderr, "PBX: Another PBX process is running. Please kill the other one.\n");
389                 else    fprintf(stderr, "Locking process failed: errno=%d\n", errno);
390                 goto free;
391         }
392         created_lock = 1;
393
394         /* initialize admin socket */
395         if (admin_init())
396         {
397                 fprintf(stderr, "Unable to initialize admin socket.\n");
398                 goto free;
399         }
400
401         /* generate alaw / ulaw tables */
402         generate_tables(options.law);
403
404         /* load tones (if requested) */
405         if (fetch_tones() == 0)
406         {
407                 fprintf(stderr, "Unable to fetch tones into memory.\n");
408                 goto free;
409         }
410
411         /* read interfaces and open ports */
412         if (!read_interfaces())
413         {
414                 PERROR_RUNTIME("No interfaces specified or failed to parse interface.conf.\n");
415                 fprintf(stderr, "No interfaces specified or failed to parse interface.conf.\n");
416                 goto free;
417         }
418         relink_interfaces();
419         interface_first = interface_newlist;
420         free_interfaces(interface_newlist);
421         interface_newlist = NULL;
422         
423         /* locking memory paging */
424         i = 0;
425         while(i < 10)
426         {
427                 if (mlockall(MCL_CURRENT | MCL_FUTURE) >= 0)
428                         break;
429                 usleep(200000);
430                 i++;
431         }
432         if (i == 10)
433         {
434                 switch(errno)
435                 {
436                         case ENOMEM:
437                         fprintf(stderr, "Not enough memory to lock paging, exitting...\n");
438                         break;
439                         case EPERM:
440                         fprintf(stderr, "No permission to lock paging, exitting...\n");
441                         break;
442                         case EFAULT:
443                         fprintf(stderr, "'Bad address' while locking paging, exitting...\n");
444                         break;
445                         default:
446                         fprintf(stderr, "Unknown error %d while locking paging, exitting...\n", errno);
447                 }
448                 goto free;
449         }
450
451         /* set real time scheduler & priority */
452         if (options.schedule > 1)
453         {
454                 memset(&schedp, 0, sizeof(schedp));
455                 schedp.sched_priority = options.schedule;
456                 ret = sched_setscheduler(0, SCHED_RR, &schedp);
457                 if (ret < 0)
458                 {
459                         PERROR("Scheduling failed with given priority %d (errno = %d).\nCheck options.conf 'schedule', exitting...\n", options.schedule, errno);
460                         goto free;
461                 }
462         }
463
464         /* signal handlers */   
465         signal(SIGINT,sighandler);
466         signal(SIGHUP,sighandler);
467         signal(SIGTERM,sighandler);
468         signal(SIGPIPE,sighandler);
469         created_signal = 1;
470
471         /*** main loop ***/
472         printf("%s %s started, waiting for calls...\n", NAME, VERSION_STRING);
473         GET_NOW();
474 #ifdef DEBUG_DURATION
475         start_d = now_d;
476         durationupdate = now;
477         idle_duration = isdn_duration = port_duration = epoint_duration = call_duration = message_duration = admin_duration = 0;
478 #endif
479         quit = 0;
480         while(!quit)
481         {
482                 /* all loops must be counted from the beginning since nodes might get freed during handler */
483                 all_idle = 1;
484
485                 /* handle mISDN messages from kernel */
486                 debug_prefix = "ISDN";
487                 if (mISDN_handler())
488                         all_idle = 0;
489 #ifdef DEBUG_DURATION
490                 GET_NOW();
491                 isdn_duration += (now_d - start_d);
492                 start_d = now_d;
493 #endif
494 BUDETECT
495
496                 /* loop through all port ports and call their handler */
497                 port_again:
498                 port = port_first;
499                 while(port)
500                 {
501                         debug_prefix = port->p_name;
502                         debug_count++;
503                         ret = port->handler();
504                         if (ret)
505                                 all_idle = 0;
506                         if (ret < 0) /* port has been destroyed */
507                                 goto port_again;
508                         port = port->next;
509                 }
510 #ifdef DEBUG_DURATION
511                 GET_NOW();
512                 port_duration += (now_d - start_d);
513                 start_d = now_d;
514 #endif
515
516                 /* loop through all epoint and call their handler */
517                 epoint_again:
518                 epoint = epoint_first;
519                 while(epoint)
520                 {
521                         debug_prefix = prefix_string;
522                         SPRINT(prefix_string, "ep%ld", epoint->ep_serial);
523                         debug_count++;
524                         ret = epoint->handler();
525                         if (ret)
526                                 all_idle = 0;
527                         if (ret < 0) /* epoint has been destroyed */
528                                 goto epoint_again;
529                         epoint = epoint->next;
530                 }
531 #ifdef DEBUG_DURATION
532                 GET_NOW();
533                 epoint_duration += (now_d - start_d);
534                 start_d = now_d;
535 #endif
536
537                 /* loop through all calls and call their handler */
538                 call_again:
539                 call = call_first;
540                 while(call)
541                 {
542                         debug_prefix = "call";
543                         debug_count++;
544                         ret = call->handler();
545                         if (ret)
546                                 all_idle = 0;
547                         if (ret < 0) /* call has been destroyed */
548                                 goto call_again;
549                         call = call->next;
550                 }
551 #ifdef DEBUG_DURATION
552                 GET_NOW();
553                 call_duration += (now_d - start_d);
554                 start_d = now_d;
555 #endif
556
557                 debug_prefix = 0;
558
559                 /* process any message */
560                 debug_count++;
561                 debug_prefix = "message";
562                 while ((message = message_get()))
563                 {
564                         all_idle = 0;
565                         switch(message->flow)
566                         {
567                                 case PORT_TO_EPOINT:
568                                 debug_prefix = "msg port->epoint";
569                                 epoint = find_epoint_id(message->id_to);
570                                 if (epoint)
571                                 {
572                                         if (epoint->ep_app)
573                                         {
574                                                 epoint->ep_app->ea_message_port(message->id_from, message->type, &message->param);
575                                         } else
576                                         {
577                                                 PDEBUG(DEBUG_MSG, "Warning: message %s from port %d to endpoint %d. endpoint doesn't have an application.\n", messages_txt[message->type], message->id_from, message->id_to);
578                                         }
579                                 } else
580                                 {
581                                         PDEBUG(DEBUG_MSG, "Warning: message %s from port %d to endpoint %d. endpoint doesn't exist anymore.\n", messages_txt[message->type], message->id_from, message->id_to);
582                                 }
583                                 break;
584
585                                 case EPOINT_TO_CALL:
586                                 debug_prefix = "msg epoint->call";
587                                 call = find_call_id(message->id_to);
588                                 if (call)
589                                 {
590                                         call->message_epoint(message->id_from, message->type, &message->param);
591                                 } else
592                                 {
593                                         PDEBUG(DEBUG_MSG, "Warning: message %s from endpoint %d to call %d. call doesn't exist anymore\n", messages_txt[message->type], message->id_from, message->id_to);
594                                 }
595                                 break;
596
597                                 case CALL_TO_EPOINT:
598                                 debug_prefix = "msg call->epoint";
599                                 epoint = find_epoint_id(message->id_to);
600                                 if (epoint)
601                                 {
602                                         if (epoint->ep_app)
603                                         {
604                                                 epoint->ep_app->ea_message_call(message->id_from, message->type, &message->param);
605                                         } else
606                                         {
607                                                 PDEBUG(DEBUG_MSG, "Warning: message %s from call %d to endpoint %d. endpoint doesn't have an application.\n", messages_txt[message->type], message->id_from, message->id_to);
608                                         }
609                                 } else
610                                 {
611                                         PDEBUG(DEBUG_MSG, "Warning: message %s from call %d to endpoint %d. endpoint doesn't exist anymore.\n", messages_txt[message->type], message->id_from, message->id_to);
612                                 }
613                                 break;
614
615                                 case EPOINT_TO_PORT:
616                                 debug_prefix = "msg epoint->port";
617                                 port = find_port_id(message->id_to);
618                                 if (port)
619                                 {
620                                         port->message_epoint(message->id_from, message->type, &message->param);
621 BUDETECT
622                                 } else
623                                 {
624                                         PDEBUG(DEBUG_MSG, "Warning: message %s from endpoint %d to port %d. port doesn't exist anymore\n", messages_txt[message->type], message->id_from, message->id_to);
625                                 }
626                                 break;
627
628                                 default:
629                                 PERROR("Message flow %d unknown.\n", message->flow);
630                         }
631                         message_free(message);
632                         debug_count++;
633                         debug_prefix = "message";
634                 }
635 #ifdef DEBUG_DURATION
636                 GET_NOW();
637                 message_duration += (now_d - start_d);
638                 start_d = now_d;
639 #endif
640 BUDETECT
641
642                 /* handle socket */
643                 if (admin_handle())
644                         all_idle = 0;
645 #ifdef DEBUG_DURATION
646                 GET_NOW();
647                 admin_duration += (now_d - start_d);
648                 start_d = now_d;
649 #endif
650 BUDETECT
651
652 #if 0
653                 /* check for child to exit (eliminate zombies) */
654                 if (waitpid(-1, NULL, WNOHANG) > 0)
655                 {
656                         PDEBUG(DEBUG_EPOINT, "a child process (created by endpoint) has exitted.\n");
657                         all_idle = 0;
658                 }
659 #endif
660
661                 /* do idle checking */
662                 if (idlecheck != now)
663                 {
664                         PDEBUG(DEBUG_IDLETIME, "Idle time : %d%%\n", idletime/10000);
665                         idletime = 0;
666                         idlecheck = now;
667                 }
668 #ifdef DEBUG_DURATION
669                 GET_NOW();
670                 idle_duration += (now_d - start_d);
671                 start_d = now_d;
672                 if (durationupdate != now)
673                 {
674                         durationupdate = now;
675                         printf("Idle:%3d ISDN:%3d Port:%3d Epoint:%3d Call:%3d Message:%3d Admin:%3d\n",
676                                 (int)(idle_duration*100),
677                                 (int)(isdn_duration*100),
678                                 (int)(port_duration*100),
679                                 (int)(epoint_duration*100),
680                                 (int)(call_duration*100),
681                                 (int)(message_duration*100),
682                                 (int)(admin_duration*100));
683                         idle_duration = isdn_duration = port_duration = epoint_duration = call_duration = message_duration = admin_duration = 0;
684                 }
685 #else
686                 GET_NOW();
687 #endif
688
689                 /* did we do nothing? so we wait to give time to other processes */
690                 if (all_idle)
691                 {
692                         usleep(4000); /* wait 32 samples */
693                         idletime += 4000;
694                 }
695         }
696         printf("PBX terminated\n");
697         ret=0;
698
699         /* free all */
700 free:
701
702
703         /* set scheduler & priority
704          */
705         if (options.schedule > 1)
706         {
707                 memset(&schedp, 0, sizeof(schedp));
708                 schedp.sched_priority = 0;
709                 sched_setscheduler(0, SCHED_OTHER, &schedp);
710         }
711         /* reset signals */
712         if (created_signal)
713         {
714                 signal(SIGINT,SIG_DFL);
715                 signal(SIGHUP,SIG_DFL);
716                 signal(SIGTERM,SIG_DFL);
717                 signal(SIGPIPE,SIG_DFL);
718         }
719
720         /* destroy objects */
721         debug_prefix = "free";
722
723         while(port_first)
724         {
725                 debug_count++;
726                 delete port_first;
727         }
728         while(epoint_first)
729         {
730                 debug_count++;
731                 delete epoint_first;
732         }
733         epoint_first = NULL;
734         debug_count++;
735         call_free();
736
737         /* free interfaces */
738         if (interface_first)
739                 free_interfaces(interface_first);
740         interface_first = NULL;
741
742         /* close isdn ports */
743         mISDNport_close_all();
744
745         /* flush messages */
746         debug_count++;
747         i = 0;
748         while ((message = message_get()))
749         {
750                 i++;
751                 message_free(message);
752         }
753         if (i)
754         {
755                 PDEBUG(DEBUG_MSG, "freed %d pending messages\n", i);
756         }
757
758         /* free tones */
759         if (toneset_first)
760                 free_tones();
761
762         /* free admin socket */
763         admin_cleanup();
764
765         /* close lock */
766         if (created_lock)
767                 flock(lockfd, LOCK_UN);
768         if (lockfd >= 0)
769                 close(lockfd);
770
771         /* free rulesets */
772         if (ruleset_first)
773                 ruleset_free(ruleset_first);
774         ruleset_first = NULL;
775
776         /* free mutex */
777         if (created_mutexe)
778                 if (pthread_mutex_destroy(&mutexe))
779                         fprintf(stderr, "cannot destroy 'PERROR' mutex\n");
780 //      if (created_mutext)
781 //              if (pthread_mutex_destroy(&mutext))
782 //                      fprintf(stderr, "cannot destroy 'trace' mutex\n");
783         if (created_mutexd)
784                 if (pthread_mutex_destroy(&mutexd))
785                         fprintf(stderr, "cannot destroy 'PDEBUG' mutex\n");
786
787         /* close debug */
788         if (created_debug)
789                 debug_close();
790         global_debug = 0;
791
792         /* display memory leak */
793 #define MEMCHECK(a, b) \
794         if (b) \
795         { \
796                 printf("\n******************************\n\007"); \
797                 printf("\nERROR: %d %s\n", b, a); \
798                 printf("\n******************************\n"); \
799                 ret = -1; \
800         }
801         MEMCHECK("",memuse)
802         MEMCHECK("memory block(s) left (port.cpp)",pmemuse)
803         MEMCHECK("memory block(s) left (epoint.cpp)",ememuse)
804         MEMCHECK("memory block(s) left (call.cpp)",cmemuse)
805         MEMCHECK("memory block(s) left (message.c)",mmemuse)
806         MEMCHECK("memory block(s) left (route.c)",rmemuse)
807         MEMCHECK("memory block(s) left (args)",amemuse)
808         MEMCHECK("class(es) left",classuse)
809         MEMCHECK("file descriptor(s) left",fduse)
810         MEMCHECK("file handler(s) left",fhuse)
811
812         /* take me out */
813         if (ret)
814                 printf("PBX: Exit (code %d)\n", ret);
815 #ifdef VOIP
816         return;
817 #else
818         return(ret);
819 #endif
820 }
821
822
823 #ifdef BUDETECT_DEF
824 /* special debug function to detect buffer overflow
825  */
826 int budetect_stop = 0;
827 void budetect(const char *file, int line, char *function)
828 {
829         if (budetect_stop)
830                 return;
831         /* modify this function to detect race-bugs */
832 #warning DID YOU MODIFY THIS FUNCTION TO DETECT THE BUFFER OVERFLOW BUG?
833         class Port *port;
834         class PmISDN *pmisdn;
835         struct mISDNport *mISDNport = mISDNport_first;
836         int i, ii;
837
838         while(mISDNport)
839         {
840                 i = 0;
841                 ii = mISDNport->b_num;
842                 while(i < ii)
843                 {
844                         if (mISDNport->b_port[i])
845                         {
846                                 port = port_first;
847                                 while(port)
848                                 {
849                                         if ((port->p_type&PORT_CLASS_MASK) == PORT_CLASS_ISDN)
850                                         {
851                                                 pmisdn = (class PmISDN *)port;
852                                                 if (pmisdn->p_isdn_crypt_listen)
853                                                 {
854                                                         PERROR_RUNTIME("************************************************\n");
855                                                         PERROR_RUNTIME("** BUG detected in %s, line %d, function %s\n", file, line, function);
856                                                         PERROR_RUNTIME("** p_isdn_crypt_listen = %d\n", pmisdn->p_isdn_crypt_listen);
857                                                         PERROR_RUNTIME("************************************************\n");
858                                                         budetect_stop = 1;
859                                                 }
860                                         }
861                                         if (port == mISDNport->b_port[i])
862                                                 break;
863                                         port = port->next;
864                                         if (!port)
865                                         {
866                                                 PERROR_RUNTIME("************************************************\n");
867                                                 PERROR_RUNTIME("** BUG detected in %s, line %d, function %s\n", file, line, function);
868                                                 PERROR_RUNTIME("** b_port not in list.\n");
869                                                 PERROR_RUNTIME("************************************************\n");
870                                                 budetect_stop = 1;
871                                         }
872                                 }
873                         }
874                         i++;
875                 }
876                 mISDNport = mISDNport->next;
877         }
878
879 }
880 #endif
881