| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Asterisk -- A telephony toolkit for Linux. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  |  * Copyright (C) 2005-2008, Digium, Inc. | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Matthew A. Nicholson <mnicholson@digium.com> | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  |  * Russell Bryant <russell@digium.com> | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2006-02-10 23:58:11 +00:00
										 |  |  |  * See http://www.asterisk.org for more information about
 | 
					
						
							|  |  |  |  * the Asterisk project. Please do not directly contact | 
					
						
							|  |  |  |  * any of the maintainers of this project for assistance; | 
					
						
							|  |  |  |  * the project provides a web site, mailing lists and IRC | 
					
						
							|  |  |  |  * channels for your use. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  |  * This program is free software, distributed under the terms of | 
					
						
							| 
									
										
										
										
											2006-02-10 23:58:11 +00:00
										 |  |  |  * the GNU General Public License Version 2. See the LICENSE file | 
					
						
							|  |  |  |  * at the top of the source tree. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \file | 
					
						
							|  |  |  |  * \brief SMDI support for Asterisk. | 
					
						
							|  |  |  |  * \author Matthew A. Nicholson <mnicholson@digium.com> | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  |  * \author Russell Bryant <russell@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
 | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-07 18:54:56 +00:00
										 |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <errno.h>
 | 
					
						
							|  |  |  | #include <termios.h>
 | 
					
						
							|  |  |  | #include <sys/time.h>
 | 
					
						
							|  |  |  | #include <time.h>
 | 
					
						
							|  |  |  | #include <ctype.h>
 | 
					
						
							| 
									
										
										
										
											2006-02-10 23:58:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | #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"
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | #include "asterisk/stringfields.h"
 | 
					
						
							|  |  |  | #include "asterisk/linkedlists.h"
 | 
					
						
							|  |  |  | #include "asterisk/app.h"
 | 
					
						
							|  |  |  | #include "asterisk/pbx.h"
 | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* Message expiry time in milliseconds */ | 
					
						
							|  |  |  | #define SMDI_MSG_EXPIRY_TIME	30000 /* 30 seconds */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char config_file[] = "smdi.conf"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | /*! \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; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*! \brief SMDI interface container. */ | 
					
						
							|  |  |  | struct ast_smdi_interface_container { | 
					
						
							|  |  |  | 	ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface); | 
					
						
							|  |  |  | } smdi_ifaces; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | /*! \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); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | /*! 
 | 
					
						
							|  |  |  |  * \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) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	ast_mutex_lock(&iface->md_q_lock); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg); | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	ast_cond_broadcast(&iface->md_q_cond); | 
					
						
							|  |  |  | 	ast_mutex_unlock(&iface->md_q_lock); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \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) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	ast_mutex_lock(&iface->mwi_q_lock); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	ASTOBJ_CONTAINER_LINK_END(&iface->mwi_q, mwi_msg); | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	ast_cond_broadcast(&iface->mwi_q_cond); | 
					
						
							|  |  |  | 	ast_mutex_unlock(&iface->mwi_q_lock); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | static int smdi_toggle_mwi(struct ast_smdi_interface *iface, const char *mailbox, int on) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	FILE *file; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	if (!(file = fopen(iface->name, "w"))) { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 		ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno)); | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	}	 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ASTOBJ_WRLOCK(iface); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	fprintf(file, "%s:MWI ", on ? "OP" : "RMV"); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	for (i = 0; i < iface->msdstrip; i++) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	   fprintf(file, "0"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fprintf(file, "%s!\x04", mailbox); | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	fclose(file); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ASTOBJ_UNLOCK(iface); | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ast_log(LOG_DEBUG, "Sent MWI %s message for %s on %s\n", on ? "set" : "unset",  | 
					
						
							|  |  |  | 		mailbox, iface->name); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	return smdi_toggle_mwi(iface, mailbox, 1); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return smdi_toggle_mwi(iface, mailbox, 0); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	ast_mutex_lock(&iface->md_q_lock); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg); | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	ast_cond_broadcast(&iface->md_q_cond); | 
					
						
							|  |  |  | 	ast_mutex_unlock(&iface->md_q_lock); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	ast_mutex_lock(&iface->mwi_q_lock); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg); | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	ast_cond_broadcast(&iface->mwi_q_cond); | 
					
						
							|  |  |  | 	ast_mutex_unlock(&iface->mwi_q_lock); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | enum smdi_message_type { | 
					
						
							|  |  |  | 	SMDI_MWI, | 
					
						
							|  |  |  | 	SMDI_MD, | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 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; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 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); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	return -1; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | static inline void *unlink_from_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	switch (type) { | 
					
						
							|  |  |  | 	case SMDI_MWI: | 
					
						
							|  |  |  | 		return ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q); | 
					
						
							|  |  |  | 	case SMDI_MD: | 
					
						
							|  |  |  | 		return ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 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; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	return ast_tv(0, 0); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 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) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct timeval now; | 
					
						
							|  |  |  | 	long elapsed = 0; | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	void *msg; | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	lock_msg_q(iface, type); | 
					
						
							|  |  |  | 	msg = unlink_from_msg_q(iface, type); | 
					
						
							|  |  |  | 	unlock_msg_q(iface, type); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* purge old messages */ | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	now = ast_tvnow(); | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	while (msg) { | 
					
						
							|  |  |  | 		elapsed = ast_tvdiff_ms(now, msg_timestamp(msg, type)); | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (elapsed > iface->msg_expiry) { | 
					
						
							|  |  |  | 			/* found an expired message */ | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			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); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *smdi_msg_pop(struct ast_smdi_interface *iface, enum smdi_message_type type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	void *msg; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	purge_old_messages(iface, type); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	lock_msg_q(iface, type); | 
					
						
							|  |  |  | 	msg = unlink_from_msg_q(iface, type); | 
					
						
							|  |  |  | 	unlock_msg_q(iface, type); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return msg; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | enum { | 
					
						
							|  |  |  | 	OPT_SEARCH_TERMINAL = (1 << 0), | 
					
						
							|  |  |  | 	OPT_SEARCH_NUMBER   = (1 << 1), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | static void *smdi_msg_find(struct ast_smdi_interface *iface, | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 	enum smdi_message_type type, const char *search_key, struct ast_flags options) | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	void *msg = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	purge_old_messages(iface, type); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (type) { | 
					
						
							|  |  |  | 	case SMDI_MD: | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 		if (ast_test_flag(&options, OPT_SEARCH_TERMINAL)) { | 
					
						
							|  |  |  | 			struct ast_smdi_md_message *md_msg = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* Searching by the message desk terminal */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do { | 
					
						
							|  |  |  | 				if (!strcasecmp(iterator->mesg_desk_term, search_key)) | 
					
						
							|  |  |  | 					md_msg = ASTOBJ_REF(iterator); | 
					
						
							|  |  |  | 			} while (0); ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			msg = md_msg; | 
					
						
							|  |  |  | 		} else if (ast_test_flag(&options, OPT_SEARCH_NUMBER)) { | 
					
						
							|  |  |  | 			struct ast_smdi_md_message *md_msg = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* Searching by the message desk number */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do { | 
					
						
							|  |  |  | 				if (!strcasecmp(iterator->mesg_desk_num, search_key)) | 
					
						
							|  |  |  | 					md_msg = ASTOBJ_REF(iterator); | 
					
						
							|  |  |  | 			} while (0); ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			msg = md_msg; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			/* Searching by the forwarding station */ | 
					
						
							|  |  |  | 			msg = ASTOBJ_CONTAINER_FIND(&iface->md_q, search_key); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case SMDI_MWI: | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 		msg = ASTOBJ_CONTAINER_FIND(&iface->mwi_q, search_key); | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return msg; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *smdi_message_wait(struct ast_smdi_interface *iface, int timeout,  | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 	enum smdi_message_type type, const char *search_key, struct ast_flags options) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	struct timeval start; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	long diff = 0; | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	void *msg; | 
					
						
							| 
									
										
										
										
											2008-05-19 16:50:52 +00:00
										 |  |  | 	ast_cond_t *cond = NULL; | 
					
						
							|  |  |  | 	ast_mutex_t *lock = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (type) { | 
					
						
							|  |  |  | 	case SMDI_MWI: | 
					
						
							|  |  |  | 		cond = &iface->mwi_q_cond; | 
					
						
							|  |  |  | 		lock = &iface->mwi_q_lock; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SMDI_MD: | 
					
						
							|  |  |  | 		cond = &iface->md_q_cond; | 
					
						
							|  |  |  | 		lock = &iface->md_q_lock; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	start = ast_tvnow(); | 
					
						
							|  |  |  | 	while (diff < timeout) { | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 		struct timespec ts = { 0, }; | 
					
						
							|  |  |  | 		struct timeval tv; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 		lock_msg_q(iface, type); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 		if ((msg = smdi_msg_find(iface, type, search_key, options))) { | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			unlock_msg_q(iface, type); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 			return msg; | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		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. */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-19 16:50:52 +00:00
										 |  |  | 		ast_cond_timedwait(cond, lock, &ts); | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 		if ((msg = smdi_msg_find(iface, type, search_key, options))) { | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			unlock_msg_q(iface, type); | 
					
						
							|  |  |  | 			return msg; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		unlock_msg_q(iface, type); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* check timeout */ | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 		diff = ast_tvdiff_ms(ast_tvnow(), start); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	return NULL; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 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) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 	struct ast_flags options = { 0 }; | 
					
						
							|  |  |  | 	return smdi_message_wait(iface, timeout, SMDI_MD, NULL, options); | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 	struct ast_flags options = { 0 }; | 
					
						
							|  |  |  | 	return smdi_message_wait(iface, timeout, SMDI_MWI, NULL, options); | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout, | 
					
						
							|  |  |  | 	const char *station) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 	struct ast_flags options = { 0 }; | 
					
						
							|  |  |  | 	return smdi_message_wait(iface, timeout, SMDI_MWI, station, options); | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name)); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | /*! 
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Read an SMDI message. | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2006-05-09 16:24:07 +00:00
										 |  |  |  * \param iface_p the SMDI interface to read from. | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * 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 */ | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	while ((c = fgetc(iface->file))) { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* check if this is the start of a message */ | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 		if (!start) { | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			if (c == 'M') { | 
					
						
							|  |  |  | 				ast_log(LOG_DEBUG, "Read an 'M' to start an SMDI message\n"); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				start = 1; | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 		 | 
					
						
							|  |  |  | 		if (c == 'D') { /* MD message */ | 
					
						
							|  |  |  | 			start = 0; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			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; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 				/* 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); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			/* 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; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 				/* 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); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			/* 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; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			ast_log(LOG_DEBUG, "The calling station is '%s'\n", md_msg->calling_st); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			/* 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); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 		} else if (c == 'W') { /* MWI message */ | 
					
						
							|  |  |  | 			start = 0; | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			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); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			/* 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; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 				/* store c in md_msg->fwd_st */ | 
					
						
							|  |  |  | 				if (i >= iface->msdstrip) | 
					
						
							|  |  |  | 					*cp++ = c; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			/* 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; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	free(msg); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	free(msg); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | static void destroy_mailbox_mapping(struct mailbox_mapping *mm) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	ast_string_field_free_memory(mm); | 
					
						
							|  |  |  | 	ASTOBJ_UNREF(mm->iface, ast_smdi_interface_destroy); | 
					
						
							|  |  |  | 	free(mm); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void destroy_all_mailbox_mappings(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct mailbox_mapping *mm; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_mutex_lock(&mwi_monitor.lock); | 
					
						
							|  |  |  | 	while ((mm = AST_LIST_REMOVE_HEAD(&mwi_monitor.mailbox_mappings, entry))) | 
					
						
							|  |  |  | 		destroy_mailbox_mapping(mm); | 
					
						
							|  |  |  | 	ast_mutex_unlock(&mwi_monitor.lock); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void append_mailbox_mapping(struct ast_variable *var, struct ast_smdi_interface *iface) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct mailbox_mapping *mm; | 
					
						
							|  |  |  | 	char *mailbox, *context; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(mm = ast_calloc(1, sizeof(*mm)))) | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	if (ast_string_field_init(mm, 32)) { | 
					
						
							|  |  |  | 		free(mm); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	ast_string_field_set(mm, smdi, var->name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	context = ast_strdupa(var->value); | 
					
						
							|  |  |  | 	mailbox = strsep(&context, "@"); | 
					
						
							|  |  |  | 	if (ast_strlen_zero(context)) | 
					
						
							|  |  |  | 		context = "default"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_string_field_set(mm, mailbox, mailbox); | 
					
						
							|  |  |  | 	ast_string_field_set(mm, context, context); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mm->iface = ASTOBJ_REF(iface); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_mutex_lock(&mwi_monitor.lock); | 
					
						
							|  |  |  | 	AST_LIST_INSERT_TAIL(&mwi_monitor.mailbox_mappings, mm, entry); | 
					
						
							|  |  |  | 	ast_mutex_unlock(&mwi_monitor.lock); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \note Called with the mwi_monitor.lock locked | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void poll_mailbox(struct mailbox_mapping *mm) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char buf[1024]; | 
					
						
							|  |  |  | 	unsigned int state; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	snprintf(buf, sizeof(buf), "%s@%s", mm->mailbox, mm->context); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	state = !!ast_app_has_voicemail(mm->mailbox, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (state != mm->cur_state) { | 
					
						
							|  |  |  | 		if (state) | 
					
						
							|  |  |  | 			ast_smdi_mwi_set(mm->iface, mm->smdi); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			ast_smdi_mwi_unset(mm->iface, mm->smdi); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		mm->cur_state = state; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *mwi_monitor_handler(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	while (!mwi_monitor.stop) { | 
					
						
							|  |  |  | 		struct timespec ts = { 0, }; | 
					
						
							|  |  |  | 		struct timeval tv; | 
					
						
							|  |  |  | 		struct mailbox_mapping *mm; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ast_mutex_lock(&mwi_monitor.lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		mwi_monitor.last_poll = ast_tvnow(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		AST_LIST_TRAVERSE(&mwi_monitor.mailbox_mappings, mm, entry) | 
					
						
							|  |  |  | 			poll_mailbox(mm); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Sleep up to the configured polling interval.  Allow unload_module()
 | 
					
						
							|  |  |  | 		 * to signal us to wake up and exit. */ | 
					
						
							|  |  |  | 		tv = ast_tvadd(mwi_monitor.last_poll, ast_tv(mwi_monitor.polling_interval, 0)); | 
					
						
							|  |  |  | 		ts.tv_sec = tv.tv_sec; | 
					
						
							|  |  |  | 		ts.tv_nsec = tv.tv_usec * 1000; | 
					
						
							|  |  |  | 		ast_cond_timedwait(&mwi_monitor.cond, &mwi_monitor.lock, &ts); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ast_mutex_unlock(&mwi_monitor.lock); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ast_smdi_interface *alloc_smdi_interface(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_smdi_interface *iface; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(iface = ast_calloc(1, sizeof(*iface)))) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ASTOBJ_INIT(iface); | 
					
						
							|  |  |  | 	ASTOBJ_CONTAINER_INIT(&iface->md_q); | 
					
						
							|  |  |  | 	ASTOBJ_CONTAINER_INIT(&iface->mwi_q); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_mutex_init(&iface->md_q_lock); | 
					
						
							|  |  |  | 	ast_cond_init(&iface->md_q_cond, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_mutex_init(&iface->mwi_q_lock); | 
					
						
							|  |  |  | 	ast_cond_init(&iface->mwi_q_cond, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return iface; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \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); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	if (!conf) { | 
					
						
							| 
									
										
										
										
											2006-02-11 07:05:49 +00:00
										 |  |  | 		if (reload) | 
					
						
							|  |  |  | 			ast_log(LOG_NOTICE, "Unable to reload config %s: SMDI untouched\n", config_file); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			ast_log(LOG_NOTICE, "Unable to load config %s: SMDI disabled\n", config_file); | 
					
						
							|  |  |  | 		return 1; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* 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. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	if (reload) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 		ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	for (v = ast_variable_browse(conf, "interfaces"); v; v = v->next) { | 
					
						
							|  |  |  | 		if (!strcasecmp(v->name, "baudrate")) { | 
					
						
							|  |  |  | 			if (!strcasecmp(v->value, "9600")) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				baud_rate = B9600; | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			else if (!strcasecmp(v->value, "4800")) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				baud_rate = B4800; | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			else if (!strcasecmp(v->value, "2400")) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				baud_rate = B2400; | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			else if (!strcasecmp(v->value, "1200")) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				baud_rate = B1200; | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 			else { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				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; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 		} else if (!strcasecmp(v->name, "msdstrip")) { | 
					
						
							|  |  |  | 			if (!sscanf(v->value, "%d", &msdstrip)) { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno); | 
					
						
							|  |  |  | 				msdstrip = 0; | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 			} else if (0 > msdstrip || msdstrip > 9) { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno); | 
					
						
							|  |  |  | 				msdstrip = 0; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 		} else if (!strcasecmp(v->name, "msgexpirytime")) { | 
					
						
							|  |  |  | 			if (!sscanf(v->value, "%ld", &msg_expiry)) { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno); | 
					
						
							|  |  |  | 				msg_expiry = SMDI_MSG_EXPIRY_TIME; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 		} else if (!strcasecmp(v->name, "paritybit")) { | 
					
						
							|  |  |  | 			if (!strcasecmp(v->value, "even")) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				paritybit = PARENB; | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 			else if (!strcasecmp(v->value, "odd")) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				paritybit = PARENB | PARODD; | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 			else if (!strcasecmp(v->value, "none")) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				paritybit = ~PARENB; | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 			else { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno); | 
					
						
							|  |  |  | 				paritybit = PARENB; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 		} else if (!strcasecmp(v->name, "charsize")) { | 
					
						
							|  |  |  | 			if (!strcasecmp(v->value, "7")) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				charsize = CS7; | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 			else if (!strcasecmp(v->value, "8")) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				charsize = CS8; | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 			else { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno); | 
					
						
							|  |  |  | 				charsize = CS7; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 		} else if (!strcasecmp(v->name, "twostopbits")) { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 			stopbits = ast_true(v->name); | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 		} else if (!strcasecmp(v->name, "smdiport")) { | 
					
						
							|  |  |  | 			if (reload) { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				/* 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. */ | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 				if ((iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 					ast_log(LOG_NOTICE, "SMDI interface %s already running, not restarting\n", iface->name); | 
					
						
							|  |  |  | 					ASTOBJ_UNMARK(iface); | 
					
						
							|  |  |  | 					ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); | 
					
						
							|  |  |  | 					continue; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			 | 
					
						
							|  |  |  | 			if (!(iface = alloc_smdi_interface())) | 
					
						
							| 
									
										
										
										
											2006-06-16 16:30:42 +00:00
										 |  |  | 				continue; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			ast_copy_string(iface->name, v->value, sizeof(iface->name)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-07 16:39:23 +00:00
										 |  |  | 			iface->thread = AST_PTHREADT_NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 			if (!(iface->file = fopen(iface->name, "r"))) { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s)\n", iface->name, strerror(errno)); | 
					
						
							|  |  |  | 				ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 			iface->fd = fileno(iface->file); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* Set the proper attributes for our serial port. */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* get the current attributes from the port */ | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 			if (tcgetattr(iface->fd, &iface->mode)) { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				ast_log(LOG_ERROR, "Error getting atributes of %s (%s)\n", iface->name, strerror(errno)); | 
					
						
							|  |  |  | 				ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* set the desired speed */ | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 			if (cfsetispeed(&iface->mode, baud_rate) || cfsetospeed(&iface->mode, baud_rate)) { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				ast_log(LOG_ERROR, "Error setting baud rate on %s (%s)\n", iface->name, strerror(errno)); | 
					
						
							|  |  |  | 				ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			/* set the stop bits */ | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 			if (stopbits) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 			   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 */ | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 			if (tcsetattr(iface->fd, TCSAFLUSH, &iface->mode)) { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				ast_log(LOG_ERROR, "Error setting attributes on %s (%s)\n", iface->name, strerror(errno)); | 
					
						
							|  |  |  | 				ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* set the msdstrip */ | 
					
						
							|  |  |  | 			iface->msdstrip = msdstrip; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* set the message expiry time */ | 
					
						
							|  |  |  | 			iface->msg_expiry = msg_expiry; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 			/* start the listener thread */ | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 			if (option_verbose > 2) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				ast_verbose(VERBOSE_PREFIX_3 "Starting SMDI monitor thread for %s\n", iface->name); | 
					
						
							| 
									
										
										
										
											2006-10-04 19:47:22 +00:00
										 |  |  | 			if (ast_pthread_create_background(&iface->thread, NULL, smdi_read, iface)) { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 				ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name); | 
					
						
							|  |  |  | 				ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 			ASTOBJ_CONTAINER_LINK(&smdi_ifaces, iface); | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 			ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | 			ast_module_ref(ast_module_info->self); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	destroy_all_mailbox_mappings(); | 
					
						
							|  |  |  | 	mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL; | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	iface = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (v = ast_variable_browse(conf, "mailboxes"); v; v = v->next) { | 
					
						
							|  |  |  | 		if (!strcasecmp(v->name, "smdiport")) { | 
					
						
							|  |  |  | 			if (iface) | 
					
						
							|  |  |  | 				ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (!(iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) { | 
					
						
							|  |  |  | 				ast_log(LOG_NOTICE, "SMDI interface %s not found\n", iface->name); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else if (!strcasecmp(v->name, "pollinginterval")) { | 
					
						
							|  |  |  | 			if (sscanf(v->value, "%u", &mwi_monitor.polling_interval) != 1) { | 
					
						
							|  |  |  | 				ast_log(LOG_ERROR, "Invalid value for pollinginterval: %s\n", v->value); | 
					
						
							|  |  |  | 				mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			if (!iface) { | 
					
						
							|  |  |  | 				ast_log(LOG_ERROR, "Mailbox mapping ignored, no valid SMDI interface specified in mailboxes section\n"); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			append_mailbox_mapping(v, iface); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (iface) | 
					
						
							|  |  |  | 		ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	ast_config_destroy(conf); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	if (!AST_LIST_EMPTY(&mwi_monitor.mailbox_mappings) && mwi_monitor.thread == AST_PTHREADT_NULL | 
					
						
							|  |  |  | 		&& ast_pthread_create_background(&mwi_monitor.thread, NULL, mwi_monitor_handler, NULL)) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "Failed to start MWI monitoring thread.  This module will not operate.\n"); | 
					
						
							|  |  |  | 		return AST_MODULE_LOAD_FAILURE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	/* Prune any interfaces we should no longer monitor. */ | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	if (reload) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 		ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces, ast_smdi_interface_destroy); | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	ASTOBJ_CONTAINER_RDLOCK(&smdi_ifaces); | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	/* TODO: this is bad, we need an ASTOBJ method for this! */ | 
					
						
							|  |  |  | 	if (!smdi_ifaces.head) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 		res = 1; | 
					
						
							|  |  |  | 	ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces); | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | struct smdi_msg_datastore { | 
					
						
							|  |  |  | 	unsigned int id; | 
					
						
							|  |  |  | 	struct ast_smdi_interface *iface; | 
					
						
							|  |  |  | 	struct ast_smdi_md_message *md_msg; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void smdi_msg_datastore_destroy(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct smdi_msg_datastore *smd = data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (smd->iface) | 
					
						
							|  |  |  | 		ASTOBJ_UNREF(smd->iface, ast_smdi_interface_destroy); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (smd->md_msg) | 
					
						
							|  |  |  | 		ASTOBJ_UNREF(smd->md_msg, ast_smdi_md_message_destroy); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	free(smd); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct ast_datastore_info smdi_msg_datastore_info = { | 
					
						
							|  |  |  | 	.type = "SMDIMSG", | 
					
						
							|  |  |  | 	.destroy = smdi_msg_datastore_destroy, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int smdi_msg_id; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*! In milliseconds */ | 
					
						
							|  |  |  | #define SMDI_RETRIEVE_TIMEOUT_DEFAULT 3000
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | AST_APP_OPTIONS(smdi_msg_ret_options, BEGIN_OPTIONS | 
					
						
							|  |  |  | 	AST_APP_OPTION('t', OPT_SEARCH_TERMINAL), | 
					
						
							|  |  |  | 	AST_APP_OPTION('n', OPT_SEARCH_NUMBER), | 
					
						
							|  |  |  | END_OPTIONS ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | static int smdi_msg_retrieve_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_module_user *u; | 
					
						
							|  |  |  | 	AST_DECLARE_APP_ARGS(args, | 
					
						
							|  |  |  | 		AST_APP_ARG(port); | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 		AST_APP_ARG(search_key); | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 		AST_APP_ARG(timeout); | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 		AST_APP_ARG(options); | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	); | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 	struct ast_flags options = { 0 }; | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	unsigned int timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT; | 
					
						
							|  |  |  | 	int res = -1; | 
					
						
							|  |  |  | 	char *parse = NULL; | 
					
						
							|  |  |  | 	struct smdi_msg_datastore *smd = NULL; | 
					
						
							|  |  |  | 	struct ast_datastore *datastore = NULL; | 
					
						
							|  |  |  | 	struct ast_smdi_interface *iface = NULL; | 
					
						
							|  |  |  | 	struct ast_smdi_md_message *md_msg = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	u = ast_module_user_add(chan); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_strlen_zero(data)) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE requires an argument\n"); | 
					
						
							|  |  |  | 		goto return_error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!chan) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE must be used with a channel\n"); | 
					
						
							|  |  |  | 		goto return_error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_autoservice_start(chan); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	parse = ast_strdupa(data); | 
					
						
							|  |  |  | 	AST_STANDARD_APP_ARGS(args, parse); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 	if (ast_strlen_zero(args.port) || ast_strlen_zero(args.search_key)) { | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 		ast_log(LOG_ERROR, "Invalid arguments provided to SMDI_MSG_RETRIEVE\n"); | 
					
						
							|  |  |  | 		goto return_error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(iface = ast_smdi_interface_find(args.port))) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "SMDI port '%s' not found\n", args.port); | 
					
						
							|  |  |  | 		goto return_error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 	if (!ast_strlen_zero(args.options)) { | 
					
						
							|  |  |  | 		ast_app_parse_options(smdi_msg_ret_options, &options, NULL, args.options); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	if (!ast_strlen_zero(args.timeout)) { | 
					
						
							|  |  |  | 		if (sscanf(args.timeout, "%u", &timeout) != 1) { | 
					
						
							|  |  |  | 			ast_log(LOG_ERROR, "'%s' is not a valid timeout\n", args.timeout); | 
					
						
							|  |  |  | 			timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 	if (!(md_msg = smdi_message_wait(iface, timeout, SMDI_MD, args.search_key, options))) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "No SMDI message retrieved for search key '%s' after " | 
					
						
							|  |  |  | 			"waiting %u ms.\n", args.search_key, timeout); | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 		goto return_error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(smd = ast_calloc(1, sizeof(*smd)))) | 
					
						
							|  |  |  | 		goto return_error; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	smd->iface = ASTOBJ_REF(iface); | 
					
						
							|  |  |  | 	smd->md_msg = ASTOBJ_REF(md_msg); | 
					
						
							|  |  |  | 	smd->id = ast_atomic_fetchadd_int((int *) &smdi_msg_id, 1); | 
					
						
							|  |  |  | 	snprintf(buf, len, "%u", smd->id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(datastore = ast_channel_datastore_alloc(&smdi_msg_datastore_info, buf))) | 
					
						
							|  |  |  | 		goto return_error; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	datastore->data = smd; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_channel_lock(chan); | 
					
						
							|  |  |  | 	ast_channel_datastore_add(chan, datastore); | 
					
						
							|  |  |  | 	ast_channel_unlock(chan); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	res = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | return_error: | 
					
						
							|  |  |  | 	if (iface) | 
					
						
							|  |  |  | 		ASTOBJ_UNREF(iface, ast_smdi_interface_destroy); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (md_msg) | 
					
						
							|  |  |  | 		ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (smd && !datastore) | 
					
						
							|  |  |  | 		smdi_msg_datastore_destroy(smd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (parse) | 
					
						
							|  |  |  | 		ast_autoservice_stop(chan); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_module_user_remove(u); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int smdi_msg_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_module_user *u; | 
					
						
							|  |  |  | 	int res = -1; | 
					
						
							|  |  |  | 	AST_DECLARE_APP_ARGS(args, | 
					
						
							|  |  |  | 		AST_APP_ARG(id); | 
					
						
							|  |  |  | 		AST_APP_ARG(component); | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | 	char *parse; | 
					
						
							|  |  |  | 	struct ast_datastore *datastore = NULL; | 
					
						
							|  |  |  | 	struct smdi_msg_datastore *smd = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	u = ast_module_user_add(chan); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!chan) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "SMDI_MSG can not be called without a channel\n"); | 
					
						
							|  |  |  | 		goto return_error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_strlen_zero(data)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "SMDI_MSG requires an argument\n"); | 
					
						
							|  |  |  | 		goto return_error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	parse = ast_strdupa(data); | 
					
						
							|  |  |  | 	AST_STANDARD_APP_ARGS(args, parse); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_strlen_zero(args.id)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n"); | 
					
						
							|  |  |  | 		goto return_error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_strlen_zero(args.component)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n"); | 
					
						
							|  |  |  | 		goto return_error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_channel_lock(chan); | 
					
						
							|  |  |  | 	datastore = ast_channel_datastore_find(chan, &smdi_msg_datastore_info, args.id); | 
					
						
							|  |  |  | 	ast_channel_unlock(chan); | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	if (!datastore) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "No SMDI message found for message ID '%s'\n", args.id); | 
					
						
							|  |  |  | 		goto return_error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	smd = datastore->data; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 	if (!strcasecmp(args.component, "number")) { | 
					
						
							|  |  |  | 		ast_copy_string(buf, smd->md_msg->mesg_desk_num, len); | 
					
						
							|  |  |  | 	} else if (!strcasecmp(args.component, "terminal")) { | 
					
						
							|  |  |  | 		ast_copy_string(buf, smd->md_msg->mesg_desk_term, len); | 
					
						
							|  |  |  | 	} else if (!strcasecmp(args.component, "station")) { | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 		ast_copy_string(buf, smd->md_msg->fwd_st, len); | 
					
						
							|  |  |  | 	} else if (!strcasecmp(args.component, "callerid")) { | 
					
						
							|  |  |  | 		ast_copy_string(buf, smd->md_msg->calling_st, len); | 
					
						
							|  |  |  | 	} else if (!strcasecmp(args.component, "type")) { | 
					
						
							|  |  |  | 		snprintf(buf, len, "%c", smd->md_msg->type); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "'%s' is not a valid message component for SMDI_MSG\n", | 
					
						
							|  |  |  | 			args.component); | 
					
						
							|  |  |  | 		goto return_error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	res = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | return_error: | 
					
						
							|  |  |  | 	ast_module_user_remove(u); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ast_custom_function smdi_msg_retrieve_function = { | 
					
						
							|  |  |  | 	.name = "SMDI_MSG_RETRIEVE", | 
					
						
							|  |  |  | 	.synopsis = "Retrieve an SMDI message.", | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 	.syntax = "SMDI_MSG_RETRIEVE(<smdi port>,<search key>[,timeout[,options]])", | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	.desc =  | 
					
						
							|  |  |  | 	"   This function is used to retrieve an incoming SMDI message.  It returns\n" | 
					
						
							|  |  |  | 	"an ID which can be used with the SMDI_MSG() function to access details of\n" | 
					
						
							|  |  |  | 	"the message.  Note that this is a destructive function in the sense that\n" | 
					
						
							|  |  |  | 	"once an SMDI message is retrieved using this function, it is no longer in\n" | 
					
						
							|  |  |  | 	"the global SMDI message queue, and can not be accessed by any other Asterisk\n" | 
					
						
							|  |  |  | 	"channels.  The timeout for this function is optional, and the default is\n" | 
					
						
							|  |  |  | 	"3 seconds.  When providing a timeout, it should be in milliseconds.\n" | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 	"   The default search is done on the forwarding station ID.  However, if\n" | 
					
						
							|  |  |  | 	"you set one of the search key options in the options field, you can change\n" | 
					
						
							|  |  |  | 	"this behavior.\n" | 
					
						
							|  |  |  | 	"   Options:\n" | 
					
						
							|  |  |  | 	"     t - Instead of searching on the forwarding station, search on the message\n" | 
					
						
							|  |  |  | 	"         desk terminal.\n" | 
					
						
							|  |  |  | 	"     n - Instead of searching on the forwarding station, search on the message\n" | 
					
						
							|  |  |  | 	"         desk number.\n" | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	"", | 
					
						
							|  |  |  | 	.read = smdi_msg_retrieve_read, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ast_custom_function smdi_msg_function = { | 
					
						
							|  |  |  | 	.name = "SMDI_MSG", | 
					
						
							|  |  |  | 	.synopsis = "Retrieve details about an SMDI message.", | 
					
						
							|  |  |  | 	.syntax = "SMDI_MSG(<message_id>,<component>)", | 
					
						
							|  |  |  | 	.desc =  | 
					
						
							|  |  |  | 	"   This function is used to access details of an SMDI message that was\n" | 
					
						
							|  |  |  | 	"pulled from the incoming SMDI message queue using the SMDI_MSG_RETRIEVE()\n" | 
					
						
							|  |  |  | 	"function.\n" | 
					
						
							|  |  |  | 	"   Valid message components are:\n" | 
					
						
							| 
									
										
										
										
											2008-06-05 16:38:52 +00:00
										 |  |  | 	"      number   - The message desk number\n" | 
					
						
							|  |  |  | 	"      terminal - The message desk terminal\n" | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	"      station  - The forwarding station\n" | 
					
						
							|  |  |  | 	"      callerid - The callerID of the calling party that was forwarded\n" | 
					
						
							|  |  |  | 	"      type     - The call type.  The value here is the exact character\n" | 
					
						
							|  |  |  | 	"                 that came in on the SMDI link.  Typically, example values\n" | 
					
						
							|  |  |  | 	"                 are: D - Direct Calls, A - Forward All Calls,\n" | 
					
						
							|  |  |  | 	"                      B - Forward Busy Calls, N - Forward No Answer Calls\n" | 
					
						
							|  |  |  | 	"", | 
					
						
							|  |  |  | 	.read = smdi_msg_read, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | static int load_module(void) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	int res; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	/* initialize our containers */ | 
					
						
							|  |  |  | 	memset(&smdi_ifaces, 0, sizeof(smdi_ifaces)); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	ASTOBJ_CONTAINER_INIT(&smdi_ifaces); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	ast_mutex_init(&mwi_monitor.lock); | 
					
						
							|  |  |  | 	ast_cond_init(&mwi_monitor.cond, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_custom_function_register(&smdi_msg_retrieve_function); | 
					
						
							|  |  |  | 	ast_custom_function_register(&smdi_msg_function); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	/* load the config and start the listener threads*/ | 
					
						
							|  |  |  | 	res = smdi_load(0); | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	if (res < 0) { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 		return res; | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	} else if (res == 1) { | 
					
						
							| 
									
										
										
										
											2007-10-12 15:45:09 +00:00
										 |  |  | 		ast_log(LOG_WARNING, "No SMDI interfaces are available to listen on, not starting SMDI listener.\n"); | 
					
						
							|  |  |  | 		return AST_MODULE_LOAD_DECLINE; | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | static int unload_module(void) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	/* this destructor stops any running smdi_read threads */ | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces, ast_smdi_interface_destroy); | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 	destroy_all_mailbox_mappings(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_mutex_lock(&mwi_monitor.lock); | 
					
						
							|  |  |  | 	mwi_monitor.stop = 1; | 
					
						
							|  |  |  | 	ast_cond_signal(&mwi_monitor.cond); | 
					
						
							|  |  |  | 	ast_mutex_unlock(&mwi_monitor.lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-27 15:52:02 +00:00
										 |  |  | 	if (mwi_monitor.thread != AST_PTHREADT_NULL) { | 
					
						
							|  |  |  | 		pthread_join(mwi_monitor.thread, NULL); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-02-26 00:25:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ast_custom_function_unregister(&smdi_msg_retrieve_function); | 
					
						
							|  |  |  | 	ast_custom_function_unregister(&smdi_msg_function); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | static int reload(void) | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	int res; | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 	res = smdi_load(1); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	if (res < 0) { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 		return res; | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	} else if (res == 1) { | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | 		ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n"); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2006-02-10 23:55:47 +00:00
										 |  |  | 	} else | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2006-02-10 21:50:56 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Simplified Message Desk Interface (SMDI) Resource", | 
					
						
							|  |  |  | 		.load = load_module, | 
					
						
							|  |  |  | 		.unload = unload_module, | 
					
						
							|  |  |  | 		.reload = reload, | 
					
						
							|  |  |  | 	       ); |