2 * Asterisk -- A telephony toolkit for Linux.
4 * Copyright (C) 2005-2006, Digium, Inc.
6 * Matthew A. Nicholson <mnicholson@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief SMDI support for Asterisk.
22 * \author Matthew A. Nicholson <mnicholson@digium.com>
27 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
37 #include "asterisk/module.h"
38 #include "asterisk/lock.h"
39 #include "asterisk/utils.h"
40 #include "asterisk/smdi.h"
41 #include "asterisk/config.h"
42 #include "asterisk/astobj.h"
43 #include "asterisk/io.h"
44 #include "asterisk/logger.h"
45 #include "asterisk/utils.h"
46 #include "asterisk/options.h"
48 /* Message expiry time in milliseconds */
49 #define SMDI_MSG_EXPIRY_TIME 30000 /* 30 seconds */
51 static const char config_file
[] = "smdi.conf";
53 static void ast_smdi_md_message_push(struct ast_smdi_interface
*iface
, struct ast_smdi_md_message
*msg
);
54 static void ast_smdi_mwi_message_push(struct ast_smdi_interface
*iface
, struct ast_smdi_mwi_message
*msg
);
56 static void *smdi_read(void *iface_p
);
57 static int smdi_load(int reload
);
59 struct module_symbols
*me
; /* initialized in load_module() */
61 /*! \brief SMDI interface container. */
62 struct ast_smdi_interface_container
{
63 ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface
);
68 * \brief Push an SMDI message to the back of an interface's message queue.
69 * \param iface a pointer to the interface to use.
70 * \param md_msg a pointer to the message to use.
72 static void ast_smdi_md_message_push(struct ast_smdi_interface
*iface
, struct ast_smdi_md_message
*md_msg
)
74 ASTOBJ_CONTAINER_LINK_END(&iface
->md_q
, md_msg
);
79 * \brief Push an SMDI message to the back of an interface's message queue.
80 * \param iface a pointer to the interface to use.
81 * \param mwi_msg a pointer to the message to use.
83 static void ast_smdi_mwi_message_push(struct ast_smdi_interface
*iface
, struct ast_smdi_mwi_message
*mwi_msg
)
85 ASTOBJ_CONTAINER_LINK_END(&iface
->mwi_q
, mwi_msg
);
89 * \brief Set the MWI indicator for a mailbox.
90 * \param iface the interface to use.
91 * \param mailbox the mailbox to use.
93 int ast_smdi_mwi_set(struct ast_smdi_interface
*iface
, const char *mailbox
)
98 file
= fopen(iface
->name
, "w");
100 ast_log(LOG_ERROR
, "Error opening SMDI interface %s (%s) for writing\n", iface
->name
, strerror(errno
));
104 ASTOBJ_WRLOCK(iface
);
106 fprintf(file
, "OP:MWI ");
108 for(i
= 0; i
< iface
->msdstrip
; i
++)
111 fprintf(file
, "%s!\x04", mailbox
);
114 ASTOBJ_UNLOCK(iface
);
115 ast_log(LOG_DEBUG
, "Sent MWI set message for %s on %s\n", mailbox
, iface
->name
);
120 * \brief Unset the MWI indicator for a mailbox.
121 * \param iface the interface to use.
122 * \param mailbox the mailbox to use.
124 int ast_smdi_mwi_unset(struct ast_smdi_interface
*iface
, const char *mailbox
)
129 file
= fopen(iface
->name
, "w");
131 ast_log(LOG_ERROR
, "Error opening SMDI interface %s (%s) for writing\n", iface
->name
, strerror(errno
));
135 ASTOBJ_WRLOCK(iface
);
137 fprintf(file
, "RMV:MWI ");
139 for(i
= 0; i
< iface
->msdstrip
; i
++)
142 fprintf(file
, "%s!\x04", mailbox
);
145 ASTOBJ_UNLOCK(iface
);
146 ast_log(LOG_DEBUG
, "Sent MWI unset message for %s on %s\n", mailbox
, iface
->name
);
151 * \brief Put an SMDI message back in the front of the queue.
152 * \param iface a pointer to the interface to use.
153 * \param md_msg a pointer to the message to use.
155 * This function puts a message back in the front of the specified queue. It
156 * should be used if a message was popped but is not going to be processed for
157 * some reason, and the message needs to be returned to the queue.
159 void ast_smdi_md_message_putback(struct ast_smdi_interface
*iface
, struct ast_smdi_md_message
*md_msg
)
161 ASTOBJ_CONTAINER_LINK_START(&iface
->md_q
, md_msg
);
165 * \brief Put an SMDI message back in the front of the queue.
166 * \param iface a pointer to the interface to use.
167 * \param mwi_msg a pointer to the message to use.
169 * This function puts a message back in the front of the specified queue. It
170 * should be used if a message was popped but is not going to be processed for
171 * some reason, and the message needs to be returned to the queue.
173 void ast_smdi_mwi_message_putback(struct ast_smdi_interface
*iface
, struct ast_smdi_mwi_message
*mwi_msg
)
175 ASTOBJ_CONTAINER_LINK_START(&iface
->mwi_q
, mwi_msg
);
179 * \brief Get the next SMDI message from the queue.
180 * \param iface a pointer to the interface to use.
182 * This function pulls the first unexpired message from the SMDI message queue
183 * on the specified interface. It will purge all expired SMDI messages before
186 * \return the next SMDI message, or NULL if there were no pending messages.
188 struct ast_smdi_md_message
*ast_smdi_md_message_pop(struct ast_smdi_interface
*iface
)
190 struct ast_smdi_md_message
*md_msg
= ASTOBJ_CONTAINER_UNLINK_START(&iface
->md_q
);
194 /* purge old messages */
197 elapsed
= ast_tvdiff_ms(now
, md_msg
->timestamp
);
199 if (elapsed
> iface
->msg_expiry
) {
200 /* found an expired message */
201 ASTOBJ_UNREF(md_msg
, ast_smdi_md_message_destroy
);
202 ast_log(LOG_NOTICE
, "Purged expired message from %s SMDI MD message queue. Message was %ld milliseconds too old.\n",
203 iface
->name
, elapsed
- iface
->msg_expiry
);
204 md_msg
= ASTOBJ_CONTAINER_UNLINK_START(&iface
->md_q
);
207 /* good message, return it */
216 * \brief Get the next SMDI message from the queue.
217 * \param iface a pointer to the interface to use.
218 * \param timeout the time to wait before returning in milliseconds.
220 * This function pulls a message from the SMDI message queue on the specified
221 * interface. If no message is available this function will wait the specified
222 * amount of time before returning.
224 * \return the next SMDI message, or NULL if there were no pending messages and
225 * the timeout has expired.
227 extern struct ast_smdi_md_message
*ast_smdi_md_message_wait(struct ast_smdi_interface
*iface
, int timeout
)
229 struct timeval start
;
231 struct ast_smdi_md_message
*msg
;
234 while (diff
< timeout
) {
236 if ((msg
= ast_smdi_md_message_pop(iface
)))
240 diff
= ast_tvdiff_ms(ast_tvnow(), start
);
243 return (ast_smdi_md_message_pop(iface
));
247 * \brief Get the next SMDI message from the queue.
248 * \param iface a pointer to the interface to use.
250 * This function pulls the first unexpired message from the SMDI message queue
251 * on the specified interface. It will purge all expired SMDI messages before
254 * \return the next SMDI message, or NULL if there were no pending messages.
256 extern struct ast_smdi_mwi_message
*ast_smdi_mwi_message_pop(struct ast_smdi_interface
*iface
)
258 struct ast_smdi_mwi_message
*mwi_msg
= ASTOBJ_CONTAINER_UNLINK_START(&iface
->mwi_q
);
262 /* purge old messages */
265 elapsed
= ast_tvdiff_ms(now
, mwi_msg
->timestamp
);
267 if (elapsed
> iface
->msg_expiry
) {
268 /* found an expired message */
269 ASTOBJ_UNREF(mwi_msg
, ast_smdi_mwi_message_destroy
);
270 ast_log(LOG_NOTICE
, "Purged expired message from %s SMDI MWI message queue. Message was %ld milliseconds too old.\n",
271 iface
->name
, elapsed
- iface
->msg_expiry
);
272 mwi_msg
= ASTOBJ_CONTAINER_UNLINK_START(&iface
->mwi_q
);
275 /* good message, return it */
284 * \brief Get the next SMDI message from the queue.
285 * \param iface a pointer to the interface to use.
286 * \param timeout the time to wait before returning in milliseconds.
288 * This function pulls a message from the SMDI message queue on the specified
289 * interface. If no message is available this function will wait the specified
290 * amount of time before returning.
292 * \return the next SMDI message, or NULL if there were no pending messages and
293 * the timeout has expired.
295 extern struct ast_smdi_mwi_message
*ast_smdi_mwi_message_wait(struct ast_smdi_interface
*iface
, int timeout
)
297 struct timeval start
;
299 struct ast_smdi_mwi_message
*msg
;
302 while (diff
< timeout
) {
304 if ((msg
= ast_smdi_mwi_message_pop(iface
)))
308 diff
= ast_tvdiff_ms(ast_tvnow(), start
);
311 return (ast_smdi_mwi_message_pop(iface
));
315 * \brief Find an SMDI interface with the specified name.
316 * \param iface_name the name/port of the interface to search for.
318 * \return a pointer to the interface located or NULL if none was found. This
319 * actually returns an ASTOBJ reference and should be released using
320 * #ASTOBJ_UNREF(iface, ast_smdi_interface_destroy).
322 extern struct ast_smdi_interface
*ast_smdi_interface_find(const char *iface_name
)
324 return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces
, iface_name
));
327 /*! \brief Read an SMDI message.
329 * \param iface_p the SMDI interface to read from.
331 * This function loops and reads from and SMDI interface. It must be stopped
332 * using pthread_cancel().
334 static void *smdi_read(void *iface_p
)
336 struct ast_smdi_interface
*iface
= iface_p
;
337 struct ast_smdi_md_message
*md_msg
;
338 struct ast_smdi_mwi_message
*mwi_msg
;
344 /* read an smdi message */
345 while ((c
= fgetc(iface
->file
))) {
347 /* check if this is the start of a message */
352 else { /* Determine if this is a MD or MWI message */
353 if(c
== 'D') { /* MD message */
356 if (!(md_msg
= ast_calloc(1, sizeof(*md_msg
)))) {
357 ASTOBJ_UNREF(iface
,ast_smdi_interface_destroy
);
363 /* read the message desk number */
364 for(i
= 0; i
< SMDI_MESG_DESK_NUM_LEN
; i
++)
365 md_msg
->mesg_desk_num
[i
] = fgetc(iface
->file
);
367 md_msg
->mesg_desk_num
[SMDI_MESG_DESK_NUM_LEN
] = '\0';
369 /* read the message desk terminal number */
370 for(i
= 0; i
< SMDI_MESG_DESK_TERM_LEN
; i
++)
371 md_msg
->mesg_desk_term
[i
] = fgetc(iface
->file
);
373 md_msg
->mesg_desk_term
[SMDI_MESG_DESK_TERM_LEN
] = '\0';
375 /* read the message type */
376 md_msg
->type
= fgetc(iface
->file
);
378 /* read the forwarding station number (may be blank) */
379 cp
= &md_msg
->fwd_st
[0];
380 for (i
= 0; i
< SMDI_MAX_STATION_NUM_LEN
+ 1; i
++) {
381 if((c
= fgetc(iface
->file
)) == ' ') {
386 /* store c in md_msg->fwd_st */
387 if( i
>= iface
->msdstrip
)
391 /* make sure the value is null terminated, even if this truncates it */
392 md_msg
->fwd_st
[SMDI_MAX_STATION_NUM_LEN
] = '\0';
395 /* read the calling station number (may be blank) */
396 cp
= &md_msg
->calling_st
[0];
397 for (i
= 0; i
< SMDI_MAX_STATION_NUM_LEN
+ 1; i
++) {
398 if (!isdigit((c
= fgetc(iface
->file
)))) {
403 /* store c in md_msg->calling_st */
404 if (i
>= iface
->msdstrip
)
408 /* make sure the value is null terminated, even if this truncates it */
409 md_msg
->calling_st
[SMDI_MAX_STATION_NUM_LEN
] = '\0';
412 /* add the message to the message queue */
413 md_msg
->timestamp
= ast_tvnow();
414 ast_smdi_md_message_push(iface
, md_msg
);
415 ast_log(LOG_DEBUG
, "Recieved SMDI MD message on %s\n", iface
->name
);
417 ASTOBJ_UNREF(md_msg
, ast_smdi_md_message_destroy
);
419 } else if(c
== 'W') { /* MWI message */
422 if (!(mwi_msg
= ast_calloc(1, sizeof(*mwi_msg
)))) {
423 ASTOBJ_UNREF(iface
,ast_smdi_interface_destroy
);
427 ASTOBJ_INIT(mwi_msg
);
429 /* discard the 'I' (from 'MWI') */
432 /* read the forwarding station number (may be blank) */
433 cp
= &mwi_msg
->fwd_st
[0];
434 for (i
= 0; i
< SMDI_MAX_STATION_NUM_LEN
+ 1; i
++) {
435 if ((c
= fgetc(iface
->file
)) == ' ') {
440 /* store c in md_msg->fwd_st */
441 if (i
>= iface
->msdstrip
)
445 /* make sure the station number is null terminated, even if this will truncate it */
446 mwi_msg
->fwd_st
[SMDI_MAX_STATION_NUM_LEN
] = '\0';
449 /* read the mwi failure cause */
450 for (i
= 0; i
< SMDI_MWI_FAIL_CAUSE_LEN
; i
++)
451 mwi_msg
->cause
[i
] = fgetc(iface
->file
);
453 mwi_msg
->cause
[SMDI_MWI_FAIL_CAUSE_LEN
] = '\0';
455 /* add the message to the message queue */
456 mwi_msg
->timestamp
= ast_tvnow();
457 ast_smdi_mwi_message_push(iface
, mwi_msg
);
458 ast_log(LOG_DEBUG
, "Recieved SMDI MWI message on %s\n", iface
->name
);
460 ASTOBJ_UNREF(mwi_msg
, ast_smdi_mwi_message_destroy
);
462 ast_log(LOG_ERROR
, "Unknown SMDI message type recieved on %s (M%c).\n", iface
->name
, c
);
468 ast_log(LOG_ERROR
, "Error reading from SMDI interface %s, stopping listener thread\n", iface
->name
);
469 ASTOBJ_UNREF(iface
,ast_smdi_interface_destroy
);
473 /*! \brief ast_smdi_md_message destructor. */
474 void ast_smdi_md_message_destroy(struct ast_smdi_md_message
*msg
)
479 /*! \brief ast_smdi_mwi_message destructor. */
480 void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message
*msg
)
485 /*! \brief ast_smdi_interface destructor. */
486 void ast_smdi_interface_destroy(struct ast_smdi_interface
*iface
)
488 if (iface
->thread
!= AST_PTHREADT_NULL
&& iface
->thread
!= AST_PTHREADT_STOP
) {
489 pthread_cancel(iface
->thread
);
490 pthread_join(iface
->thread
, NULL
);
493 iface
->thread
= AST_PTHREADT_STOP
;
498 ASTOBJ_CONTAINER_DESTROYALL(&iface
->md_q
, ast_smdi_md_message_destroy
);
499 ASTOBJ_CONTAINER_DESTROYALL(&iface
->mwi_q
, ast_smdi_mwi_message_destroy
);
500 ASTOBJ_CONTAINER_DESTROY(&iface
->md_q
);
501 ASTOBJ_CONTAINER_DESTROY(&iface
->mwi_q
);
504 ast_module_unref(ast_module_info
->self
);
509 * \brief Load and reload SMDI configuration.
510 * \param reload this should be 1 if we are reloading and 0 if not.
512 * This function loads/reloads the SMDI configuration and starts and stops
513 * interfaces accordingly.
515 * \return zero on success, -1 on failure, and 1 if no smdi interfaces were started.
517 static int smdi_load(int reload
)
519 struct ast_config
*conf
;
520 struct ast_variable
*v
;
521 struct ast_smdi_interface
*iface
= NULL
;
525 speed_t baud_rate
= B9600
; /* 9600 baud rate */
526 tcflag_t paritybit
= PARENB
; /* even parity checking */
527 tcflag_t charsize
= CS7
; /* seven bit characters */
528 int stopbits
= 0; /* One stop bit */
530 int msdstrip
= 0; /* strip zero digits */
531 long msg_expiry
= SMDI_MSG_EXPIRY_TIME
;
533 conf
= ast_config_load(config_file
);
537 ast_log(LOG_NOTICE
, "Unable to reload config %s: SMDI untouched\n", config_file
);
539 ast_log(LOG_NOTICE
, "Unable to load config %s: SMDI disabled\n", config_file
);
543 /* Mark all interfaces that we are listening on. We will unmark them
544 * as we find them in the config file, this way we know any interfaces
545 * still marked after we have finished parsing the config file should
549 ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces
);
551 for (v
= ast_variable_browse(conf
, "interfaces"); v
; v
= v
->next
) {
552 if (!strcasecmp(v
->name
, "baudrate")) {
553 if (!strcasecmp(v
->value
, "9600"))
555 else if(!strcasecmp(v
->value
, "4800"))
557 else if(!strcasecmp(v
->value
, "2400"))
559 else if(!strcasecmp(v
->value
, "1200"))
562 ast_log(LOG_NOTICE
, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v
->value
, config_file
, v
->lineno
);
565 } else if (!strcasecmp(v
->name
, "msdstrip")) {
566 if (!sscanf(v
->value
, "%d", &msdstrip
)) {
567 ast_log(LOG_NOTICE
, "Invalid msdstrip value in %s (line %d), using default\n", config_file
, v
->lineno
);
569 } else if (0 > msdstrip
|| msdstrip
> 9) {
570 ast_log(LOG_NOTICE
, "Invalid msdstrip value in %s (line %d), using default\n", config_file
, v
->lineno
);
573 } else if (!strcasecmp(v
->name
, "msgexpirytime")) {
574 if (!sscanf(v
->value
, "%ld", &msg_expiry
)) {
575 ast_log(LOG_NOTICE
, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file
, v
->lineno
);
576 msg_expiry
= SMDI_MSG_EXPIRY_TIME
;
578 } else if (!strcasecmp(v
->name
, "paritybit")) {
579 if (!strcasecmp(v
->value
, "even"))
581 else if (!strcasecmp(v
->value
, "odd"))
582 paritybit
= PARENB
| PARODD
;
583 else if (!strcasecmp(v
->value
, "none"))
586 ast_log(LOG_NOTICE
, "Invalid parity bit setting in %s (line %d), using default\n", config_file
, v
->lineno
);
589 } else if (!strcasecmp(v
->name
, "charsize")) {
590 if (!strcasecmp(v
->value
, "7"))
592 else if (!strcasecmp(v
->value
, "8"))
595 ast_log(LOG_NOTICE
, "Invalid character size setting in %s (line %d), using default\n", config_file
, v
->lineno
);
598 } else if (!strcasecmp(v
->name
, "twostopbits")) {
599 stopbits
= ast_true(v
->name
);
600 } else if (!strcasecmp(v
->name
, "smdiport")) {
602 /* we are reloading, check if we are already
603 * monitoring this interface, if we are we do
604 * not want to start it again. This also has
605 * the side effect of not updating different
606 * setting for the serial port, but it should
607 * be trivial to rewrite this section so that
608 * options on the port are changed without
609 * restarting the interface. Or the interface
610 * could be restarted with out emptying the
612 if ((iface
= ASTOBJ_CONTAINER_FIND(&smdi_ifaces
, v
->value
))) {
613 ast_log(LOG_NOTICE
, "SMDI interface %s already running, not restarting\n", iface
->name
);
614 ASTOBJ_UNMARK(iface
);
615 ASTOBJ_UNREF(iface
, ast_smdi_interface_destroy
);
620 if (!(iface
= ast_calloc(1, sizeof(*iface
))))
624 ASTOBJ_CONTAINER_INIT(&iface
->md_q
);
625 ASTOBJ_CONTAINER_INIT(&iface
->mwi_q
);
627 ast_copy_string(iface
->name
, v
->value
, sizeof(iface
->name
));
629 if (!(iface
->file
= fopen(iface
->name
, "r"))) {
630 ast_log(LOG_ERROR
, "Error opening SMDI interface %s (%s)\n", iface
->name
, strerror(errno
));
631 ASTOBJ_UNREF(iface
, ast_smdi_interface_destroy
);
635 iface
->fd
= fileno(iface
->file
);
637 /* Set the proper attributes for our serial port. */
639 /* get the current attributes from the port */
640 if (tcgetattr(iface
->fd
, &iface
->mode
)) {
641 ast_log(LOG_ERROR
, "Error getting atributes of %s (%s)\n", iface
->name
, strerror(errno
));
642 ASTOBJ_UNREF(iface
, ast_smdi_interface_destroy
);
646 /* set the desired speed */
647 if (cfsetispeed(&iface
->mode
, baud_rate
) || cfsetospeed(&iface
->mode
, baud_rate
)) {
648 ast_log(LOG_ERROR
, "Error setting baud rate on %s (%s)\n", iface
->name
, strerror(errno
));
649 ASTOBJ_UNREF(iface
, ast_smdi_interface_destroy
);
653 /* set the stop bits */
655 iface
->mode
.c_cflag
= iface
->mode
.c_cflag
| CSTOPB
; /* set two stop bits */
657 iface
->mode
.c_cflag
= iface
->mode
.c_cflag
& ~CSTOPB
; /* set one stop bit */
660 iface
->mode
.c_cflag
= (iface
->mode
.c_cflag
& ~PARENB
& ~PARODD
) | paritybit
;
662 /* set the character size */
663 iface
->mode
.c_cflag
= (iface
->mode
.c_cflag
& ~CSIZE
) | charsize
;
665 /* commit the desired attributes */
666 if (tcsetattr(iface
->fd
, TCSAFLUSH
, &iface
->mode
)) {
667 ast_log(LOG_ERROR
, "Error setting attributes on %s (%s)\n", iface
->name
, strerror(errno
));
668 ASTOBJ_UNREF(iface
, ast_smdi_interface_destroy
);
672 /* set the msdstrip */
673 iface
->msdstrip
= msdstrip
;
675 /* set the message expiry time */
676 iface
->msg_expiry
= msg_expiry
;
678 /* start the listner thread */
679 if (option_verbose
> 2)
680 ast_verbose(VERBOSE_PREFIX_3
"Starting SMDI monitor thread for %s\n", iface
->name
);
681 if (ast_pthread_create_background(&iface
->thread
, NULL
, smdi_read
, iface
)) {
682 ast_log(LOG_ERROR
, "Error starting SMDI monitor thread for %s\n", iface
->name
);
683 ASTOBJ_UNREF(iface
, ast_smdi_interface_destroy
);
687 ASTOBJ_CONTAINER_LINK(&smdi_ifaces
, iface
);
688 ASTOBJ_UNREF(iface
, ast_smdi_interface_destroy
);
689 ast_module_ref(ast_module_info
->self
);
691 ast_log(LOG_NOTICE
, "Ignoring unknown option %s in %s\n", v
->name
, config_file
);
694 ast_config_destroy(conf
);
696 /* Prune any interfaces we should no longer monitor. */
698 ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces
, ast_smdi_interface_destroy
);
700 ASTOBJ_CONTAINER_RDLOCK(&smdi_ifaces
);
701 /* TODO: this is bad, we need an ASTOBJ method for this! */
702 if (!smdi_ifaces
.head
)
704 ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces
);
709 static int load_module(void)
713 /* initialize our containers */
714 memset(&smdi_ifaces
, 0, sizeof(smdi_ifaces
));
715 ASTOBJ_CONTAINER_INIT(&smdi_ifaces
);
717 /* load the config and start the listener threads*/
721 } else if (res
== 1) {
722 ast_log(LOG_WARNING
, "No SMDI interfaces are available to listen on, not starting SDMI listener.\n");
723 return AST_MODULE_LOAD_DECLINE
;;
728 static int unload_module(void)
730 /* this destructor stops any running smdi_read threads */
731 ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces
, ast_smdi_interface_destroy
);
732 ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces
);
737 static int reload(void)
745 } else if (res
== 1) {
746 ast_log(LOG_WARNING
, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n");
752 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_GLOBAL_SYMBOLS
, "Simplified Message Desk Interface (SMDI) Resource",
754 .unload
= unload_module
,