Asterisk - The Open Source Telephony Project  GIT-master-09303e8
res_smdi.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2005-2008, Digium, Inc.
5  *
6  * Matthew A. Nicholson <mnicholson@digium.com>
7  * Russell Bryant <russell@digium.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19 
20 /*!
21  * \file
22  * \brief SMDI support for Asterisk.
23  * \author Matthew A. Nicholson <mnicholson@digium.com>
24  * \author Russell Bryant <russell@digium.com>
25  *
26  * Here is a useful mailing list post that describes SMDI protocol details:
27  * http://lists.digium.com/pipermail/asterisk-dev/2003-June/000884.html
28  *
29  * \todo This module currently has its own mailbox monitoring thread. This should
30  * be converted to MWI subscriptions and just let the optional global voicemail
31  * polling thread handle it.
32  */
33 
34 /*! \li \ref res_smdi.c uses the configuration file \ref smdi.conf
35  * \addtogroup configuration_file Configuration Files
36  */
37 
38 /*!
39  * \page smdi.conf smdi.conf
40  * \verbinclude smdi.conf.sample
41  */
42 
43 /*** MODULEINFO
44  <support_level>extended</support_level>
45  ***/
46 
47 #include "asterisk.h"
48 
49 #include <termios.h>
50 #include <sys/time.h>
51 #include <time.h>
52 #include <ctype.h>
53 
54 #include "asterisk/module.h"
55 #include "asterisk/lock.h"
56 #include "asterisk/utils.h"
57 #define AST_API_MODULE
58 #include "asterisk/smdi.h"
59 #include "asterisk/config.h"
60 #include "asterisk/io.h"
61 #include "asterisk/stringfields.h"
62 #include "asterisk/linkedlists.h"
63 #include "asterisk/app.h"
64 #include "asterisk/mwi.h"
65 #include "asterisk/pbx.h"
66 #include "asterisk/channel.h"
67 
68 /* Message expiry time in milliseconds */
69 #define SMDI_MSG_EXPIRY_TIME 30000 /* 30 seconds */
70 
71 /*** DOCUMENTATION
72 
73  <function name="SMDI_MSG_RETRIEVE" language="en_US">
74  <synopsis>
75  Retrieve an SMDI message.
76  </synopsis>
77  <syntax>
78  <parameter name="smdi port" required="true" />
79  <parameter name="search key" required="true" />
80  <parameter name="timeout" />
81  <parameter name="options">
82  <enumlist>
83  <enum name="t">
84  <para>Instead of searching on the forwarding station, search on the message desk terminal.</para>
85  </enum>
86  <enum name="n">
87  <para>Instead of searching on the forwarding station, search on the message desk number.</para>
88  </enum>
89  </enumlist>
90  </parameter>
91  </syntax>
92  <description>
93  <para>This function is used to retrieve an incoming SMDI message. It returns
94  an ID which can be used with the SMDI_MSG() function to access details of
95  the message. Note that this is a destructive function in the sense that
96  once an SMDI message is retrieved using this function, it is no longer in
97  the global SMDI message queue, and can not be accessed by any other Asterisk
98  channels. The timeout for this function is optional, and the default is
99  3 seconds. When providing a timeout, it should be in milliseconds.
100  </para>
101  <para>The default search is done on the forwarding station ID. However, if
102  you set one of the search key options in the options field, you can change
103  this behavior.
104  </para>
105  </description>
106  <see-also>
107  <ref type="function">SMDI_MSG</ref>
108  </see-also>
109  </function>
110  <function name="SMDI_MSG" language="en_US">
111  <synopsis>
112  Retrieve details about an SMDI message.
113  </synopsis>
114  <syntax>
115  <parameter name="message_id" required="true" />
116  <parameter name="component" required="true">
117  <para>Valid message components are:</para>
118  <enumlist>
119  <enum name="number">
120  <para>The message desk number</para>
121  </enum>
122  <enum name="terminal">
123  <para>The message desk terminal</para>
124  </enum>
125  <enum name="station">
126  <para>The forwarding station</para>
127  </enum>
128  <enum name="callerid">
129  <para>The callerID of the calling party that was forwarded</para>
130  </enum>
131  <enum name="type">
132  <para>The call type. The value here is the exact character
133  that came in on the SMDI link. Typically, example values
134  are:</para>
135  <para>Options:</para>
136  <enumlist>
137  <enum name="D">
138  <para>Direct Calls</para>
139  </enum>
140  <enum name="A">
141  <para>Forward All Calls</para>
142  </enum>
143  <enum name="B">
144  <para>Forward Busy Calls</para>
145  </enum>
146  <enum name="N">
147  <para>Forward No Answer Calls</para>
148  </enum>
149  </enumlist>
150  </enum>
151  </enumlist>
152  </parameter>
153  </syntax>
154  <description>
155  <para>This function is used to access details of an SMDI message that was
156  pulled from the incoming SMDI message queue using the SMDI_MSG_RETRIEVE()
157  function.</para>
158  </description>
159  <see-also>
160  <ref type="function">SMDI_MSG_RETRIEVE</ref>
161  </see-also>
162  </function>
163  ***/
164 
165 static const char config_file[] = "smdi.conf";
166 
175  FILE *file;
176  int fd;
177  pthread_t thread;
178  struct termios mode;
179  int msdstrip;
181 };
182 
183 static AO2_GLOBAL_OBJ_STATIC(smdi_ifaces);
184 
185 /*! \brief A mapping between an SMDI mailbox ID and an Asterisk mailbox */
187  /*! This is the current state of the mailbox. It is simply on or
188  * off to indicate if there are messages waiting or not. */
189  unsigned int cur_state:1;
190  /*! A Pointer to the appropriate SMDI interface */
193  /*! The Name of the mailbox for the SMDI link. */
194  AST_STRING_FIELD(smdi);
195  /*! The name of the mailbox on the Asterisk side */
197  /*! The name of the voicemail context in use */
199  );
201 };
202 
203 /*! 10 seconds */
204 #define DEFAULT_POLLING_INTERVAL 10
205 
206 /*! \brief Data that gets used by the SMDI MWI monitoring thread */
207 static struct {
208  /*! The thread ID */
209  pthread_t thread;
212  /*! A list of mailboxes that need to be monitored */
214  /*! Polling Interval for checking mailbox status */
215  unsigned int polling_interval;
216  /*! Set to 1 to tell the polling thread to stop */
217  unsigned int stop:1;
218  /*! The time that the last poll began */
219  struct timeval last_poll;
220 } mwi_monitor = {
221  .thread = AST_PTHREADT_NULL,
222 };
223 
224 static void smdi_interface_destroy(void *obj)
225 {
226  struct ast_smdi_interface *iface = obj;
227  int mod_unref_defer = 0;
228 
229  if (iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) {
230  pthread_cancel(iface->thread);
231  pthread_join(iface->thread, NULL);
232  mod_unref_defer = 1;
233  }
234 
235  iface->thread = AST_PTHREADT_STOP;
236 
237  if (iface->file) {
238  fclose(iface->file);
239  }
240 
241  ao2_cleanup(iface->md_q);
242  ast_mutex_destroy(&iface->md_q_lock);
243  ast_cond_destroy(&iface->md_q_cond);
244 
245  ao2_cleanup(iface->mwi_q);
246  ast_mutex_destroy(&iface->mwi_q_lock);
247  ast_cond_destroy(&iface->mwi_q_cond);
248 
249  if (mod_unref_defer) {
251  }
252 }
253 
254 /*!
255  * \internal
256  * \brief Push an SMDI message to the back of an interface's message queue.
257  * \param iface a pointer to the interface to use.
258  * \param md_msg a pointer to the message to use.
259  */
260 static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
261 {
262  ast_mutex_lock(&iface->md_q_lock);
263  ao2_link(iface->md_q, md_msg);
264  ast_cond_broadcast(&iface->md_q_cond);
265  ast_mutex_unlock(&iface->md_q_lock);
266 }
267 
268 /*!
269  * \internal
270  * \brief Push an SMDI message to the back of an interface's message queue.
271  * \param iface a pointer to the interface to use.
272  * \param mwi_msg a pointer to the message to use.
273  */
274 static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
275 {
276  ast_mutex_lock(&iface->mwi_q_lock);
277  ao2_link(iface->mwi_q, mwi_msg);
279  ast_mutex_unlock(&iface->mwi_q_lock);
280 }
281 
282 static int smdi_toggle_mwi(struct ast_smdi_interface *iface, const char *mailbox, int on)
283 {
284  FILE *file;
285  int i;
286 
287  if (!(file = fopen(iface->name, "w"))) {
288  ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
289  return 1;
290  }
291 
292  ao2_wrlock(iface);
293 
294  fprintf(file, "%s:MWI ", on ? "OP" : "RMV");
295 
296  for (i = 0; i < iface->msdstrip; i++)
297  fprintf(file, "0");
298 
299  fprintf(file, "%s!\x04", mailbox);
300 
301  fclose(file);
302 
303  ao2_unlock(iface);
304  ast_debug(1, "Sent MWI set message for %s on %s\n", mailbox, iface->name);
305 
306  return 0;
307 }
308 
310 {
311  return smdi_toggle_mwi(iface, mailbox, 1);
312 }
313 
315 {
316  return smdi_toggle_mwi(iface, mailbox, 0);
317 }
318 
322 };
323 
324 static inline int lock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
325 {
326  switch (type) {
327  case SMDI_MWI:
328  return ast_mutex_lock(&iface->mwi_q_lock);
329  case SMDI_MD:
330  return ast_mutex_lock(&iface->md_q_lock);
331  }
332 
333  return -1;
334 }
335 
336 static inline int unlock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
337 {
338  switch (type) {
339  case SMDI_MWI:
340  return ast_mutex_unlock(&iface->mwi_q_lock);
341  case SMDI_MD:
342  return ast_mutex_unlock(&iface->md_q_lock);
343  }
344 
345  return -1;
346 }
347 
348 static inline void *unlink_from_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
349 {
350  switch (type) {
351  case SMDI_MWI:
352  return ao2_callback(iface->mwi_q, OBJ_UNLINK, NULL, NULL);
353  case SMDI_MD:
354  return ao2_callback(iface->md_q, OBJ_UNLINK, NULL, NULL);
355  }
356 
357  return NULL;
358 }
359 
360 static inline struct timeval msg_timestamp(void *msg, enum smdi_message_type type)
361 {
362  struct ast_smdi_md_message *md_msg = msg;
363  struct ast_smdi_mwi_message *mwi_msg = msg;
364 
365  switch (type) {
366  case SMDI_MWI:
367  return mwi_msg->timestamp;
368  case SMDI_MD:
369  return md_msg->timestamp;
370  }
371 
372  return ast_tv(0, 0);
373 }
374 
376 {
377  struct timeval now = ast_tvnow();
378  long elapsed = 0;
379  void *msg;
380 
381  lock_msg_q(iface, type);
382  msg = unlink_from_msg_q(iface, type);
383  unlock_msg_q(iface, type);
384 
385  /* purge old messages */
386  while (msg) {
387  elapsed = ast_tvdiff_ms(now, msg_timestamp(msg, type));
388 
389  if (elapsed > iface->msg_expiry) {
390  /* found an expired message */
391  ao2_ref(msg, -1);
392  ast_log(LOG_NOTICE, "Purged expired message from %s SMDI %s message queue. "
393  "Message was %ld milliseconds too old.\n",
394  iface->name, (type == SMDI_MD) ? "MD" : "MWI",
395  elapsed - iface->msg_expiry);
396 
397  lock_msg_q(iface, type);
398  msg = unlink_from_msg_q(iface, type);
399  unlock_msg_q(iface, type);
400  } else {
401  /* good message, put it back and return */
402  switch (type) {
403  case SMDI_MD:
404  ast_smdi_md_message_push(iface, msg);
405  break;
406  case SMDI_MWI:
407  ast_smdi_mwi_message_push(iface, msg);
408  break;
409  }
410  ao2_ref(msg, -1);
411  break;
412  }
413  }
414 }
415 
416 static void *smdi_msg_pop(struct ast_smdi_interface *iface, enum smdi_message_type type)
417 {
418  void *msg;
419 
420  purge_old_messages(iface, type);
421 
422  lock_msg_q(iface, type);
423  msg = unlink_from_msg_q(iface, type);
424  unlock_msg_q(iface, type);
425 
426  return msg;
427 }
428 
429 enum {
431  OPT_SEARCH_NUMBER = (1 << 1),
432 };
433 
434 static void *smdi_msg_find(struct ast_smdi_interface *iface,
435  enum smdi_message_type type, const char *search_key, struct ast_flags options)
436 {
437  void *msg = NULL;
438 
439  purge_old_messages(iface, type);
440 
441  switch (type) {
442  case SMDI_MD:
443  if (ast_strlen_zero(search_key)) {
444  /* No search key provided (the code from chan_dahdi does this).
445  * Just pop the top message off of the queue. */
446 
447  msg = ao2_callback(iface->md_q, 0, NULL, NULL);
448  } else if (ast_test_flag(&options, OPT_SEARCH_TERMINAL)) {
449  /* Searching by the message desk terminal */
450  struct ast_smdi_md_message md_msg = { .name = "" };
451  strncpy(md_msg.mesg_desk_term, search_key, SMDI_MESG_DESK_TERM_LEN);
452  msg = ao2_find(iface->md_q, &md_msg, OBJ_SEARCH_OBJECT);
453  } else if (ast_test_flag(&options, OPT_SEARCH_NUMBER)) {
454  /* Searching by the message desk number */
455  struct ast_smdi_md_message md_msg = { .name = "" };
456  strncpy(md_msg.mesg_desk_num, search_key, SMDI_MESG_DESK_NUM_LEN);
457  msg = ao2_find(iface->md_q, &md_msg, OBJ_SEARCH_OBJECT);
458  } else {
459  /* Searching by the forwarding station */
460  msg = ao2_find(iface->md_q, search_key, OBJ_SEARCH_KEY);
461  }
462  break;
463  case SMDI_MWI:
464  if (ast_strlen_zero(search_key)) {
465  /* No search key provided (the code from chan_dahdi does this).
466  * Just pop the top message off of the queue. */
467 
468  msg = ao2_callback(iface->mwi_q, 0, NULL, NULL);
469  } else {
470  msg = ao2_find(iface->mwi_q, search_key, OBJ_SEARCH_KEY);
471  }
472  break;
473  }
474 
475  return msg;
476 }
477 
478 static void *smdi_message_wait(struct ast_smdi_interface *iface, int timeout,
479  enum smdi_message_type type, const char *search_key, struct ast_flags options)
480 {
481  struct timeval start;
482  long diff = 0;
483  void *msg;
484  ast_cond_t *cond = NULL;
485  ast_mutex_t *lock = NULL;
486 
487  switch (type) {
488  case SMDI_MWI:
489  cond = &iface->mwi_q_cond;
490  lock = &iface->mwi_q_lock;
491  break;
492  case SMDI_MD:
493  cond = &iface->md_q_cond;
494  lock = &iface->md_q_lock;
495  break;
496  }
497 
498  start = ast_tvnow();
499 
500  while (diff < timeout) {
501  struct timespec ts = { 0, };
502  struct timeval wait;
503 
504  lock_msg_q(iface, type);
505 
506  if ((msg = smdi_msg_find(iface, type, search_key, options))) {
507  unlock_msg_q(iface, type);
508  return msg;
509  }
510 
511  wait = ast_tvadd(start, ast_tv(0, timeout));
512  ts.tv_sec = wait.tv_sec;
513  ts.tv_nsec = wait.tv_usec * 1000;
514 
515  /* If there were no messages in the queue, then go to sleep until one
516  * arrives. */
517 
518  ast_cond_timedwait(cond, lock, &ts);
519 
520  if ((msg = smdi_msg_find(iface, type, search_key, options))) {
521  unlock_msg_q(iface, type);
522  return msg;
523  }
524 
525  unlock_msg_q(iface, type);
526 
527  /* check timeout */
528  diff = ast_tvdiff_ms(ast_tvnow(), start);
529  }
530 
531  return NULL;
532 }
533 
535 {
536  return smdi_msg_pop(iface, SMDI_MD);
537 }
538 
540 {
541  struct ast_flags options = { 0 };
542  return smdi_message_wait(iface, timeout, SMDI_MD, NULL, options);
543 }
544 
546 {
547  return smdi_msg_pop(iface, SMDI_MWI);
548 }
549 
551 {
552  struct ast_flags options = { 0 };
553  return smdi_message_wait(iface, timeout, SMDI_MWI, NULL, options);
554 }
555 
557  const char *station)
558 {
559  struct ast_flags options = { 0 };
560  return smdi_message_wait(iface, timeout, SMDI_MWI, station, options);
561 }
562 
564 {
565  struct ao2_container *c;
566  struct ast_smdi_interface *iface = NULL;
567 
568  c = ao2_global_obj_ref(smdi_ifaces);
569  if (c) {
570  iface = ao2_find(c, iface_name, OBJ_SEARCH_KEY);
571  ao2_ref(c, -1);
572  }
573 
574  return iface;
575 }
576 
577 /*!
578  * \internal
579  * \brief Read an SMDI message.
580  *
581  * \param iface_p the SMDI interface to read from.
582  *
583  * This function loops and reads from and SMDI interface. It must be stopped
584  * using pthread_cancel().
585  */
586 static void *smdi_read(void *iface_p)
587 {
588  struct ast_smdi_interface *iface = iface_p;
589  struct ast_smdi_md_message *md_msg;
590  struct ast_smdi_mwi_message *mwi_msg;
591  char *cp = NULL;
592  int i, c;
593  int start = 0;
594 
595  /* read an smdi message */
596  while ((c = fgetc(iface->file))) {
597 
598  /* check if this is the start of a message */
599  if (!start) {
600  if (c == 'M') {
601  ast_debug(1, "Read an 'M' to start an SMDI message\n");
602  start = 1;
603  }
604  continue;
605  }
606 
607  if (c == 'D') { /* MD message */
608  start = 0;
609 
610  ast_debug(1, "Read a 'D' ... it's an MD message.\n");
611 
612  md_msg = ao2_alloc(sizeof(*md_msg), NULL);
613  if (!md_msg) {
614  return NULL;
615  }
616 
617  /* read the message desk number */
618  for (i = 0; i < sizeof(md_msg->mesg_desk_num) - 1; i++) {
619  c = fgetc(iface->file);
620  if (c == EOF) {
621  ast_log(LOG_ERROR, "Unexpected EOF while reading MD message\n");
622  ao2_ref(md_msg, -1);
623  return NULL;
624  }
625  md_msg->mesg_desk_num[i] = (char) c;
626  ast_debug(1, "Read a '%c'\n", md_msg->mesg_desk_num[i]);
627  }
628 
629  md_msg->mesg_desk_num[sizeof(md_msg->mesg_desk_num) - 1] = '\0';
630 
631  ast_debug(1, "The message desk number is '%s'\n", md_msg->mesg_desk_num);
632 
633  /* read the message desk terminal number */
634  for (i = 0; i < sizeof(md_msg->mesg_desk_term) - 1; i++) {
635  c = fgetc(iface->file);
636  if (c == EOF) {
637  ast_log(LOG_ERROR, "Unexpected EOF while reading SMDI message\n");
638  ao2_ref(md_msg, -1);
639  return NULL;
640  }
641  md_msg->mesg_desk_term[i] = (char) c;
642  ast_debug(1, "Read a '%c'\n", md_msg->mesg_desk_term[i]);
643  }
644 
645  md_msg->mesg_desk_term[sizeof(md_msg->mesg_desk_term) - 1] = '\0';
646 
647  ast_debug(1, "The message desk terminal is '%s'\n", md_msg->mesg_desk_term);
648 
649  /* read the message type */
650  c = fgetc(iface->file);
651  if (c == EOF) {
652  ast_log(LOG_ERROR, "Unexpected EOF while reading SMDI message\n");
653  ao2_ref(md_msg, -1);
654  return NULL;
655  }
656  md_msg->type = (char) c;
657 
658  ast_debug(1, "Message type is '%c'\n", md_msg->type);
659 
660  /* read the forwarding station number (may be blank) */
661  cp = &md_msg->fwd_st[0];
662  for (i = 0; i < sizeof(md_msg->fwd_st) - 1; i++) {
663  if ((c = fgetc(iface->file)) == ' ') {
664  *cp = '\0';
665  ast_debug(1, "Read a space, done looking for the forwarding station\n");
666  break;
667  }
668 
669  /* store c in md_msg->fwd_st */
670  if (i >= iface->msdstrip) {
671  ast_debug(1, "Read a '%c' and stored it in the forwarding station buffer\n", c);
672  *cp++ = c;
673  } else {
674  ast_debug(1, "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);
675  }
676  }
677 
678  /* make sure the value is null terminated, even if this truncates it */
679  md_msg->fwd_st[sizeof(md_msg->fwd_st) - 1] = '\0';
680  cp = NULL;
681 
682  ast_debug(1, "The forwarding station is '%s'\n", md_msg->fwd_st);
683 
684  /* Put the fwd_st in the name field so that we can use ao2_find to look
685  * up a message on this field */
686  ast_copy_string(md_msg->name, md_msg->fwd_st, sizeof(md_msg->name));
687 
688  /* read the calling station number (may be blank) */
689  cp = &md_msg->calling_st[0];
690  for (i = 0; i < sizeof(md_msg->calling_st) - 1; i++) {
691  if (!isdigit((c = fgetc(iface->file)))) {
692  *cp = '\0';
693  ast_debug(1, "Read a '%c', but didn't store it in the calling station buffer because it's not a digit\n", c);
694  if (c == ' ') {
695  /* Don't break on a space. We may read the space before the calling station
696  * here if the forwarding station buffer filled up. */
697  i--; /* We're still on the same character */
698  continue;
699  }
700  break;
701  }
702 
703  /* store c in md_msg->calling_st */
704  if (i >= iface->msdstrip) {
705  ast_debug(1, "Read a '%c' and stored it in the calling station buffer\n", c);
706  *cp++ = c;
707  } else {
708  ast_debug(1, "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);
709  }
710  }
711 
712  /* make sure the value is null terminated, even if this truncates it */
713  md_msg->calling_st[sizeof(md_msg->calling_st) - 1] = '\0';
714  cp = NULL;
715 
716  ast_debug(1, "The calling station is '%s'\n", md_msg->calling_st);
717 
718  /* add the message to the message queue */
719  md_msg->timestamp = ast_tvnow();
720  ast_smdi_md_message_push(iface, md_msg);
721  ast_debug(1, "Received SMDI MD message on %s\n", iface->name);
722 
723  ao2_ref(md_msg, -1);
724 
725  } else if (c == 'W') { /* MWI message */
726  start = 0;
727 
728  ast_debug(1, "Read a 'W', it's an MWI message. (No more debug coming for MWI messages)\n");
729 
730  mwi_msg = ao2_alloc(sizeof(*mwi_msg), NULL);
731  if (!mwi_msg) {
732  return NULL;
733  }
734 
735  /* discard the 'I' (from 'MWI') */
736  fgetc(iface->file);
737 
738  /* read the forwarding station number (may be blank) */
739  cp = &mwi_msg->fwd_st[0];
740  for (i = 0; i < sizeof(mwi_msg->fwd_st) - 1; i++) {
741  if ((c = fgetc(iface->file)) == ' ') {
742  *cp = '\0';
743  break;
744  }
745 
746  /* store c in md_msg->fwd_st */
747  if (i >= iface->msdstrip)
748  *cp++ = c;
749  }
750 
751  /* make sure the station number is null terminated, even if this will truncate it */
752  mwi_msg->fwd_st[sizeof(mwi_msg->fwd_st) - 1] = '\0';
753  cp = NULL;
754 
755  /* Put the fwd_st in the name field so that we can use ao2_find to look
756  * up a message on this field */
757  ast_copy_string(mwi_msg->name, mwi_msg->fwd_st, sizeof(mwi_msg->name));
758 
759  /* read the mwi failure cause */
760  for (i = 0; i < sizeof(mwi_msg->cause) - 1; i++) {
761  c = fgetc(iface->file);
762  if (c == EOF) {
763  ast_log(LOG_ERROR, "Unexpected EOF while reading MWI message\n");
764  ao2_ref(mwi_msg, -1);
765  return NULL;
766  }
767  mwi_msg->cause[i] = (char) c;
768  }
769 
770  mwi_msg->cause[sizeof(mwi_msg->cause) - 1] = '\0';
771 
772  /* add the message to the message queue */
773  mwi_msg->timestamp = ast_tvnow();
774  ast_smdi_mwi_message_push(iface, mwi_msg);
775  ast_debug(1, "Received SMDI MWI message on %s\n", iface->name);
776 
777  ao2_ref(mwi_msg, -1);
778  } else {
779  ast_log(LOG_ERROR, "Unknown SMDI message type received on %s (M%c).\n", iface->name, c);
780  start = 0;
781  }
782  }
783 
784  ast_log(LOG_ERROR, "Error reading from SMDI interface %s, stopping listener thread\n", iface->name);
785  return NULL;
786 }
787 
789 {
791  ao2_ref(mm->iface, -1);
792  ast_free(mm);
793 }
794 
796 {
797  struct mailbox_mapping *mm;
798 
799  ast_mutex_lock(&mwi_monitor.lock);
800  while ((mm = AST_LIST_REMOVE_HEAD(&mwi_monitor.mailbox_mappings, entry)))
802  ast_mutex_unlock(&mwi_monitor.lock);
803 }
804 
805 static void append_mailbox_mapping(struct ast_variable *var, struct ast_smdi_interface *iface)
806 {
807  struct mailbox_mapping *mm;
808  char *mailbox, *context;
809 
810  if (!(mm = ast_calloc_with_stringfields(1, struct mailbox_mapping, 32)))
811  return;
812 
813  ast_string_field_set(mm, smdi, var->name);
814 
815  context = ast_strdupa(var->value);
816  mailbox = strsep(&context, "@");
817  if (ast_strlen_zero(context))
818  context = "default";
819 
820  ast_string_field_set(mm, mailbox, mailbox);
821  ast_string_field_set(mm, context, context);
822 
823  mm->iface = ao2_bump(iface);
824 
825  ast_mutex_lock(&mwi_monitor.lock);
826  AST_LIST_INSERT_TAIL(&mwi_monitor.mailbox_mappings, mm, entry);
827  ast_mutex_unlock(&mwi_monitor.lock);
828 }
829 
830 /*!
831  * \note Called with the mwi_monitor.lock locked
832  */
833 static void poll_mailbox(struct mailbox_mapping *mm)
834 {
835  char buf[1024];
836  unsigned int state;
837 
838  snprintf(buf, sizeof(buf), "%s@%s", mm->mailbox, mm->context);
839 
840  state = !!ast_app_has_voicemail(mm->mailbox, NULL);
841 
842  if (state != mm->cur_state) {
843  if (state)
844  ast_smdi_mwi_set(mm->iface, mm->smdi);
845  else
846  ast_smdi_mwi_unset(mm->iface, mm->smdi);
847 
848  mm->cur_state = state;
849  }
850 }
851 
852 static void *mwi_monitor_handler(void *data)
853 {
854  while (!mwi_monitor.stop) {
855  struct timespec ts = { 0, };
856  struct timeval polltime;
857  struct mailbox_mapping *mm;
858 
859  ast_mutex_lock(&mwi_monitor.lock);
860 
861  mwi_monitor.last_poll = ast_tvnow();
862 
863  AST_LIST_TRAVERSE(&mwi_monitor.mailbox_mappings, mm, entry)
864  poll_mailbox(mm);
865 
866  /* Sleep up to the configured polling interval. Allow unload_module()
867  * to signal us to wake up and exit. */
868  polltime = ast_tvadd(mwi_monitor.last_poll, ast_tv(mwi_monitor.polling_interval, 0));
869  ts.tv_sec = polltime.tv_sec;
870  ts.tv_nsec = polltime.tv_usec * 1000;
871  ast_cond_timedwait(&mwi_monitor.cond, &mwi_monitor.lock, &ts);
872 
873  ast_mutex_unlock(&mwi_monitor.lock);
874  }
875 
876  return NULL;
877 }
878 
879 static int smdi_mwi_q_cmp_fn(void *obj, void *data, int flags)
880 {
881  struct ast_smdi_mwi_message *msg = obj;
882  char *str = data;
883  return !strcmp(msg->name, str) ? CMP_MATCH | CMP_STOP : 0;
884 }
885 
886 static int smdi_md_q_cmp_fn(void *obj, void *arg, int flags)
887 {
888  const struct ast_smdi_md_message *msg = obj;
889  const struct ast_smdi_md_message *search_msg = arg;
890  const char *search_key = arg;
891  int cmp = 0;
892 
893  switch (flags & OBJ_SEARCH_MASK) {
894  case OBJ_SEARCH_OBJECT:
895  if (!ast_strlen_zero(search_msg->mesg_desk_num)) {
896  cmp = strcmp(msg->mesg_desk_num, search_msg->mesg_desk_num);
897  }
898  if (!ast_strlen_zero(search_msg->mesg_desk_term)) {
899  cmp |= strcmp(msg->mesg_desk_term, search_msg->mesg_desk_term);
900  }
901  break;
902  case OBJ_SEARCH_KEY:
903  cmp = strcmp(msg->name, search_key);
904  break;
905  }
906 
907  if (cmp) {
908  return 0;
909  }
910 
911  return CMP_MATCH;
912 }
913 
915 {
916  struct ast_smdi_interface *iface;
917 
918  if (!(iface = ao2_alloc(sizeof(*iface), smdi_interface_destroy))) {
919  return NULL;
920  }
921 
924 
925  ast_mutex_init(&iface->md_q_lock);
926  ast_cond_init(&iface->md_q_cond, NULL);
927 
928  ast_mutex_init(&iface->mwi_q_lock);
929  ast_cond_init(&iface->mwi_q_cond, NULL);
930 
931  return iface;
932 }
933 
934 static int smdi_ifaces_cmp_fn(void *obj, void *data, int flags)
935 {
936  struct ast_smdi_interface *iface = obj;
937 
938  char *str = data;
939  return !strcmp(iface->name, str) ? CMP_MATCH | CMP_STOP : 0;
940 }
941 
942 /*!
943  * \internal
944  * \brief Load and reload SMDI configuration.
945  * \param reload this should be 1 if we are reloading and 0 if not.
946  *
947  * This function loads/reloads the SMDI configuration and starts and stops
948  * interfaces accordingly.
949  *
950  * \return zero on success, -1 on failure, and 1 if no smdi interfaces were started.
951  */
952 static int smdi_load(int reload)
953 {
954  struct ast_config *conf;
955  struct ast_variable *v;
956  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
957  int res = 0;
958  RAII_VAR(struct ao2_container *, new_ifaces, NULL, ao2_cleanup);
959  RAII_VAR(struct ao2_container *, old_ifaces, ao2_global_obj_ref(smdi_ifaces), ao2_cleanup);
960  struct ast_smdi_interface *mailbox_iface = NULL;
961 
962  /* Config options */
963  speed_t baud_rate = B9600; /* 9600 baud rate */
964  tcflag_t paritybit = PARENB; /* even parity checking */
965  tcflag_t charsize = CS7; /* seven bit characters */
966  int stopbits = 0; /* One stop bit */
967 
968  int msdstrip = 0; /* strip zero digits */
970 
971  if (!(conf = ast_config_load(config_file, config_flags)) || conf == CONFIG_STATUS_FILEINVALID) {
972  if (reload)
973  ast_log(LOG_NOTICE, "Unable to reload config %s: SMDI untouched\n", config_file);
974  else
975  ast_log(LOG_NOTICE, "Unable to load config %s: SMDI disabled\n", config_file);
976  return 1;
977  } else if (conf == CONFIG_STATUS_FILEUNCHANGED)
978  return 0;
979 
981  if (!new_ifaces) {
982  ast_config_destroy(conf);
983  return -1;
984  }
985 
986  for (v = ast_variable_browse(conf, "interfaces"); v; v = v->next) {
987  RAII_VAR(struct ast_smdi_interface *, iface, NULL, ao2_cleanup);
988 
989  if (!strcasecmp(v->name, "baudrate")) {
990  if (!strcasecmp(v->value, "9600"))
991  baud_rate = B9600;
992  else if (!strcasecmp(v->value, "4800"))
993  baud_rate = B4800;
994  else if (!strcasecmp(v->value, "2400"))
995  baud_rate = B2400;
996  else if (!strcasecmp(v->value, "1200"))
997  baud_rate = B1200;
998  else {
999  ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno);
1000  baud_rate = B9600;
1001  }
1002  } else if (!strcasecmp(v->name, "msdstrip")) {
1003  if (!sscanf(v->value, "%30d", &msdstrip)) {
1004  ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
1005  msdstrip = 0;
1006  } else if (0 > msdstrip || msdstrip > 9) {
1007  ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
1008  msdstrip = 0;
1009  }
1010  } else if (!strcasecmp(v->name, "msgexpirytime")) {
1011  if (!sscanf(v->value, "%30ld", &msg_expiry)) {
1012  ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno);
1013  msg_expiry = SMDI_MSG_EXPIRY_TIME;
1014  }
1015  } else if (!strcasecmp(v->name, "paritybit")) {
1016  if (!strcasecmp(v->value, "even"))
1017  paritybit = PARENB;
1018  else if (!strcasecmp(v->value, "odd"))
1019  paritybit = PARENB | PARODD;
1020  else if (!strcasecmp(v->value, "none"))
1021  paritybit = ~PARENB;
1022  else {
1023  ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno);
1024  paritybit = PARENB;
1025  }
1026  } else if (!strcasecmp(v->name, "charsize")) {
1027  if (!strcasecmp(v->value, "7"))
1028  charsize = CS7;
1029  else if (!strcasecmp(v->value, "8"))
1030  charsize = CS8;
1031  else {
1032  ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno);
1033  charsize = CS7;
1034  }
1035  } else if (!strcasecmp(v->name, "twostopbits")) {
1036  stopbits = ast_true(v->name);
1037  } else if (!strcasecmp(v->name, "smdiport")) {
1038  if (reload && old_ifaces) {
1039  /* we are reloading, check if we are already
1040  * monitoring this interface, if we are we do
1041  * not want to start it again. This also has
1042  * the side effect of not updating different
1043  * setting for the serial port, but it should
1044  * be trivial to rewrite this section so that
1045  * options on the port are changed without
1046  * restarting the interface. Or the interface
1047  * could be restarted with out emptying the
1048  * queue. */
1049  if ((iface = ao2_find(old_ifaces, v->value, OBJ_SEARCH_KEY))) {
1050  ast_log(LOG_NOTICE, "SMDI interface %s already running, not restarting\n", iface->name);
1051  ao2_link(new_ifaces, iface);
1052  continue;
1053  }
1054  }
1055 
1056  if (!(iface = alloc_smdi_interface()))
1057  continue;
1058 
1059  ast_copy_string(iface->name, v->value, sizeof(iface->name));
1060 
1061  iface->thread = AST_PTHREADT_NULL;
1062 
1063  if (!(iface->file = fopen(iface->name, "r"))) {
1064  ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s)\n", iface->name, strerror(errno));
1065  continue;
1066  }
1067 
1068  iface->fd = fileno(iface->file);
1069 
1070  /* Set the proper attributes for our serial port. */
1071 
1072  /* get the current attributes from the port */
1073  if (tcgetattr(iface->fd, &iface->mode)) {
1074  ast_log(LOG_ERROR, "Error getting atributes of %s (%s)\n", iface->name, strerror(errno));
1075  continue;
1076  }
1077 
1078  /* set the desired speed */
1079  if (cfsetispeed(&iface->mode, baud_rate) || cfsetospeed(&iface->mode, baud_rate)) {
1080  ast_log(LOG_ERROR, "Error setting baud rate on %s (%s)\n", iface->name, strerror(errno));
1081  continue;
1082  }
1083 
1084  /* set the stop bits */
1085  if (stopbits)
1086  iface->mode.c_cflag = iface->mode.c_cflag | CSTOPB; /* set two stop bits */
1087  else
1088  iface->mode.c_cflag = iface->mode.c_cflag & ~CSTOPB; /* set one stop bit */
1089 
1090  /* set the parity */
1091  iface->mode.c_cflag = (iface->mode.c_cflag & ~PARENB & ~PARODD) | paritybit;
1092 
1093  /* set the character size */
1094  iface->mode.c_cflag = (iface->mode.c_cflag & ~CSIZE) | charsize;
1095 
1096  /* commit the desired attributes */
1097  if (tcsetattr(iface->fd, TCSAFLUSH, &iface->mode)) {
1098  ast_log(LOG_ERROR, "Error setting attributes on %s (%s)\n", iface->name, strerror(errno));
1099  continue;
1100  }
1101 
1102  /* set the msdstrip */
1103  iface->msdstrip = msdstrip;
1104 
1105  /* set the message expiry time */
1106  iface->msg_expiry = msg_expiry;
1107 
1108  /*
1109  * start the listener thread
1110  *
1111  * The listener thread does not actually hold a ref to iface. When all
1112  * external refs go away, the destructor will stop the listener thread
1113  * before actually destroying the iface object.
1114  */
1115  ast_verb(3, "Starting SMDI monitor thread for %s\n", iface->name);
1116  if (ast_pthread_create_background(&iface->thread, NULL, smdi_read, iface)) {
1117  ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name);
1118  continue;
1119  }
1120 
1121  ao2_link(new_ifaces, iface);
1123  } else {
1124  ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file);
1125  }
1126  }
1127 
1129  mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL;
1130 
1131  for (v = ast_variable_browse(conf, "mailboxes"); v; v = v->next) {
1132  if (!strcasecmp(v->name, "smdiport")) {
1133  ao2_cleanup(mailbox_iface);
1134 
1135  if (!(mailbox_iface = ao2_find(new_ifaces, v->value, OBJ_SEARCH_KEY))) {
1136  ast_log(LOG_NOTICE, "SMDI interface %s not found\n", v->value);
1137  continue;
1138  }
1139  } else if (!strcasecmp(v->name, "pollinginterval")) {
1140  if (sscanf(v->value, "%30u", &mwi_monitor.polling_interval) != 1) {
1141  ast_log(LOG_ERROR, "Invalid value for pollinginterval: %s\n", v->value);
1142  mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL;
1143  }
1144  } else {
1145  if (!mailbox_iface) {
1146  ast_log(LOG_ERROR, "Mailbox mapping ignored, no valid SMDI interface specified in mailboxes section\n");
1147  continue;
1148  }
1149  append_mailbox_mapping(v, mailbox_iface);
1150  }
1151  }
1152  ao2_cleanup(mailbox_iface);
1153 
1154  ast_config_destroy(conf);
1155 
1156  ao2_global_obj_replace_unref(smdi_ifaces, new_ifaces);
1157 
1158  if (!AST_LIST_EMPTY(&mwi_monitor.mailbox_mappings) && mwi_monitor.thread == AST_PTHREADT_NULL
1159  && ast_pthread_create_background(&mwi_monitor.thread, NULL, mwi_monitor_handler, NULL)) {
1160  ast_log(LOG_ERROR, "Failed to start MWI monitoring thread. This module will not operate.\n");
1161  return -1;
1162  }
1163 
1164  if (!ao2_container_count(new_ifaces)) {
1165  res = 1;
1166  }
1167 
1168  return res;
1169 }
1170 
1172  unsigned int id;
1175 };
1176 
1177 static void smdi_msg_datastore_destroy(void *data)
1178 {
1179  struct smdi_msg_datastore *smd = data;
1180 
1181  ao2_cleanup(smd->iface);
1182  ao2_cleanup(smd->md_msg);
1183 
1184  ast_free(smd);
1185 }
1186 
1188  .type = "SMDIMSG",
1189  .destroy = smdi_msg_datastore_destroy,
1190 };
1191 
1192 static int smdi_msg_id;
1193 
1194 /*! In milliseconds */
1195 #define SMDI_RETRIEVE_TIMEOUT_DEFAULT 3000
1196 
1201 
1202 static int smdi_msg_retrieve_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1203 {
1204  struct ast_module_user *u;
1206  AST_APP_ARG(port);
1207  AST_APP_ARG(search_key);
1208  AST_APP_ARG(timeout);
1210  );
1211  struct ast_flags options = { 0 };
1212  unsigned int timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT;
1213  int res = -1;
1214  char *parse = NULL;
1215  struct smdi_msg_datastore *smd = NULL;
1216  struct ast_datastore *datastore = NULL;
1217  struct ast_smdi_interface *iface = NULL;
1218  struct ast_smdi_md_message *md_msg = NULL;
1219 
1220  u = ast_module_user_add(chan);
1221 
1222  if (ast_strlen_zero(data)) {
1223  ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE requires an argument\n");
1224  goto return_error;
1225  }
1226 
1227  if (!chan) {
1228  ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE must be used with a channel\n");
1229  goto return_error;
1230  }
1231 
1232  ast_autoservice_start(chan);
1233 
1234  parse = ast_strdupa(data);
1235  AST_STANDARD_APP_ARGS(args, parse);
1236 
1237  if (ast_strlen_zero(args.port) || ast_strlen_zero(args.search_key)) {
1238  ast_log(LOG_ERROR, "Invalid arguments provided to SMDI_MSG_RETRIEVE\n");
1239  goto return_error;
1240  }
1241 
1242  if (!(iface = ast_smdi_interface_find(args.port))) {
1243  ast_log(LOG_ERROR, "SMDI port '%s' not found\n", args.port);
1244  goto return_error;
1245  }
1246 
1247  if (!ast_strlen_zero(args.options)) {
1248  ast_app_parse_options(smdi_msg_ret_options, &options, NULL, args.options);
1249  }
1250 
1251  if (!ast_strlen_zero(args.timeout)) {
1252  if (sscanf(args.timeout, "%30u", &timeout) != 1) {
1253  ast_log(LOG_ERROR, "'%s' is not a valid timeout\n", args.timeout);
1255  }
1256  }
1257 
1258  if (!(md_msg = smdi_message_wait(iface, timeout, SMDI_MD, args.search_key, options))) {
1259  ast_log(LOG_WARNING, "No SMDI message retrieved for search key '%s' after "
1260  "waiting %u ms.\n", args.search_key, timeout);
1261  goto return_error;
1262  }
1263 
1264  if (!(smd = ast_calloc(1, sizeof(*smd))))
1265  goto return_error;
1266 
1267  smd->iface = ao2_bump(iface);
1268  smd->md_msg = ao2_bump(md_msg);
1269  smd->id = ast_atomic_fetchadd_int((int *) &smdi_msg_id, 1);
1270  snprintf(buf, len, "%u", smd->id);
1271 
1272  if (!(datastore = ast_datastore_alloc(&smdi_msg_datastore_info, buf)))
1273  goto return_error;
1274 
1275  datastore->data = smd;
1276 
1277  ast_channel_lock(chan);
1278  ast_channel_datastore_add(chan, datastore);
1279  ast_channel_unlock(chan);
1280 
1281  res = 0;
1282 
1283 return_error:
1284  ao2_cleanup(iface);
1285  ao2_cleanup(md_msg);
1286 
1287  if (smd && !datastore)
1289 
1290  if (parse)
1291  ast_autoservice_stop(chan);
1292 
1294 
1295  return res;
1296 }
1297 
1298 static int smdi_msg_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1299 {
1300  struct ast_module_user *u;
1301  int res = -1;
1303  AST_APP_ARG(id);
1304  AST_APP_ARG(component);
1305  );
1306  char *parse;
1307  struct ast_datastore *datastore = NULL;
1308  struct smdi_msg_datastore *smd = NULL;
1309 
1310  u = ast_module_user_add(chan);
1311 
1312  if (!chan) {
1313  ast_log(LOG_ERROR, "SMDI_MSG can not be called without a channel\n");
1314  goto return_error;
1315  }
1316 
1317  if (ast_strlen_zero(data)) {
1318  ast_log(LOG_WARNING, "SMDI_MSG requires an argument\n");
1319  goto return_error;
1320  }
1321 
1322  parse = ast_strdupa(data);
1323  AST_STANDARD_APP_ARGS(args, parse);
1324 
1325  if (ast_strlen_zero(args.id)) {
1326  ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n");
1327  goto return_error;
1328  }
1329 
1330  if (ast_strlen_zero(args.component)) {
1331  ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n");
1332  goto return_error;
1333  }
1334 
1335  ast_channel_lock(chan);
1336  datastore = ast_channel_datastore_find(chan, &smdi_msg_datastore_info, args.id);
1337  ast_channel_unlock(chan);
1338 
1339  if (!datastore) {
1340  ast_log(LOG_WARNING, "No SMDI message found for message ID '%s'\n", args.id);
1341  goto return_error;
1342  }
1343 
1344  smd = datastore->data;
1345 
1346  if (!strcasecmp(args.component, "number")) {
1347  ast_copy_string(buf, smd->md_msg->mesg_desk_num, len);
1348  } else if (!strcasecmp(args.component, "terminal")) {
1349  ast_copy_string(buf, smd->md_msg->mesg_desk_term, len);
1350  } else if (!strcasecmp(args.component, "station")) {
1351  ast_copy_string(buf, smd->md_msg->fwd_st, len);
1352  } else if (!strcasecmp(args.component, "callerid")) {
1353  ast_copy_string(buf, smd->md_msg->calling_st, len);
1354  } else if (!strcasecmp(args.component, "type")) {
1355  snprintf(buf, len, "%c", smd->md_msg->type);
1356  } else {
1357  ast_log(LOG_ERROR, "'%s' is not a valid message component for SMDI_MSG\n",
1358  args.component);
1359  goto return_error;
1360  }
1361 
1362  res = 0;
1363 
1364 return_error:
1366 
1367  return res;
1368 }
1369 
1371  .name = "SMDI_MSG_RETRIEVE",
1372  .read = smdi_msg_retrieve_read,
1373 };
1374 
1376  .name = "SMDI_MSG",
1377  .read = smdi_msg_read,
1378 };
1379 
1380 static int unload_module(void)
1381 {
1382  ao2_global_obj_release(smdi_ifaces);
1383 
1385 
1386  ast_mutex_lock(&mwi_monitor.lock);
1387  mwi_monitor.stop = 1;
1388  ast_cond_signal(&mwi_monitor.cond);
1389  ast_mutex_unlock(&mwi_monitor.lock);
1390 
1391  if (mwi_monitor.thread != AST_PTHREADT_NULL) {
1392  pthread_join(mwi_monitor.thread, NULL);
1393  }
1394 
1395  ast_custom_function_unregister(&smdi_msg_retrieve_function);
1396  ast_custom_function_unregister(&smdi_msg_function);
1397 
1398  return 0;
1399 }
1400 
1401 /*!
1402  * \brief Load the module
1403  *
1404  * Module loading including tests for configuration or dependencies.
1405  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
1406  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
1407  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
1408  * configuration file or other non-critical problem return
1409  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
1410  */
1411 static int load_module(void)
1412 {
1413  int res;
1414 
1415  ast_mutex_init(&mwi_monitor.lock);
1416  ast_cond_init(&mwi_monitor.cond, NULL);
1417 
1418  /* load the config and start the listener threads*/
1419  res = smdi_load(0);
1420  if (res < 0) {
1421  unload_module();
1422  return AST_MODULE_LOAD_DECLINE;
1423  } else if (res == 1) {
1424  ast_log(LOG_NOTICE, "No SMDI interfaces are available to listen on, not starting SMDI listener.\n");
1425  }
1426 
1427  ast_custom_function_register(&smdi_msg_retrieve_function);
1428  ast_custom_function_register(&smdi_msg_function);
1429 
1430  return AST_MODULE_LOAD_SUCCESS;
1431 }
1432 
1433 static int reload(void)
1434 {
1435  int res;
1436 
1437  res = smdi_load(1);
1438  if (res < 0) {
1439  return res;
1440  } else if (res == 1) {
1441  ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n");
1442  }
1443  return 0;
1444 }
1445 
1446 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Simplified Message Desk Interface (SMDI) Resource",
1447  .support_level = AST_MODULE_SUPPORT_EXTENDED,
1448  .load = load_module,
1449  .unload = unload_module,
1450  .reload = reload,
1451  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
1452 );
const char * name
Definition: pbx.h:119
const char * type
Definition: datastore.h:32
struct ast_smdi_mwi_message *AST_OPTIONAL_API_NAME() ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
Get the next SMDI message from the queue.
Definition: res_smdi.c:545
struct ast_variable * next
static const char type[]
Definition: chan_ooh323.c:109
enum sip_cc_notify_state state
Definition: chan_sip.c:960
static const struct ast_app_option smdi_msg_ret_options[128]
Definition: res_smdi.c:1200
#define ast_channel_lock(chan)
Definition: channel.h:2902
Main Channel structure associated with a channel.
ast_mutex_t md_q_lock
Definition: res_smdi.c:170
char fwd_st[SMDI_MAX_STATION_NUM_LEN+1]
Definition: smdi.h:69
Asterisk locking-related definitions:
static int unlock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
Definition: res_smdi.c:336
Asterisk main include file. File version handling, generic pbx functions.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
char name[SMDI_MESG_NAME_LEN]
Definition: smdi.h:66
static void * smdi_msg_pop(struct ast_smdi_interface *iface, enum smdi_message_type type)
Definition: res_smdi.c:416
static int smdi_load(int reload)
Definition: res_smdi.c:952
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
static struct @484 mwi_monitor
Data that gets used by the SMDI MWI monitoring thread.
char name[SMDI_MAX_FILENAME_LEN]
Definition: res_smdi.c:168
unsigned int cur_state
Definition: res_smdi.c:189
static void smdi_interface_destroy(void *obj)
Definition: res_smdi.c:224
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1105
#define ast_test_flag(p, flag)
Definition: utils.h:63
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1216
#define BEGIN_OPTIONS
unsigned int polling_interval
Definition: res_smdi.c:215
const ast_string_field context
Definition: res_smdi.c:199
Time-related functions and macros.
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static void purge_old_messages(struct ast_smdi_interface *iface, enum smdi_message_type type)
Definition: res_smdi.c:375
static void poll_mailbox(struct mailbox_mapping *mm)
Definition: res_smdi.c:833
static struct ast_custom_function smdi_msg_retrieve_function
Definition: res_smdi.c:1370
char mesg_desk_num[SMDI_MESG_DESK_NUM_LEN+1]
Definition: smdi.h:67
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
char calling_st[SMDI_MAX_STATION_NUM_LEN+1]
Definition: smdi.h:70
#define LOG_WARNING
Definition: logger.h:274
int AST_OPTIONAL_API_NAME() ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
Unset the MWI indicator for a mailbox.
Definition: res_smdi.c:314
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
#define CONFIG_STATUS_FILEINVALID
static void destroy_mailbox_mapping(struct mailbox_mapping *mm)
Definition: res_smdi.c:788
static int timeout
Definition: cdr_mysql.c:86
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Definition: astobj2.h:1335
Structure for variables, used for configurations and for channel variables.
#define var
Definition: ast_expr2f.c:614
static int smdi_msg_retrieve_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: res_smdi.c:1202
#define SMDI_MAX_FILENAME_LEN
Definition: smdi.h:42
Structure for a data store type.
Definition: datastore.h:31
#define ast_calloc_with_stringfields(n, type, size)
Allocate a structure with embedded stringfields in a single allocation.
Definition: stringfields.h:426
#define ast_cond_init(cond, attr)
Definition: lock.h:199
#define ao2_global_obj_ref(holder)
Definition: astobj2.h:925
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:337
static void * smdi_msg_find(struct ast_smdi_interface *iface, enum smdi_message_type type, const char *search_key, struct ast_flags options)
Definition: res_smdi.c:434
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
#define ast_mutex_lock(a)
Definition: lock.h:187
A mapping between an SMDI mailbox ID and an Asterisk mailbox.
Definition: res_smdi.c:186
#define ao2_unlock(a)
Definition: astobj2.h:730
static struct test_val c
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:98
Structure for a data store object.
Definition: datastore.h:68
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2390
const char * str
Definition: app_jack.c:147
const char * args
static struct ast_custom_function smdi_msg_function
Definition: res_smdi.c:1375
#define NULL
Definition: resample.c:96
I/O Management (derived from Cheops-NG)
static int smdi_mwi_q_cmp_fn(void *obj, void *data, int flags)
Definition: res_smdi.c:879
static const struct ast_datastore_info smdi_msg_datastore_info
Definition: res_smdi.c:1187
#define ao2_wrlock(a)
Definition: astobj2.h:720
struct ast_smdi_interface * iface
Definition: res_smdi.c:1173
#define ast_module_user_remove(user)
Definition: module.h:427
char name[SMDI_MESG_NAME_LEN]
Definition: smdi.h:52
#define ast_cond_signal(cond)
Definition: lock.h:201
#define ast_verb(level,...)
Definition: logger.h:455
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:755
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
static AO2_GLOBAL_OBJ_STATIC(smdi_ifaces)
Utility functions.
#define ast_module_unref(mod)
Release a reference to the module.
Definition: module.h:469
pthread_cond_t ast_cond_t
Definition: lock.h:176
char cause[SMDI_MWI_FAIL_CAUSE_LEN+1]
Definition: smdi.h:54
All configuration options for statsd client.
Definition: res_statsd.c:95
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define ast_pthread_create_background(a, b, c, d)
Definition: utils.h:507
static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
Definition: res_smdi.c:274
struct @484::@487 mailbox_mappings
#define ao2_bump(obj)
Definition: astobj2.h:491
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)
Definition: res_smdi.c:478
Configuration File Parser.
static void destroy_all_mailbox_mappings(void)
Definition: res_smdi.c:795
static int smdi_md_q_cmp_fn(void *obj, void *arg, int flags)
Definition: res_smdi.c:886
static char mailbox[AST_MAX_MAILBOX_UNIQUEID]
Definition: chan_mgcp.c:204
#define SMDI_RETRIEVE_TIMEOUT_DEFAULT
Definition: res_smdi.c:1195
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:444
#define ast_log
Definition: astobj2.c:42
ast_cond_t mwi_q_cond
Definition: res_smdi.c:174
#define SMDI_MESG_DESK_NUM_LEN
Definition: smdi.h:38
struct ast_smdi_interface * iface
Definition: res_smdi.c:191
#define ast_config_load(filename, flags)
Load a config file.
static struct timeval msg_timestamp(void *msg, enum smdi_message_type type)
Definition: res_smdi.c:360
struct ast_module * self
Definition: module.h:342
struct ast_smdi_md_message *AST_OPTIONAL_API_NAME() ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
Get the next SMDI message from the queue.
Definition: res_smdi.c:539
General Asterisk PBX channel definitions.
smdi_message_type
Definition: res_smdi.c:319
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:851
#define DEFAULT_POLLING_INTERVAL
Definition: res_smdi.c:204
#define AST_PTHREADT_NULL
Definition: lock.h:66
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:299
#define ao2_ref(o, delta)
Definition: astobj2.h:464
int AST_OPTIONAL_API_NAME() ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
Set the MWI indicator for a mailbox.
Definition: res_smdi.c:309
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
#define SMDI_MESG_DESK_TERM_LEN
Definition: smdi.h:39
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
A set of macros to manage forward-linked lists.
#define ast_cond_broadcast(cond)
Definition: lock.h:202
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
AST_LIST_HEAD_NOLOCK(contactliststruct, contact)
static int smdi_toggle_mwi(struct ast_smdi_interface *iface, const char *mailbox, int on)
Definition: res_smdi.c:282
An SMDI message desk message.
Definition: smdi.h:65
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:2829
#define ast_module_user_add(chan)
Definition: module.h:426
Core PBX routines and definitions.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
struct ast_smdi_mwi_message *AST_OPTIONAL_API_NAME() ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
Get the next SMDI message from the queue.
Definition: res_smdi.c:550
#define CONFIG_STATUS_FILEUNCHANGED
char mesg_desk_term[SMDI_MESG_DESK_TERM_LEN+1]
Definition: smdi.h:68
#define LOG_ERROR
Definition: logger.h:285
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: main/utils.c:1822
static void * unlink_from_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
Definition: res_smdi.c:348
ast_mutex_t mwi_q_lock
Definition: res_smdi.c:173
#define ao2_global_obj_release(holder)
Definition: astobj2.h:865
static int smdi_ifaces_cmp_fn(void *obj, void *data, int flags)
Definition: res_smdi.c:934
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
const ast_string_field mailbox
Definition: res_smdi.c:199
int errno
pthread_t thread
Definition: res_smdi.c:177
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: extconf.c:2283
#define ast_cond_destroy(cond)
Definition: lock.h:200
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
#define LOG_NOTICE
Definition: logger.h:263
static void smdi_msg_datastore_destroy(void *data)
Definition: res_smdi.c:1177
struct ast_smdi_mwi_message *AST_OPTIONAL_API_NAME() ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout, const char *station)
Definition: res_smdi.c:556
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
struct ao2_container * mwi_q
Definition: res_smdi.c:172
#define ast_strlen_zero(a)
Definition: muted.c:73
unsigned int stop
Definition: res_smdi.c:217
#define ast_channel_unlock(chan)
Definition: channel.h:2903
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1872
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
ast_mutex_t lock
Definition: res_smdi.c:210
SMDI support for Asterisk.
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
char fwd_st[SMDI_MAX_STATION_NUM_LEN+1]
Definition: smdi.h:53
struct timeval timestamp
Definition: smdi.h:55
Structure used to handle boolean flags.
Definition: utils.h:199
static int unload_module(void)
Definition: res_smdi.c:1380
int ast_app_has_voicemail(const char *mailboxes, const char *folder)
Determine if a given mailbox has any voicemail If folder is NULL, defaults to "INBOX". If folder is "INBOX", includes the number of messages in the "Urgent" folder.
Definition: main/app.c:655
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
static int lock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
Definition: res_smdi.c:324
static int smdi_msg_id
Definition: res_smdi.c:1192
static int load_module(void)
Load the module.
Definition: res_smdi.c:1411
static void append_mailbox_mapping(struct ast_variable *var, struct ast_smdi_interface *iface)
Definition: res_smdi.c:805
static int smdi_msg_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: res_smdi.c:1298
#define ao2_global_obj_replace_unref(holder, obj)
Definition: astobj2.h:908
void * data
Definition: datastore.h:70
The arg parameter is an object of the same type.
Definition: astobj2.h:1091
static void * mwi_monitor_handler(void *data)
Definition: res_smdi.c:852
char * strsep(char **str, const char *delims)
const ast_string_field smdi
Definition: res_smdi.c:199
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
ast_cond_t md_q_cond
Definition: res_smdi.c:171
struct timeval ast_tv(ast_time_t sec, ast_suseconds_t usec)
Returns a timeval from sec, usec.
Definition: time.h:226
ast_cond_t cond
Definition: res_smdi.c:211
#define END_OPTIONS
unsigned int id
Definition: res_smdi.c:1172
Asterisk MWI API.
#define AST_PTHREADT_STOP
Definition: lock.h:67
static int reload(void)
Definition: res_smdi.c:1433
#define SMDI_MSG_EXPIRY_TIME
Definition: res_smdi.c:69
static void * smdi_read(void *iface_p)
Definition: res_smdi.c:586
struct termios mode
Definition: res_smdi.c:178
static struct ast_smdi_interface * alloc_smdi_interface(void)
Definition: res_smdi.c:914
Definition: search.h:40
struct timeval timestamp
Definition: smdi.h:72
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:89
struct ast_smdi_md_message * md_msg
Definition: res_smdi.c:1174
#define AST_OPTIONAL_API_NAME(name)
Expands to the name of the implementation function.
Definition: optional_api.h:228
#define ast_mutex_init(pmutex)
Definition: lock.h:184
Generic container type.
static struct test_options options
Search option field mask.
Definition: astobj2.h:1076
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:116
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
Definition: res_smdi.c:260
#define ast_mutex_destroy(a)
Definition: lock.h:186
struct ao2_container * md_q
Definition: res_smdi.c:169
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
struct ast_smdi_md_message *AST_OPTIONAL_API_NAME() ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
Get the next SMDI message from the queue.
Definition: res_smdi.c:534
struct ast_smdi_interface *AST_OPTIONAL_API_NAME() ast_smdi_interface_find(const char *iface_name)
Find an SMDI interface with the specified name.
Definition: res_smdi.c:563
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2376
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:368
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
struct timeval last_poll
Definition: res_smdi.c:219
#define ast_cond_timedwait(cond, mutex, time)
Definition: lock.h:204
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1508
static const char config_file[]
Definition: res_smdi.c:165
Structure for mutex and tracking information.
Definition: lock.h:135
An SMDI message waiting indicator message.
Definition: smdi.h:51
#define ast_mutex_unlock(a)
Definition: lock.h:188
#define AST_APP_ARG(name)
Define an application argument.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:514
#define ast_module_ref(mod)
Hold a reference to the module.
Definition: module.h:443
#define ao2_link(container, obj)
Definition: astobj2.h:1549