00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include <asterisk.h>
00026
00027 #include <errno.h>
00028 #include <stdlib.h>
00029 #include <string.h>
00030 #include <stdio.h>
00031
00032 #include <unistd.h>
00033 #include <sys/poll.h>
00034 #include <netinet/in.h>
00035
00036 #include "asterisk/module.h"
00037 #include "asterisk/logger.h"
00038 #include "asterisk/options.h"
00039 #include "asterisk/utils.h"
00040 #include "asterisk/sched.h"
00041 #include "asterisk/cli.h"
00042 #include "asterisk/lock.h"
00043
00044 #include "config.h"
00045 #include "lffifo.h"
00046 #include "utils.h"
00047 #include "mtp.h"
00048 #include "isup.h"
00049 #include "l4isup.h"
00050 #ifdef SCCP
00051 #include "l4sccp.h"
00052 #endif
00053 #include "cluster.h"
00054
00055
00056
00057
00058
00059 AST_MUTEX_DEFINE_STATIC(mtp_control_mutex);
00060 static struct lffifo *mtp_control_fifo = NULL;
00061
00062
00063
00064
00065 static pthread_t mtp_thread = AST_PTHREADT_NULL;
00066 static int mtp_thread_running = 0;
00067
00068
00069
00070 static pthread_t monitor_thread = AST_PTHREADT_NULL;
00071 static int monitor_running = 0;
00072
00073
00074
00075
00076 AST_MUTEX_DEFINE_STATIC(dump_mutex);
00077 static FILE *dump_in_fh = NULL;
00078 static FILE *dump_out_fh = NULL;
00079 static int dump_do_fisu, dump_do_lssu, dump_do_msu;
00080
00081
00082 static const char desc[] = "SS7 Protocol Support";
00083 static const char config[] = "ss7.conf";
00084
00085
00086
00087
00088 static int cmd_version(int fd, int argc, char *argv[]);
00089 static int cmd_dump_status(int fd, int argc, char *argv[]);
00090 static int cmd_dump_stop(int fd, int argc, char *argv[]);
00091 static int cmd_dump_start(int fd, int argc, char *argv[]);
00092 static char *complete_dump_stop(char *line, char *word, int pos, int state);
00093 static char *complete_dump_start(char *line, char *word, int pos, int state);
00094 static int cmd_link_up(int fd, int argc, char *argv[]);
00095 static int cmd_link_down(int fd, int argc, char *argv[]);
00096 static int cmd_link_status(int fd, int argc, char *argv[]);
00097
00098 static struct ast_cli_entry my_clis[] = {
00099 { {"ss7", "version", NULL}, cmd_version,
00100 "Show current version of chan_ss7",
00101 "Usage: ss7 version\n",
00102 NULL
00103 },
00104
00105 { { "ss7", "dump", "start", NULL}, cmd_dump_start,
00106 "Start raw MTP2 dump to a file",
00107 "Usage: ss7 dump start <file> [in|out|both] [fisu] [lssu] [msu]\n"
00108 " Start raw mtp2 dump to file. Either incoming, outgoing, or both(default).\n"
00109 " Optinally specify which of fisu, lssu, and msu should be dumped.\n"
00110 " The output can be converted with `text2pcap -t %s. -l 140' and\n"
00111 " subsequently fed to Ethereal for protocol decoding.\n",
00112 complete_dump_start
00113 },
00114
00115 { {"ss7", "dump", "stop", NULL}, cmd_dump_stop,
00116 "Stop a running raw MTP2 dump",
00117 "Usage: ss7 dump stop [in|out|both]\n"
00118 " Stop raw mtp2 dump started with \"ss7 start dump\". Either incoming,\n"
00119 " outgoing, or both(default).\n",
00120 complete_dump_stop
00121 },
00122
00123 { {"ss7", "dump", "status", NULL}, cmd_dump_status,
00124 "Stop what dumps are running",
00125 "Usage: ss7 dump status\n",
00126 NULL
00127 },
00128
00129 #ifndef MODULETEST
00130 { {"ss7", "link", "down", NULL}, cmd_link_down,
00131 "Stop the MTP2 link(s) [logical-link-no]...",
00132 "Usage: ss7 link down [logical-link-no]\n"
00133 " Take the link(s) down; it will be down until started explicitly with\n"
00134 " 'ss7 link up'.\n"
00135 " Until then, it will continuously transmit LSSU 'OS' (out-of-service)\n"
00136 " frames.\n"
00137 " If no logical-link-no argument is given, all links are affected.\n",
00138 NULL
00139 },
00140
00141 { {"ss7", "link", "up", NULL}, cmd_link_up,
00142 "Start the MTP2 link(s) [logical-link-no]...",
00143 "Usage: ss7 link up\n"
00144 " Attempt to take the MTP2 link(s) up with the initial alignment procedure.\n"
00145 " If no logical-link-no argument is given, all links are affected.\n",
00146 NULL
00147 },
00148
00149 { {"ss7", "link", "status", NULL}, cmd_link_status,
00150 "Show status of the MTP2 links",
00151 "Usage: ss7 link status\n"
00152 " Show the status of the MTP2 links.\n",
00153 NULL
00154 },
00155 #endif
00156
00157 { {"ss7", "block", NULL}, cmd_block,
00158 "Set circuits in local maintenance blocked mode",
00159 "Usage: ss7 block <first> <count> [<linksetname>]\n"
00160 " Set <count> lines into local maintenance blocked mode, starting at circuit <first>on linkset <linksetname>\n",
00161 NULL
00162 },
00163
00164 { {"ss7", "unblock", NULL}, cmd_unblock,
00165 "Remove local maintenance blocked mode from circuits",
00166 "Usage: ss7 unblock <first> <count> [<linksetname>]\n"
00167 " Remove <count> lines from local maintenance blocked mode, starting at circuit <first> on linkset <linksetname>.\n",
00168 NULL
00169 },
00170
00171 { {"ss7", "linestat", NULL}, cmd_linestat,
00172 "Show line states",
00173 "Usage: ss7 linestat\n"
00174 " Show status for all circuits.\n",
00175 NULL
00176 },
00177
00178 { {"ss7", "show", "channels", NULL}, cmd_linestat,
00179 "Show channel states",
00180 "Usage: ss7 show channels\n"
00181 " Show status for all channels.\n",
00182 NULL
00183 },
00184
00185 { {"ss7", "cluster", "start", NULL}, cmd_cluster_start,
00186 "Start cluster",
00187 "Usage: ss7 cluster start\n"
00188 " Start the cluster.\n",
00189 NULL
00190 },
00191
00192 { {"ss7", "cluster", "stop", NULL}, cmd_cluster_stop,
00193 "Stop cluster",
00194 "Usage: ss7 cluster stop\n"
00195 " Stop the cluster.\n",
00196 NULL
00197 },
00198
00199 { {"ss7", "cluster", "status", NULL}, cmd_cluster_status,
00200 "Show status of the cluster",
00201 "Usage: ss7 cluster status\n"
00202 " Show the status of the cluster.\n",
00203 NULL
00204 },
00205
00206 { {"ss7", "reset", NULL}, cmd_reset,
00207 "Reset all circuits",
00208 "Usage: ss7 reset\n"
00209 " Reset all circuits.\n",
00210 NULL
00211 },
00212
00213 { { "ss7", "mtp", "data", NULL}, mtp_cmd_data,
00214 "Copy hex encoded string to MTP",
00215 "Usage: ss7 mtp data string\n"
00216 " Copy hex encoded string to MTP",
00217 NULL,
00218 },
00219
00220 #ifdef MODULETEST
00221 { {"ss7", "testfailover", NULL}, cmd_testfailover,
00222 "Test the failover mechanism",
00223 "Usage: ss7 testfailover"
00224 " Test the failover mechanism.\n",
00225 NULL
00226 },
00227 { {"ss7", "moduletest", NULL}, cmd_moduletest,
00228 "Run a moduletest",
00229 "Usage: ss7 moduletest <no>"
00230 " Run moduletest <no>.\n",
00231 NULL
00232 },
00233 #endif
00234 };
00235
00236
00237
00238 static void mtp_enqueue_control(struct mtp_req *req) {
00239 int res;
00240
00241 ast_mutex_lock(&mtp_control_mutex);
00242 res = lffifo_put(mtp_control_fifo, (unsigned char *)req, sizeof(struct mtp_req) + req->len);
00243 ast_mutex_unlock(&mtp_control_mutex);
00244 if(res != 0) {
00245 ast_log(LOG_WARNING, "MTP control fifo full (MTP thread hanging?).\n");
00246 }
00247 }
00248
00249
00250 static int start_mtp_thread(void)
00251 {
00252 return start_thread(&mtp_thread, mtp_thread_main, &mtp_thread_running, 15);
00253 }
00254
00255 static void stop_mtp_thread(void)
00256 {
00257 mtp_thread_signal_stop();
00258 stop_thread(mtp_thread, &mtp_thread_running);
00259 }
00260
00261 static int cmd_link_up_down(int fd, int argc, char *argv[], int updown) {
00262 static unsigned char buf[sizeof(struct mtp_req)];
00263 struct mtp_req *req = (struct mtp_req *)buf;
00264 int i;
00265
00266 req->typ = updown;
00267 req->len = sizeof(req->link);
00268 if(argc > 3) {
00269 for (i = 3; i < argc; i++) {
00270 int link_ix = atoi(argv[i]);
00271 ast_log(LOG_DEBUG, "MPT control link %s %d\n", updown == MTP_REQ_LINK_UP ? "up" : "down", link_ix);
00272 if (link_ix >= this_hosts_linkset.n_schannels) {
00273 ast_log(LOG_ERROR, "Link index out of range %d, max %d.\n", link_ix, this_hosts_linkset.n_schannels);
00274 return RESULT_FAILURE;
00275 }
00276 req->link.link_ix = link_ix;
00277 mtp_enqueue_control(req);
00278 }
00279 }
00280 else {
00281 for (i=0; i < this_hosts_linkset.n_schannels; i++) {
00282 ast_log(LOG_DEBUG, "MTP control link %s %d\n", updown == MTP_REQ_LINK_UP ? "up" : "down", i);
00283 req->link.link_ix = i;
00284 mtp_enqueue_control(req);
00285 }
00286 }
00287 return RESULT_SUCCESS;
00288 }
00289
00290
00291 static int cmd_link_down(int fd, int argc, char *argv[]) {
00292 return cmd_link_up_down(fd, argc, argv, MTP_REQ_LINK_DOWN);
00293 }
00294
00295
00296 static int cmd_link_up(int fd, int argc, char *argv[]) {
00297 return cmd_link_up_down(fd, argc, argv, MTP_REQ_LINK_UP);
00298 }
00299
00300
00301 static int cmd_link_status(int fd, int argc, char *argv[]) {
00302 char buff[256];
00303 int i;
00304
00305 for (i = 0; i < this_hosts_linkset.n_schannels; i++) {
00306 if (this_hosts_linkset.schannels[i]->enabled) {
00307 if (mtp_cmd_linkstatus(buff, i) == 0)
00308 ast_cli(fd, buff);
00309 }
00310 }
00311 return RESULT_SUCCESS;
00312 }
00313
00314
00315 static char *complete_generic(char *word, int state, char **options, int entries) {
00316 int which = 0;
00317 int i;
00318
00319 for(i = 0; i < entries; i++) {
00320 if(0 == strncasecmp(word, options[i], strlen(word))) {
00321 if(++which > state) {
00322 return strdup(options[i]);
00323 }
00324 }
00325 }
00326 return NULL;
00327 }
00328
00329 static char *dir_options[] = { "in", "out", "both", };
00330 static char *filter_options[] = { "fisu", "lssu", "msu", };
00331
00332 static char *complete_dump_start(char *line, char *word, int pos, int state) {
00333 if(pos == 4) {
00334 return complete_generic(word, state, dir_options,
00335 sizeof(dir_options)/sizeof(dir_options[0]));
00336 } else if(pos > 4) {
00337 return complete_generic(word, state, filter_options,
00338 sizeof(filter_options)/sizeof(filter_options[0]));
00339 } else {
00340
00341 return NULL;
00342 }
00343 }
00344
00345 static char *complete_dump_stop(char *line, char *word, int pos, int state) {
00346 if(pos == 3) {
00347 return complete_generic(word, state, dir_options,
00348 sizeof(dir_options)/sizeof(dir_options[0]));
00349 } else {
00350 return NULL;
00351 }
00352 }
00353
00354 static int cmd_dump_start(int fd, int argc, char *argv[]) {
00355 int in, out;
00356 int i;
00357 int fisu,lssu,msu;
00358 FILE *fh;
00359
00360 if(argc < 4) {
00361 return RESULT_SHOWUSAGE;
00362 }
00363
00364 if(argc == 4) {
00365 in = 1;
00366 out = 1;
00367 } else {
00368 if(0 == strcasecmp(argv[4], "in")) {
00369 in = 1;
00370 out = 0;
00371 } else if(0 == strcasecmp(argv[4], "out")) {
00372 in = 0;
00373 out = 1;
00374 } else if(0 == strcasecmp(argv[4], "both")) {
00375 in = 1;
00376 out = 1;
00377 } else {
00378 return RESULT_SHOWUSAGE;
00379 }
00380 }
00381
00382 ast_mutex_lock(&dump_mutex);
00383 if((in && dump_in_fh != NULL) || (out && dump_out_fh != NULL)) {
00384 ast_cli(fd, "Dump already running, must be stopped (with 'ss7 stop dump') "
00385 "before new can be started.\n");
00386 ast_mutex_unlock(&dump_mutex);
00387 return RESULT_FAILURE;
00388 }
00389
00390 if(argc <= 5) {
00391 fisu = 0;
00392 lssu = 0;
00393 msu = 1;
00394 } else {
00395 fisu = 0;
00396 lssu = 0;
00397 msu = 0;
00398 for(i = 5; i < argc; i++) {
00399 if(0 == strcasecmp(argv[i], "fisu")) {
00400 fisu = 1;
00401 } else if(0 == strcasecmp(argv[i], "lssu")) {
00402 lssu = 1;
00403 } else if(0 == strcasecmp(argv[i], "msu")) {
00404 msu = 1;
00405 } else {
00406 ast_mutex_unlock(&dump_mutex);
00407 return RESULT_SHOWUSAGE;
00408 }
00409 }
00410 }
00411
00412 fh = fopen(argv[3], "w");
00413 if(fh == NULL) {
00414 ast_cli(fd, "Error opening file '%s': %s.\n", argv[3], strerror(errno));
00415 ast_mutex_unlock(&dump_mutex);
00416 return RESULT_FAILURE;
00417 }
00418
00419 if(in) {
00420 dump_in_fh = fh;
00421 }
00422 if(out) {
00423 dump_out_fh = fh;
00424 }
00425 dump_do_fisu = fisu;
00426 dump_do_lssu = lssu;
00427 dump_do_msu = msu;
00428
00429 ast_mutex_unlock(&dump_mutex);
00430 return RESULT_SUCCESS;
00431 }
00432
00433 static int cmd_dump_stop(int fd, int argc, char *argv[]) {
00434 int in, out;
00435
00436 if(argc == 3) {
00437 in = 1;
00438 out = 1;
00439 } else if(argc == 4) {
00440 if(0 == strcasecmp(argv[3], "in")) {
00441 in = 1;
00442 out = 0;
00443 } else if(0 == strcasecmp(argv[3], "out")) {
00444 in = 0;
00445 out = 1;
00446 } else if(0 == strcasecmp(argv[3], "both")) {
00447 in = 1;
00448 out = 1;
00449 } else {
00450 return RESULT_SHOWUSAGE;
00451 }
00452 } else {
00453 return RESULT_SHOWUSAGE;
00454 }
00455
00456 ast_mutex_lock(&dump_mutex);
00457
00458 if((in && !out && dump_in_fh == NULL) ||
00459 (out && !in && dump_out_fh == NULL) ||
00460 (in && out && dump_in_fh == NULL && dump_out_fh == NULL)) {
00461 ast_cli(fd, "No dump running.\n");
00462 ast_mutex_unlock(&dump_mutex);
00463 return RESULT_SUCCESS;
00464 }
00465
00466 if(in && dump_in_fh != NULL) {
00467 if(dump_out_fh == dump_in_fh) {
00468
00469 dump_out_fh = NULL;
00470 }
00471 fclose(dump_in_fh);
00472 dump_in_fh = NULL;
00473 }
00474 if(out && dump_out_fh != NULL) {
00475 fclose(dump_out_fh);
00476 dump_out_fh = NULL;
00477 }
00478
00479 ast_mutex_unlock(&dump_mutex);
00480 return RESULT_SUCCESS;
00481 }
00482
00483 static int cmd_dump_status(int fd, int argc, char *argv[]) {
00484 ast_mutex_lock(&dump_mutex);
00485
00486
00487
00488
00489 ast_cli(fd, "Yuck! what is going on here?!?\n");
00490 if(dump_in_fh != NULL) {
00491 ast_cli(fd, "Dump of incoming frames is running.\n");
00492 }
00493 if(dump_out_fh != NULL) {
00494 ast_cli(fd, "Dump of outgoing frames is running.\n");
00495 }
00496 if(dump_in_fh != NULL || dump_out_fh != NULL) {
00497 ast_cli(fd, "Filter:%s%s%s.\n",
00498 (dump_do_fisu ? " fisu" : ""),
00499 (dump_do_lssu ? " lssu" : ""),
00500 (dump_do_msu ? " msu" : ""));
00501 }
00502
00503 ast_mutex_unlock(&dump_mutex);
00504 return RESULT_SUCCESS;
00505 }
00506
00507
00508 static int cmd_version(int fd, int argc, char *argv[]) {
00509
00510 ast_cli(fd, "chan_ss7 version %s\n", CHAN_SS7_VERSION);
00511
00512 return RESULT_SUCCESS;
00513 }
00514
00515
00516
00517
00518
00519
00520 static void *monitor_main(void *data) {
00521 int res;
00522 struct pollfd fds[1];
00523 struct lffifo *receive_fifo = mtp_get_receive_fifo();
00524 FILE *dump_fh;
00525 int i;
00526
00527 ast_verbose(VERBOSE_PREFIX_3 "Starting monitor thread, pid=%d.\n", getpid());
00528
00529 fds[0].fd = mtp_get_receive_pipe();
00530 fds[0].events = POLLIN;
00531
00532 while(monitor_running) {
00533 int timeout = timers_wait();
00534 fds[0].revents = 0;
00535
00536 res = poll(fds, 1, timeout);
00537 if(res < 0) {
00538 if(errno == EINTR) {
00539
00540 } else {
00541 ast_log(LOG_ERROR, "poll() failure, errno=%d: %s\n",
00542 errno, strerror(errno));
00543 }
00544 } else if(res > 0) {
00545
00546 unsigned char dummy[512];
00547 unsigned char eventbuf[MTP_EVENT_MAX_SIZE];
00548 struct mtp_event *event;
00549
00550
00551
00552
00553 read(fds[0].fd, dummy, sizeof(dummy));
00554
00555
00556 while((res = lffifo_get(receive_fifo, eventbuf, sizeof(eventbuf))) != 0) {
00557 if(res < 0) {
00558 ast_log(LOG_ERROR, "Yuck! oversized frame in receive fifo, bailing out.\n");
00559 return NULL;
00560 }
00561 event = (struct mtp_event *)eventbuf;
00562 switch(event->typ) {
00563 case MTP_EVENT_ISUP:
00564 l4isup_event(event);
00565 break;
00566 case MTP_EVENT_SCCP:
00567 #ifdef SCCP
00568 l4sccp_event(event);
00569 #endif
00570 break;
00571
00572 case MTP_EVENT_LOG:
00573 ast_log(event->log.level, event->log.file, event->log.line,
00574 event->log.function, "%s", event->buf);
00575 break;
00576
00577 case MTP_EVENT_DUMP:
00578 ast_mutex_lock(&dump_mutex);
00579
00580 if(event->dump.out) {
00581 dump_fh = dump_out_fh;
00582 } else {
00583 dump_fh = dump_in_fh;
00584 }
00585 if(dump_fh != NULL) {
00586 if(event->len < 3 ||
00587 ( !(event->buf[2] == 0 && !dump_do_fisu) &&
00588 !((event->buf[2] == 1 || event->buf[2] == 2) && !dump_do_lssu) &&
00589 !(event->buf[2] > 2 && !dump_do_msu))) {
00590 fprintf(dump_fh, "%.6f 0%s", event->dump.position/8000,
00591 event->dump.out ? "\t\t" : "");
00592 for(i=0; i < event->len; i++) {
00593 fprintf(dump_fh, " %02x", event->buf[i]);
00594 }
00595 fprintf(dump_fh, "\n");
00596 fflush(dump_fh);
00597 }
00598 }
00599
00600 ast_mutex_unlock(&dump_mutex);
00601 break;
00602
00603 case MTP_EVENT_RAWDUMP:
00604 #if 0
00605
00606
00607 {
00608 FILE * f = fopen(event->rawdump.out ? "/tmp/out" : "/tmp/in", "ab");
00609 if(f) {
00610 fwrite(event->buf, 1, event->len, f);
00611 fclose(f);
00612 } else {
00613 ast_log(LOG_WARNING, "Error opening raw dump output: %s.\n",
00614 strerror(errno));
00615 }
00616 }
00617 #endif
00618 break;
00619
00620 case MTP_EVENT_STATUS:
00621 {
00622 struct link* link = event->status.link;
00623 char* name = link ? link->name : "(peer)";
00624 switch(event->status.link_state) {
00625 case MTP_EVENT_STATUS_LINK_UP:
00626 l4isup_link_status_change(link, 1);
00627 #ifdef SCCP
00628 l4sccp_link_status_change(link, 1);
00629 #endif
00630 ast_log(LOG_WARNING, "MTP is now UP on link '%s'.\n", name);
00631 break;
00632 case MTP_EVENT_STATUS_LINK_DOWN:
00633 l4isup_link_status_change(link, 0);
00634 #ifdef SCCP
00635 l4sccp_link_status_change(link, 0);
00636 #endif
00637 ast_log(LOG_WARNING, "MTP is now DOWN on link '%s'.\n", name);
00638 break;
00639 case MTP_EVENT_STATUS_INSERVICE:
00640 ast_log(LOG_WARNING, "MTP is now INSERVICE for linkset '%s'.\n", link->linkset->name);
00641 l4isup_inservice(link);
00642 #ifdef SCCP
00643 l4sccp_inservice(link);
00644 #endif
00645 break;
00646 default:
00647 ast_log(LOG_NOTICE, "Unknown event type STATUS (%d), "
00648 "not processed.\n", event->status.link_state);
00649 }
00650 }
00651 break;
00652
00653 default:
00654 ast_log(LOG_NOTICE, "Unexpected mtp event type %d.\n", event->typ);
00655 }
00656 }
00657 }
00658
00659
00660
00661
00662
00663
00664 run_timers();
00665 }
00666 return NULL;
00667 }
00668
00669
00670 static void stop_monitor(void) {
00671 if(monitor_running) {
00672 monitor_running = 0;
00673
00674 pthread_join(monitor_thread, NULL);
00675 }
00676 }
00677
00678 static int load_module(void)
00679 {
00680 int i;
00681
00682 if(load_config(0)) {
00683 return AST_MODULE_LOAD_DECLINE;
00684 }
00685
00686 if (timers_init()) {
00687 ast_log(LOG_ERROR, "Unable to initialize timres.\n");
00688 return AST_MODULE_LOAD_FAILURE;
00689 }
00690 isup_init();
00691 #ifdef SCCP
00692 sccp_init();
00693 #endif
00694
00695 if(mtp_init()) {
00696 ast_log(LOG_ERROR, "Unable to initialize MTP.\n");
00697 return AST_MODULE_LOAD_FAILURE;
00698 }
00699 if(start_mtp_thread()) {
00700 ast_log(LOG_ERROR, "Unable to start MTP thread.\n");
00701 return AST_MODULE_LOAD_FAILURE;
00702 }
00703 mtp_control_fifo = mtp_get_control_fifo();
00704
00705 monitor_running = 1;
00706
00707 if(ast_pthread_create(&monitor_thread, NULL, monitor_main, NULL) < 0) {
00708 ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
00709 monitor_running = 0;
00710 return AST_MODULE_LOAD_FAILURE;
00711 }
00712
00713
00714 ast_cli_register_multiple(my_clis, sizeof(my_clis)/ sizeof(my_clis[0]));
00715
00716 ast_verbose(VERBOSE_PREFIX_3 "SS7 channel loaded successfully.\n");
00717 return AST_MODULE_LOAD_SUCCESS;
00718 }
00719
00720 static int unload_module(void)
00721 {
00722 ast_cli_unregister_multiple(my_clis, sizeof(my_clis)/ sizeof(my_clis[0]));
00723
00724 #ifdef SCCP
00725 sccp_cleanup();
00726 #endif
00727 isup_cleanup();
00728
00729 ast_mutex_lock(&dump_mutex);
00730 if(dump_in_fh != NULL) {
00731 if(dump_in_fh == dump_out_fh) {
00732 dump_out_fh = NULL;
00733 }
00734 fclose(dump_in_fh);
00735 dump_in_fh = NULL;
00736 }
00737 if(dump_out_fh != NULL) {
00738 fclose(dump_out_fh);
00739 dump_out_fh = NULL;
00740 }
00741 ast_mutex_unlock(&dump_mutex);
00742
00743 if(monitor_running) {
00744 stop_monitor();
00745 }
00746 stop_mtp_thread();
00747 mtp_cleanup();
00748 timers_cleanup();
00749
00750
00751 destroy_config();
00752 ast_verbose(VERBOSE_PREFIX_3 "SS7 channel unloaded.\n");
00753 return 0;
00754 }
00755 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, desc);
00756