diff options
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | apps/Makefile | 5 | ||||
-rw-r--r-- | apps/app_voicemail.c | 56 | ||||
-rw-r--r-- | channels/Makefile | 7 | ||||
-rw-r--r-- | channels/chan_zap.c | 87 | ||||
-rw-r--r-- | configs/smdi.conf.sample | 43 | ||||
-rw-r--r-- | configs/voicemail.conf.sample | 16 | ||||
-rw-r--r-- | configs/zapata.conf.sample | 9 | ||||
-rw-r--r-- | doc/smdi.txt | 34 | ||||
-rw-r--r-- | include/asterisk/callerid.h | 3 | ||||
-rw-r--r-- | include/asterisk/smdi.h | 120 | ||||
-rw-r--r-- | res/Makefile | 7 | ||||
-rw-r--r-- | res/res_smdi.c | 794 |
13 files changed, 1172 insertions, 14 deletions
@@ -73,6 +73,11 @@ DEBUG=-g3 #-pg #OPTIONS += -DLOW_MEMORY #endif +# +# Asterisk SMDI integration +# +WITH_SMDI = 1 + # Optional debugging parameters DEBUG_THREADS = #-DDUMP_SCHEDULER #-DDEBUG_SCHEDULER #-DDEBUG_THREADS #-DDO_CRASH #-DDETECT_DEADLOCKS diff --git a/apps/Makefile b/apps/Makefile index 554efa71f..b3fb93071 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -59,7 +59,12 @@ CFLAGS+=-fPIC APPS+=app_sms.so endif +# Asterisk SMDI integration # +ifeq (${WITH_SMDI},1) +CFLAGS+=-DWITH_SMDI +endif + # If you have UnixODBC you can use ODBC voicemail # storage # diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index eee1d3ddb..cb6d9e876 100644 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -73,6 +73,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/cli.h" #include "asterisk/utils.h" #include "asterisk/stringfields.h" +#ifdef WITH_SMDI +#include "asterisk/smdi.h" +#define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */ +#endif #ifdef USE_ODBC_STORAGE #include "asterisk/res_odbc.h" #endif @@ -393,7 +397,9 @@ static int silencethreshold = 128; static char serveremail[80]; static char mailcmd[160]; /* Configurable mail cmd */ static char externnotify[160]; - +#ifdef WITH_SMDI +static struct ast_smdi_interface *smdi_iface = NULL; +#endif static char vmfmts[80]; static int vmminmessage; static int vmmaxmessage; @@ -2318,13 +2324,39 @@ static void run_externnotify(char *context, char *extension) char arguments[255]; char ext_context[256] = ""; int newvoicemails = 0, oldvoicemails = 0; +#ifdef WITH_SMDI + struct ast_smdi_mwi_message *mwi_msg; +#endif if (!ast_strlen_zero(context)) snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context); else ast_copy_string(ext_context, extension, sizeof(ext_context)); +#ifdef WITH_SMDI + if (!strcasecmp(externnotify, "smdi")) { + + if (ast_app_has_voicemail(ext_context, NULL)) + ast_smdi_mwi_set(smdi_iface, extension); + else + ast_smdi_mwi_unset(smdi_iface, extension); + + mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT); + if (mwi_msg) { + ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name); + if (!strncmp(mwi_msg->cause, "INV", 3)) + ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st); + else if (!strncmp(mwi_msg->cause, "BLK", 3)) + ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st); + ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause); + ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy); + } else { + ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name); + } + } else if (!ast_strlen_zero(externnotify)) { +#else if (!ast_strlen_zero(externnotify)) { +#endif if (messagecount(ext_context, &newvoicemails, &oldvoicemails)) { ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension); } else { @@ -5842,6 +5874,9 @@ static int load_config(void) char *cat; struct ast_variable *var; char *notifystr = NULL; +#ifdef WITH_SMDI + char *smdistr = NULL; +#endif char *astattach; char *astsearch; char *astsaycid; @@ -5951,6 +5986,25 @@ static int load_config(void) if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) { ast_copy_string(externnotify, notifystr, sizeof(externnotify)); ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify); +#ifdef WITH_SMDI + if(!strcasecmp(externnotify, "smdi")) { + ast_log(LOG_DEBUG, "Using SMDI for external voicemail notification\n"); + + if ((smdistr = ast_variable_retrieve(cfg, "general", "smdiport"))) { + smdi_iface = ast_smdi_interface_find(smdistr); + } else { + ast_log(LOG_DEBUG, "No SMDI interface set, trying default (/dev/ttyS0)\n"); + smdi_iface = ast_smdi_interface_find("/dev/ttyS0"); + } + + if(!smdi_iface) { + ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling external voicemail notification\n"); + externnotify[0] = '\0'; + } else { + ast_log(LOG_DEBUG, "Using SMDI port %s\n", smdi_iface->name); + } + } +#endif } else { externnotify[0] = '\0'; } diff --git a/channels/Makefile b/channels/Makefile index d772d398e..417046177 100644 --- a/channels/Makefile +++ b/channels/Makefile @@ -65,6 +65,13 @@ ifneq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/linux/ixjuser.h)$(wildcard CHANNEL_LIBS+=chan_phone.so endif +# +# Asterisk SMDI integration +# +ifeq (${WITH_SMDI},1) +CFLAGS+=-DWITH_SMDI +endif + ifneq ($(wildcard h323/libchanh323.a),) CHANNEL_LIBS+=chan_h323.so endif diff --git a/channels/chan_zap.c b/channels/chan_zap.c index 1eca4706e..a078c63fa 100644 --- a/channels/chan_zap.c +++ b/channels/chan_zap.c @@ -101,6 +101,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/utils.h" #include "asterisk/transcap.h" #include "asterisk/stringfields.h" +#ifdef WITH_SMDI +#include "asterisk/smdi.h" +#include "asterisk/astobj.h" +#define SMDI_MD_WAIT_TIMEOUT 1500 /* 1.5 seconds */ +#endif #ifndef ZT_SIG_HARDHDLC #error "Your zaptel is too old. please update" @@ -279,7 +284,10 @@ static char mailbox[AST_MAX_EXTENSION]; static int amaflags = 0; static int adsi = 0; - +#ifdef WITH_SMDI +static int use_smdi = 0; +static char smdi_port[SMDI_MAX_FILENAME_LEN] = "/dev/ttyS0"; +#endif static int numbufs = 4; static int cur_prewink = -1; @@ -605,6 +613,10 @@ static struct zt_pvt { unsigned int r2blocked:1; unsigned int sigchecked:1; #endif +#ifdef WITH_SMDI + unsigned int use_smdi:1; /* Whether to use SMDI on this channel */ + struct ast_smdi_interface *smdi_iface; /* The serial port to listen for SMDI data on */ +#endif struct zt_distRings drings; @@ -2165,6 +2177,10 @@ static void destroy_zt_pvt(struct zt_pvt **pvt) p->prev->next = p->next; if(p->next) p->next->prev = p->prev; +#ifdef WITH_SMDI + if(p->use_smdi) + ASTOBJ_UNREF(p->smdi_iface, ast_smdi_interface_destroy); +#endif ast_mutex_destroy(&p->lock); free(p); *pvt = NULL; @@ -5274,7 +5290,7 @@ static void *ss_thread(void *data) unsigned char buf[256]; char dtmfcid[300]; char dtmfbuf[300]; - struct callerid_state *cs; + struct callerid_state *cs=NULL; char *name=NULL, *number=NULL; int distMatches; int curRingData[3]; @@ -5282,7 +5298,9 @@ static void *ss_thread(void *data) int counter1; int counter; int samples = 0; - +#ifdef WITH_SMDI + struct ast_smdi_md_message *smdi_msg = NULL; +#endif int flags; int i; int timeout; @@ -5928,10 +5946,35 @@ lax); } } #endif +#ifdef WITH_SMDI + /* check for SMDI messages */ + if(p->use_smdi && p->smdi_iface) { + smdi_msg = ast_smdi_md_message_wait(p->smdi_iface, SMDI_MD_WAIT_TIMEOUT); + + if(smdi_msg != NULL) { + ast_copy_string(chan->exten, smdi_msg->fwd_st, sizeof(chan->exten)); + + if (smdi_msg->type == 'B') + pbx_builtin_setvar_helper(chan, "_SMDI_VM_TYPE", "b"); + else if (smdi_msg->type == 'N') + pbx_builtin_setvar_helper(chan, "_SMDI_VM_TYPE", "u"); + + ast_log(LOG_DEBUG, "Recieved SMDI message on %s\n", chan->name); + } else { + ast_log(LOG_WARNING, "SMDI enabled but no SMDI message present\n"); + } + } + + if (p->use_callerid && (p->cid_signalling == CID_SIG_SMDI && smdi_msg)) { + number = smdi_msg->calling_st; + /* If we want caller id, we're in a prering state due to a polarity reversal * and we're set to use a polarity reversal to trigger the start of caller id, * grab the caller id and wait for ringing to start... */ + } else if (p->use_callerid && (chan->_state == AST_STATE_PRERING && p->cid_start == CID_START_POLARITY)) { +#else if (p->use_callerid && (chan->_state == AST_STATE_PRERING && p->cid_start == CID_START_POLARITY)) { +#endif /* If set to use DTMF CID signalling, listen for DTMF */ if (p->cid_signalling == CID_SIG_DTMF) { int i = 0; @@ -6297,7 +6340,10 @@ lax); ast_shrink_phone_number(number); ast_set_callerid(chan, number, name, number); - +#ifdef WITH_SMDI + if (smdi_msg) + ASTOBJ_UNREF(smdi_msg, ast_smdi_md_message_destroy); +#endif if (cs) callerid_free(cs); ast_setstate(chan, AST_STATE_RING); @@ -7274,6 +7320,9 @@ static struct zt_pvt *mkintf(int channel, int signalling, int radio, struct zt_p tmp->callwaitingcallerid = callwaitingcallerid; tmp->threewaycalling = threewaycalling; tmp->adsi = adsi; +#ifdef WITH_SMDI + tmp->use_smdi = use_smdi; +#endif tmp->permhidecallerid = hidecallerid; tmp->callreturn = callreturn; tmp->echocancel = echocancel; @@ -7306,6 +7355,22 @@ static struct zt_pvt *mkintf(int channel, int signalling, int radio, struct zt_p } } +#ifdef WITH_SMDI + if (tmp->cid_signalling == CID_SIG_SMDI) { + if (!tmp->use_smdi) { + ast_log(LOG_WARNING, "SMDI callerid requires SMDI to be enabled, enabling...\n"); + tmp->use_smdi = 1; + } + } + if (tmp->use_smdi) { + tmp->smdi_iface = ast_smdi_interface_find(smdi_port); + if (!(tmp->smdi_iface)) { + ast_log(LOG_ERROR, "Invalid SMDI port specfied, disabling SMDI support\n"); + tmp->use_smdi = 0; + } + } +#endif + ast_copy_string(tmp->accountcode, accountcode, sizeof(tmp->accountcode)); tmp->amaflags = amaflags; if (!here) { @@ -10479,12 +10544,12 @@ static int setup_zap(int reload) cid_signalling = CID_SIG_V23; else if (!strcasecmp(v->value, "dtmf")) cid_signalling = CID_SIG_DTMF; +#ifdef WITH_SMDI + else if (!strcasecmp(v->value, "smdi")) + cid_signalling = CID_SIG_SMDI; +#endif else if (!strcasecmp(v->value, "v23_jp")) -#ifdef ZT_EVENT_RINGBEGIN cid_signalling = CID_SIG_V23_JP; -#else - ast_log(LOG_WARNING, "Asterisk compiled aginst Zaptel version too old to support japanese CallerID!\n"); -#endif else if (ast_true(v->value)) cid_signalling = CID_SIG_BELL; } else if (!strcasecmp(v->name, "cidstart")) { @@ -10507,6 +10572,12 @@ static int setup_zap(int reload) ast_copy_string(mailbox, v->value, sizeof(mailbox)); } else if (!strcasecmp(v->name, "adsi")) { adsi = ast_true(v->value); +#ifdef WITH_SMDI + } else if (!strcasecmp(v->name, "usesmdi")) { + use_smdi = ast_true(v->value); + } else if (!strcasecmp(v->name, "smdiport")) { + ast_copy_string(smdi_port, v->value, sizeof(smdi_port)); +#endif } else if (!strcasecmp(v->name, "transfer")) { transfer = ast_true(v->value); } else if (!strcasecmp(v->name, "canpark")) { diff --git a/configs/smdi.conf.sample b/configs/smdi.conf.sample new file mode 100644 index 000000000..aa58bfbdd --- /dev/null +++ b/configs/smdi.conf.sample @@ -0,0 +1,43 @@ +; Asterisk SMDI configuration + +[interfaces] +; Specify serial ports to listen for SMDI messages on below. These will be +; referenced later in zapata.conf. If you do not specify any interfaces then +; SMDI will be disabled. Interfaces can have several different attributes +; associated with them. + +; Set the number of stop bits to use per character here. The default is no, +; in which case one stop bit will be used. + +;twostopbits = no + +; Character size or bit length is the size of each character sent accross the +; link. Character size can be 7 or 8. The default is 7. + +;charsize = 7 + +; If you need parity checking enabled you can turn it on here. Acceptable +; values are even, odd, and none. The default is even. + +;paritybit = even + +; The baudrate to use for this port. Acceptable values are 1200, 2400, 4800, +; and 9600. The default is 9600. + +;baudrate = 1200 + +; Often the numbering scheme for a set of mailboxes or extensions will not be 7 +; or 10 digits (as SMDI requires). Use the msdstrip option to strip unused +; digits from the start of numbers. + +;msdstrip = 0 + +; Occasionally Asterisk and the SMDI switch may become out of sync. If this +; happens, Asterisk will appear one or several calls behind as it processes +; voicemail requests. To prevent this from hapening adjust the msgexpirytime. +; This will make Asterisk discard old SMDI messages that have not yet been +; processed. The default expiry time is 30000 milliseconds. + +;msgexpirytime = 30000 + +;smdiport => /dev/ttyS0 diff --git a/configs/voicemail.conf.sample b/configs/voicemail.conf.sample index 68588dd8e..d7322860b 100644 --- a/configs/voicemail.conf.sample +++ b/configs/voicemail.conf.sample @@ -49,12 +49,18 @@ maxsilence=10 silencethreshold=128 ; Max number of failed login attempts maxlogins=3 -; If you need to have an external program, i.e. /usr/bin/myapp called when a -; voicemail is left, delivered, or your voicemailbox is checked, uncomment -; this: + +; If you need to have an external program, i.e. /usr/bin/myapp +; called when a voicemail is left, delivered, or your voicemailbox +; is checked, uncomment this. It can also be set to 'smdi' to use +; smdi for external notification. If it is 'smdi', smdiport should +; be set to a valid port as specfied in smdi.conf. + ;externnotify=/usr/bin/myapp -; If you need to have an external program, i.e. /usr/bin/myapp called when a -; voicemail password is changed, uncomment this: +;smdiport=/dev/ttyS0 + +; If you need to have an external program, i.e. /usr/bin/myapp +; called when a voicemail password is changed, uncomment this: ;externpass=/usr/bin/myapp ; For the directory, you can override the intro file if you want ;directoryintro=dir-intro diff --git a/configs/zapata.conf.sample b/configs/zapata.conf.sample index 1fc0179db..8fad060ae 100644 --- a/configs/zapata.conf.sample +++ b/configs/zapata.conf.sample @@ -225,6 +225,7 @@ usecallerid=yes ; v23 = v23 as used in the UK ; v23_jp = v23 as used in Japan ; dtmf = DTMF as used in Denmark, Sweden and Netherlands +; smdi = Use SMDI for callerid. Requires SMDI to be enabled (usesmdi). ; ;cidsignalling=bell ; @@ -380,6 +381,14 @@ immediate=no ; ;adsi=yes ; +; SMDI (Simplified Message Desk Interface) can be enabled on a per-channel +; basis if you would like that channel to behave like an SMDI message desk. +; The SMDI port specfied should have already been defined in smdi.conf. The +; default port is /dev/ttyS0. +; +;usesmdi=yes +;smdiport=/dev/ttyS0 +; ; On trunk interfaces (FXS) and E&M interfaces (E&M, Wink, Feature Group D ; etc, it can be useful to perform busy detection either in an effort to ; detect hangup or for detecting busies. This enables listening for diff --git a/doc/smdi.txt b/doc/smdi.txt new file mode 100644 index 000000000..63cc957d9 --- /dev/null +++ b/doc/smdi.txt @@ -0,0 +1,34 @@ +Asterisk SMDI (Simple Message Desk Interface) integration +--------------------------------------------------------- + +Support for using Asterisk as an SMDI message desk was developed by Matthew A. +Nicholson <mnicholson@digium.com>. To enable SMDI support in asterisk edit the +Makefile file and uncomment the line regarding SMDI so it reads like +this: + +# +# Asterisk SMDI integration +# +WITH_SMDI = 1 + +SMDI integration is configured in smdi.conf, zaptel.conf, and voicemail.conf. +Various characteristics of the SMDI interfaces to be used (serial ports) are +defined in smdi.conf. SMDI integration for callerid and MWI are defined in +zaptel.conf and voicemail.conf respectively. + +When SMDI is enabled and a call comes into Asterisk, the forwarding station +number is used as the destination for the call and any callerid information +present is used. This way you can configure your extensions.conf as follows to +behave as a message desk. + +[default] + +exten => _XXXXXXX,1,VoiceMail(${EXTEN}|${SMDI_VM_TYPE}) +exten => _XXXXXXX,n,Hangup + +exten => s,1,VoiceMailMain(${CALLERIDNUM}) +exten => s,n,Hangup + +The ${SMDI_VM_TYPE} variable will be set to u, b, or nothing depending on the +contents of the type of SMDI message received. + diff --git a/include/asterisk/callerid.h b/include/asterisk/callerid.h index 7daa1a8f9..a2b5ce69e 100644 --- a/include/asterisk/callerid.h +++ b/include/asterisk/callerid.h @@ -42,6 +42,9 @@ #define CID_SIG_V23 2 #define CID_SIG_DTMF 3 #define CID_SIG_V23_JP 4 +#ifdef WITH_SMDI +#define CID_SIG_SMDI 5 +#endif #define CID_START_RING 1 #define CID_START_POLARITY 2 diff --git a/include/asterisk/smdi.h b/include/asterisk/smdi.h new file mode 100644 index 000000000..1313ee1cb --- /dev/null +++ b/include/asterisk/smdi.h @@ -0,0 +1,120 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * SMDI support for Asterisk. + * + * Copyright (C) 2005, Digium, Inc. + * + * Matthew A. Nicholson <mnicholson@digium.com> + * + * This program is free software, distributed under the terms of + * the GNU General Public License. + */ + +/*! + * \file + * \brief SMDI support for Asterisk. + * \author Matthew A. Nicholson <mnicholson@digium.com> + */ + + +/* C is simply a ego booster for those who want to do objects the hard way. */ + + +#ifndef AST_SMDI_H +#define AST_SMDI_H + +#include "asterisk/config.h" +#include "asterisk/module.h" +#include "asterisk/astobj.h" +#include <termios.h> +#include <time.h> + +#define SMDI_MESG_DESK_NUM_LEN 3 +#define SMDI_MESG_DESK_TERM_LEN 4 +#define SMDI_MWI_FAIL_CAUSE_LEN 3 +#define SMDI_MAX_STATION_NUM_LEN 10 +#define SMDI_MAX_FILENAME_LEN 256 + +/*! + * \brief An SMDI message waiting indicator message. + * + * The ast_smdi_mwi_message structure contains the parsed out parts of an smdi + * message. Each ast_smdi_interface structure has a message queue consisting + * ast_smdi_mwi_message structures. + */ +struct ast_smdi_mwi_message { + ASTOBJ_COMPONENTS(struct ast_smdi_mwi_message); + char fwd_st[SMDI_MAX_STATION_NUM_LEN + 1]; /* forwarding station number */ + char cause[SMDI_MWI_FAIL_CAUSE_LEN + 1]; /* the type of failure */ + struct timeval timestamp; /* a timestamp for the message */ +}; + +/*! + * \brief An SMDI message desk message. + * + * The ast_smdi_md_message structure contains the parsed out parts of an smdi + * message. Each ast_smdi_interface structure has a message queue consisting + * ast_smdi_md_message structures. + */ +struct ast_smdi_md_message { + ASTOBJ_COMPONENTS(struct ast_smdi_md_message); + char mesg_desk_num[SMDI_MESG_DESK_NUM_LEN + 1]; /* message desk number */ + char mesg_desk_term[SMDI_MESG_DESK_TERM_LEN + 1]; /* message desk terminal */ + char fwd_st[SMDI_MAX_STATION_NUM_LEN + 1]; /* forwarding station number */ + char calling_st[SMDI_MAX_STATION_NUM_LEN + 1]; /* calling station number */ + char type; /* the type of the call */ + struct timeval timestamp; /* a timestamp for the message */ +}; + +/*! \brief SMDI message desk message queue. */ +struct ast_smdi_md_queue { + ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_md_message); +}; + +/*! \brief SMDI message waiting indicator message queue. */ +struct ast_smdi_mwi_queue { + ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_mwi_message); +}; + +/*! + * \brief SMDI interface structure. + * + * The ast_smdi_interface structure holds information on a serial port that + * should be monitored for SMDI activity. The structure contains a message + * queue of messages that have been recieved on the interface. + */ +struct ast_smdi_interface { + ASTOBJ_COMPONENTS_FULL(struct ast_smdi_interface, SMDI_MAX_FILENAME_LEN, 1); + struct ast_smdi_md_queue md_q; + struct ast_smdi_mwi_queue mwi_q; + FILE *file; + int fd; + pthread_t thread; + struct termios mode; + int msdstrip; + long msg_expiry; +}; + + +/* MD message queue functions */ +extern struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface); +extern struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout); +extern void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *msg); + +/* MWI message queue functions */ +extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface); +extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout); +extern void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *msg); + +extern struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name); + +/* MWI functions */ +extern int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox); +extern int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox); + +extern void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg); +extern void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg); + +extern void ast_smdi_interface_destroy(struct ast_smdi_interface *iface); +#endif diff --git a/res/Makefile b/res/Makefile index 5b127713d..0ade18337 100644 --- a/res/Makefile +++ b/res/Makefile @@ -64,6 +64,13 @@ else CFLAGS+=-DOPENSSL_NO_KRB5 -fPIC endif +# +# Asterisk SMDI integration +# +ifeq (${WITH_SMDI},1) +MODS+=res_smdi.so +endif + all: depend $(MODS) install: all diff --git a/res/res_smdi.c b/res/res_smdi.c new file mode 100644 index 000000000..7c83478a2 --- /dev/null +++ b/res/res_smdi.c @@ -0,0 +1,794 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * SMDI support for Asterisk. + * + * Copyright (C) 2005, Digium, Inc. + * + * Matthew A. Nicholson <mnicholson@digium.com> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <termios.h> +#include <sys/time.h> +#include <time.h> +#include <ctype.h> +#include "asterisk/module.h" +#include "asterisk/lock.h" +#include "asterisk/utils.h" +#include "asterisk/smdi.h" +#include "asterisk/config.h" +#include "asterisk/astobj.h" +#include "asterisk/io.h" +#include "asterisk/logger.h" +#include "asterisk/utils.h" +#include "asterisk/options.h" + +/*! + * \file + * \brief SMDI support for Asterisk. + * \author Matthew A. Nicholson <mnicholson@digium.com> + */ + +/* Message expiry time in milliseconds */ +#define SMDI_MSG_EXPIRY_TIME 30000 /* 30 seconds */ + +static const char tdesc[] = "Asterisk Simplified Message Desk Interface (SMDI) Module"; +static const char config_file[] = "smdi.conf"; + +static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *msg); +static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *msg); + +static void *smdi_read(void *iface_p); +static int smdi_load(int reload); + +/* Use count stuff */ + +AST_MUTEX_DEFINE_STATIC(localuser_lock); +static int localusecnt = 0; + +/*! \brief SMDI interface container. */ +struct ast_smdi_interface_container { + ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface); +} smdi_ifaces; + +/*! + * \internal + * \brief Push an SMDI message to the back of an interface's message queue. + * \param iface a pointer to the interface to use. + * \param md_msg a pointer to the message to use. + */ +static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg) +{ + ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg); +} + +/*! + * \internal + * \brief Push an SMDI message to the back of an interface's message queue. + * \param iface a pointer to the interface to use. + * \param mwi_msg a pointer to the message to use. + */ +static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg) +{ + ASTOBJ_CONTAINER_LINK_END(&iface->mwi_q, mwi_msg); +} + +/*! + * \brief Set the MWI indicator for a mailbox. + * \param iface the interface to use. + * \param mailbox the mailbox to use. + */ +extern int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox) +{ + FILE *file; + int i; + + file = fopen(iface->name, "w"); + if(!file) { + ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno)); + return 1; + } + + ASTOBJ_WRLOCK(iface); + + fprintf(file, "OP:MWI "); + + for(i = 0; i < iface->msdstrip; i++) + fprintf(file, "0"); + + fprintf(file, "%s!\x04", mailbox); + fclose(file); + + ASTOBJ_UNLOCK(iface); + ast_log(LOG_DEBUG, "Sent MWI set message for %s on %s\n", mailbox, iface->name); + return 0; +} + +/*! + * \brief Unset the MWI indicator for a mailbox. + * \param iface the interface to use. + * \param mailbox the mailbox to use. + */ +extern int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox) +{ + FILE *file; + int i; + + file = fopen(iface->name, "w"); + if(!file) { + ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno)); + return 1; + } + + ASTOBJ_WRLOCK(iface); + + fprintf(file, "RMV:MWI "); + + for(i = 0; i < iface->msdstrip; i++) + fprintf(file, "0"); + + fprintf(file, "%s!\x04", mailbox); + fclose(file); + + ASTOBJ_UNLOCK(iface); + ast_log(LOG_DEBUG, "Sent MWI unset message for %s on %s", mailbox, iface->name); + return 0; +} + +/*! + * \brief Put an SMDI message back in the front of the queue. + * \param iface a pointer to the interface to use. + * \param msg a pointer to the message to use. + * + * This function puts a message back in the front of the specified queue. It + * should be used if a message was popped but is not going to be processed for + * some reason, and the message needs to be returned to the queue. + */ +extern void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg) +{ + ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg); +} + +/*! + * \brief Put an SMDI message back in the front of the queue. + * \param iface a pointer to the interface to use. + * \param msg a pointer to the message to use. + * + * This function puts a message back in the front of the specified queue. It + * should be used if a message was popped but is not going to be processed for + * some reason, and the message needs to be returned to the queue. + */ +extern void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg) +{ + ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg); +} + +/*! + * \brief Get the next SMDI message from the queue. + * \param iface a pointer to the interface to use. + * + * This function pulls the first unexpired message from the SMDI message queue + * on the specified interface. It will purge all expired SMDI messages before + * returning. + * + * \return the next SMDI message, or NULL if there were no pending messages. + */ +extern struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface) +{ + struct ast_smdi_md_message *md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q); + struct timeval now; + long elapsed = 0; + + /* purge old messages */ + gettimeofday(&now, NULL); + while(md_msg) + { + /* calculate the elapsed time since this message was recieved ( in milliseconds) */ + elapsed = (now.tv_sec - md_msg->timestamp.tv_sec) * 1000; + elapsed += (now.tv_usec - md_msg->timestamp.tv_usec) / 1000; + + if(elapsed > iface->msg_expiry) + { /* found an expired message */ + ASTOBJ_UNREF(md_msg,ast_smdi_md_message_destroy); + ast_log(LOG_NOTICE, "Purged expired message from %s SMDI MD message queue. Message was %ld milliseconds too old.", iface->name, elapsed - iface->msg_expiry); + md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q); + } + else /* good message, return it */ + { + break; + } + } + + return md_msg; +} + +/*! + * \brief Get the next SMDI message from the queue. + * \param iface a pointer to the interface to use. + * \param timeout the time to wait before returning in milliseconds. + * + * This function pulls a message from the SMDI message queue on the specified + * interface. If no message is available this function will wait the specified + * amount of time before returning. + * + * \return the next SMDI message, or NULL if there were no pending messages and + * the timeout has expired. + */ +extern struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout) +{ + struct timeval start, end; + long diff = 0; + struct ast_smdi_md_message *msg; + + gettimeofday(&start, NULL); + while(diff < timeout) { + + if((msg = ast_smdi_md_message_pop(iface))) + return msg; + + /* check timeout */ + gettimeofday(&end, NULL); + diff = (end.tv_sec - start.tv_sec) * 1000; + diff += (end.tv_usec - start.tv_usec) / 1000; + } + return (ast_smdi_md_message_pop(iface)); +} + +/*! + * \brief Get the next SMDI message from the queue. + * \param iface a pointer to the interface to use. + * + * This function pulls the first unexpired message from the SMDI message queue + * on the specified interface. It will purge all expired SMDI messages before + * returning. + * + * \return the next SMDI message, or NULL if there were no pending messages. + */ +extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface) +{ + struct ast_smdi_mwi_message *mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q); + struct timeval now; + long elapsed = 0; + + /* purge old messages */ + gettimeofday(&now, NULL); + while(mwi_msg) + { + /* calculate the elapsed time since this message was recieved ( in milliseconds) */ + elapsed = (now.tv_sec - mwi_msg->timestamp.tv_sec) * 1000; + elapsed += (now.tv_usec - mwi_msg->timestamp.tv_usec) / 1000; + + if(elapsed > iface->msg_expiry) + { /* found an expired message */ + ASTOBJ_UNREF(mwi_msg,ast_smdi_mwi_message_destroy); + ast_log(LOG_NOTICE, "Purged expired message from %s SMDI MWI message queue. Message was %ld milliseconds too old.", iface->name, elapsed - iface->msg_expiry); + mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q); + } + else /* good message, return it */ + { + break; + } + } + + return mwi_msg; + return (ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q)); +} + +/*! + * \brief Get the next SMDI message from the queue. + * \param iface a pointer to the interface to use. + * \param timeout the time to wait before returning in milliseconds. + * + * This function pulls a message from the SMDI message queue on the specified + * interface. If no message is available this function will wait the specified + * amount of time before returning. + * + * \return the next SMDI message, or NULL if there were no pending messages and + * the timeout has expired. + */ +extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout) +{ + struct timeval start, end; + long diff = 0; + struct ast_smdi_mwi_message *msg; + + gettimeofday(&start, NULL); + while(diff < timeout) { + + if((msg = ast_smdi_mwi_message_pop(iface))) + return msg; + + /* check timeout */ + gettimeofday(&end, NULL); + diff = (end.tv_sec - start.tv_sec) * 1000; + diff += (end.tv_usec - start.tv_usec) / 1000; + } + return (ast_smdi_mwi_message_pop(iface)); +} + +/*! + * \brief Find an SMDI interface with the specified name. + * \param iface_name the name/port of the interface to search for. + * + * \return a pointer to the interface located or NULL if none was found. This + * actually returns an ASTOBJ reference and should be released using + * #ASTOBJ_UNREF(iface, ast_smdi_interface_destroy). + */ +extern struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name) +{ + return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces,iface_name)); +} + +/*! \brief Read an SMDI message. + * + * \param iface the SMDI interface to read from. + * + * This function loops and reads from and SMDI interface. It must be stopped + * using pthread_cancel(). + */ +static void *smdi_read(void *iface_p) +{ + struct ast_smdi_interface *iface = iface_p; + struct ast_smdi_md_message *md_msg; + struct ast_smdi_mwi_message *mwi_msg; + char c = '\0'; + char *cp = NULL; + int i; + int start = 0; + + /* read an smdi message */ + while((c = fgetc(iface->file))) { + + /* check if this is the start of a message */ + if(!start) + { + if(c == 'M') + start = 1; + } + else /* Determine if this is a MD or MWI message */ + { + if(c == 'D') { /* MD message */ + start = 0; + + md_msg = malloc(sizeof(struct ast_smdi_md_message)); + if(!md_msg) { + ast_log(LOG_ERROR, "Error allocating memory for ast_smdi_md_message. Stopping listner thread.\n"); + ASTOBJ_UNREF(iface,ast_smdi_interface_destroy); + return NULL; + } + memset(md_msg, 0, sizeof(struct ast_smdi_md_message)); + + ASTOBJ_INIT(md_msg); + + /* read the message desk number */ + for(i = 0; i < SMDI_MESG_DESK_NUM_LEN; i++) { + md_msg->mesg_desk_num[i] = fgetc(iface->file); + } + md_msg->mesg_desk_num[SMDI_MESG_DESK_NUM_LEN] = '\0'; + + /* read the message desk terminal number */ + for(i = 0; i < SMDI_MESG_DESK_TERM_LEN; i++) { + md_msg->mesg_desk_term[i] = fgetc(iface->file); + } + md_msg->mesg_desk_term[SMDI_MESG_DESK_TERM_LEN] = '\0'; + + /* read the message type */ + md_msg->type = fgetc(iface->file); + + /* read the forwarding station number (may be blank) */ + cp = &md_msg->fwd_st[0]; + for(i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) { + if((c = fgetc(iface->file)) == ' ') { + *cp = '\0'; + break; + } + + /* store c in md_msg->fwd_st */ + if( i >= iface->msdstrip) { + *cp = c; + cp++; + } + } + /* make sure the value is null terminated, even if this truncates it */ + md_msg->fwd_st[SMDI_MAX_STATION_NUM_LEN] = '\0'; + cp = NULL; + + /* read the calling station number (may be blank) */ + cp = &md_msg->calling_st[0]; + for(i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) { + if(!isdigit( (c = fgetc(iface->file)) )) { + *cp = '\0'; + break; + } + + /* store c in md_msg->calling_st */ + if( i >= iface->msdstrip) { + *cp = c; + cp++; + } + } + /* make sure the value is null terminated, even if this truncates it */ + md_msg->calling_st[SMDI_MAX_STATION_NUM_LEN] = '\0'; + cp = NULL; + + /* add the message to the message queue */ + gettimeofday(&md_msg->timestamp, NULL); + ast_smdi_md_message_push(iface, md_msg); + ast_log(LOG_DEBUG, "Recieved SMDI MD message on %s\n", iface->name); + + ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy); + + } else if(c == 'W') { /* MWI message */ + start = 0; + + mwi_msg = malloc(sizeof(struct ast_smdi_mwi_message)); + if(!mwi_msg) { + ast_log(LOG_ERROR, "Error allocating memory for ast_smdi_mwi_message. Stopping listner thread.\n"); + ASTOBJ_UNREF(iface,ast_smdi_interface_destroy); + return NULL; + } + memset(mwi_msg, 0, sizeof(struct ast_smdi_mwi_message)); + + ASTOBJ_INIT(mwi_msg); + + /* discard the 'I' (from 'MWI') */ + fgetc(iface->file); + + /* read the forwarding station number (may be blank) */ + cp = &mwi_msg->fwd_st[0]; + for(i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) { + if((c = fgetc(iface->file)) == ' ') { + *cp = '\0'; + break; + } + + /* store c in md_msg->fwd_st */ + if( i >= iface->msdstrip) { + *cp = c; + cp++; + } + } + /* make sure the station number is null terminated, even if this will truncate it */ + mwi_msg->fwd_st[SMDI_MAX_STATION_NUM_LEN] = '\0'; + cp = NULL; + + /* read the mwi failure cause */ + for(i = 0; i < SMDI_MWI_FAIL_CAUSE_LEN; i++) { + mwi_msg->cause[i] = fgetc(iface->file); + } + mwi_msg->cause[SMDI_MWI_FAIL_CAUSE_LEN] = '\0'; + + /* add the message to the message queue */ + gettimeofday(&mwi_msg->timestamp, NULL); + ast_smdi_mwi_message_push(iface, mwi_msg); + ast_log(LOG_DEBUG, "Recieved SMDI MWI message on %s\n", iface->name); + + ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy); + } else { + ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c); + start = 0; + } + } + } + + ast_log(LOG_ERROR, "Error reading from SMDI interface %s, stopping listener thread\n", iface->name); + ASTOBJ_UNREF(iface,ast_smdi_interface_destroy); + return NULL; +} + +/*! \brief ast_smdi_md_message destructor. */ +void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg) +{ + free(msg); +} + +/*! \brief ast_smdi_mwi_message destructor. */ +void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg) +{ + free(msg); +} + +/*! \brief ast_smdi_interface destructor. */ +void ast_smdi_interface_destroy(struct ast_smdi_interface *iface) +{ + if(iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) { + pthread_cancel(iface->thread); + pthread_join(iface->thread, NULL); + } + + iface->thread = AST_PTHREADT_STOP; + + if(iface->file) + fclose(iface->file); + + ASTOBJ_CONTAINER_DESTROYALL(&iface->md_q, ast_smdi_md_message_destroy); + ASTOBJ_CONTAINER_DESTROYALL(&iface->mwi_q, ast_smdi_mwi_message_destroy); + ASTOBJ_CONTAINER_DESTROY(&iface->md_q); + ASTOBJ_CONTAINER_DESTROY(&iface->mwi_q); + free(iface); + + ast_mutex_lock(&localuser_lock); + localusecnt--; + ast_mutex_unlock(&localuser_lock); + ast_update_use_count(); +} + +/*! + * \internal + * \brief Load and reload SMDI configuration. + * \param reload this should be 1 if we are reloading and 0 if not. + * + * This function loads/reloads the SMDI configuration and starts and stops + * interfaces accordingly. + * + * \return zero on success, -1 on failure, and 1 if no smdi interfaces were started. + */ +static int smdi_load(int reload) +{ + struct ast_config *conf; + struct ast_variable *v; + struct ast_smdi_interface *iface = NULL; + int res = 0; + + /* Config options */ + speed_t baud_rate = B9600; /* 9600 baud rate */ + tcflag_t paritybit = PARENB; /* even parity checking */ + tcflag_t charsize = CS7; /* seven bit characters */ + int stopbits = 0; /* One stop bit */ + + int msdstrip = 0; /* strip zero digits */ + long msg_expiry = SMDI_MSG_EXPIRY_TIME; + + conf = ast_config_load(config_file); + + if(!conf) { + ast_log(LOG_ERROR, "Unable to load config %s\n", config_file); + return -1; + } + + /* Mark all interfaces that we are listening on. We will unmark them + * as we find them in the config file, this way we know any interfaces + * still marked after we have finished parsing the config file should + * be stopped. + */ + if(reload) + ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces); + + v = ast_variable_browse(conf, "interfaces"); + while(v) { + if(!strcasecmp(v->name, "baudrate")) { + if(!strcasecmp(v->value, "9600")) { + baud_rate = B9600; + } else if(!strcasecmp(v->value, "4800")) { + baud_rate = B4800; + } else if(!strcasecmp(v->value, "2400")) { + baud_rate = B2400; + } else if(!strcasecmp(v->value, "1200")) { + baud_rate = B1200; + } else { + ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno); + baud_rate = B9600; + } + } else if(!strcasecmp(v->name, "msdstrip")) { + if(!sscanf(v->value, "%d", &msdstrip)) { + ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno); + msdstrip = 0; + } else if(0 > msdstrip || msdstrip > 9) { + ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno); + msdstrip = 0; + } + } else if(!strcasecmp(v->name, "msgexpirytime")) { + if(!sscanf(v->value, "%ld", &msg_expiry)) { + ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno); + msg_expiry = SMDI_MSG_EXPIRY_TIME; + } + } else if(!strcasecmp(v->name, "paritybit")) { + if(!strcasecmp(v->value, "even")) { + paritybit = PARENB; + } else if(!strcasecmp(v->value, "odd")) { + paritybit = PARENB | PARODD; + } else if(!strcasecmp(v->value, "none")) { + paritybit = ~PARENB; + } else { + ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno); + paritybit = PARENB; + } + } else if(!strcasecmp(v->name, "charsize")) { + if(!strcasecmp(v->value, "7")) { + charsize = CS7; + } else if(!strcasecmp(v->value, "8")) { + charsize = CS8; + } else { + ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno); + charsize = CS7; + } + } else if(!strcasecmp(v->name, "twostopbits")) { + stopbits = ast_true(v->name); + } else if(!strcasecmp(v->name, "smdiport")) { + if(reload) { + /* we are reloading, check if we are already + * monitoring this interface, if we are we do + * not want to start it again. This also has + * the side effect of not updating different + * setting for the serial port, but it should + * be trivial to rewrite this section so that + * options on the port are changed without + * restarting the interface. Or the interface + * could be restarted with out emptying the + * queue. */ + iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value); + if(iface) { + ast_log(LOG_NOTICE, "SMDI interface %s already running, not restarting\n", iface->name); + ASTOBJ_UNMARK(iface); + ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); + v = v->next; + continue; + } + } + + iface = malloc(sizeof(struct ast_smdi_interface)); + + ASTOBJ_INIT(iface); + ASTOBJ_CONTAINER_INIT(&iface->md_q); + ASTOBJ_CONTAINER_INIT(&iface->mwi_q); + iface->md_q.head = NULL; + iface->mwi_q.head = NULL; + iface->thread = AST_PTHREADT_NULL; + memset(&iface->mode, 0, sizeof(iface->mode)); + + ast_copy_string(iface->name, v->value, sizeof(iface->name)); + + iface->file = fopen(iface->name, "r"); + if(!(iface->file)) { + ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s)\n", iface->name, strerror(errno)); + ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); + v = v->next; + continue; + } + iface->fd = fileno(iface->file); + + /* Set the proper attributes for our serial port. */ + + /* get the current attributes from the port */ + if(tcgetattr(iface->fd, &iface->mode)) { + ast_log(LOG_ERROR, "Error getting atributes of %s (%s)\n", iface->name, strerror(errno)); + ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); + v = v->next; + continue; + } + + /* set the desired speed */ + if(cfsetispeed(&iface->mode, baud_rate) || cfsetospeed(&iface->mode, baud_rate)) { + ast_log(LOG_ERROR, "Error setting baud rate on %s (%s)\n", iface->name, strerror(errno)); + ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); + v = v->next; + continue; + } + + /* set the stop bits */ + if(stopbits) + iface->mode.c_cflag = iface->mode.c_cflag | CSTOPB; /* set two stop bits */ + else + iface->mode.c_cflag = iface->mode.c_cflag & ~CSTOPB; /* set one stop bit */ + + /* set the parity */ + iface->mode.c_cflag = (iface->mode.c_cflag & ~PARENB & ~PARODD) | paritybit; + + /* set the character size */ + iface->mode.c_cflag = (iface->mode.c_cflag & ~CSIZE) | charsize; + + /* commit the desired attributes */ + if(tcsetattr(iface->fd, TCSAFLUSH, &iface->mode)) { + ast_log(LOG_ERROR, "Error setting attributes on %s (%s)\n", iface->name, strerror(errno)); + ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); + v = v->next; + continue; + } + + /* set the msdstrip */ + iface->msdstrip = msdstrip; + + /* set the message expiry time */ + iface->msg_expiry = msg_expiry; + + /* start the listner thread */ + if(option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Starting SMDI monitor thread for %s\n", iface->name); + if(ast_pthread_create(&iface->thread, NULL, smdi_read, iface)) { + ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name); + ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); + v = v->next; + continue; + } + ASTOBJ_CONTAINER_LINK(&smdi_ifaces, iface); + ast_mutex_lock(&localuser_lock); + localusecnt++; + ast_mutex_unlock(&localuser_lock); + ast_update_use_count(); + } else { + ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file); + } + v = v->next; + } + ast_config_destroy(conf); + + /* Prune any interfaces we should no longer monitor. */ + if(reload) + ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces, ast_smdi_interface_destroy); + + ASTOBJ_CONTAINER_RDLOCK(&smdi_ifaces); + if(!smdi_ifaces.head) + res = 1; + ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces); + + return res; +} + + +char *description(void) +{ + return (char *)tdesc; +} + +int load_module(void) +{ + int res; + + /* initilize our containers */ + ASTOBJ_CONTAINER_INIT(&smdi_ifaces); + smdi_ifaces.head = NULL; + + /* load the config and start the listener threads*/ + res = smdi_load(0); + if(res < 0) { + return res; + } else if(res == 1) { + ast_log(LOG_WARNING, "No SMDI interfaces are available to listen on, not starting SDMI listener.\n"); + return 0; + } + + return 0; +} + +int unload_module(void) +{ + /* this destructor stops any running smdi_read threads */ + ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces,ast_smdi_interface_destroy); + ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces); + + return 0; +} + +int reload(void) +{ + int res; + res = smdi_load(1); + + if(res < 0) { + return res; + } else if(res == 1) { + ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n"); + return 0; + } + + /* notify the listner thread? */ + return 0; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} |