Thu Apr 28 2011 17:16:19

Asterisk developer's documentation


res_smdi.c File Reference

SMDI support for Asterisk. More...

#include "asterisk.h"
#include <termios.h>
#include <sys/time.h>
#include <time.h>
#include <ctype.h>
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/utils.h"
#include "asterisk/smdi.h"
#include "asterisk/config.h"
#include "asterisk/astobj.h"
#include "asterisk/io.h"
#include "asterisk/stringfields.h"
#include "asterisk/linkedlists.h"
#include "asterisk/app.h"
#include "asterisk/pbx.h"
#include "asterisk/channel.h"
Include dependency graph for res_smdi.c:

Go to the source code of this file.

Data Structures

struct  ast_smdi_interface
struct  ast_smdi_interface_container
 SMDI interface container. More...
struct  ast_smdi_md_queue
 SMDI message desk message queue. More...
struct  ast_smdi_mwi_queue
 SMDI message waiting indicator message queue. More...
struct  mailbox_mapping
 A mapping between an SMDI mailbox ID and an Asterisk mailbox. More...
struct  smdi_msg_datastore

Defines

#define AST_API_MODULE
#define DEFAULT_POLLING_INTERVAL   10
#define SMDI_MSG_EXPIRY_TIME   30000
#define SMDI_RETRIEVE_TIMEOUT_DEFAULT   3000

Enumerations

enum  { OPT_SEARCH_TERMINAL = (1 << 0), OPT_SEARCH_NUMBER = (1 << 1) }
enum  smdi_message_type { SMDI_MWI, SMDI_MD }

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int _unload_module (int fromload)
static struct ast_smdi_interfacealloc_smdi_interface (void)
static void append_mailbox_mapping (struct ast_variable *var, struct ast_smdi_interface *iface)
static void ast_smdi_interface_destroy (struct ast_smdi_interface *iface)
struct ast_smdi_interfaceast_smdi_interface_find (const char *iface_name)
 Find an SMDI interface with the specified name.
void ast_smdi_interface_unref (struct ast_smdi_interface *iface)
void ast_smdi_md_message_destroy (struct ast_smdi_md_message *msg)
 ast_smdi_md_message destructor.
struct ast_smdi_md_messageast_smdi_md_message_pop (struct ast_smdi_interface *iface)
 Get the next SMDI message from the queue.
static void ast_smdi_md_message_push (struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
void ast_smdi_md_message_putback (struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
 Put an SMDI message back in the front of the queue.
struct ast_smdi_md_messageast_smdi_md_message_wait (struct ast_smdi_interface *iface, int timeout)
 Get the next SMDI message from the queue.
void ast_smdi_mwi_message_destroy (struct ast_smdi_mwi_message *msg)
 ast_smdi_mwi_message destructor.
struct ast_smdi_mwi_messageast_smdi_mwi_message_pop (struct ast_smdi_interface *iface)
 Get the next SMDI message from the queue.
static void ast_smdi_mwi_message_push (struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
void ast_smdi_mwi_message_putback (struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
 Put an SMDI message back in the front of the queue.
struct ast_smdi_mwi_messageast_smdi_mwi_message_wait (struct ast_smdi_interface *iface, int timeout)
 Get the next SMDI message from the queue.
struct ast_smdi_mwi_messageast_smdi_mwi_message_wait_station (struct ast_smdi_interface *iface, int timeout, const char *station)
int ast_smdi_mwi_set (struct ast_smdi_interface *iface, const char *mailbox)
 Set the MWI indicator for a mailbox.
int ast_smdi_mwi_unset (struct ast_smdi_interface *iface, const char *mailbox)
 Unset the MWI indicator for a mailbox.
static void destroy_all_mailbox_mappings (void)
static void destroy_mailbox_mapping (struct mailbox_mapping *mm)
static int load_module (void)
static int lock_msg_q (struct ast_smdi_interface *iface, enum smdi_message_type type)
static struct timeval msg_timestamp (void *msg, enum smdi_message_type type)
static void * mwi_monitor_handler (void *data)
static void poll_mailbox (struct mailbox_mapping *mm)
static void purge_old_messages (struct ast_smdi_interface *iface, enum smdi_message_type type)
static int reload (void)
static int smdi_load (int reload)
static void * smdi_message_wait (struct ast_smdi_interface *iface, int timeout, enum smdi_message_type type, const char *search_key, struct ast_flags options)
static void smdi_msg_datastore_destroy (void *data)
static void * smdi_msg_find (struct ast_smdi_interface *iface, enum smdi_message_type type, const char *search_key, struct ast_flags options)
static void * smdi_msg_pop (struct ast_smdi_interface *iface, enum smdi_message_type type)
static int smdi_msg_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int smdi_msg_retrieve_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static void * smdi_read (void *iface_p)
static int smdi_toggle_mwi (struct ast_smdi_interface *iface, const char *mailbox, int on)
static void * unlink_from_msg_q (struct ast_smdi_interface *iface, enum smdi_message_type type)
static int unload_module (void)
static int unlock_msg_q (struct ast_smdi_interface *iface, enum smdi_message_type type)
static void unref_msg (void *msg, enum smdi_message_type type)

Variables

static struct ast_module_info
__MODULE_INFO_SECTION 
__mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS , .description = "Simplified Message Desk Interface (SMDI) Resource" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, }
static struct ast_module_infoast_module_info = &__mod_info
static const char config_file [] = "smdi.conf"
struct {
   ast_cond_t   cond
   struct timeval   last_poll
   ast_mutex_t   lock
   struct {
      struct mailbox_mapping *   first
      struct mailbox_mapping *   last
   }   mailbox_mappings
   unsigned int   polling_interval
   unsigned int   stop:1
   pthread_t   thread
mwi_monitor
struct ast_smdi_interface_container smdi_ifaces
static int smdi_loaded
static struct ast_datastore_info smdi_msg_datastore_info
static struct ast_custom_function smdi_msg_function
static int smdi_msg_id
static struct ast_app_option smdi_msg_ret_options [128] = { [ 't' ] = { .flag = OPT_SEARCH_TERMINAL }, [ 'n' ] = { .flag = OPT_SEARCH_NUMBER }, }
static struct ast_custom_function smdi_msg_retrieve_function

Detailed Description

SMDI support for Asterisk.

Author:
Matthew A. Nicholson <mnicholson@digium.com>
Russell Bryant <russell@digium.com>

Here is a useful mailing list post that describes SMDI protocol details: http://lists.digium.com/pipermail/asterisk-dev/2003-June/000884.html

Todo:
This module currently has its own mailbox monitoring thread. This should be converted to MWI subscriptions and just let the optional global voicemail polling thread handle it.

Definition in file res_smdi.c.


Define Documentation

#define AST_API_MODULE

Definition at line 46 of file res_smdi.c.

#define DEFAULT_POLLING_INTERVAL   10

10 seconds

Definition at line 113 of file res_smdi.c.

Referenced by smdi_load().

#define SMDI_MSG_EXPIRY_TIME   30000

Definition at line 58 of file res_smdi.c.

Referenced by smdi_load().

#define SMDI_RETRIEVE_TIMEOUT_DEFAULT   3000

In milliseconds

Definition at line 1116 of file res_smdi.c.

Referenced by smdi_msg_retrieve_read().


Enumeration Type Documentation

anonymous enum
Enumerator:
OPT_SEARCH_TERMINAL 
OPT_SEARCH_NUMBER 

Definition at line 371 of file res_smdi.c.

     {
   OPT_SEARCH_TERMINAL = (1 << 0),
   OPT_SEARCH_NUMBER   = (1 << 1),
};
Enumerator:
SMDI_MWI 
SMDI_MD 

Definition at line 247 of file res_smdi.c.

                       {
   SMDI_MWI,
   SMDI_MD,
};

Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 1424 of file res_smdi.c.

static void __unreg_module ( void  ) [static]

Definition at line 1424 of file res_smdi.c.

static struct ast_smdi_interface* alloc_smdi_interface ( void  ) [static, read]
static void append_mailbox_mapping ( struct ast_variable var,
struct ast_smdi_interface iface 
) [static]

Definition at line 753 of file res_smdi.c.

References ast_calloc, AST_LIST_INSERT_TAIL, ast_mutex_lock(), ast_mutex_unlock(), ast_strdupa, ast_string_field_init, ast_string_field_set, ast_strlen_zero(), ASTOBJ_REF, context, free, mailbox_mapping::iface, mailbox, mwi_monitor, ast_variable::name, mailbox_mapping::smdi, strsep(), and ast_variable::value.

Referenced by smdi_load().

{
   struct mailbox_mapping *mm;
   char *mailbox, *context;

   if (!(mm = ast_calloc(1, sizeof(*mm))))
      return;
   
   if (ast_string_field_init(mm, 32)) {
      free(mm);
      return;
   }

   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);
}
struct ast_smdi_interface* ast_smdi_interface_find ( const char *  iface_name) [read]

Find an SMDI interface with the specified name.

Parameters:
iface_namethe name/port of the interface to search for.
Returns:
a pointer to the interface located or NULL if none was found. This actually returns an ASTOBJ reference and should be released using ASTOBJ_UNREF(iface, ast_smdi_interface_destroy).

Definition at line 529 of file res_smdi.c.

References ASTOBJ_CONTAINER_FIND, and smdi_ifaces.

Referenced by load_config(), mkintf(), and smdi_msg_retrieve_read().

{
   return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name));
}
void ast_smdi_interface_unref ( struct ast_smdi_interface iface)

Definition at line 161 of file res_smdi.c.

References ast_smdi_interface_destroy(), and ASTOBJ_UNREF.

Referenced by destroy_dahdi_pvt().

void ast_smdi_md_message_destroy ( struct ast_smdi_md_message msg)
struct ast_smdi_md_message* ast_smdi_md_message_pop ( struct ast_smdi_interface iface) [read]

Get the next SMDI message from the queue.

Parameters:
ifacea pointer to the interface to use.

This function pulls the first unexpired message from the SMDI message queue on the specified interface. It will purge all expired SMDI messages before returning.

Returns:
the next SMDI message, or NULL if there were no pending messages.

Definition at line 500 of file res_smdi.c.

References SMDI_MD, and smdi_msg_pop().

{
   return smdi_msg_pop(iface, SMDI_MD);
}
static void ast_smdi_md_message_push ( struct ast_smdi_interface iface,
struct ast_smdi_md_message md_msg 
) [static]
void ast_smdi_md_message_putback ( struct ast_smdi_interface iface,
struct ast_smdi_md_message msg 
)

Put an SMDI message back in the front of the queue.

Parameters:
ifacea pointer to the interface to use.
md_msga pointer to the message to use.

This function puts a message back in the front of the specified queue. It should be used if a message was popped but is not going to be processed for some reason, and the message needs to be returned to the queue.

Definition at line 231 of file res_smdi.c.

References ast_cond_broadcast(), ast_mutex_lock(), ast_mutex_unlock(), ASTOBJ_CONTAINER_LINK_START, ast_smdi_interface::md_q, ast_smdi_interface::md_q_cond, and ast_smdi_interface::md_q_lock.

struct ast_smdi_md_message* ast_smdi_md_message_wait ( struct ast_smdi_interface iface,
int  timeout 
) [read]

Get the next SMDI message from the queue.

Parameters:
ifacea pointer to the interface to use.
timeoutthe time to wait before returning in milliseconds.

This function pulls a message from the SMDI message queue on the specified interface. If no message is available this function will wait the specified amount of time before returning.

Returns:
the next SMDI message, or NULL if there were no pending messages and the timeout has expired.

Definition at line 505 of file res_smdi.c.

References SMDI_MD, and smdi_message_wait().

Referenced by ss_thread().

{
   struct ast_flags options = { 0 };
   return smdi_message_wait(iface, timeout, SMDI_MD, NULL, options);
}
void ast_smdi_mwi_message_destroy ( struct ast_smdi_mwi_message msg)

ast_smdi_mwi_message destructor.

Definition at line 731 of file res_smdi.c.

References ast_free.

Referenced by ast_smdi_interface_destroy(), run_externnotify(), smdi_read(), and unref_msg().

{
   ast_free(msg);
}
struct ast_smdi_mwi_message* ast_smdi_mwi_message_pop ( struct ast_smdi_interface iface) [read]

Get the next SMDI message from the queue.

Parameters:
ifacea pointer to the interface to use.

This function pulls the first unexpired message from the SMDI message queue on the specified interface. It will purge all expired SMDI messages before returning.

Returns:
the next SMDI message, or NULL if there were no pending messages.

Definition at line 511 of file res_smdi.c.

References smdi_msg_pop(), and SMDI_MWI.

{
   return smdi_msg_pop(iface, SMDI_MWI);
}
void ast_smdi_mwi_message_putback ( struct ast_smdi_interface iface,
struct ast_smdi_mwi_message msg 
)

Put an SMDI message back in the front of the queue.

Parameters:
ifacea pointer to the interface to use.
mwi_msga pointer to the message to use.

This function puts a message back in the front of the specified queue. It should be used if a message was popped but is not going to be processed for some reason, and the message needs to be returned to the queue.

Definition at line 239 of file res_smdi.c.

References ast_cond_broadcast(), ast_mutex_lock(), ast_mutex_unlock(), ASTOBJ_CONTAINER_LINK_START, ast_smdi_interface::mwi_q, ast_smdi_interface::mwi_q_cond, and ast_smdi_interface::mwi_q_lock.

struct ast_smdi_mwi_message* ast_smdi_mwi_message_wait ( struct ast_smdi_interface iface,
int  timeout 
) [read]

Get the next SMDI message from the queue.

Parameters:
ifacea pointer to the interface to use.
timeoutthe time to wait before returning in milliseconds.

This function pulls a message from the SMDI message queue on the specified interface. If no message is available this function will wait the specified amount of time before returning.

Returns:
the next SMDI message, or NULL if there were no pending messages and the timeout has expired.

Definition at line 516 of file res_smdi.c.

References smdi_message_wait(), and SMDI_MWI.

{
   struct ast_flags options = { 0 };
   return smdi_message_wait(iface, timeout, SMDI_MWI, NULL, options);
}
struct ast_smdi_mwi_message* ast_smdi_mwi_message_wait_station ( struct ast_smdi_interface iface,
int  timeout,
const char *  station 
) [read]

Definition at line 522 of file res_smdi.c.

References smdi_message_wait(), and SMDI_MWI.

Referenced by run_externnotify().

{
   struct ast_flags options = { 0 };
   return smdi_message_wait(iface, timeout, SMDI_MWI, station, options);
}
int ast_smdi_mwi_set ( struct ast_smdi_interface iface,
const char *  mailbox 
)

Set the MWI indicator for a mailbox.

Parameters:
ifacethe interface to use.
mailboxthe mailbox to use.

Definition at line 221 of file res_smdi.c.

References smdi_toggle_mwi().

Referenced by poll_mailbox(), and run_externnotify().

{
   return smdi_toggle_mwi(iface, mailbox, 1);
}
int ast_smdi_mwi_unset ( struct ast_smdi_interface iface,
const char *  mailbox 
)

Unset the MWI indicator for a mailbox.

Parameters:
ifacethe interface to use.
mailboxthe mailbox to use.

Definition at line 226 of file res_smdi.c.

References smdi_toggle_mwi().

Referenced by poll_mailbox(), and run_externnotify().

{
   return smdi_toggle_mwi(iface, mailbox, 0);
}
static void destroy_all_mailbox_mappings ( void  ) [static]
static void destroy_mailbox_mapping ( struct mailbox_mapping mm) [static]
static int load_module ( void  ) [static]

Definition at line 1341 of file res_smdi.c.

References _unload_module(), ast_cond_init(), ast_custom_function_register, ast_log(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_mutex_init(), ASTOBJ_CONTAINER_INIT, LOG_NOTICE, mwi_monitor, smdi_ifaces, and smdi_load().

{
   int res;
   smdi_loaded = 1;

   /* initialize our containers */
   memset(&smdi_ifaces, 0, sizeof(smdi_ifaces));
   ASTOBJ_CONTAINER_INIT(&smdi_ifaces);

   ast_mutex_init(&mwi_monitor.lock);
   ast_cond_init(&mwi_monitor.cond, NULL);

   /* load the config and start the listener threads*/
   res = smdi_load(0);
   if (res < 0) {
      _unload_module(1);
      return res;
   } else if (res == 1) {
      _unload_module(1);
      ast_log(LOG_NOTICE, "No SMDI interfaces are available to listen on, not starting SMDI listener.\n");
      return AST_MODULE_LOAD_DECLINE;
   }

   ast_custom_function_register(&smdi_msg_retrieve_function);
   ast_custom_function_register(&smdi_msg_function);

   return AST_MODULE_LOAD_SUCCESS;
}
static int lock_msg_q ( struct ast_smdi_interface iface,
enum smdi_message_type  type 
) [inline, static]

Definition at line 252 of file res_smdi.c.

References ast_mutex_lock(), ast_smdi_interface::md_q_lock, ast_smdi_interface::mwi_q_lock, SMDI_MD, and SMDI_MWI.

Referenced by purge_old_messages(), smdi_message_wait(), and smdi_msg_pop().

{
   switch (type) {
   case SMDI_MWI:
      return ast_mutex_lock(&iface->mwi_q_lock);
   case SMDI_MD:  
      return ast_mutex_lock(&iface->md_q_lock);
   }
   
   return -1;
}
static struct timeval msg_timestamp ( void *  msg,
enum smdi_message_type  type 
) [static, read]

Definition at line 287 of file res_smdi.c.

References ast_tv(), msg, SMDI_MD, SMDI_MWI, ast_smdi_md_message::timestamp, ast_smdi_mwi_message::timestamp, and type.

Referenced by purge_old_messages().

{
   struct ast_smdi_md_message *md_msg = msg;
   struct ast_smdi_mwi_message *mwi_msg = msg;

   switch (type) {
   case SMDI_MWI:
      return mwi_msg->timestamp;
   case SMDI_MD:
      return md_msg->timestamp;
   }

   return ast_tv(0, 0);
}
static void* mwi_monitor_handler ( void *  data) [static]

Definition at line 805 of file res_smdi.c.

References ast_cond_timedwait(), AST_LIST_TRAVERSE, ast_mutex_lock(), ast_mutex_unlock(), ast_tv(), ast_tvadd(), ast_tvnow(), mwi_monitor, and poll_mailbox().

Referenced by smdi_load().

{
   while (!mwi_monitor.stop) {
      struct timespec ts = { 0, };
      struct timeval polltime;
      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. */
      polltime = ast_tvadd(mwi_monitor.last_poll, ast_tv(mwi_monitor.polling_interval, 0));
      ts.tv_sec = polltime.tv_sec;
      ts.tv_nsec = polltime.tv_usec * 1000;
      ast_cond_timedwait(&mwi_monitor.cond, &mwi_monitor.lock, &ts);

      ast_mutex_unlock(&mwi_monitor.lock);
   }

   return NULL;
}
static void poll_mailbox ( struct mailbox_mapping mm) [static]
Note:
Called with the mwi_monitor.lock locked

Definition at line 786 of file res_smdi.c.

References ast_app_has_voicemail(), ast_smdi_mwi_set(), ast_smdi_mwi_unset(), buf, mailbox_mapping::context, mailbox_mapping::cur_state, mailbox_mapping::iface, mailbox_mapping::mailbox, and mailbox_mapping::smdi.

Referenced by mwi_monitor_handler().

{
   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 purge_old_messages ( struct ast_smdi_interface iface,
enum smdi_message_type  type 
) [static]

Definition at line 317 of file res_smdi.c.

References ast_log(), ast_smdi_md_message_push(), ast_smdi_mwi_message_push(), ast_tvdiff_ms(), ast_tvnow(), lock_msg_q(), LOG_NOTICE, msg, ast_smdi_interface::msg_expiry, msg_timestamp(), ast_smdi_interface::name, SMDI_MD, SMDI_MWI, unlink_from_msg_q(), unlock_msg_q(), and unref_msg().

Referenced by smdi_msg_find(), and smdi_msg_pop().

{
   struct timeval now = ast_tvnow();
   long elapsed = 0;
   void *msg;
   
   lock_msg_q(iface, type);
   msg = unlink_from_msg_q(iface, type);
   unlock_msg_q(iface, type);

   /* purge old messages */
   while (msg) {
      elapsed = ast_tvdiff_ms(now, msg_timestamp(msg, type));

      if (elapsed > iface->msg_expiry) {
         /* found an expired message */
         unref_msg(msg, type);
         ast_log(LOG_NOTICE, "Purged expired message from %s SMDI %s message queue.  "
            "Message was %ld milliseconds too old.\n",
            iface->name, (type == SMDI_MD) ? "MD" : "MWI", 
            elapsed - iface->msg_expiry);

         lock_msg_q(iface, type);
         msg = unlink_from_msg_q(iface, type);
         unlock_msg_q(iface, type);
      } else {
         /* good message, put it back and return */
         switch (type) {
         case SMDI_MD:
            ast_smdi_md_message_push(iface, msg);
            break;
         case SMDI_MWI:
            ast_smdi_mwi_message_push(iface, msg);
            break;
         }
         unref_msg(msg, type);
         break;
      }
   }
}
static int reload ( void  ) [static]

Definition at line 1405 of file res_smdi.c.

References ast_log(), LOG_WARNING, and smdi_load().

{
   int res;

   res = smdi_load(1);

   if (res < 0) {
      return res;
   } else if (res == 1) {
      ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n");
      return 0;
   } else
      return 0;
}
static int smdi_load ( int  reload) [static]

Definition at line 862 of file res_smdi.c.

References alloc_smdi_interface(), append_mailbox_mapping(), ast_config_destroy(), ast_config_load, ast_copy_string(), AST_LIST_EMPTY, ast_log(), AST_MODULE_LOAD_FAILURE, ast_module_ref(), ast_pthread_create_background, AST_PTHREADT_NULL, ast_smdi_interface_destroy(), ast_true(), ast_variable_browse(), ast_verb, ASTOBJ_CONTAINER_FIND, ASTOBJ_CONTAINER_LINK, ASTOBJ_CONTAINER_MARKALL, ASTOBJ_CONTAINER_PRUNE_MARKED, ASTOBJ_CONTAINER_RDLOCK, ASTOBJ_CONTAINER_UNLOCK, ASTOBJ_UNMARK, ASTOBJ_UNREF, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, DEFAULT_POLLING_INTERVAL, destroy_all_mailbox_mappings(), errno, ast_smdi_interface::fd, ast_smdi_interface::file, ast_variable::lineno, LOG_ERROR, LOG_NOTICE, ast_smdi_interface::mode, ast_smdi_interface::msdstrip, ast_smdi_interface::msg_expiry, mwi_monitor, mwi_monitor_handler(), ast_smdi_interface::name, ast_variable::name, ast_variable::next, ast_module_info::self, smdi_ifaces, SMDI_MSG_EXPIRY_TIME, smdi_read(), ast_smdi_interface::thread, and ast_variable::value.

Referenced by load_module(), and reload().

{
   struct ast_config *conf;
   struct ast_variable *v;
   struct ast_smdi_interface *iface = NULL;
   struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
   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;

   if (!(conf = ast_config_load(config_file, config_flags)) || conf == CONFIG_STATUS_FILEINVALID) {
      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;
   } else if (conf == CONFIG_STATUS_FILEUNCHANGED)
      return 0;

   /* Mark all interfaces that we are listening on.  We will unmark them
    * as we find them in the config file, this way we know any interfaces
    * still marked after we have finished parsing the config file should
    * be stopped.
    */
   if (reload)
      ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces);

   for (v = ast_variable_browse(conf, "interfaces"); v; v = v->next) {
      if (!strcasecmp(v->name, "baudrate")) {
         if (!strcasecmp(v->value, "9600"))
            baud_rate = B9600;
         else if (!strcasecmp(v->value, "4800"))
            baud_rate = B4800;
         else if (!strcasecmp(v->value, "2400"))
            baud_rate = B2400;
         else if (!strcasecmp(v->value, "1200"))
            baud_rate = B1200;
         else {
            ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno);
            baud_rate = B9600;
         }
      } else if (!strcasecmp(v->name, "msdstrip")) {
         if (!sscanf(v->value, "%30d", &msdstrip)) {
            ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
            msdstrip = 0;
         } else if (0 > msdstrip || msdstrip > 9) {
            ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
            msdstrip = 0;
         }
      } else if (!strcasecmp(v->name, "msgexpirytime")) {
         if (!sscanf(v->value, "%30ld", &msg_expiry)) {
            ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno);
            msg_expiry = SMDI_MSG_EXPIRY_TIME;
         }
      } else if (!strcasecmp(v->name, "paritybit")) {
         if (!strcasecmp(v->value, "even"))
            paritybit = PARENB;
         else if (!strcasecmp(v->value, "odd"))
            paritybit = PARENB | PARODD;
         else if (!strcasecmp(v->value, "none"))
            paritybit = ~PARENB;
         else {
            ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno);
            paritybit = PARENB;
         }
      } else if (!strcasecmp(v->name, "charsize")) {
         if (!strcasecmp(v->value, "7"))
            charsize = CS7;
         else if (!strcasecmp(v->value, "8"))
            charsize = CS8;
         else {
            ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno);
            charsize = CS7;
         }
      } else if (!strcasecmp(v->name, "twostopbits")) {
         stopbits = ast_true(v->name);
      } else if (!strcasecmp(v->name, "smdiport")) {
         if (reload) {
            /* we are reloading, check if we are already
             * monitoring this interface, if we are we do
             * not want to start it again.  This also has
             * the side effect of not updating different
             * setting for the serial port, but it should
             * be trivial to rewrite this section so that
             * options on the port are changed without
             * restarting the interface.  Or the interface
             * could be restarted with out emptying the
             * queue. */
            if ((iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
               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;
            }
         }
         
         if (!(iface = alloc_smdi_interface()))
            continue;

         ast_copy_string(iface->name, v->value, sizeof(iface->name));

         iface->thread = AST_PTHREADT_NULL;

         if (!(iface->file = fopen(iface->name, "r"))) {
            ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s)\n", iface->name, strerror(errno));
            ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
            continue;
         }

         iface->fd = fileno(iface->file);

         /* Set the proper attributes for our serial port. */

         /* get the current attributes from the port */
         if (tcgetattr(iface->fd, &iface->mode)) {
            ast_log(LOG_ERROR, "Error getting atributes of %s (%s)\n", iface->name, strerror(errno));
            ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
            continue;
         }

         /* set the desired speed */
         if (cfsetispeed(&iface->mode, baud_rate) || cfsetospeed(&iface->mode, baud_rate)) {
            ast_log(LOG_ERROR, "Error setting baud rate on %s (%s)\n", iface->name, strerror(errno));
            ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
            continue;
         }
         
         /* set the stop bits */
         if (stopbits)
            iface->mode.c_cflag = iface->mode.c_cflag | CSTOPB;   /* set two stop bits */
         else
            iface->mode.c_cflag = iface->mode.c_cflag & ~CSTOPB;  /* set one stop bit */
         
         /* set the parity */
         iface->mode.c_cflag = (iface->mode.c_cflag & ~PARENB & ~PARODD) | paritybit;
         
         /* set the character size */
         iface->mode.c_cflag = (iface->mode.c_cflag & ~CSIZE) | charsize;
         
         /* commit the desired attributes */
         if (tcsetattr(iface->fd, TCSAFLUSH, &iface->mode)) {
            ast_log(LOG_ERROR, "Error setting attributes on %s (%s)\n", iface->name, strerror(errno));
            ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
            continue;
         }

         /* set the msdstrip */
         iface->msdstrip = msdstrip;

         /* set the message expiry time */
         iface->msg_expiry = msg_expiry;

         /* start the listener thread */
         ast_verb(3, "Starting SMDI monitor thread for %s\n", iface->name);
         if (ast_pthread_create_background(&iface->thread, NULL, smdi_read, iface)) {
            ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name);
            ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
            continue;
         }

         ASTOBJ_CONTAINER_LINK(&smdi_ifaces, iface);
         ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
         ast_module_ref(ast_module_info->self);
      } else {
         ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file);
      }
   }

   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", v->value);
            continue;
         }
      } else if (!strcasecmp(v->name, "pollinginterval")) {
         if (sscanf(v->value, "%30u", &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);

   ast_config_destroy(conf);
   
   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;
   }

   /* Prune any interfaces we should no longer monitor. */
   if (reload)
      ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces, ast_smdi_interface_destroy);
   
   ASTOBJ_CONTAINER_RDLOCK(&smdi_ifaces);
   /* TODO: this is bad, we need an ASTOBJ method for this! */
   if (!smdi_ifaces.head)
      res = 1;
   ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces);
   
   return res;
}
static void* smdi_message_wait ( struct ast_smdi_interface iface,
int  timeout,
enum smdi_message_type  type,
const char *  search_key,
struct ast_flags  options 
) [static]

Definition at line 444 of file res_smdi.c.

References ast_cond_timedwait(), ast_tv(), ast_tvadd(), ast_tvdiff_ms(), ast_tvnow(), cond, lock, lock_msg_q(), ast_smdi_interface::md_q_cond, ast_smdi_interface::md_q_lock, msg, ast_smdi_interface::mwi_q_cond, ast_smdi_interface::mwi_q_lock, SMDI_MD, smdi_msg_find(), SMDI_MWI, and unlock_msg_q().

Referenced by ast_smdi_md_message_wait(), ast_smdi_mwi_message_wait(), ast_smdi_mwi_message_wait_station(), and smdi_msg_retrieve_read().

{
   struct timeval start;
   long diff = 0;
   void *msg;
   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;
   }

   start = ast_tvnow();

   while (diff < timeout) {
      struct timespec ts = { 0, };
      struct timeval wait;

      lock_msg_q(iface, type);

      if ((msg = smdi_msg_find(iface, type, search_key, options))) {
         unlock_msg_q(iface, type);
         return msg;
      }

      wait = ast_tvadd(start, ast_tv(0, timeout));
      ts.tv_sec = wait.tv_sec;
      ts.tv_nsec = wait.tv_usec * 1000;

      /* If there were no messages in the queue, then go to sleep until one
       * arrives. */

      ast_cond_timedwait(cond, lock, &ts);

      if ((msg = smdi_msg_find(iface, type, search_key, options))) {
         unlock_msg_q(iface, type);
         return msg;
      }

      unlock_msg_q(iface, type);

      /* check timeout */
      diff = ast_tvdiff_ms(ast_tvnow(), start);
   }

   return NULL;
}
static void smdi_msg_datastore_destroy ( void *  data) [static]
static void* smdi_msg_find ( struct ast_smdi_interface iface,
enum smdi_message_type  type,
const char *  search_key,
struct ast_flags  options 
) [static]

Definition at line 376 of file res_smdi.c.

References ast_strlen_zero(), ast_test_flag, ASTOBJ_CONTAINER_TRAVERSE, ASTOBJ_REF, ast_smdi_interface::md_q, msg, OPT_SEARCH_TERMINAL, purge_old_messages(), and SMDI_MD.

Referenced by smdi_message_wait().

{
   void *msg = NULL;

   purge_old_messages(iface, type);

   switch (type) {
   case SMDI_MD:
      if (ast_strlen_zero(search_key)) {
         struct ast_smdi_md_message *md_msg = NULL;

         /* No search key provided (the code from chan_dahdi does this).
          * Just pop the top message off of the queue. */

         ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do {
            md_msg = ASTOBJ_REF(iterator);
         } while (0); );

         msg = md_msg;
      } else 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);
      }
      break;
   case SMDI_MWI:
      if (ast_strlen_zero(search_key)) {
         struct ast_smdi_mwi_message *mwi_msg = NULL;

         /* No search key provided (the code from chan_dahdi does this).
          * Just pop the top message off of the queue. */

         ASTOBJ_CONTAINER_TRAVERSE(&iface->mwi_q, !mwi_msg, do {
            mwi_msg = ASTOBJ_REF(iterator);
         } while (0); );

         msg = mwi_msg;
      } else {
         msg = ASTOBJ_CONTAINER_FIND(&iface->mwi_q, search_key);
      }
      break;
   }

   return msg;
}
static void* smdi_msg_pop ( struct ast_smdi_interface iface,
enum smdi_message_type  type 
) [static]

Definition at line 358 of file res_smdi.c.

References lock_msg_q(), msg, purge_old_messages(), unlink_from_msg_q(), and unlock_msg_q().

Referenced by ast_smdi_md_message_pop(), and ast_smdi_mwi_message_pop().

{
   void *msg;

   purge_old_messages(iface, type);

   lock_msg_q(iface, type);
   msg = unlink_from_msg_q(iface, type);
   unlock_msg_q(iface, type);

   return msg;
}
static int smdi_msg_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 1222 of file res_smdi.c.

References AST_APP_ARG, ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_log(), ast_module_user_add, ast_module_user_remove, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_smdi_md_message::calling_st, ast_datastore::data, ast_smdi_md_message::fwd_st, LOG_ERROR, LOG_WARNING, smdi_msg_datastore::md_msg, ast_smdi_md_message::mesg_desk_num, ast_smdi_md_message::mesg_desk_term, parse(), and ast_smdi_md_message::type.

{
   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;

   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")) {
      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 res;
}
static int smdi_msg_retrieve_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 1123 of file res_smdi.c.

References AST_APP_ARG, ast_app_parse_options(), ast_atomic_fetchadd_int(), ast_autoservice_start(), ast_autoservice_stop(), ast_calloc, ast_channel_datastore_add(), ast_channel_lock, ast_channel_unlock, ast_datastore_alloc(), AST_DECLARE_APP_ARGS, ast_log(), ast_module_user_add, ast_module_user_remove, ast_smdi_interface_destroy(), ast_smdi_interface_find(), ast_smdi_md_message_destroy(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ASTOBJ_REF, ASTOBJ_UNREF, ast_datastore::data, smdi_msg_datastore::id, smdi_msg_datastore::iface, LOG_ERROR, LOG_WARNING, smdi_msg_datastore::md_msg, parse(), SMDI_MD, smdi_message_wait(), smdi_msg_datastore_destroy(), smdi_msg_ret_options, and SMDI_RETRIEVE_TIMEOUT_DEFAULT.

{
   struct ast_module_user *u;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(port);
      AST_APP_ARG(search_key);
      AST_APP_ARG(timeout);
      AST_APP_ARG(options);
   );
   struct ast_flags options = { 0 };
   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);

   if (ast_strlen_zero(args.port) || ast_strlen_zero(args.search_key)) {
      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;
   }

   if (!ast_strlen_zero(args.options)) {
      ast_app_parse_options(smdi_msg_ret_options, &options, NULL, args.options);
   }

   if (!ast_strlen_zero(args.timeout)) {
      if (sscanf(args.timeout, "%30u", &timeout) != 1) {
         ast_log(LOG_ERROR, "'%s' is not a valid timeout\n", args.timeout);
         timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT;
      }
   }

   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);
      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_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 void* smdi_read ( void *  iface_p) [static]

Definition at line 543 of file res_smdi.c.

References ast_calloc, ast_copy_string(), ast_log(), ast_smdi_interface_destroy(), ast_smdi_md_message_destroy(), ast_smdi_md_message_push(), ast_smdi_mwi_message_destroy(), ast_smdi_mwi_message_push(), ast_tvnow(), ASTOBJ_INIT, ASTOBJ_UNREF, ast_smdi_md_message::calling_st, ast_smdi_mwi_message::cause, ast_smdi_interface::file, ast_smdi_mwi_message::fwd_st, ast_smdi_md_message::fwd_st, LOG_DEBUG, LOG_ERROR, ast_smdi_md_message::mesg_desk_num, ast_smdi_md_message::mesg_desk_term, ast_smdi_interface::msdstrip, ast_smdi_mwi_message::name, ast_smdi_interface::name, ast_smdi_md_message::name, ast_smdi_mwi_message::timestamp, ast_smdi_md_message::timestamp, and ast_smdi_md_message::type.

Referenced by smdi_load().

{
   struct ast_smdi_interface *iface = iface_p;
   struct ast_smdi_md_message *md_msg;
   struct ast_smdi_mwi_message *mwi_msg;
   char c = '\0';
   char *cp = NULL;
   int i;
   int start = 0;
      
   /* read an smdi message */
   while ((c = fgetc(iface->file))) {

      /* check if this is the start of a message */
      if (!start) {
         if (c == 'M') {
            ast_log(LOG_DEBUG, "Read an 'M' to start an SMDI message\n");
            start = 1;
         }
         continue;
      }
      
      if (c == 'D') { /* MD message */
         start = 0;

         ast_log(LOG_DEBUG, "Read a 'D' ... it's an MD message.\n");

         if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
            ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
            return NULL;
         }
         
         ASTOBJ_INIT(md_msg);

         /* read the message desk number */
         for (i = 0; i < sizeof(md_msg->mesg_desk_num) - 1; i++) {
            md_msg->mesg_desk_num[i] = fgetc(iface->file);
            ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_num[i]);
         }

         md_msg->mesg_desk_num[sizeof(md_msg->mesg_desk_num) - 1] = '\0';
         
         ast_log(LOG_DEBUG, "The message desk number is '%s'\n", md_msg->mesg_desk_num);

         /* read the message desk terminal number */
         for (i = 0; i < sizeof(md_msg->mesg_desk_term) - 1; i++) {
            md_msg->mesg_desk_term[i] = fgetc(iface->file);
            ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_term[i]);
         }

         md_msg->mesg_desk_term[sizeof(md_msg->mesg_desk_term) - 1] = '\0';

         ast_log(LOG_DEBUG, "The message desk terminal is '%s'\n", md_msg->mesg_desk_term);

         /* read the message type */
         md_msg->type = fgetc(iface->file);
       
         ast_log(LOG_DEBUG, "Message type is '%c'\n", md_msg->type);

         /* read the forwarding station number (may be blank) */
         cp = &md_msg->fwd_st[0];
         for (i = 0; i < sizeof(md_msg->fwd_st) - 1; i++) {
            if ((c = fgetc(iface->file)) == ' ') {
               *cp = '\0';
               ast_log(LOG_DEBUG, "Read a space, done looking for the forwarding station\n");
               break;
            }

            /* store c in md_msg->fwd_st */
            if (i >= iface->msdstrip) {
               ast_log(LOG_DEBUG, "Read a '%c' and stored it in the forwarding station buffer\n", c);
               *cp++ = c;
            } else {
               ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the fwd station buffer, because of the msdstrip setting (%d < %d)\n", c, i, iface->msdstrip);
            }
         }

         /* make sure the value is null terminated, even if this truncates it */
         md_msg->fwd_st[sizeof(md_msg->fwd_st) - 1] = '\0';
         cp = NULL;

         ast_log(LOG_DEBUG, "The forwarding station is '%s'\n", md_msg->fwd_st);

         /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
          * up a message on this field */
         ast_copy_string(md_msg->name, md_msg->fwd_st, sizeof(md_msg->name));

         /* read the calling station number (may be blank) */
         cp = &md_msg->calling_st[0];
         for (i = 0; i < sizeof(md_msg->calling_st) - 1; i++) {
            if (!isdigit((c = fgetc(iface->file)))) {
               *cp = '\0';
               ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the calling station buffer because it's not a digit\n", c);
               if (c == ' ') {
                  /* Don't break on a space.  We may read the space before the calling station
                   * here if the forwarding station buffer filled up. */
                  i--; /* We're still on the same character */
                  continue;
               }
               break;
            }

            /* store c in md_msg->calling_st */
            if (i >= iface->msdstrip) {
               ast_log(LOG_DEBUG, "Read a '%c' and stored it in the calling station buffer\n", c);
               *cp++ = c;
            } else {
               ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the calling station buffer, because of the msdstrip setting (%d < %d)\n", c, i, iface->msdstrip);
            }
         }

         /* make sure the value is null terminated, even if this truncates it */
         md_msg->calling_st[sizeof(md_msg->calling_st) - 1] = '\0';
         cp = NULL;

         ast_log(LOG_DEBUG, "The calling station is '%s'\n", md_msg->calling_st);

         /* add the message to the message queue */
         md_msg->timestamp = ast_tvnow();
         ast_smdi_md_message_push(iface, md_msg);
         ast_log(LOG_DEBUG, "Received SMDI MD message on %s\n", iface->name);
         
         ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);

      } else if (c == 'W') { /* MWI message */
         start = 0;

         ast_log(LOG_DEBUG, "Read a 'W', it's an MWI message. (No more debug coming for MWI messages)\n");

         if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
            ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
            return NULL;
         }

         ASTOBJ_INIT(mwi_msg);

         /* discard the 'I' (from 'MWI') */
         fgetc(iface->file);
         
         /* read the forwarding station number (may be blank) */
         cp = &mwi_msg->fwd_st[0];
         for (i = 0; i < sizeof(mwi_msg->fwd_st) - 1; i++) {
            if ((c = fgetc(iface->file)) == ' ') {
               *cp = '\0';
               break;
            }

            /* store c in md_msg->fwd_st */
            if (i >= iface->msdstrip)
               *cp++ = c;
         }

         /* make sure the station number is null terminated, even if this will truncate it */
         mwi_msg->fwd_st[sizeof(mwi_msg->fwd_st) - 1] = '\0';
         cp = NULL;
         
         /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
          * up a message on this field */
         ast_copy_string(mwi_msg->name, mwi_msg->fwd_st, sizeof(mwi_msg->name));

         /* read the mwi failure cause */
         for (i = 0; i < sizeof(mwi_msg->cause) - 1; i++)
            mwi_msg->cause[i] = fgetc(iface->file);

         mwi_msg->cause[sizeof(mwi_msg->cause) - 1] = '\0';

         /* add the message to the message queue */
         mwi_msg->timestamp = ast_tvnow();
         ast_smdi_mwi_message_push(iface, mwi_msg);
         ast_log(LOG_DEBUG, "Received 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 received on %s (M%c).\n", iface->name, c);
         start = 0;
      }
   }

   ast_log(LOG_ERROR, "Error reading from SMDI interface %s, stopping listener thread\n", iface->name);
   ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
   return NULL;
}
static int smdi_toggle_mwi ( struct ast_smdi_interface iface,
const char *  mailbox,
int  on 
) [static]

Definition at line 194 of file res_smdi.c.

References ast_debug, ast_log(), ASTOBJ_UNLOCK, ASTOBJ_WRLOCK, errno, LOG_ERROR, ast_smdi_interface::msdstrip, and ast_smdi_interface::name.

Referenced by ast_smdi_mwi_set(), and ast_smdi_mwi_unset().

{
   FILE *file;
   int i;
   
   if (!(file = fopen(iface->name, "w"))) {
      ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
      return 1;
   }  
   
   ASTOBJ_WRLOCK(iface);
   
   fprintf(file, "%s:MWI ", on ? "OP" : "RMV");
   
   for (i = 0; i < iface->msdstrip; i++)
      fprintf(file, "0");

   fprintf(file, "%s!\x04", mailbox);

   fclose(file);

   ASTOBJ_UNLOCK(iface);
   ast_debug(1, "Sent MWI set message for %s on %s\n", mailbox, iface->name);

   return 0;
}
static void* unlink_from_msg_q ( struct ast_smdi_interface iface,
enum smdi_message_type  type 
) [inline, static]

Definition at line 276 of file res_smdi.c.

References ASTOBJ_CONTAINER_UNLINK_START, ast_smdi_interface::md_q, ast_smdi_interface::mwi_q, SMDI_MD, and SMDI_MWI.

Referenced by purge_old_messages(), and smdi_msg_pop().

{
   switch (type) {
   case SMDI_MWI:
      return ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
   case SMDI_MD:
      return ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
   }
   return NULL;
}
static int unload_module ( void  ) [static]

Definition at line 1400 of file res_smdi.c.

References _unload_module().

{
   return _unload_module(0);
}
static int unlock_msg_q ( struct ast_smdi_interface iface,
enum smdi_message_type  type 
) [inline, static]

Definition at line 264 of file res_smdi.c.

References ast_mutex_unlock(), ast_smdi_interface::md_q_lock, ast_smdi_interface::mwi_q_lock, SMDI_MD, and SMDI_MWI.

Referenced by purge_old_messages(), smdi_message_wait(), and smdi_msg_pop().

{
   switch (type) {
   case SMDI_MWI:
      return ast_mutex_unlock(&iface->mwi_q_lock);
   case SMDI_MD:
      return ast_mutex_unlock(&iface->md_q_lock);
   }

   return -1;
}
static void unref_msg ( void *  msg,
enum smdi_message_type  type 
) [inline, static]

Definition at line 302 of file res_smdi.c.

References ast_smdi_md_message_destroy(), ast_smdi_mwi_message_destroy(), ASTOBJ_UNREF, msg, SMDI_MD, and SMDI_MWI.

Referenced by purge_old_messages().

{
   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);
      break;
   case SMDI_MD:
      ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
      break;
   }
}

Variable Documentation

struct ast_module_info __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS , .description = "Simplified Message Desk Interface (SMDI) Resource" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 1424 of file res_smdi.c.

Definition at line 1424 of file res_smdi.c.

Definition at line 120 of file res_smdi.c.

const char config_file[] = "smdi.conf" [static]

Definition at line 60 of file res_smdi.c.

Definition at line 122 of file res_smdi.c.

Definition at line 122 of file res_smdi.c.

struct timeval last_poll

The time that the last poll began

Definition at line 128 of file res_smdi.c.

Definition at line 119 of file res_smdi.c.

struct { ... } mailbox_mappings
unsigned int polling_interval

Polling Interval for checking mailbox status

Definition at line 124 of file res_smdi.c.

int smdi_loaded [static]

Definition at line 61 of file res_smdi.c.

Initial value:
 {
   .type = "SMDIMSG",
   .destroy = smdi_msg_datastore_destroy,
}

Definition at line 1108 of file res_smdi.c.

Definition at line 1318 of file res_smdi.c.

int smdi_msg_id [static]

Definition at line 1113 of file res_smdi.c.

struct ast_app_option smdi_msg_ret_options[128] = { [ 't' ] = { .flag = OPT_SEARCH_TERMINAL }, [ 'n' ] = { .flag = OPT_SEARCH_NUMBER }, } [static]

Definition at line 1121 of file res_smdi.c.

Referenced by smdi_msg_retrieve_read().

Definition at line 1294 of file res_smdi.c.

unsigned int stop

Set to 1 to tell the polling thread to stop

Definition at line 126 of file res_smdi.c.

pthread_t thread

The thread ID

Definition at line 118 of file res_smdi.c.