2f119380b0a6d48d13559196ca65a7ab247c8f07
[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 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, "LCR: Signal received: %d\n", sigset);
173                 PERROR("Signal received: %d\n", sigset);
174         }
175 }
176
177
178 /*
179  * the main
180  */
181 int main(int argc, char *argv[])
182 {
183         int                     ret = -1;
184         int                     lockfd = -1; /* file lock */
185         struct message          *message;
186         class Port              *port;
187         class Endpoint          *epoint;
188         class Join              *join;
189         int                     i;
190         int                     all_idle;
191         char                    prefix_string[64];
192         struct sched_param      schedp;
193         char                    *debug_prefix = "alloc";
194         int                     created_mutexd = 0,/* created_mutext = 0,*/ created_mutexe = 0,
195                                 created_lock = 0, created_signal = 0, created_debug = 0;
196 #ifdef DEBUG_DURATION
197         time_t                  durationupdate;
198         double                  idle_duration, isdn_duration, port_duration, epoint_duration, join_duration, message_duration, admin_duration;
199         double                  start_d;
200 #endif
201         int                     idletime = 0, idlecheck = 0;
202         char                    tracetext[256];
203
204         /* current time */
205         GET_NOW();
206
207         /* show version */
208         printf("\n** %s  Version %s\n\n", NAME, VERSION_STRING);
209
210         /* show options */
211         if (argc <= 1)
212         {
213                 usage:
214                 printf("\n");
215                 printf("Usage: lcr (query | start | fork | rules | route)\n");
216                 printf("query     = Show available isdn ports.\n");
217                 printf("start     = Run lcr normally, abort with CTRL+C.\n");
218                 printf("fork      = Do daemon fork and run as background process.\n");
219                 printf("interface = Get help of available interface syntax.\n");
220                 printf("rules     = Get help of available routing rule syntax.\n");
221                 printf("rules [action] = Get individual help for given action.\n");
222 //              printf("route = Show current routing as it is parsed.\n");
223                 printf("\n");
224                 ret = 999;
225                 goto free;
226         }
227
228         /* init crc */
229         crc_init();
230
231         /* check for root (real or effective) */
232         if (getuid() && geteuid())
233         {
234                 fprintf(stderr, "Please run %s as super-user.\n", NAME);
235                 goto free;
236         }
237
238         /* the mutex init */
239         if (pthread_mutex_init(&mutexd, NULL))
240         {
241                 fprintf(stderr, "cannot create 'PDEBUG' mutex\n");
242                 goto free;
243         }
244         created_mutexd = 1;
245 //      if (pthread_mutex_init(&mutext, NULL))
246 //      {
247 //              fprintf(stderr, "cannot create 'trace' mutex\n");
248 //              goto free;
249 //      }
250 //      created_mutext = 1;
251         if (pthread_mutex_init(&mutexe, NULL))
252         {
253                 fprintf(stderr, "cannot create 'PERROR' mutex\n");
254                 goto free;
255         }
256         created_mutexe = 1;
257
258         /* show interface */
259         if (!(strcasecmp(argv[1],"interface")))
260         {
261                 doc_interface();
262                 ret = 0;
263                 goto free;
264         }
265
266         /* show rules */
267         if (!(strcasecmp(argv[1],"rules")))
268         {
269                 if (argc <= 2)
270                         doc_rules(NULL);
271                 else
272                         doc_rules(argv[2]);
273                 ret = 0;
274                 goto free;
275         }
276
277         /* query available isdn ports */
278         if (!(strcasecmp(argv[1],"query")))
279         {
280                 mISDN_port_info();
281                 ret = 0;
282                 goto free;
283         }
284
285         /* read options */
286         if (read_options() == 0)
287                 goto free;
288
289         /* init mISDN */
290         if (mISDN_initialize() < 0)
291                 goto free;
292         created_debug = 1;
293
294         /* read ruleset(s) */
295         if (!(ruleset_first = ruleset_parse()))
296                 goto free;
297
298         /* set pointer to main ruleset */
299         ruleset_main = getrulesetbyname("main");
300         if (!ruleset_main)
301         {
302                 fprintf(stderr, "\n***\n -> Missing 'main' ruleset, causing ALL calls to be disconnected.\n***\n\n");
303                 PDEBUG(DEBUG_LOG, "Missing 'main' ruleset, causing ALL calls to be disconnected.\n");
304                 sleep(2);
305         }
306
307 #if 0
308         /* query available isdn ports */
309         if (!(strcasecmp(argv[1],"route")))
310         {
311                 ruleset_debug(ruleset_first);
312                 ret = 0;
313                 goto free;
314         }
315 #endif
316
317         /* do fork in special cases */
318         if (!(strcasecmp(argv[1],"fork")))
319         {
320                 pid_t pid;
321
322                 /* do daemon fork */
323                 pid = fork();
324
325                 if (pid < 0)
326                 {
327                         fprintf(stderr, "Cannot fork!\n");
328                         goto free;
329                 }
330                 if (pid != 0)
331                 {
332                         exit(0);
333                 }
334                 usleep(200000);
335                 printf("\n");
336                 
337                 /* do second fork */
338                 pid = fork();
339
340                 if (pid < 0)
341                 {
342                         fprintf(stderr, "Cannot fork!\n");
343                         goto free;
344                 }
345                 if (pid != 0)
346                 {
347                         printf("LCR: Starting daemon.\n");
348                         exit(0);
349                 }
350                 nooutput = 1;
351         } else
352         /* if not start */
353         if (!!strcasecmp(argv[1],"start"))
354         {
355                 goto usage;
356         }
357
358         /* create lock and lock! */
359         if ((lockfd = open("/var/run/lcr.lock", O_CREAT, 0)) < 0)
360         {
361                 fprintf(stderr, "Cannot create lock file: /var/run/lcr.lock\n");
362                 goto free;
363         }
364         if (flock(lockfd, LOCK_EX|LOCK_NB) < 0)
365         {
366                 if (errno == EWOULDBLOCK)
367                         fprintf(stderr, "LCR: Another LCR process is running. Please kill the other one.\n");
368                 else    fprintf(stderr, "Locking process failed: errno=%d\n", errno);
369                 goto free;
370         }
371         created_lock = 1;
372
373         /* initialize admin socket */
374         if (admin_init())
375         {
376                 fprintf(stderr, "Unable to initialize admin socket.\n");
377                 goto free;
378         }
379
380         /* generate alaw / ulaw tables */
381         generate_tables(options.law);
382
383         /* load tones (if requested) */
384         if (fetch_tones() == 0)
385         {
386                 fprintf(stderr, "Unable to fetch tones into memory.\n");
387                 goto free;
388         }
389
390         /* read interfaces and open ports */
391         if (!read_interfaces())
392         {
393                 PERROR_RUNTIME("No interfaces specified or failed to parse interface.conf.\n");
394                 fprintf(stderr, "No interfaces specified or failed to parse interface.conf.\n");
395                 goto free;
396         }
397         relink_interfaces();
398         interface_first = interface_newlist;
399         interface_newlist = NULL;
400         
401         /* locking memory paging */
402         i = 0;
403         while(i < 10)
404         {
405                 if (mlockall(MCL_CURRENT | MCL_FUTURE) >= 0)
406                         break;
407                 usleep(200000);
408                 i++;
409         }
410         if (i == 10)
411         {
412                 switch(errno)
413                 {
414                         case ENOMEM:
415                         fprintf(stderr, "Not enough memory to lock paging, exitting...\n");
416                         break;
417                         case EPERM:
418                         fprintf(stderr, "No permission to lock paging, exitting...\n");
419                         break;
420                         case EFAULT:
421                         fprintf(stderr, "'Bad address' while locking paging, exitting...\n");
422                         break;
423                         default:
424                         fprintf(stderr, "Unknown error %d while locking paging, exitting...\n", errno);
425                 }
426                 goto free;
427         }
428
429         /* set real time scheduler & priority */
430         if (options.schedule > 1)
431         {
432                 memset(&schedp, 0, sizeof(schedp));
433                 schedp.sched_priority = options.schedule;
434                 ret = sched_setscheduler(0, SCHED_RR, &schedp);
435                 if (ret < 0)
436                 {
437                         PERROR("Scheduling failed with given priority %d (errno = %d).\nCheck options.conf 'schedule', exitting...\n", options.schedule, errno);
438                         goto free;
439                 }
440         }
441
442         /* signal handlers */   
443         signal(SIGINT,sighandler);
444         signal(SIGHUP,sighandler);
445         signal(SIGTERM,sighandler);
446         signal(SIGPIPE,sighandler);
447         created_signal = 1;
448
449         /*** main loop ***/
450         SPRINT(tracetext, "%s %s started, waiting for calls...", NAME, VERSION_STRING);
451         start_trace(0, NULL, NULL, NULL, 0, 0, 0, tracetext);
452         printf("%s\n", tracetext);
453         end_trace();
454         GET_NOW();
455 #ifdef DEBUG_DURATION
456         start_d = now_d;
457         durationupdate = now;
458         idle_duration = isdn_duration = port_duration = epoint_duration = call_duration = message_duration = admin_duration = 0;
459 #endif
460         quit = 0;
461         while(!quit)
462         {
463                 /* all loops must be counted from the beginning since nodes might get freed during handler */
464                 all_idle = 1;
465
466                 /* handle mISDN messages from kernel */
467                 debug_prefix = "ISDN";
468                 if (mISDN_handler())
469                         all_idle = 0;
470 #ifdef DEBUG_DURATION
471                 GET_NOW();
472                 isdn_duration += (now_d - start_d);
473                 start_d = now_d;
474 #endif
475 BUDETECT
476
477                 /* loop through all port ports and call their handler */
478                 port_again:
479                 port = port_first;
480                 while(port)
481                 {
482                         debug_prefix = port->p_name;
483                         debug_count++;
484                         ret = port->handler();
485                         if (ret)
486                                 all_idle = 0;
487                         if (ret < 0) /* port has been destroyed */
488                                 goto port_again;
489                         port = port->next;
490                 }
491 #ifdef DEBUG_DURATION
492                 GET_NOW();
493                 port_duration += (now_d - start_d);
494                 start_d = now_d;
495 #endif
496
497                 /* loop through all epoint and call their handler */
498                 epoint_again:
499                 epoint = epoint_first;
500                 while(epoint)
501                 {
502                         debug_prefix = prefix_string;
503                         SPRINT(prefix_string, "ep%ld", epoint->ep_serial);
504                         debug_count++;
505                         ret = epoint->handler();
506                         if (ret)
507                                 all_idle = 0;
508                         if (ret < 0) /* epoint has been destroyed */
509                                 goto epoint_again;
510                         epoint = epoint->next;
511                 }
512 #ifdef DEBUG_DURATION
513                 GET_NOW();
514                 epoint_duration += (now_d - start_d);
515                 start_d = now_d;
516 #endif
517
518                 /* loop through all joins and call their handler */
519                 join_again:
520                 join = join_first;
521                 while(join)
522                 {
523                         debug_prefix = "join";
524                         debug_count++;
525                         ret = join->handler();
526                         if (ret)
527                                 all_idle = 0;
528                         if (ret < 0) /* join has been destroyed */
529                                 goto join_again;
530                         join = join->next;
531                 }
532 #ifdef DEBUG_DURATION
533                 GET_NOW();
534                 join_duration += (now_d - start_d);
535                 start_d = now_d;
536 #endif
537
538                 debug_prefix = 0;
539
540                 /* process any message */
541                 debug_count++;
542                 debug_prefix = "message";
543                 while ((message = message_get()))
544                 {
545                         all_idle = 0;
546                         switch(message->flow)
547                         {
548                                 case PORT_TO_EPOINT:
549                                 debug_prefix = "msg port->epoint";
550                                 epoint = find_epoint_id(message->id_to);
551                                 if (epoint)
552                                 {
553                                         if (epoint->ep_app)
554                                         {
555                                                 epoint->ep_app->ea_message_port(message->id_from, message->type, &message->param);
556                                         } else
557                                         {
558                                                 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);
559                                         }
560                                 } else
561                                 {
562                                         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);
563                                 }
564                                 break;
565
566                                 case EPOINT_TO_JOIN:
567                                 debug_prefix = "msg epoint->join";
568                                 join = find_join_id(message->id_to);
569                                 if (join)
570                                 {
571                                         join->message_epoint(message->id_from, message->type, &message->param);
572                                 } else
573                                 {
574                                         PDEBUG(DEBUG_MSG, "Warning: message %s from endpoint %d to join %d. join doesn't exist anymore\n", messages_txt[message->type], message->id_from, message->id_to);
575                                 }
576                                 break;
577
578                                 case JOIN_TO_EPOINT:
579                                 debug_prefix = "msg join->epoint";
580                                 epoint = find_epoint_id(message->id_to);
581                                 if (epoint)
582                                 {
583                                         if (epoint->ep_app)
584                                         {
585                                                 epoint->ep_app->ea_message_join(message->id_from, message->type, &message->param);
586                                         } else
587                                         {
588                                                 PDEBUG(DEBUG_MSG, "Warning: message %s from join %d to endpoint %d. endpoint doesn't have an application.\n", messages_txt[message->type], message->id_from, message->id_to);
589                                         }
590                                 } else
591                                 {
592                                         PDEBUG(DEBUG_MSG, "Warning: message %s from join %d to endpoint %d. endpoint doesn't exist anymore.\n", messages_txt[message->type], message->id_from, message->id_to);
593                                 }
594                                 break;
595
596                                 case EPOINT_TO_PORT:
597                                 debug_prefix = "msg epoint->port";
598                                 port = find_port_id(message->id_to);
599                                 if (port)
600                                 {
601                                         port->message_epoint(message->id_from, message->type, &message->param);
602 BUDETECT
603                                 } else
604                                 {
605                                         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);
606                                 }
607                                 break;
608
609                                 default:
610                                 PERROR("Message flow %d unknown.\n", message->flow);
611                         }
612                         message_free(message);
613                         debug_count++;
614                         debug_prefix = "message";
615                 }
616 #ifdef DEBUG_DURATION
617                 GET_NOW();
618                 message_duration += (now_d - start_d);
619                 start_d = now_d;
620 #endif
621 BUDETECT
622
623                 /* handle socket */
624                 if (admin_handle())
625                         all_idle = 0;
626 #ifdef DEBUG_DURATION
627                 GET_NOW();
628                 admin_duration += (now_d - start_d);
629                 start_d = now_d;
630 #endif
631 BUDETECT
632
633 #if 0
634                 /* check for child to exit (eliminate zombies) */
635                 if (waitpid(-1, NULL, WNOHANG) > 0)
636                 {
637                         PDEBUG(DEBUG_EPOINT, "a child process (created by endpoint) has exitted.\n");
638                         all_idle = 0;
639                 }
640 #endif
641
642                 /* do idle checking */
643                 if (idlecheck != now)
644                 {
645                         PDEBUG(DEBUG_IDLETIME, "Idle time : %d%%\n", idletime/10000);
646                         idletime = 0;
647                         idlecheck = now;
648                 }
649 #ifdef DEBUG_DURATION
650                 GET_NOW();
651                 idle_duration += (now_d - start_d);
652                 start_d = now_d;
653                 if (durationupdate != now)
654                 {
655                         durationupdate = now;
656                         printf("Idle:%3d ISDN:%3d Port:%3d Epoint:%3d Join:%3d Message:%3d Admin:%3d\n",
657                                 (int)(idle_duration*100),
658                                 (int)(isdn_duration*100),
659                                 (int)(port_duration*100),
660                                 (int)(epoint_duration*100),
661                                 (int)(join_duration*100),
662                                 (int)(message_duration*100),
663                                 (int)(admin_duration*100));
664                         idle_duration = isdn_duration = port_duration = epoint_duration = join_duration = message_duration = admin_duration = 0;
665                 }
666 #else
667                 GET_NOW();
668 #endif
669
670                 /* did we do nothing? so we wait to give time to other processes */
671                 if (all_idle)
672                 {
673                         usleep(4000); /* wait 32 samples */
674                         idletime += 4000;
675                 }
676         }
677         SPRINT(tracetext, "%s terminated", NAME);
678         printf("%s\n", tracetext);
679         start_trace(0, NULL, NULL, NULL, 0, 0, 0, tracetext);
680         if (ret)
681                 add_trace("error", NULL, "%d", ret);
682         end_trace();
683         ret=0;
684
685         /* free all */
686 free:
687
688
689         /* set scheduler & priority
690          */
691         if (options.schedule > 1)
692         {
693                 memset(&schedp, 0, sizeof(schedp));
694                 schedp.sched_priority = 0;
695                 sched_setscheduler(0, SCHED_OTHER, &schedp);
696         }
697         /* reset signals */
698         if (created_signal)
699         {
700                 signal(SIGINT,SIG_DFL);
701                 signal(SIGHUP,SIG_DFL);
702                 signal(SIGTERM,SIG_DFL);
703                 signal(SIGPIPE,SIG_DFL);
704         }
705
706         /* destroy objects */
707         debug_prefix = "free";
708
709         while(port_first)
710         {
711                 debug_count++;
712                 delete port_first;
713         }
714         while(epoint_first)
715         {
716                 debug_count++;
717                 delete epoint_first;
718         }
719         epoint_first = NULL;
720         debug_count++;
721         join_free();
722
723         /* free interfaces */
724         if (interface_first)
725                 free_interfaces(interface_first);
726         interface_first = NULL;
727
728         /* close isdn ports */
729         mISDNport_close_all();
730
731         /* flush messages */
732         debug_count++;
733         i = 0;
734         while ((message = message_get()))
735         {
736                 i++;
737                 message_free(message);
738         }
739         if (i)
740         {
741                 PDEBUG(DEBUG_MSG, "freed %d pending messages\n", i);
742         }
743
744         /* free tones */
745         if (toneset_first)
746                 free_tones();
747
748         /* free admin socket */
749         admin_cleanup();
750
751         /* close lock */
752         if (created_lock)
753                 flock(lockfd, LOCK_UN);
754         if (lockfd >= 0)
755                 close(lockfd);
756
757         /* free rulesets */
758         if (ruleset_first)
759                 ruleset_free(ruleset_first);
760         ruleset_first = NULL;
761
762         /* free mutex */
763         if (created_mutexe)
764                 if (pthread_mutex_destroy(&mutexe))
765                         fprintf(stderr, "cannot destroy 'PERROR' mutex\n");
766 //      if (created_mutext)
767 //              if (pthread_mutex_destroy(&mutext))
768 //                      fprintf(stderr, "cannot destroy 'trace' mutex\n");
769         if (created_mutexd)
770                 if (pthread_mutex_destroy(&mutexd))
771                         fprintf(stderr, "cannot destroy 'PDEBUG' mutex\n");
772
773         /* deinitialize mISDN */
774         mISDN_deinitialize();
775         global_debug = 0;
776
777         /* display memory leak */
778 #define MEMCHECK(a, b) \
779         if (b) \
780         { \
781                 SPRINT(tracetext, a, NAME); \
782                 start_trace(0, NULL, NULL, NULL, 0, 0, 0, tracetext); \
783                 if (ret) add_trace("blocks", NULL, "%d", b); \
784                 end_trace(); \
785                 printf("\n******************************\n\007"); \
786                 printf("\nERROR: %d %s\n", b, a); \
787                 printf("\n******************************\n"); \
788                 ret = -1; \
789         }
790         MEMCHECK("",memuse)
791         MEMCHECK("memory block(s) left (port.cpp ...)",pmemuse)
792         MEMCHECK("memory block(s) left (epoint*.cpp ...)",ememuse)
793         MEMCHECK("memory block(s) left (join*.cpp)",cmemuse)
794         MEMCHECK("memory block(s) left (message.c)",mmemuse)
795         MEMCHECK("memory block(s) left (route.c)",rmemuse)
796         MEMCHECK("memory block(s) left (args)",amemuse)
797         MEMCHECK("class(es) left",classuse)
798         MEMCHECK("file descriptor(s) left",fduse)
799         MEMCHECK("file handler(s) left",fhuse)
800
801         /* take me out */
802         return(ret);
803 }
804
805
806 #ifdef BUDETECT_DEF
807 /* special debug function to detect buffer overflow
808  */
809 int budetect_stop = 0;
810 void budetect(const char *file, int line, char *function)
811 {
812         if (budetect_stop)
813                 return;
814         /* modify this function to detect race-bugs */
815 #warning DID YOU MODIFY THIS FUNCTION TO DETECT THE BUFFER OVERFLOW BUG?
816         class Port *port;
817         class PmISDN *pmisdn;
818         struct mISDNport *mISDNport = mISDNport_first;
819         int i, ii;
820
821         while(mISDNport)
822         {
823                 i = 0;
824                 ii = mISDNport->b_num;
825                 while(i < ii)
826                 {
827                         if (mISDNport->b_port[i])
828                         {
829                                 port = port_first;
830                                 while(port)
831                                 {
832                                         if ((port->p_type&PORT_CLASS_MASK) == PORT_CLASS_ISDN)
833                                         {
834                                                 pmisdn = (class PmISDN *)port;
835                                                 if (pmisdn->p_isdn_crypt_listen)
836                                                 {
837                                                         PERROR_RUNTIME("************************************************\n");
838                                                         PERROR_RUNTIME("** BUG detected in %s, line %d, function %s\n", file, line, function);
839                                                         PERROR_RUNTIME("** p_isdn_crypt_listen = %d\n", pmisdn->p_isdn_crypt_listen);
840                                                         PERROR_RUNTIME("************************************************\n");
841                                                         budetect_stop = 1;
842                                                 }
843                                         }
844                                         if (port == mISDNport->b_port[i])
845                                                 break;
846                                         port = port->next;
847                                         if (!port)
848                                         {
849                                                 PERROR_RUNTIME("************************************************\n");
850                                                 PERROR_RUNTIME("** BUG detected in %s, line %d, function %s\n", file, line, function);
851                                                 PERROR_RUNTIME("** b_port not in list.\n");
852                                                 PERROR_RUNTIME("************************************************\n");
853                                                 budetect_stop = 1;
854                                         }
855                                 }
856                         }
857                         i++;
858                 }
859                 mISDNport = mISDNport->next;
860         }
861
862 }
863 #endif
864