Add some other files to ignore
[asterisk-bristuff.git] / res / res_smdi.c
blob406a740c9cac7dd3b02b9574e2558624e4a764ea
1 /*
2 * Asterisk -- A telephony toolkit for Linux.
4 * Copyright (C) 2005-2008, Digium, Inc.
6 * Matthew A. Nicholson <mnicholson@digium.com>
7 * Russell Bryant <russell@digium.com>
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.
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.
20 /*!
21 * \file
22 * \brief SMDI support for Asterisk.
23 * \author Matthew A. Nicholson <mnicholson@digium.com>
24 * \author Russell Bryant <russell@digium.com>
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
30 #include "asterisk.h"
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <errno.h>
37 #include <termios.h>
38 #include <sys/time.h>
39 #include <time.h>
40 #include <ctype.h>
42 #include "asterisk/module.h"
43 #include "asterisk/lock.h"
44 #include "asterisk/utils.h"
45 #include "asterisk/smdi.h"
46 #include "asterisk/config.h"
47 #include "asterisk/astobj.h"
48 #include "asterisk/io.h"
49 #include "asterisk/logger.h"
50 #include "asterisk/utils.h"
51 #include "asterisk/options.h"
52 #include "asterisk/stringfields.h"
53 #include "asterisk/linkedlists.h"
54 #include "asterisk/app.h"
55 #include "asterisk/pbx.h"
57 /* Message expiry time in milliseconds */
58 #define SMDI_MSG_EXPIRY_TIME 30000 /* 30 seconds */
60 static const char config_file[] = "smdi.conf";
62 /*! \brief SMDI message desk message queue. */
63 struct ast_smdi_md_queue {
64 ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_md_message);
67 /*! \brief SMDI message waiting indicator message queue. */
68 struct ast_smdi_mwi_queue {
69 ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_mwi_message);
72 struct ast_smdi_interface {
73 ASTOBJ_COMPONENTS_FULL(struct ast_smdi_interface, SMDI_MAX_FILENAME_LEN, 1);
74 struct ast_smdi_md_queue md_q;
75 ast_mutex_t md_q_lock;
76 ast_cond_t md_q_cond;
77 struct ast_smdi_mwi_queue mwi_q;
78 ast_mutex_t mwi_q_lock;
79 ast_cond_t mwi_q_cond;
80 FILE *file;
81 int fd;
82 pthread_t thread;
83 struct termios mode;
84 int msdstrip;
85 long msg_expiry;
88 /*! \brief SMDI interface container. */
89 struct ast_smdi_interface_container {
90 ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface);
91 } smdi_ifaces;
93 /*! \brief A mapping between an SMDI mailbox ID and an Asterisk mailbox */
94 struct mailbox_mapping {
95 /*! This is the current state of the mailbox. It is simply on or
96 * off to indicate if there are messages waiting or not. */
97 unsigned int cur_state:1;
98 /*! A Pointer to the appropriate SMDI interface */
99 struct ast_smdi_interface *iface;
100 AST_DECLARE_STRING_FIELDS(
101 /*! The Name of the mailbox for the SMDI link. */
102 AST_STRING_FIELD(smdi);
103 /*! The name of the mailbox on the Asterisk side */
104 AST_STRING_FIELD(mailbox);
105 /*! The name of the voicemail context in use */
106 AST_STRING_FIELD(context);
108 AST_LIST_ENTRY(mailbox_mapping) entry;
111 /*! 10 seconds */
112 #define DEFAULT_POLLING_INTERVAL 10
114 /*! \brief Data that gets used by the SMDI MWI monitoring thread */
115 static struct {
116 /*! The thread ID */
117 pthread_t thread;
118 ast_mutex_t lock;
119 ast_cond_t cond;
120 /*! A list of mailboxes that need to be monitored */
121 AST_LIST_HEAD_NOLOCK(, mailbox_mapping) mailbox_mappings;
122 /*! Polling Interval for checking mailbox status */
123 unsigned int polling_interval;
124 /*! Set to 1 to tell the polling thread to stop */
125 unsigned int stop:1;
126 /*! The time that the last poll began */
127 struct timeval last_poll;
128 } mwi_monitor = {
129 .thread = AST_PTHREADT_NULL,
132 static void ast_smdi_interface_destroy(struct ast_smdi_interface *iface)
134 if (iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) {
135 pthread_cancel(iface->thread);
136 pthread_join(iface->thread, NULL);
139 iface->thread = AST_PTHREADT_STOP;
141 if (iface->file)
142 fclose(iface->file);
144 ASTOBJ_CONTAINER_DESTROYALL(&iface->md_q, ast_smdi_md_message_destroy);
145 ASTOBJ_CONTAINER_DESTROYALL(&iface->mwi_q, ast_smdi_mwi_message_destroy);
146 ASTOBJ_CONTAINER_DESTROY(&iface->md_q);
147 ASTOBJ_CONTAINER_DESTROY(&iface->mwi_q);
149 ast_mutex_destroy(&iface->md_q_lock);
150 ast_cond_destroy(&iface->md_q_cond);
152 ast_mutex_destroy(&iface->mwi_q_lock);
153 ast_cond_destroy(&iface->mwi_q_cond);
155 free(iface);
157 ast_module_unref(ast_module_info->self);
160 void ast_smdi_interface_unref(struct ast_smdi_interface *iface)
162 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
165 /*!
166 * \internal
167 * \brief Push an SMDI message to the back of an interface's message queue.
168 * \param iface a pointer to the interface to use.
169 * \param md_msg a pointer to the message to use.
171 static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
173 ast_mutex_lock(&iface->md_q_lock);
174 ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg);
175 ast_cond_broadcast(&iface->md_q_cond);
176 ast_mutex_unlock(&iface->md_q_lock);
180 * \internal
181 * \brief Push an SMDI message to the back of an interface's message queue.
182 * \param iface a pointer to the interface to use.
183 * \param mwi_msg a pointer to the message to use.
185 static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
187 ast_mutex_lock(&iface->mwi_q_lock);
188 ASTOBJ_CONTAINER_LINK_END(&iface->mwi_q, mwi_msg);
189 ast_cond_broadcast(&iface->mwi_q_cond);
190 ast_mutex_unlock(&iface->mwi_q_lock);
193 static int smdi_toggle_mwi(struct ast_smdi_interface *iface, const char *mailbox, int on)
195 FILE *file;
196 int i;
198 if (!(file = fopen(iface->name, "w"))) {
199 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
200 return 1;
203 ASTOBJ_WRLOCK(iface);
205 fprintf(file, "%s:MWI ", on ? "OP" : "RMV");
207 for (i = 0; i < iface->msdstrip; i++)
208 fprintf(file, "0");
210 fprintf(file, "%s!\x04", mailbox);
212 fclose(file);
214 ASTOBJ_UNLOCK(iface);
216 ast_log(LOG_DEBUG, "Sent MWI %s message for %s on %s\n", on ? "set" : "unset",
217 mailbox, iface->name);
219 return 0;
222 int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
224 return smdi_toggle_mwi(iface, mailbox, 1);
227 int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
229 return smdi_toggle_mwi(iface, mailbox, 0);
232 void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
234 ast_mutex_lock(&iface->md_q_lock);
235 ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg);
236 ast_cond_broadcast(&iface->md_q_cond);
237 ast_mutex_unlock(&iface->md_q_lock);
240 void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
242 ast_mutex_lock(&iface->mwi_q_lock);
243 ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg);
244 ast_cond_broadcast(&iface->mwi_q_cond);
245 ast_mutex_unlock(&iface->mwi_q_lock);
248 enum smdi_message_type {
249 SMDI_MWI,
250 SMDI_MD,
253 static inline int lock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
255 switch (type) {
256 case SMDI_MWI:
257 return ast_mutex_lock(&iface->mwi_q_lock);
258 case SMDI_MD:
259 return ast_mutex_lock(&iface->md_q_lock);
262 return -1;
265 static inline int unlock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
267 switch (type) {
268 case SMDI_MWI:
269 return ast_mutex_unlock(&iface->mwi_q_lock);
270 case SMDI_MD:
271 return ast_mutex_unlock(&iface->md_q_lock);
274 return -1;
277 static inline void *unlink_from_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
279 switch (type) {
280 case SMDI_MWI:
281 return ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
282 case SMDI_MD:
283 return ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
286 return NULL;
289 static inline struct timeval msg_timestamp(void *msg, enum smdi_message_type type)
291 struct ast_smdi_md_message *md_msg = msg;
292 struct ast_smdi_mwi_message *mwi_msg = msg;
294 switch (type) {
295 case SMDI_MWI:
296 return mwi_msg->timestamp;
297 case SMDI_MD:
298 return md_msg->timestamp;
301 return ast_tv(0, 0);
304 static inline void unref_msg(void *msg, enum smdi_message_type type)
306 struct ast_smdi_md_message *md_msg = msg;
307 struct ast_smdi_mwi_message *mwi_msg = msg;
309 switch (type) {
310 case SMDI_MWI:
311 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
312 case SMDI_MD:
313 ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
317 static void purge_old_messages(struct ast_smdi_interface *iface, enum smdi_message_type type)
319 struct timeval now;
320 long elapsed = 0;
321 void *msg;
323 lock_msg_q(iface, type);
324 msg = unlink_from_msg_q(iface, type);
325 unlock_msg_q(iface, type);
327 /* purge old messages */
328 now = ast_tvnow();
329 while (msg) {
330 elapsed = ast_tvdiff_ms(now, msg_timestamp(msg, type));
332 if (elapsed > iface->msg_expiry) {
333 /* found an expired message */
334 unref_msg(msg, type);
335 ast_log(LOG_NOTICE, "Purged expired message from %s SMDI %s message queue. "
336 "Message was %ld milliseconds too old.\n",
337 iface->name, (type == SMDI_MD) ? "MD" : "MWI",
338 elapsed - iface->msg_expiry);
340 lock_msg_q(iface, type);
341 msg = unlink_from_msg_q(iface, type);
342 unlock_msg_q(iface, type);
343 } else {
344 /* good message, put it back and return */
345 switch (type) {
346 case SMDI_MD:
347 ast_smdi_md_message_push(iface, msg);
348 break;
349 case SMDI_MWI:
350 ast_smdi_mwi_message_push(iface, msg);
351 break;
353 unref_msg(msg, type);
354 break;
359 static void *smdi_msg_pop(struct ast_smdi_interface *iface, enum smdi_message_type type)
361 void *msg;
363 purge_old_messages(iface, type);
365 lock_msg_q(iface, type);
366 msg = unlink_from_msg_q(iface, type);
367 unlock_msg_q(iface, type);
369 return msg;
372 enum {
373 OPT_SEARCH_TERMINAL = (1 << 0),
374 OPT_SEARCH_NUMBER = (1 << 1),
377 static void *smdi_msg_find(struct ast_smdi_interface *iface,
378 enum smdi_message_type type, const char *search_key, struct ast_flags options)
380 void *msg = NULL;
382 purge_old_messages(iface, type);
384 switch (type) {
385 case SMDI_MD:
386 if (ast_test_flag(&options, OPT_SEARCH_TERMINAL)) {
387 struct ast_smdi_md_message *md_msg = NULL;
389 /* Searching by the message desk terminal */
391 ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do {
392 if (!strcasecmp(iterator->mesg_desk_term, search_key))
393 md_msg = ASTOBJ_REF(iterator);
394 } while (0); );
396 msg = md_msg;
397 } else if (ast_test_flag(&options, OPT_SEARCH_NUMBER)) {
398 struct ast_smdi_md_message *md_msg = NULL;
400 /* Searching by the message desk number */
402 ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do {
403 if (!strcasecmp(iterator->mesg_desk_num, search_key))
404 md_msg = ASTOBJ_REF(iterator);
405 } while (0); );
407 msg = md_msg;
408 } else {
409 /* Searching by the forwarding station */
410 msg = ASTOBJ_CONTAINER_FIND(&iface->md_q, search_key);
412 break;
413 case SMDI_MWI:
414 msg = ASTOBJ_CONTAINER_FIND(&iface->mwi_q, search_key);
415 break;
418 return msg;
421 static void *smdi_message_wait(struct ast_smdi_interface *iface, int timeout,
422 enum smdi_message_type type, const char *search_key, struct ast_flags options)
424 struct timeval start;
425 long diff = 0;
426 void *msg;
427 ast_cond_t *cond = NULL;
428 ast_mutex_t *lock = NULL;
430 switch (type) {
431 case SMDI_MWI:
432 cond = &iface->mwi_q_cond;
433 lock = &iface->mwi_q_lock;
434 break;
435 case SMDI_MD:
436 cond = &iface->md_q_cond;
437 lock = &iface->md_q_lock;
438 break;
441 start = ast_tvnow();
442 while (diff < timeout) {
443 struct timespec ts = { 0, };
444 struct timeval tv;
446 lock_msg_q(iface, type);
448 if ((msg = smdi_msg_find(iface, type, search_key, options))) {
449 unlock_msg_q(iface, type);
450 return msg;
453 tv = ast_tvadd(start, ast_tv(0, timeout));
454 ts.tv_sec = tv.tv_sec;
455 ts.tv_nsec = tv.tv_usec * 1000;
457 /* If there were no messages in the queue, then go to sleep until one
458 * arrives. */
460 ast_cond_timedwait(cond, lock, &ts);
462 if ((msg = smdi_msg_find(iface, type, search_key, options))) {
463 unlock_msg_q(iface, type);
464 return msg;
467 unlock_msg_q(iface, type);
469 /* check timeout */
470 diff = ast_tvdiff_ms(ast_tvnow(), start);
473 return NULL;
476 struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
478 return smdi_msg_pop(iface, SMDI_MD);
481 struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
483 struct ast_flags options = { 0 };
484 return smdi_message_wait(iface, timeout, SMDI_MD, NULL, options);
487 struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
489 return smdi_msg_pop(iface, SMDI_MWI);
492 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
494 struct ast_flags options = { 0 };
495 return smdi_message_wait(iface, timeout, SMDI_MWI, NULL, options);
498 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout,
499 const char *station)
501 struct ast_flags options = { 0 };
502 return smdi_message_wait(iface, timeout, SMDI_MWI, station, options);
505 struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
507 return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name));
510 /*!
511 * \internal
512 * \brief Read an SMDI message.
514 * \param iface_p the SMDI interface to read from.
516 * This function loops and reads from and SMDI interface. It must be stopped
517 * using pthread_cancel().
519 static void *smdi_read(void *iface_p)
521 struct ast_smdi_interface *iface = iface_p;
522 struct ast_smdi_md_message *md_msg;
523 struct ast_smdi_mwi_message *mwi_msg;
524 char c = '\0';
525 char *cp = NULL;
526 int i;
527 int start = 0;
529 /* read an smdi message */
530 while ((c = fgetc(iface->file))) {
532 /* check if this is the start of a message */
533 if (!start) {
534 if (c == 'M') {
535 ast_log(LOG_DEBUG, "Read an 'M' to start an SMDI message\n");
536 start = 1;
538 continue;
541 if (c == 'D') { /* MD message */
542 start = 0;
544 ast_log(LOG_DEBUG, "Read a 'D' ... it's an MD message.\n");
546 if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
547 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
548 return NULL;
551 ASTOBJ_INIT(md_msg);
553 /* read the message desk number */
554 for (i = 0; i < sizeof(md_msg->mesg_desk_num) - 1; i++) {
555 md_msg->mesg_desk_num[i] = fgetc(iface->file);
556 ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_num[i]);
559 md_msg->mesg_desk_num[sizeof(md_msg->mesg_desk_num) - 1] = '\0';
561 ast_log(LOG_DEBUG, "The message desk number is '%s'\n", md_msg->mesg_desk_num);
563 /* read the message desk terminal number */
564 for (i = 0; i < sizeof(md_msg->mesg_desk_term) - 1; i++) {
565 md_msg->mesg_desk_term[i] = fgetc(iface->file);
566 ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_term[i]);
569 md_msg->mesg_desk_term[sizeof(md_msg->mesg_desk_term) - 1] = '\0';
571 ast_log(LOG_DEBUG, "The message desk terminal is '%s'\n", md_msg->mesg_desk_term);
573 /* read the message type */
574 md_msg->type = fgetc(iface->file);
576 ast_log(LOG_DEBUG, "Message type is '%c'\n", md_msg->type);
578 /* read the forwarding station number (may be blank) */
579 cp = &md_msg->fwd_st[0];
580 for (i = 0; i < sizeof(md_msg->fwd_st) - 1; i++) {
581 if ((c = fgetc(iface->file)) == ' ') {
582 *cp = '\0';
583 ast_log(LOG_DEBUG, "Read a space, done looking for the forwarding station\n");
584 break;
587 /* store c in md_msg->fwd_st */
588 if (i >= iface->msdstrip) {
589 ast_log(LOG_DEBUG, "Read a '%c' and stored it in the forwarding station buffer\n", c);
590 *cp++ = c;
591 } else {
592 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);
596 /* make sure the value is null terminated, even if this truncates it */
597 md_msg->fwd_st[sizeof(md_msg->fwd_st) - 1] = '\0';
598 cp = NULL;
600 ast_log(LOG_DEBUG, "The forwarding station is '%s'\n", md_msg->fwd_st);
602 /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
603 * up a message on this field */
604 ast_copy_string(md_msg->name, md_msg->fwd_st, sizeof(md_msg->name));
606 /* read the calling station number (may be blank) */
607 cp = &md_msg->calling_st[0];
608 for (i = 0; i < sizeof(md_msg->calling_st) - 1; i++) {
609 if (!isdigit((c = fgetc(iface->file)))) {
610 *cp = '\0';
611 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);
612 if (c == ' ') {
613 /* Don't break on a space. We may read the space before the calling station
614 * here if the forwarding station buffer filled up. */
615 i--; /* We're still on the same character */
616 continue;
618 break;
621 /* store c in md_msg->calling_st */
622 if (i >= iface->msdstrip) {
623 ast_log(LOG_DEBUG, "Read a '%c' and stored it in the calling station buffer\n", c);
624 *cp++ = c;
625 } else {
626 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);
630 /* make sure the value is null terminated, even if this truncates it */
631 md_msg->calling_st[sizeof(md_msg->calling_st) - 1] = '\0';
632 cp = NULL;
634 ast_log(LOG_DEBUG, "The calling station is '%s'\n", md_msg->calling_st);
636 /* add the message to the message queue */
637 md_msg->timestamp = ast_tvnow();
638 ast_smdi_md_message_push(iface, md_msg);
639 ast_log(LOG_DEBUG, "Recieved SMDI MD message on %s\n", iface->name);
641 ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
643 } else if (c == 'W') { /* MWI message */
644 start = 0;
646 ast_log(LOG_DEBUG, "Read a 'W', it's an MWI message. (No more debug coming for MWI messages)\n");
648 if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
649 ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
650 return NULL;
653 ASTOBJ_INIT(mwi_msg);
655 /* discard the 'I' (from 'MWI') */
656 fgetc(iface->file);
658 /* read the forwarding station number (may be blank) */
659 cp = &mwi_msg->fwd_st[0];
660 for (i = 0; i < sizeof(mwi_msg->fwd_st) - 1; i++) {
661 if ((c = fgetc(iface->file)) == ' ') {
662 *cp = '\0';
663 break;
666 /* store c in md_msg->fwd_st */
667 if (i >= iface->msdstrip)
668 *cp++ = c;
671 /* make sure the station number is null terminated, even if this will truncate it */
672 mwi_msg->fwd_st[sizeof(mwi_msg->fwd_st) - 1] = '\0';
673 cp = NULL;
675 /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
676 * up a message on this field */
677 ast_copy_string(mwi_msg->name, mwi_msg->fwd_st, sizeof(mwi_msg->name));
679 /* read the mwi failure cause */
680 for (i = 0; i < sizeof(mwi_msg->cause) - 1; i++)
681 mwi_msg->cause[i] = fgetc(iface->file);
683 mwi_msg->cause[sizeof(mwi_msg->cause) - 1] = '\0';
685 /* add the message to the message queue */
686 mwi_msg->timestamp = ast_tvnow();
687 ast_smdi_mwi_message_push(iface, mwi_msg);
688 ast_log(LOG_DEBUG, "Recieved SMDI MWI message on %s\n", iface->name);
690 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
691 } else {
692 ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c);
693 start = 0;
697 ast_log(LOG_ERROR, "Error reading from SMDI interface %s, stopping listener thread\n", iface->name);
698 ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
699 return NULL;
702 void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg)
704 free(msg);
707 void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
709 free(msg);
712 static void destroy_mailbox_mapping(struct mailbox_mapping *mm)
714 ast_string_field_free_memory(mm);
715 ASTOBJ_UNREF(mm->iface, ast_smdi_interface_destroy);
716 free(mm);
719 static void destroy_all_mailbox_mappings(void)
721 struct mailbox_mapping *mm;
723 ast_mutex_lock(&mwi_monitor.lock);
724 while ((mm = AST_LIST_REMOVE_HEAD(&mwi_monitor.mailbox_mappings, entry)))
725 destroy_mailbox_mapping(mm);
726 ast_mutex_unlock(&mwi_monitor.lock);
729 static void append_mailbox_mapping(struct ast_variable *var, struct ast_smdi_interface *iface)
731 struct mailbox_mapping *mm;
732 char *mailbox, *context;
734 if (!(mm = ast_calloc(1, sizeof(*mm))))
735 return;
737 if (ast_string_field_init(mm, 32)) {
738 free(mm);
739 return;
742 ast_string_field_set(mm, smdi, var->name);
744 context = ast_strdupa(var->value);
745 mailbox = strsep(&context, "@");
746 if (ast_strlen_zero(context))
747 context = "default";
749 ast_string_field_set(mm, mailbox, mailbox);
750 ast_string_field_set(mm, context, context);
752 mm->iface = ASTOBJ_REF(iface);
754 ast_mutex_lock(&mwi_monitor.lock);
755 AST_LIST_INSERT_TAIL(&mwi_monitor.mailbox_mappings, mm, entry);
756 ast_mutex_unlock(&mwi_monitor.lock);
760 * \note Called with the mwi_monitor.lock locked
762 static void poll_mailbox(struct mailbox_mapping *mm)
764 char buf[1024];
765 unsigned int state;
767 snprintf(buf, sizeof(buf), "%s@%s", mm->mailbox, mm->context);
769 state = !!ast_app_has_voicemail(mm->mailbox, NULL);
771 if (state != mm->cur_state) {
772 if (state)
773 ast_smdi_mwi_set(mm->iface, mm->smdi);
774 else
775 ast_smdi_mwi_unset(mm->iface, mm->smdi);
777 mm->cur_state = state;
781 static void *mwi_monitor_handler(void *data)
783 while (!mwi_monitor.stop) {
784 struct timespec ts = { 0, };
785 struct timeval tv;
786 struct mailbox_mapping *mm;
788 ast_mutex_lock(&mwi_monitor.lock);
790 mwi_monitor.last_poll = ast_tvnow();
792 AST_LIST_TRAVERSE(&mwi_monitor.mailbox_mappings, mm, entry)
793 poll_mailbox(mm);
795 /* Sleep up to the configured polling interval. Allow unload_module()
796 * to signal us to wake up and exit. */
797 tv = ast_tvadd(mwi_monitor.last_poll, ast_tv(mwi_monitor.polling_interval, 0));
798 ts.tv_sec = tv.tv_sec;
799 ts.tv_nsec = tv.tv_usec * 1000;
800 ast_cond_timedwait(&mwi_monitor.cond, &mwi_monitor.lock, &ts);
802 ast_mutex_unlock(&mwi_monitor.lock);
805 return NULL;
808 static struct ast_smdi_interface *alloc_smdi_interface(void)
810 struct ast_smdi_interface *iface;
812 if (!(iface = ast_calloc(1, sizeof(*iface))))
813 return NULL;
815 ASTOBJ_INIT(iface);
816 ASTOBJ_CONTAINER_INIT(&iface->md_q);
817 ASTOBJ_CONTAINER_INIT(&iface->mwi_q);
819 ast_mutex_init(&iface->md_q_lock);
820 ast_cond_init(&iface->md_q_cond, NULL);
822 ast_mutex_init(&iface->mwi_q_lock);
823 ast_cond_init(&iface->mwi_q_cond, NULL);
825 return iface;
829 * \internal
830 * \brief Load and reload SMDI configuration.
831 * \param reload this should be 1 if we are reloading and 0 if not.
833 * This function loads/reloads the SMDI configuration and starts and stops
834 * interfaces accordingly.
836 * \return zero on success, -1 on failure, and 1 if no smdi interfaces were started.
838 static int smdi_load(int reload)
840 struct ast_config *conf;
841 struct ast_variable *v;
842 struct ast_smdi_interface *iface = NULL;
843 int res = 0;
845 /* Config options */
846 speed_t baud_rate = B9600; /* 9600 baud rate */
847 tcflag_t paritybit = PARENB; /* even parity checking */
848 tcflag_t charsize = CS7; /* seven bit characters */
849 int stopbits = 0; /* One stop bit */
851 int msdstrip = 0; /* strip zero digits */
852 long msg_expiry = SMDI_MSG_EXPIRY_TIME;
854 conf = ast_config_load(config_file);
856 if (!conf) {
857 if (reload)
858 ast_log(LOG_NOTICE, "Unable to reload config %s: SMDI untouched\n", config_file);
859 else
860 ast_log(LOG_NOTICE, "Unable to load config %s: SMDI disabled\n", config_file);
861 return 1;
864 /* Mark all interfaces that we are listening on. We will unmark them
865 * as we find them in the config file, this way we know any interfaces
866 * still marked after we have finished parsing the config file should
867 * be stopped.
869 if (reload)
870 ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces);
872 for (v = ast_variable_browse(conf, "interfaces"); v; v = v->next) {
873 if (!strcasecmp(v->name, "baudrate")) {
874 if (!strcasecmp(v->value, "9600"))
875 baud_rate = B9600;
876 else if (!strcasecmp(v->value, "4800"))
877 baud_rate = B4800;
878 else if (!strcasecmp(v->value, "2400"))
879 baud_rate = B2400;
880 else if (!strcasecmp(v->value, "1200"))
881 baud_rate = B1200;
882 else {
883 ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno);
884 baud_rate = B9600;
886 } else if (!strcasecmp(v->name, "msdstrip")) {
887 if (!sscanf(v->value, "%d", &msdstrip)) {
888 ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
889 msdstrip = 0;
890 } else if (0 > msdstrip || msdstrip > 9) {
891 ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
892 msdstrip = 0;
894 } else if (!strcasecmp(v->name, "msgexpirytime")) {
895 if (!sscanf(v->value, "%ld", &msg_expiry)) {
896 ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno);
897 msg_expiry = SMDI_MSG_EXPIRY_TIME;
899 } else if (!strcasecmp(v->name, "paritybit")) {
900 if (!strcasecmp(v->value, "even"))
901 paritybit = PARENB;
902 else if (!strcasecmp(v->value, "odd"))
903 paritybit = PARENB | PARODD;
904 else if (!strcasecmp(v->value, "none"))
905 paritybit = ~PARENB;
906 else {
907 ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno);
908 paritybit = PARENB;
910 } else if (!strcasecmp(v->name, "charsize")) {
911 if (!strcasecmp(v->value, "7"))
912 charsize = CS7;
913 else if (!strcasecmp(v->value, "8"))
914 charsize = CS8;
915 else {
916 ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno);
917 charsize = CS7;
919 } else if (!strcasecmp(v->name, "twostopbits")) {
920 stopbits = ast_true(v->name);
921 } else if (!strcasecmp(v->name, "smdiport")) {
922 if (reload) {
923 /* we are reloading, check if we are already
924 * monitoring this interface, if we are we do
925 * not want to start it again. This also has
926 * the side effect of not updating different
927 * setting for the serial port, but it should
928 * be trivial to rewrite this section so that
929 * options on the port are changed without
930 * restarting the interface. Or the interface
931 * could be restarted with out emptying the
932 * queue. */
933 if ((iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
934 ast_log(LOG_NOTICE, "SMDI interface %s already running, not restarting\n", iface->name);
935 ASTOBJ_UNMARK(iface);
936 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
937 continue;
941 if (!(iface = alloc_smdi_interface()))
942 continue;
944 ast_copy_string(iface->name, v->value, sizeof(iface->name));
946 iface->thread = AST_PTHREADT_NULL;
948 if (!(iface->file = fopen(iface->name, "r"))) {
949 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s)\n", iface->name, strerror(errno));
950 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
951 continue;
954 iface->fd = fileno(iface->file);
956 /* Set the proper attributes for our serial port. */
958 /* get the current attributes from the port */
959 if (tcgetattr(iface->fd, &iface->mode)) {
960 ast_log(LOG_ERROR, "Error getting atributes of %s (%s)\n", iface->name, strerror(errno));
961 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
962 continue;
965 /* set the desired speed */
966 if (cfsetispeed(&iface->mode, baud_rate) || cfsetospeed(&iface->mode, baud_rate)) {
967 ast_log(LOG_ERROR, "Error setting baud rate on %s (%s)\n", iface->name, strerror(errno));
968 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
969 continue;
972 /* set the stop bits */
973 if (stopbits)
974 iface->mode.c_cflag = iface->mode.c_cflag | CSTOPB; /* set two stop bits */
975 else
976 iface->mode.c_cflag = iface->mode.c_cflag & ~CSTOPB; /* set one stop bit */
978 /* set the parity */
979 iface->mode.c_cflag = (iface->mode.c_cflag & ~PARENB & ~PARODD) | paritybit;
981 /* set the character size */
982 iface->mode.c_cflag = (iface->mode.c_cflag & ~CSIZE) | charsize;
984 /* commit the desired attributes */
985 if (tcsetattr(iface->fd, TCSAFLUSH, &iface->mode)) {
986 ast_log(LOG_ERROR, "Error setting attributes on %s (%s)\n", iface->name, strerror(errno));
987 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
988 continue;
991 /* set the msdstrip */
992 iface->msdstrip = msdstrip;
994 /* set the message expiry time */
995 iface->msg_expiry = msg_expiry;
997 /* start the listener thread */
998 if (option_verbose > 2)
999 ast_verbose(VERBOSE_PREFIX_3 "Starting SMDI monitor thread for %s\n", iface->name);
1000 if (ast_pthread_create_background(&iface->thread, NULL, smdi_read, iface)) {
1001 ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name);
1002 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
1003 continue;
1006 ASTOBJ_CONTAINER_LINK(&smdi_ifaces, iface);
1007 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
1008 ast_module_ref(ast_module_info->self);
1009 } else {
1010 ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file);
1014 destroy_all_mailbox_mappings();
1015 mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL;
1017 iface = NULL;
1019 for (v = ast_variable_browse(conf, "mailboxes"); v; v = v->next) {
1020 if (!strcasecmp(v->name, "smdiport")) {
1021 if (iface)
1022 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
1024 if (!(iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
1025 ast_log(LOG_NOTICE, "SMDI interface %s not found\n", iface->name);
1026 continue;
1028 } else if (!strcasecmp(v->name, "pollinginterval")) {
1029 if (sscanf(v->value, "%u", &mwi_monitor.polling_interval) != 1) {
1030 ast_log(LOG_ERROR, "Invalid value for pollinginterval: %s\n", v->value);
1031 mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL;
1033 } else {
1034 if (!iface) {
1035 ast_log(LOG_ERROR, "Mailbox mapping ignored, no valid SMDI interface specified in mailboxes section\n");
1036 continue;
1038 append_mailbox_mapping(v, iface);
1042 if (iface)
1043 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
1045 ast_config_destroy(conf);
1047 if (!AST_LIST_EMPTY(&mwi_monitor.mailbox_mappings) && mwi_monitor.thread == AST_PTHREADT_NULL
1048 && ast_pthread_create_background(&mwi_monitor.thread, NULL, mwi_monitor_handler, NULL)) {
1049 ast_log(LOG_ERROR, "Failed to start MWI monitoring thread. This module will not operate.\n");
1050 return AST_MODULE_LOAD_FAILURE;
1053 /* Prune any interfaces we should no longer monitor. */
1054 if (reload)
1055 ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces, ast_smdi_interface_destroy);
1057 ASTOBJ_CONTAINER_RDLOCK(&smdi_ifaces);
1058 /* TODO: this is bad, we need an ASTOBJ method for this! */
1059 if (!smdi_ifaces.head)
1060 res = 1;
1061 ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces);
1063 return res;
1066 struct smdi_msg_datastore {
1067 unsigned int id;
1068 struct ast_smdi_interface *iface;
1069 struct ast_smdi_md_message *md_msg;
1072 static void smdi_msg_datastore_destroy(void *data)
1074 struct smdi_msg_datastore *smd = data;
1076 if (smd->iface)
1077 ASTOBJ_UNREF(smd->iface, ast_smdi_interface_destroy);
1079 if (smd->md_msg)
1080 ASTOBJ_UNREF(smd->md_msg, ast_smdi_md_message_destroy);
1082 free(smd);
1085 static const struct ast_datastore_info smdi_msg_datastore_info = {
1086 .type = "SMDIMSG",
1087 .destroy = smdi_msg_datastore_destroy,
1090 static int smdi_msg_id;
1092 /*! In milliseconds */
1093 #define SMDI_RETRIEVE_TIMEOUT_DEFAULT 3000
1095 AST_APP_OPTIONS(smdi_msg_ret_options, BEGIN_OPTIONS
1096 AST_APP_OPTION('t', OPT_SEARCH_TERMINAL),
1097 AST_APP_OPTION('n', OPT_SEARCH_NUMBER),
1098 END_OPTIONS );
1100 static int smdi_msg_retrieve_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
1102 struct ast_module_user *u;
1103 AST_DECLARE_APP_ARGS(args,
1104 AST_APP_ARG(port);
1105 AST_APP_ARG(search_key);
1106 AST_APP_ARG(timeout);
1107 AST_APP_ARG(options);
1109 struct ast_flags options = { 0 };
1110 unsigned int timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT;
1111 int res = -1;
1112 char *parse = NULL;
1113 struct smdi_msg_datastore *smd = NULL;
1114 struct ast_datastore *datastore = NULL;
1115 struct ast_smdi_interface *iface = NULL;
1116 struct ast_smdi_md_message *md_msg = NULL;
1118 u = ast_module_user_add(chan);
1120 if (ast_strlen_zero(data)) {
1121 ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE requires an argument\n");
1122 goto return_error;
1125 if (!chan) {
1126 ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE must be used with a channel\n");
1127 goto return_error;
1130 ast_autoservice_start(chan);
1132 parse = ast_strdupa(data);
1133 AST_STANDARD_APP_ARGS(args, parse);
1135 if (ast_strlen_zero(args.port) || ast_strlen_zero(args.search_key)) {
1136 ast_log(LOG_ERROR, "Invalid arguments provided to SMDI_MSG_RETRIEVE\n");
1137 goto return_error;
1140 if (!(iface = ast_smdi_interface_find(args.port))) {
1141 ast_log(LOG_ERROR, "SMDI port '%s' not found\n", args.port);
1142 goto return_error;
1145 if (!ast_strlen_zero(args.options)) {
1146 ast_app_parse_options(smdi_msg_ret_options, &options, NULL, args.options);
1149 if (!ast_strlen_zero(args.timeout)) {
1150 if (sscanf(args.timeout, "%u", &timeout) != 1) {
1151 ast_log(LOG_ERROR, "'%s' is not a valid timeout\n", args.timeout);
1152 timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT;
1156 if (!(md_msg = smdi_message_wait(iface, timeout, SMDI_MD, args.search_key, options))) {
1157 ast_log(LOG_WARNING, "No SMDI message retrieved for search key '%s' after "
1158 "waiting %u ms.\n", args.search_key, timeout);
1159 goto return_error;
1162 if (!(smd = ast_calloc(1, sizeof(*smd))))
1163 goto return_error;
1165 smd->iface = ASTOBJ_REF(iface);
1166 smd->md_msg = ASTOBJ_REF(md_msg);
1167 smd->id = ast_atomic_fetchadd_int((int *) &smdi_msg_id, 1);
1168 snprintf(buf, len, "%u", smd->id);
1170 if (!(datastore = ast_channel_datastore_alloc(&smdi_msg_datastore_info, buf)))
1171 goto return_error;
1173 datastore->data = smd;
1175 ast_channel_lock(chan);
1176 ast_channel_datastore_add(chan, datastore);
1177 ast_channel_unlock(chan);
1179 res = 0;
1181 return_error:
1182 if (iface)
1183 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
1185 if (md_msg)
1186 ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
1188 if (smd && !datastore)
1189 smdi_msg_datastore_destroy(smd);
1191 if (parse)
1192 ast_autoservice_stop(chan);
1194 ast_module_user_remove(u);
1196 return res;
1199 static int smdi_msg_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
1201 struct ast_module_user *u;
1202 int res = -1;
1203 AST_DECLARE_APP_ARGS(args,
1204 AST_APP_ARG(id);
1205 AST_APP_ARG(component);
1207 char *parse;
1208 struct ast_datastore *datastore = NULL;
1209 struct smdi_msg_datastore *smd = NULL;
1211 u = ast_module_user_add(chan);
1213 if (!chan) {
1214 ast_log(LOG_ERROR, "SMDI_MSG can not be called without a channel\n");
1215 goto return_error;
1218 if (ast_strlen_zero(data)) {
1219 ast_log(LOG_WARNING, "SMDI_MSG requires an argument\n");
1220 goto return_error;
1223 parse = ast_strdupa(data);
1224 AST_STANDARD_APP_ARGS(args, parse);
1226 if (ast_strlen_zero(args.id)) {
1227 ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n");
1228 goto return_error;
1231 if (ast_strlen_zero(args.component)) {
1232 ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n");
1233 goto return_error;
1236 ast_channel_lock(chan);
1237 datastore = ast_channel_datastore_find(chan, &smdi_msg_datastore_info, args.id);
1238 ast_channel_unlock(chan);
1240 if (!datastore) {
1241 ast_log(LOG_WARNING, "No SMDI message found for message ID '%s'\n", args.id);
1242 goto return_error;
1245 smd = datastore->data;
1247 if (!strcasecmp(args.component, "number")) {
1248 ast_copy_string(buf, smd->md_msg->mesg_desk_num, len);
1249 } else if (!strcasecmp(args.component, "terminal")) {
1250 ast_copy_string(buf, smd->md_msg->mesg_desk_term, len);
1251 } else if (!strcasecmp(args.component, "station")) {
1252 ast_copy_string(buf, smd->md_msg->fwd_st, len);
1253 } else if (!strcasecmp(args.component, "callerid")) {
1254 ast_copy_string(buf, smd->md_msg->calling_st, len);
1255 } else if (!strcasecmp(args.component, "type")) {
1256 snprintf(buf, len, "%c", smd->md_msg->type);
1257 } else {
1258 ast_log(LOG_ERROR, "'%s' is not a valid message component for SMDI_MSG\n",
1259 args.component);
1260 goto return_error;
1263 res = 0;
1265 return_error:
1266 ast_module_user_remove(u);
1268 return res;
1271 static struct ast_custom_function smdi_msg_retrieve_function = {
1272 .name = "SMDI_MSG_RETRIEVE",
1273 .synopsis = "Retrieve an SMDI message.",
1274 .syntax = "SMDI_MSG_RETRIEVE(<smdi port>,<search key>[,timeout[,options]])",
1275 .desc =
1276 " This function is used to retrieve an incoming SMDI message. It returns\n"
1277 "an ID which can be used with the SMDI_MSG() function to access details of\n"
1278 "the message. Note that this is a destructive function in the sense that\n"
1279 "once an SMDI message is retrieved using this function, it is no longer in\n"
1280 "the global SMDI message queue, and can not be accessed by any other Asterisk\n"
1281 "channels. The timeout for this function is optional, and the default is\n"
1282 "3 seconds. When providing a timeout, it should be in milliseconds.\n"
1283 " The default search is done on the forwarding station ID. However, if\n"
1284 "you set one of the search key options in the options field, you can change\n"
1285 "this behavior.\n"
1286 " Options:\n"
1287 " t - Instead of searching on the forwarding station, search on the message\n"
1288 " desk terminal.\n"
1289 " n - Instead of searching on the forwarding station, search on the message\n"
1290 " desk number.\n"
1292 .read = smdi_msg_retrieve_read,
1295 static struct ast_custom_function smdi_msg_function = {
1296 .name = "SMDI_MSG",
1297 .synopsis = "Retrieve details about an SMDI message.",
1298 .syntax = "SMDI_MSG(<message_id>,<component>)",
1299 .desc =
1300 " This function is used to access details of an SMDI message that was\n"
1301 "pulled from the incoming SMDI message queue using the SMDI_MSG_RETRIEVE()\n"
1302 "function.\n"
1303 " Valid message components are:\n"
1304 " number - The message desk number\n"
1305 " terminal - The message desk terminal\n"
1306 " station - The forwarding station\n"
1307 " callerid - The callerID of the calling party that was forwarded\n"
1308 " type - The call type. The value here is the exact character\n"
1309 " that came in on the SMDI link. Typically, example values\n"
1310 " are: D - Direct Calls, A - Forward All Calls,\n"
1311 " B - Forward Busy Calls, N - Forward No Answer Calls\n"
1313 .read = smdi_msg_read,
1316 static int load_module(void)
1318 int res;
1320 /* initialize our containers */
1321 memset(&smdi_ifaces, 0, sizeof(smdi_ifaces));
1322 ASTOBJ_CONTAINER_INIT(&smdi_ifaces);
1324 ast_mutex_init(&mwi_monitor.lock);
1325 ast_cond_init(&mwi_monitor.cond, NULL);
1327 ast_custom_function_register(&smdi_msg_retrieve_function);
1328 ast_custom_function_register(&smdi_msg_function);
1330 /* load the config and start the listener threads*/
1331 res = smdi_load(0);
1332 if (res < 0) {
1333 return res;
1334 } else if (res == 1) {
1335 ast_log(LOG_WARNING, "No SMDI interfaces are available to listen on, not starting SMDI listener.\n");
1336 return AST_MODULE_LOAD_DECLINE;
1339 return 0;
1342 static int unload_module(void)
1344 /* this destructor stops any running smdi_read threads */
1345 ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces, ast_smdi_interface_destroy);
1346 ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces);
1348 destroy_all_mailbox_mappings();
1350 ast_mutex_lock(&mwi_monitor.lock);
1351 mwi_monitor.stop = 1;
1352 ast_cond_signal(&mwi_monitor.cond);
1353 ast_mutex_unlock(&mwi_monitor.lock);
1355 if (mwi_monitor.thread != AST_PTHREADT_NULL) {
1356 pthread_join(mwi_monitor.thread, NULL);
1359 ast_custom_function_unregister(&smdi_msg_retrieve_function);
1360 ast_custom_function_unregister(&smdi_msg_function);
1362 return 0;
1365 static int reload(void)
1367 int res;
1369 res = smdi_load(1);
1371 if (res < 0) {
1372 return res;
1373 } else if (res == 1) {
1374 ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n");
1375 return 0;
1376 } else
1377 return 0;
1380 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Simplified Message Desk Interface (SMDI) Resource",
1381 .load = load_module,
1382 .unload = unload_module,
1383 .reload = reload,