[asterisk-commits] russell: trunk r104120 - in /trunk: ./ apps/ channels/ configs/ include/aster...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Feb 25 18:31:41 CST 2008


Author: russell
Date: Mon Feb 25 18:31:40 2008
New Revision: 104120

URL: http://svn.digium.com/view/asterisk?view=rev&rev=104120
Log:
Merged revisions 104119 via svnmerge from 
https://origsvn.digium.com/svn/asterisk/branches/1.4

........
r104119 | russell | 2008-02-25 18:25:29 -0600 (Mon, 25 Feb 2008) | 33 lines

Merge changes from team/russell/smdi-1.4

This commit brings in a significant set of changes to the SMDI support in Asterisk.
There were a number of bugs in the current implementation, most notably being that
it was very likely on busy systems to pop off the wrong message from the SMDI message
queue.  So, this set of changes fixes the issues discovered as well as introducing
some new ways to use the SMDI support which are required to avoid the bugs with
grabbing the wrong message off of the queue.

This code introduces a new interface to SMDI, with two dialplan functions.  First,
you get an SMDI message in the dialplan using SMDI_MSG_RETRIEVE() and then you access
details in the message using the SMDI_MSG() function.  A side benefit of this is that
it now supports more than just chan_zap.

For example, with this implementation, you can have some FXO lines being terminated 
on a SIP gateway, but the SMDI link in Asterisk.

Another issue with the current implementation is that it is quite common that the
station ID that comes in on the SMDI link is not necessarily the same as the Asterisk
voicemail box.  There are now additional directives in the smdi.conf configuration
file which let you map SMDI station IDs to Asterisk voicemail boxes.

Yet another issue with the current SMDI support was related to MWI reporting over
the SMDI link.  The current code could only report a MWI change when the change
was made by someone calling into voicemail.  If the change was made by some other
entity (such as with IMAP storage, or with a web interface of some kind), then the
MWI change would never be sent.  The SMDI module can now poll for MWI changes if
configured to do so.

This work was inspired by and primarily done for the University of Pennsylvania.

(also related to issue #9260)

........

Modified:
    trunk/   (props changed)
    trunk/apps/app_voicemail.c
    trunk/channels/chan_zap.c
    trunk/configs/smdi.conf.sample
    trunk/include/asterisk/smdi.h
    trunk/res/res_smdi.c

Propchange: trunk/
------------------------------------------------------------------------------
Binary property 'branch-1.4-merged' - no diff available.

Modified: trunk/apps/app_voicemail.c
URL: http://svn.digium.com/view/asterisk/trunk/apps/app_voicemail.c?view=diff&rev=104120&r1=104119&r2=104120
==============================================================================
--- trunk/apps/app_voicemail.c (original)
+++ trunk/apps/app_voicemail.c Mon Feb 25 18:31:40 2008
@@ -2976,8 +2976,8 @@
 		else
 			ast_smdi_mwi_unset(smdi_iface, extension);
 
-		if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
-			ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
+		if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
+			ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
 			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))
@@ -2985,7 +2985,7 @@
 			ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
 			ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
 		} else {
-			ast_debug(1, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
+			ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
 		}
 	}
 
@@ -8411,8 +8411,6 @@
 			}
 			if (!smdi_iface) {
 				ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
-			} else {
-				ast_debug(1, "Using SMDI port %s\n", smdi_iface->name);
 			}
 		}
 

Modified: trunk/channels/chan_zap.c
URL: http://svn.digium.com/view/asterisk/trunk/channels/chan_zap.c?view=diff&rev=104120&r1=104119&r2=104120
==============================================================================
--- trunk/channels/chan_zap.c (original)
+++ trunk/channels/chan_zap.c Mon Feb 25 18:31:40 2008
@@ -2764,7 +2764,7 @@
 	if (p->next)
 		p->next->prev = p->prev;
 	if (p->use_smdi)
-		ASTOBJ_UNREF(p->smdi_iface, ast_smdi_interface_destroy);
+		ast_smdi_interface_unref(p->smdi_iface);
 	if (p->mwi_event_sub)
 		ast_event_unsubscribe(p->mwi_event_sub);
 	if (p->vars)

Modified: trunk/configs/smdi.conf.sample
URL: http://svn.digium.com/view/asterisk/trunk/configs/smdi.conf.sample?view=diff&rev=104120&r1=104119&r2=104120
==============================================================================
--- trunk/configs/smdi.conf.sample (original)
+++ trunk/configs/smdi.conf.sample Mon Feb 25 18:31:40 2008
@@ -41,3 +41,35 @@
 ;msgexpirytime = 30000
 
 ;smdiport => /dev/ttyS0
+
+
+[mailboxes]
+; This section configures parameters related to MWI handling for the SMDI link.
+
+; This option configures the polling interval used to check to see if the
+; mailboxes have any new messages.  This option is specified in seconds.
+; The default value is 10 seconds.
+;
+;pollinginterval=10
+
+; Every other entry in this section of the configuration file is interpreted as
+; a mapping between the mailbox ID on the SMDI link, and the local Asterisk
+; mailbox name.  In many cases, they are the same thing, but they still must be
+; listed here so that this module knows which mailboxes it needs to pay
+; attention to.
+;
+; Syntax:
+;   <SMDI mailbox ID>=<Asterisk Mailbox Name>[@Asterisk Voicemail Context]
+;
+; If no Asterisk voicemail context is specified, "default" will be assumed.
+;
+; Before specifying mailboxes, you must specify an SMDI interface.  All mailbox
+; definitions that follow will correspond to that SMDI interface.  If you specify
+; another interface, then all definitions following that will correspond to the
+; new interface.
+;
+;smdiport=/dev/ttyS0
+;2565551234=1234 at vmcontext1
+;2565555678=5678 at vmcontext2
+;smdiport=/dev/ttyS1
+;2565559999=9999

Modified: trunk/include/asterisk/smdi.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/smdi.h?view=diff&rev=104120&r1=104119&r2=104120
==============================================================================
--- trunk/include/asterisk/smdi.h (original)
+++ trunk/include/asterisk/smdi.h Mon Feb 25 18:31:40 2008
@@ -1,9 +1,10 @@
 /*
  * Asterisk -- A telephony toolkit for Linux.
  *
- * Copyright (C) 2005-2006, Digium, Inc.
+ * Copyright (C) 2005-2008, Digium, Inc.
  *
  * Matthew A. Nicholson <mnicholson at digium.com>
+ * Russell Bryant <russell at digium.com>
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk project. Please do not directly contact
@@ -20,6 +21,7 @@
  * \file
  * \brief SMDI support for Asterisk.
  * \author Matthew A. Nicholson <mnicholson at digium.com>
+ * \author Russell Bryant <russell at digium.com>
  */
 
 
@@ -73,16 +75,6 @@
 	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.
  *
@@ -90,38 +82,114 @@
  * should be monitored for SMDI activity.  The structure contains a message
  * queue of messages that have been received 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;
-};
+struct ast_smdi_interface;
 
+void ast_smdi_interface_unref(struct ast_smdi_interface *iface);
 
-/* MD message queue functions */
+/*! 
+ * \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.
+ */
 struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface);
+
+/*!
+ * \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.
+ */
 struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout);
+
+/*!
+ * \brief Put an SMDI message back in the front of the queue.
+ * \param iface a pointer to the interface to use.
+ * \param md_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.
+ */
 void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *msg);
 
-/* MWI message queue functions */
+/*!
+ * \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.
+ */
 struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface);
+
+/*!
+ * \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.
+ */
 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout);
+struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, 
+	int timeout, const char *station);
+
+/*!
+ * \brief Put an SMDI message back in the front of the queue.
+ * \param iface a pointer to the interface to use.
+ * \param mwi_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.
+ */
 void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *msg);
 
+/*!
+ * \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).
+ */
 struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name);
 
-/* MWI functions */
+/*!
+ * \brief Set the MWI indicator for a mailbox.
+ * \param iface the interface to use.
+ * \param mailbox the mailbox to use.
+ */
 int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox);
+
+/*! 
+ * \brief Unset the MWI indicator for a mailbox.
+ * \param iface the interface to use.
+ * \param mailbox the mailbox to use.
+ */
 int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox);
 
+/*! \brief ast_smdi_md_message destructor. */
 void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg);
+
+/*! \brief ast_smdi_mwi_message destructor. */
 void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg);
 
-void ast_smdi_interface_destroy(struct ast_smdi_interface *iface);
-
 #endif /* !ASTERISK_SMDI_H */

Modified: trunk/res/res_smdi.c
URL: http://svn.digium.com/view/asterisk/trunk/res/res_smdi.c?view=diff&rev=104120&r1=104119&r2=104120
==============================================================================
--- trunk/res/res_smdi.c (original)
+++ trunk/res/res_smdi.c Mon Feb 25 18:31:40 2008
@@ -1,9 +1,10 @@
 /*
  * Asterisk -- A telephony toolkit for Linux.
  *
- * Copyright (C) 2005-2006, Digium, Inc.
+ * Copyright (C) 2005-2008, Digium, Inc.
  *
  * Matthew A. Nicholson <mnicholson at digium.com>
+ * Russell Bryant <russell at digium.com>
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk project. Please do not directly contact
@@ -20,6 +21,10 @@
  * \file
  * \brief SMDI support for Asterisk.
  * \author Matthew A. Nicholson <mnicholson at digium.com>
+ * \author Russell Bryant <russell at digium.com>
+ *
+ * Here is a useful mailing list post that describes SMDI protocol details:
+ * \ref http://lists.digium.com/pipermail/asterisk-dev/2003-June/000884.html
  */
 
 #include "asterisk.h"
@@ -38,24 +43,119 @@
 #include "asterisk/config.h"
 #include "asterisk/astobj.h"
 #include "asterisk/io.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/app.h"
+#include "asterisk/pbx.h"
+#include "asterisk/channel.h"
 
 /* Message expiry time in milliseconds */
 #define SMDI_MSG_EXPIRY_TIME	30000 /* 30 seconds */
 
 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);
-
-struct module_symbols *me;	/* initialized in load_module() */
+/*! \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);
+};
+
+struct ast_smdi_interface {
+	ASTOBJ_COMPONENTS_FULL(struct ast_smdi_interface, SMDI_MAX_FILENAME_LEN, 1);
+	struct ast_smdi_md_queue md_q;
+	ast_mutex_t md_q_lock;
+	ast_cond_t md_q_cond;
+	struct ast_smdi_mwi_queue mwi_q;
+	ast_mutex_t mwi_q_lock;
+	ast_cond_t mwi_q_cond;
+	FILE *file;
+	int fd;
+	pthread_t thread;
+	struct termios mode;
+	int msdstrip;
+	long msg_expiry;
+};
 
 /*! \brief SMDI interface container. */
 struct ast_smdi_interface_container {
 	ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface);
 } smdi_ifaces;
+
+/*! \brief A mapping between an SMDI mailbox ID and an Asterisk mailbox */
+struct mailbox_mapping {
+	/*! This is the current state of the mailbox.  It is simply on or
+	 *  off to indicate if there are messages waiting or not. */
+	unsigned int cur_state:1;
+	/*! A Pointer to the appropriate SMDI interface */
+	struct ast_smdi_interface *iface;
+	AST_DECLARE_STRING_FIELDS(
+		/*! The Name of the mailbox for the SMDI link. */
+		AST_STRING_FIELD(smdi);
+		/*! The name of the mailbox on the Asterisk side */
+		AST_STRING_FIELD(mailbox);
+		/*! The name of the voicemail context in use */
+		AST_STRING_FIELD(context);
+	);
+	AST_LIST_ENTRY(mailbox_mapping) entry;
+};
+
+/*! 10 seconds */
+#define DEFAULT_POLLING_INTERVAL 10
+
+/*! \brief Data that gets used by the SMDI MWI monitoring thread */
+static struct {
+	/*! The thread ID */
+	pthread_t thread;
+	ast_mutex_t lock;
+	ast_cond_t cond;
+	/*! A list of mailboxes that need to be monitored */
+	AST_LIST_HEAD_NOLOCK(, mailbox_mapping) mailbox_mappings;
+	/*! Polling Interval for checking mailbox status */
+	unsigned int polling_interval;
+	/*! Set to 1 to tell the polling thread to stop */
+	unsigned int stop:1;
+	/*! The time that the last poll began */
+	struct timeval last_poll;
+} mwi_monitor = {
+	.thread = AST_PTHREADT_NULL,
+};
+
+static 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);
+
+	ast_mutex_destroy(&iface->md_q_lock);
+	ast_cond_destroy(&iface->md_q_cond);
+
+	ast_mutex_destroy(&iface->mwi_q_lock);
+	ast_cond_destroy(&iface->mwi_q_cond);
+
+	free(iface);
+
+	ast_module_unref(ast_module_info->self);
+}
+
+void ast_smdi_interface_unref(struct ast_smdi_interface *iface)
+{
+	ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
+}
 
 /*! 
  * \internal
@@ -65,7 +165,10 @@
  */
 static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
 {
+	ast_mutex_lock(&iface->md_q_lock);
 	ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg);
+	ast_cond_broadcast(&iface->md_q_cond);
+	ast_mutex_unlock(&iface->md_q_lock);
 }
 
 /*!
@@ -76,15 +179,13 @@
  */
 static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
 {
+	ast_mutex_lock(&iface->mwi_q_lock);
 	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.
- */
-int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
+	ast_cond_broadcast(&iface->mwi_q_cond);
+	ast_mutex_unlock(&iface->mwi_q_lock);
+}
+
+static int smdi_toggle_mwi(struct ast_smdi_interface *iface, const char *mailbox, int on)
 {
 	FILE *file;
 	int i;
@@ -96,224 +197,263 @@
 	
 	ASTOBJ_WRLOCK(iface);
 	
-	fprintf(file, "OP:MWI ");
+	fprintf(file, "%s:MWI ", on ? "OP" : "RMV");
 	
 	for (i = 0; i < iface->msdstrip; i++)
 		fprintf(file, "0");
 
 	fprintf(file, "%s!\x04", mailbox);
+
 	fclose(file);
 
 	ASTOBJ_UNLOCK(iface);
 	ast_debug(1, "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.
- */
+int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
+{
+	return smdi_toggle_mwi(iface, mailbox, 1);
+}
+
 int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
 {
-	FILE *file;
-	int i;
-
-	if (!(file = fopen(iface->name, "w"))) {
-		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_debug(1, "Sent MWI unset message for %s on %s\n", 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 md_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.
- */
+	return smdi_toggle_mwi(iface, mailbox, 0);
+}
+
 void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
 {
+	ast_mutex_lock(&iface->md_q_lock);
 	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 mwi_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.
- */
+	ast_cond_broadcast(&iface->md_q_cond);
+	ast_mutex_unlock(&iface->md_q_lock);
+}
+
 void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
 {
+	ast_mutex_lock(&iface->mwi_q_lock);
 	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.
- */
-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;
+	ast_cond_broadcast(&iface->mwi_q_cond);
+	ast_mutex_unlock(&iface->mwi_q_lock);
+}
+
+enum smdi_message_type {
+	SMDI_MWI,
+	SMDI_MD,
+};
+
+static inline int lock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
+{
+	switch (type) {
+	case SMDI_MWI:
+		return ast_mutex_lock(&iface->mwi_q_lock);
+	case SMDI_MD:	
+		return ast_mutex_lock(&iface->md_q_lock);
+	}
+	
+	return -1;
+}
+
+static inline int unlock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
+{
+	switch (type) {
+	case SMDI_MWI:
+		return ast_mutex_unlock(&iface->mwi_q_lock);
+	case SMDI_MD:
+		return ast_mutex_unlock(&iface->md_q_lock);
+	}
+
+	return -1;
+}
+
+static inline void *unlink_from_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
+{
+	switch (type) {
+	case SMDI_MWI:
+		return ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
+	case SMDI_MD:
+		return ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
+	}
+	return NULL;
+}
+
+static inline struct timeval msg_timestamp(void *msg, enum smdi_message_type type)
+{
+	struct ast_smdi_md_message *md_msg = msg;
+	struct ast_smdi_mwi_message *mwi_msg = msg;
+
+	switch (type) {
+	case SMDI_MWI:
+		return mwi_msg->timestamp;
+	case SMDI_MD:
+		return md_msg->timestamp;
+	}
+
+	return ast_tv(0, 0);
+}
+
+static inline void unref_msg(void *msg, enum smdi_message_type type)
+{
+	struct ast_smdi_md_message *md_msg = msg;
+	struct ast_smdi_mwi_message *mwi_msg = msg;
+
+	switch (type) {
+	case SMDI_MWI:
+		ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
+	case SMDI_MD:
+		ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
+	}
+}
+
+static void purge_old_messages(struct ast_smdi_interface *iface, enum smdi_message_type type)
+{
+	struct timeval now = ast_tvnow();
 	long elapsed = 0;
+	void *msg;
+	
+	lock_msg_q(iface, type);
+	msg = unlink_from_msg_q(iface, type);
+	unlock_msg_q(iface, type);
 
 	/* purge old messages */
-	now = ast_tvnow();
-	while (md_msg) {
-		elapsed = ast_tvdiff_ms(now, md_msg->timestamp);
+	while (msg) {
+		elapsed = ast_tvdiff_ms(now, msg_timestamp(msg, type));
 
 		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.\n",
-				iface->name, elapsed - iface->msg_expiry);
-			md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
-		}
-		else {
-			/* good message, return it */
+			unref_msg(msg, type);
+			ast_log(LOG_NOTICE, "Purged expired message from %s SMDI %s message queue.  "
+				"Message was %ld milliseconds too old.\n",
+				iface->name, (type == SMDI_MD) ? "MD" : "MWI", 
+				elapsed - iface->msg_expiry);
+
+			lock_msg_q(iface, type);
+			msg = unlink_from_msg_q(iface, type);
+			unlock_msg_q(iface, type);
+		} else {
+			/* good message, put it back and return */
+			switch (type) {
+			case SMDI_MD:
+				ast_smdi_md_message_push(iface, msg);
+				break;
+			case SMDI_MWI:
+				ast_smdi_mwi_message_push(iface, msg);
+				break;
+			}
+			unref_msg(msg, type);
 			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 = ast_tvnow();
+}
+
+static void *smdi_msg_pop(struct ast_smdi_interface *iface, enum smdi_message_type type)
+{
+	void *msg;
+
+	purge_old_messages(iface, type);
+
+	lock_msg_q(iface, type);
+	msg = unlink_from_msg_q(iface, type);
+	unlock_msg_q(iface, type);
+
+	return msg;
+}
+
+static void *smdi_msg_find(struct ast_smdi_interface *iface,
+	enum smdi_message_type type, const char *station)
+{
+	void *msg = NULL;
+
+	purge_old_messages(iface, type);
+
+	switch (type) {
+	case SMDI_MD:
+		msg = ASTOBJ_CONTAINER_FIND(&iface->md_q, station);
+		break;
+	case SMDI_MWI:
+		msg = ASTOBJ_CONTAINER_FIND(&iface->mwi_q, station);
+		break;
+	}
+
+	return msg;
+}
+
+static void *smdi_message_wait(struct ast_smdi_interface *iface, int timeout, 
+	enum smdi_message_type type, const char *station)
+{
+	struct timeval start;
 	long diff = 0;
-	struct ast_smdi_md_message *msg;
+	void *msg;
 
 	while (diff < timeout) {
-
-		if ((msg = ast_smdi_md_message_pop(iface)))
+		struct timespec ts = { 0, };
+		struct timeval tv;
+
+		lock_msg_q(iface, type);
+
+		if ((msg = smdi_msg_find(iface, type, station))) {
+			unlock_msg_q(iface, type);
 			return msg;
+		}
+
+		tv = ast_tvadd(start, ast_tv(0, timeout));
+		ts.tv_sec = tv.tv_sec;
+		ts.tv_nsec = tv.tv_usec * 1000;
+
+		/* If there were no messages in the queue, then go to sleep until one
+		 * arrives. */
+
+		ast_cond_timedwait(&iface->md_q_cond, &iface->md_q_lock, &ts);
+
+		if ((msg = smdi_msg_find(iface, type, station))) {
+			unlock_msg_q(iface, type);
+			return msg;
+		}
+
+		unlock_msg_q(iface, type);
 
 		/* check timeout */
 		diff = ast_tvdiff_ms(ast_tvnow(), start);
 	}
 
-	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 = ast_tvnow();
-	long elapsed = 0;
-
-	/* purge old messages */
-	while (mwi_msg)	{
-		elapsed = ast_tvdiff_ms(now, mwi_msg->timestamp);
-
-		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.\n",
-				iface->name, elapsed - iface->msg_expiry);
-			mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
-		}
-		else {
-			/* good message, return it */
-			break;
-		}
-	}
-
-	return mwi_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_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
-{
-	struct timeval start = ast_tvnow();
-	long diff = 0;
-	struct ast_smdi_mwi_message *msg;
-
-	while (diff < timeout) {
-
-		if ((msg = ast_smdi_mwi_message_pop(iface)))
-			return msg;
-
-		/* check timeout */
-		diff = ast_tvdiff_ms(ast_tvnow(), start);
-	}
-
-	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 NULL;
+}
+
+struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
+{
+	return smdi_msg_pop(iface, SMDI_MD);
+}
+
+struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
+{
+	return smdi_message_wait(iface, timeout, SMDI_MD, NULL);
+}
+
+struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
+{
+	return smdi_msg_pop(iface, SMDI_MWI);
+}
+
+struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
+{
+	return smdi_message_wait(iface, timeout, SMDI_MWI, NULL);
+}
+
+struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout,
+	const char *station)
+{
+	return smdi_message_wait(iface, timeout, SMDI_MWI, station);
+}
+
+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.
+/*! 
+ * \internal
+ * \brief Read an SMDI message.
  *
  * \param iface_p the SMDI interface to read from.
  *
@@ -335,122 +475,166 @@
 
 		/* check if this is the start of a message */
 		if (!start) {
-			if (c == 'M')
+			if (c == 'M') {
+				ast_log(LOG_DEBUG, "Read an 'M' to start an SMDI message\n");
 				start = 1;
+			}
+			continue;
 		}
-		else { /* Determine if this is a MD or MWI message */
-			if(c == 'D') { /* MD message */
-				start = 0;
-
-				if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
-					ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
-					return NULL;
+		
+		if (c == 'D') { /* MD message */
+			start = 0;
+
+			ast_log(LOG_DEBUG, "Read a 'D' ... it's an MD message.\n");
+
+			if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
+				ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
+				return NULL;
+			}
+			
+			ASTOBJ_INIT(md_msg);
+
+			/* read the message desk number */
+			for (i = 0; i < sizeof(md_msg->mesg_desk_num) - 1; i++) {
+				md_msg->mesg_desk_num[i] = fgetc(iface->file);
+				ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_num[i]);
+			}
+
+			md_msg->mesg_desk_num[sizeof(md_msg->mesg_desk_num) - 1] = '\0';
+			
+			ast_log(LOG_DEBUG, "The message desk number is '%s'\n", md_msg->mesg_desk_num);
+
+			/* read the message desk terminal number */
+			for (i = 0; i < sizeof(md_msg->mesg_desk_term) - 1; i++) {
+				md_msg->mesg_desk_term[i] = fgetc(iface->file);
+				ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_term[i]);
+			}
+
+			md_msg->mesg_desk_term[sizeof(md_msg->mesg_desk_term) - 1] = '\0';
+
+			ast_log(LOG_DEBUG, "The message desk terminal is '%s'\n", md_msg->mesg_desk_term);
+
+			/* read the message type */
+			md_msg->type = fgetc(iface->file);
+		 
+			ast_log(LOG_DEBUG, "Message type is '%c'\n", md_msg->type);
+
+			/* read the forwarding station number (may be blank) */
+			cp = &md_msg->fwd_st[0];
+			for (i = 0; i < sizeof(md_msg->fwd_st) - 1; i++) {
+				if ((c = fgetc(iface->file)) == ' ') {
+					*cp = '\0';
+					ast_log(LOG_DEBUG, "Read a space, done looking for the forwarding station\n");
+					break;
 				}
-				
-				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) {
+					ast_log(LOG_DEBUG, "Read a '%c' and stored it in the forwarding station buffer\n", c);
+					*cp++ = c;
+				} else {
+					ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the fwd station buffer, because of the msdstrip setting (%d < %d)\n", c, i, iface->msdstrip);
+				}
+			}
+
+			/* make sure the value is null terminated, even if this truncates it */
+			md_msg->fwd_st[sizeof(md_msg->fwd_st) - 1] = '\0';
+			cp = NULL;
+
+			ast_log(LOG_DEBUG, "The forwarding station is '%s'\n", md_msg->fwd_st);
+
+			/* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
+			 * up a message on this field */
+			ast_copy_string(md_msg->name, md_msg->fwd_st, sizeof(md_msg->name));
+
+			/* read the calling station number (may be blank) */
+			cp = &md_msg->calling_st[0];
+			for (i = 0; i < sizeof(md_msg->calling_st) - 1; i++) {
+				if (!isdigit((c = fgetc(iface->file)))) {
+					*cp = '\0';
+					ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the calling station buffer because it's not a digit\n", c);
+					if (c == ' ') {
+						/* Don't break on a space.  We may read the space before the calling station
+						 * here if the forwarding station buffer filled up. */
+						i--; /* We're still on the same character */
+						continue;
 					}
-
-					/* store c in md_msg->fwd_st */
-					if( i >= iface->msdstrip)
-						*cp++ = c;
+					break;
 				}
 
-				/* 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;
+				/* store c in md_msg->calling_st */
+				if (i >= iface->msdstrip) {
+					ast_log(LOG_DEBUG, "Read a '%c' and stored it in the calling station buffer\n", c);
+					*cp++ = c;
+				} else {
+					ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the calling station buffer, because of the msdstrip setting (%d < %d)\n", c, i, iface->msdstrip);
 				}
-
-				/* 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 */
-				md_msg->timestamp = ast_tvnow();
-				ast_smdi_md_message_push(iface, md_msg);
-				ast_debug(1, "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;
-
-				if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
-					ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
-					return NULL;
+			}
+
+			/* make sure the value is null terminated, even if this truncates it */
+			md_msg->calling_st[sizeof(md_msg->calling_st) - 1] = '\0';
+			cp = NULL;
+
+			ast_log(LOG_DEBUG, "The calling station is '%s'\n", md_msg->calling_st);
+
+			/* add the message to the message queue */
+			md_msg->timestamp = ast_tvnow();
+			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;
+
+			ast_log(LOG_DEBUG, "Read a 'W', it's an MWI message. (No more debug coming for MWI messages)\n");
+
+			if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
+				ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
+				return NULL;
+			}
+
+			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 < sizeof(mwi_msg->fwd_st) - 1; i++) {
+				if ((c = fgetc(iface->file)) == ' ') {
+					*cp = '\0';
+					break;
 				}
 
-				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;
-				}
-
-				/* 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 */
-				mwi_msg->timestamp = ast_tvnow();
-				ast_smdi_mwi_message_push(iface, mwi_msg);
-				ast_debug(1, "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;
-			}
+				/* store c in md_msg->fwd_st */
+				if (i >= iface->msdstrip)
+					*cp++ = c;
+			}
+
+			/* make sure the station number is null terminated, even if this will truncate it */
+			mwi_msg->fwd_st[sizeof(mwi_msg->fwd_st) - 1] = '\0';
+			cp = NULL;
+			
+			/* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
+			 * up a message on this field */
+			ast_copy_string(mwi_msg->name, mwi_msg->fwd_st, sizeof(mwi_msg->name));
+
+			/* read the mwi failure cause */
+			for (i = 0; i < sizeof(mwi_msg->cause) - 1; i++)
+				mwi_msg->cause[i] = fgetc(iface->file);
+
+			mwi_msg->cause[sizeof(mwi_msg->cause) - 1] = '\0';
+
+			/* add the message to the message queue */
+			mwi_msg->timestamp = ast_tvnow();
+			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;
 		}
 	}
 
@@ -459,38 +643,130 @@
 	return NULL;
 }
 
-/*! \brief ast_smdi_md_message destructor. */

[... 512 lines stripped ...]



More information about the asterisk-commits mailing list