Wed Jan 8 2020 09:49:50

Asterisk developer's documentation


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