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 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
00028
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <errno.h>
00032 #include <termios.h>
00033 #include <sys/time.h>
00034 #include <time.h>
00035 #include <ctype.h>
00036
00037 #include "asterisk/module.h"
00038 #include "asterisk/lock.h"
00039 #include "asterisk/utils.h"
00040 #include "asterisk/smdi.h"
00041 #include "asterisk/config.h"
00042 #include "asterisk/astobj.h"
00043 #include "asterisk/io.h"
00044 #include "asterisk/logger.h"
00045 #include "asterisk/utils.h"
00046 #include "asterisk/options.h"
00047
00048
00049 #define SMDI_MSG_EXPIRY_TIME 30000
00050
00051 static const char config_file[] = "smdi.conf";
00052
00053 static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *msg);
00054 static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *msg);
00055
00056 static void *smdi_read(void *iface_p);
00057 static int smdi_load(int reload);
00058
00059 struct module_symbols *me;
00060
00061
00062 struct ast_smdi_interface_container {
00063 ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface);
00064 } smdi_ifaces;
00065
00066
00067
00068
00069
00070
00071
00072 static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
00073 {
00074 ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg);
00075 }
00076
00077
00078
00079
00080
00081
00082
00083 static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
00084 {
00085 ASTOBJ_CONTAINER_LINK_END(&iface->mwi_q, mwi_msg);
00086 }
00087
00088
00089
00090
00091
00092
00093 int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
00094 {
00095 FILE *file;
00096 int i;
00097
00098 file = fopen(iface->name, "w");
00099 if(!file) {
00100 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
00101 return 1;
00102 }
00103
00104 ASTOBJ_WRLOCK(iface);
00105
00106 fprintf(file, "OP:MWI ");
00107
00108 for(i = 0; i < iface->msdstrip; i++)
00109 fprintf(file, "0");
00110
00111 fprintf(file, "%s!\x04", mailbox);
00112 fclose(file);
00113
00114 ASTOBJ_UNLOCK(iface);
00115 ast_log(LOG_DEBUG, "Sent MWI set message for %s on %s\n", mailbox, iface->name);
00116 return 0;
00117 }
00118
00119
00120
00121
00122
00123
00124 int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
00125 {
00126 FILE *file;
00127 int i;
00128
00129 file = fopen(iface->name, "w");
00130 if(!file) {
00131 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
00132 return 1;
00133 }
00134
00135 ASTOBJ_WRLOCK(iface);
00136
00137 fprintf(file, "RMV:MWI ");
00138
00139 for(i = 0; i < iface->msdstrip; i++)
00140 fprintf(file, "0");
00141
00142 fprintf(file, "%s!\x04", mailbox);
00143 fclose(file);
00144
00145 ASTOBJ_UNLOCK(iface);
00146 ast_log(LOG_DEBUG, "Sent MWI unset message for %s on %s\n", mailbox, iface->name);
00147 return 0;
00148 }
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159 void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
00160 {
00161 ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg);
00162 }
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173 void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
00174 {
00175 ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg);
00176 }
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188 struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
00189 {
00190 struct ast_smdi_md_message *md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
00191 struct timeval now;
00192 long elapsed = 0;
00193
00194
00195 now = ast_tvnow();
00196 while (md_msg) {
00197 elapsed = ast_tvdiff_ms(now, md_msg->timestamp);
00198
00199 if (elapsed > iface->msg_expiry) {
00200
00201 ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
00202 ast_log(LOG_NOTICE, "Purged expired message from %s SMDI MD message queue. Message was %ld milliseconds too old.\n",
00203 iface->name, elapsed - iface->msg_expiry);
00204 md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
00205 }
00206 else {
00207
00208 break;
00209 }
00210 }
00211
00212 return md_msg;
00213 }
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227 extern struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
00228 {
00229 struct timeval start;
00230 long diff = 0;
00231 struct ast_smdi_md_message *msg;
00232
00233 start = ast_tvnow();
00234 while (diff < timeout) {
00235
00236 if ((msg = ast_smdi_md_message_pop(iface)))
00237 return msg;
00238
00239
00240 diff = ast_tvdiff_ms(ast_tvnow(), start);
00241 }
00242
00243 return (ast_smdi_md_message_pop(iface));
00244 }
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256 extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
00257 {
00258 struct ast_smdi_mwi_message *mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
00259 struct timeval now;
00260 long elapsed = 0;
00261
00262
00263 now = ast_tvnow();
00264 while (mwi_msg) {
00265 elapsed = ast_tvdiff_ms(now, mwi_msg->timestamp);
00266
00267 if (elapsed > iface->msg_expiry) {
00268
00269 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
00270 ast_log(LOG_NOTICE, "Purged expired message from %s SMDI MWI message queue. Message was %ld milliseconds too old.\n",
00271 iface->name, elapsed - iface->msg_expiry);
00272 mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
00273 }
00274 else {
00275
00276 break;
00277 }
00278 }
00279
00280 return mwi_msg;
00281 }
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295 extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
00296 {
00297 struct timeval start;
00298 long diff = 0;
00299 struct ast_smdi_mwi_message *msg;
00300
00301 start = ast_tvnow();
00302 while (diff < timeout) {
00303
00304 if ((msg = ast_smdi_mwi_message_pop(iface)))
00305 return msg;
00306
00307
00308 diff = ast_tvdiff_ms(ast_tvnow(), start);
00309 }
00310
00311 return (ast_smdi_mwi_message_pop(iface));
00312 }
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322 extern struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
00323 {
00324 return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name));
00325 }
00326
00327
00328
00329
00330
00331
00332
00333
00334 static void *smdi_read(void *iface_p)
00335 {
00336 struct ast_smdi_interface *iface = iface_p;
00337 struct ast_smdi_md_message *md_msg;
00338 struct ast_smdi_mwi_message *mwi_msg;
00339 char c = '\0';
00340 char *cp = NULL;
00341 int i;
00342 int start = 0;
00343
00344
00345 while ((c = fgetc(iface->file))) {
00346
00347
00348 if (!start) {
00349 if (c == 'M')
00350 start = 1;
00351 }
00352 else {
00353 if(c == 'D') {
00354 start = 0;
00355
00356 if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
00357 ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
00358 return NULL;
00359 }
00360
00361 ASTOBJ_INIT(md_msg);
00362
00363
00364 for(i = 0; i < SMDI_MESG_DESK_NUM_LEN; i++)
00365 md_msg->mesg_desk_num[i] = fgetc(iface->file);
00366
00367 md_msg->mesg_desk_num[SMDI_MESG_DESK_NUM_LEN] = '\0';
00368
00369
00370 for(i = 0; i < SMDI_MESG_DESK_TERM_LEN; i++)
00371 md_msg->mesg_desk_term[i] = fgetc(iface->file);
00372
00373 md_msg->mesg_desk_term[SMDI_MESG_DESK_TERM_LEN] = '\0';
00374
00375
00376 md_msg->type = fgetc(iface->file);
00377
00378
00379 cp = &md_msg->fwd_st[0];
00380 for (i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
00381 if((c = fgetc(iface->file)) == ' ') {
00382 *cp = '\0';
00383 break;
00384 }
00385
00386
00387 if( i >= iface->msdstrip)
00388 *cp++ = c;
00389 }
00390
00391
00392 md_msg->fwd_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
00393 cp = NULL;
00394
00395
00396 cp = &md_msg->calling_st[0];
00397 for (i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
00398 if (!isdigit((c = fgetc(iface->file)))) {
00399 *cp = '\0';
00400 break;
00401 }
00402
00403
00404 if (i >= iface->msdstrip)
00405 *cp++ = c;
00406 }
00407
00408
00409 md_msg->calling_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
00410 cp = NULL;
00411
00412
00413 md_msg->timestamp = ast_tvnow();
00414 ast_smdi_md_message_push(iface, md_msg);
00415 ast_log(LOG_DEBUG, "Recieved SMDI MD message on %s\n", iface->name);
00416
00417 ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
00418
00419 } else if(c == 'W') {
00420 start = 0;
00421
00422 if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
00423 ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
00424 return NULL;
00425 }
00426
00427 ASTOBJ_INIT(mwi_msg);
00428
00429
00430 fgetc(iface->file);
00431
00432
00433 cp = &mwi_msg->fwd_st[0];
00434 for (i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
00435 if ((c = fgetc(iface->file)) == ' ') {
00436 *cp = '\0';
00437 break;
00438 }
00439
00440
00441 if (i >= iface->msdstrip)
00442 *cp++ = c;
00443 }
00444
00445
00446 mwi_msg->fwd_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
00447 cp = NULL;
00448
00449
00450 for (i = 0; i < SMDI_MWI_FAIL_CAUSE_LEN; i++)
00451 mwi_msg->cause[i] = fgetc(iface->file);
00452
00453 mwi_msg->cause[SMDI_MWI_FAIL_CAUSE_LEN] = '\0';
00454
00455
00456 mwi_msg->timestamp = ast_tvnow();
00457 ast_smdi_mwi_message_push(iface, mwi_msg);
00458 ast_log(LOG_DEBUG, "Recieved SMDI MWI message on %s\n", iface->name);
00459
00460 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
00461 } else {
00462 ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c);
00463 start = 0;
00464 }
00465 }
00466 }
00467
00468 ast_log(LOG_ERROR, "Error reading from SMDI interface %s, stopping listener thread\n", iface->name);
00469 ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
00470 return NULL;
00471 }
00472
00473
00474 void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg)
00475 {
00476 free(msg);
00477 }
00478
00479
00480 void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
00481 {
00482 free(msg);
00483 }
00484
00485
00486 void ast_smdi_interface_destroy(struct ast_smdi_interface *iface)
00487 {
00488 if (iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) {
00489 pthread_cancel(iface->thread);
00490 pthread_join(iface->thread, NULL);
00491 }
00492
00493 iface->thread = AST_PTHREADT_STOP;
00494
00495 if(iface->file)
00496 fclose(iface->file);
00497
00498 ASTOBJ_CONTAINER_DESTROYALL(&iface->md_q, ast_smdi_md_message_destroy);
00499 ASTOBJ_CONTAINER_DESTROYALL(&iface->mwi_q, ast_smdi_mwi_message_destroy);
00500 ASTOBJ_CONTAINER_DESTROY(&iface->md_q);
00501 ASTOBJ_CONTAINER_DESTROY(&iface->mwi_q);
00502 free(iface);
00503
00504 ast_module_unref(ast_module_info->self);
00505 }
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517 static int smdi_load(int reload)
00518 {
00519 struct ast_config *conf;
00520 struct ast_variable *v;
00521 struct ast_smdi_interface *iface = NULL;
00522 int res = 0;
00523
00524
00525 speed_t baud_rate = B9600;
00526 tcflag_t paritybit = PARENB;
00527 tcflag_t charsize = CS7;
00528 int stopbits = 0;
00529
00530 int msdstrip = 0;
00531 long msg_expiry = SMDI_MSG_EXPIRY_TIME;
00532
00533 conf = ast_config_load(config_file);
00534
00535 if (!conf) {
00536 if (reload)
00537 ast_log(LOG_NOTICE, "Unable to reload config %s: SMDI untouched\n", config_file);
00538 else
00539 ast_log(LOG_NOTICE, "Unable to load config %s: SMDI disabled\n", config_file);
00540 return 1;
00541 }
00542
00543
00544
00545
00546
00547
00548 if (reload)
00549 ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces);
00550
00551 for (v = ast_variable_browse(conf, "interfaces"); v; v = v->next) {
00552 if (!strcasecmp(v->name, "baudrate")) {
00553 if (!strcasecmp(v->value, "9600"))
00554 baud_rate = B9600;
00555 else if(!strcasecmp(v->value, "4800"))
00556 baud_rate = B4800;
00557 else if(!strcasecmp(v->value, "2400"))
00558 baud_rate = B2400;
00559 else if(!strcasecmp(v->value, "1200"))
00560 baud_rate = B1200;
00561 else {
00562 ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno);
00563 baud_rate = B9600;
00564 }
00565 } else if (!strcasecmp(v->name, "msdstrip")) {
00566 if (!sscanf(v->value, "%d", &msdstrip)) {
00567 ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
00568 msdstrip = 0;
00569 } else if (0 > msdstrip || msdstrip > 9) {
00570 ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
00571 msdstrip = 0;
00572 }
00573 } else if (!strcasecmp(v->name, "msgexpirytime")) {
00574 if (!sscanf(v->value, "%ld", &msg_expiry)) {
00575 ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno);
00576 msg_expiry = SMDI_MSG_EXPIRY_TIME;
00577 }
00578 } else if (!strcasecmp(v->name, "paritybit")) {
00579 if (!strcasecmp(v->value, "even"))
00580 paritybit = PARENB;
00581 else if (!strcasecmp(v->value, "odd"))
00582 paritybit = PARENB | PARODD;
00583 else if (!strcasecmp(v->value, "none"))
00584 paritybit = ~PARENB;
00585 else {
00586 ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno);
00587 paritybit = PARENB;
00588 }
00589 } else if (!strcasecmp(v->name, "charsize")) {
00590 if (!strcasecmp(v->value, "7"))
00591 charsize = CS7;
00592 else if (!strcasecmp(v->value, "8"))
00593 charsize = CS8;
00594 else {
00595 ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno);
00596 charsize = CS7;
00597 }
00598 } else if (!strcasecmp(v->name, "twostopbits")) {
00599 stopbits = ast_true(v->name);
00600 } else if (!strcasecmp(v->name, "smdiport")) {
00601 if (reload) {
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612 if ((iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
00613 ast_log(LOG_NOTICE, "SMDI interface %s already running, not restarting\n", iface->name);
00614 ASTOBJ_UNMARK(iface);
00615 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00616 continue;
00617 }
00618 }
00619
00620 if (!(iface = ast_calloc(1, sizeof(*iface))))
00621 continue;
00622
00623 ASTOBJ_INIT(iface);
00624 ASTOBJ_CONTAINER_INIT(&iface->md_q);
00625 ASTOBJ_CONTAINER_INIT(&iface->mwi_q);
00626
00627 ast_copy_string(iface->name, v->value, sizeof(iface->name));
00628
00629 if (!(iface->file = fopen(iface->name, "r"))) {
00630 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s)\n", iface->name, strerror(errno));
00631 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00632 continue;
00633 }
00634
00635 iface->fd = fileno(iface->file);
00636
00637
00638
00639
00640 if (tcgetattr(iface->fd, &iface->mode)) {
00641 ast_log(LOG_ERROR, "Error getting atributes of %s (%s)\n", iface->name, strerror(errno));
00642 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00643 continue;
00644 }
00645
00646
00647 if (cfsetispeed(&iface->mode, baud_rate) || cfsetospeed(&iface->mode, baud_rate)) {
00648 ast_log(LOG_ERROR, "Error setting baud rate on %s (%s)\n", iface->name, strerror(errno));
00649 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00650 continue;
00651 }
00652
00653
00654 if (stopbits)
00655 iface->mode.c_cflag = iface->mode.c_cflag | CSTOPB;
00656 else
00657 iface->mode.c_cflag = iface->mode.c_cflag & ~CSTOPB;
00658
00659
00660 iface->mode.c_cflag = (iface->mode.c_cflag & ~PARENB & ~PARODD) | paritybit;
00661
00662
00663 iface->mode.c_cflag = (iface->mode.c_cflag & ~CSIZE) | charsize;
00664
00665
00666 if (tcsetattr(iface->fd, TCSAFLUSH, &iface->mode)) {
00667 ast_log(LOG_ERROR, "Error setting attributes on %s (%s)\n", iface->name, strerror(errno));
00668 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00669 continue;
00670 }
00671
00672
00673 iface->msdstrip = msdstrip;
00674
00675
00676 iface->msg_expiry = msg_expiry;
00677
00678
00679 if (option_verbose > 2)
00680 ast_verbose(VERBOSE_PREFIX_3 "Starting SMDI monitor thread for %s\n", iface->name);
00681 if (ast_pthread_create_background(&iface->thread, NULL, smdi_read, iface)) {
00682 ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name);
00683 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00684 continue;
00685 }
00686
00687 ASTOBJ_CONTAINER_LINK(&smdi_ifaces, iface);
00688 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00689 ast_module_ref(ast_module_info->self);
00690 } else {
00691 ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file);
00692 }
00693 }
00694 ast_config_destroy(conf);
00695
00696
00697 if (reload)
00698 ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces, ast_smdi_interface_destroy);
00699
00700 ASTOBJ_CONTAINER_RDLOCK(&smdi_ifaces);
00701
00702 if (!smdi_ifaces.head)
00703 res = 1;
00704 ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces);
00705
00706 return res;
00707 }
00708
00709 static int load_module(void)
00710 {
00711 int res;
00712
00713
00714 memset(&smdi_ifaces, 0, sizeof(smdi_ifaces));
00715 ASTOBJ_CONTAINER_INIT(&smdi_ifaces);
00716
00717
00718 res = smdi_load(0);
00719 if (res < 0) {
00720 return res;
00721 } else if (res == 1) {
00722 ast_log(LOG_WARNING, "No SMDI interfaces are available to listen on, not starting SDMI listener.\n");
00723 return AST_MODULE_LOAD_DECLINE;;
00724 } else
00725 return 0;
00726 }
00727
00728 static int unload_module(void)
00729 {
00730
00731 ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces, ast_smdi_interface_destroy);
00732 ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces);
00733
00734 return 0;
00735 }
00736
00737 static int reload(void)
00738 {
00739 int res;
00740
00741 res = smdi_load(1);
00742
00743 if (res < 0) {
00744 return res;
00745 } else if (res == 1) {
00746 ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n");
00747 return 0;
00748 } else
00749 return 0;
00750 }
00751
00752 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Simplified Message Desk Interface (SMDI) Resource",
00753 .load = load_module,
00754 .unload = unload_module,
00755 .reload = reload,
00756 );