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