use a macro instead of an inline function, so that backtraces will report the caller...
[asterisk-bristuff.git] / res / res_smdi.c
bloba4d8494f63497e1eade71d916e3b6795bde86170
1 /*
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.
19 /*!
20 * \file
21 * \brief SMDI support for Asterisk.
22 * \author Matthew A. Nicholson <mnicholson@digium.com>
25 #include "asterisk.h"
27 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <termios.h>
33 #include <sys/time.h>
34 #include <time.h>
35 #include <ctype.h>
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);
64 } smdi_ifaces;
66 /*!
67 * \internal
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);
77 /*!
78 * \internal
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);
88 /*!
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)
95 FILE *file;
96 int i;
98 file = fopen(iface->name, "w");
99 if(!file) {
100 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
101 return 1;
104 ASTOBJ_WRLOCK(iface);
106 fprintf(file, "OP:MWI ");
108 for(i = 0; i < iface->msdstrip; i++)
109 fprintf(file, "0");
111 fprintf(file, "%s!\x04", mailbox);
112 fclose(file);
114 ASTOBJ_UNLOCK(iface);
115 ast_log(LOG_DEBUG, "Sent MWI set message for %s on %s\n", mailbox, iface->name);
116 return 0;
119 /*!
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)
126 FILE *file;
127 int i;
129 file = fopen(iface->name, "w");
130 if(!file) {
131 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
132 return 1;
135 ASTOBJ_WRLOCK(iface);
137 fprintf(file, "RMV:MWI ");
139 for(i = 0; i < iface->msdstrip; i++)
140 fprintf(file, "0");
142 fprintf(file, "%s!\x04", mailbox);
143 fclose(file);
145 ASTOBJ_UNLOCK(iface);
146 ast_log(LOG_DEBUG, "Sent MWI unset message for %s on %s\n", mailbox, iface->name);
147 return 0;
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);
178 /*!
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
184 * returning.
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);
191 struct timeval now;
192 long elapsed = 0;
194 /* purge old messages */
195 now = ast_tvnow();
196 while (md_msg) {
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);
206 else {
207 /* good message, return it */
208 break;
212 return md_msg;
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;
230 long diff = 0;
231 struct ast_smdi_md_message *msg;
233 start = ast_tvnow();
234 while (diff < timeout) {
236 if ((msg = ast_smdi_md_message_pop(iface)))
237 return msg;
239 /* check timeout */
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
252 * returning.
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);
259 struct timeval now;
260 long elapsed = 0;
262 /* purge old messages */
263 now = ast_tvnow();
264 while (mwi_msg) {
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);
274 else {
275 /* good message, return it */
276 break;
280 return mwi_msg;
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;
298 long diff = 0;
299 struct ast_smdi_mwi_message *msg;
301 start = ast_tvnow();
302 while (diff < timeout) {
304 if ((msg = ast_smdi_mwi_message_pop(iface)))
305 return msg;
307 /* check timeout */
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;
339 char c = '\0';
340 char *cp = NULL;
341 int i;
342 int start = 0;
344 /* read an smdi message */
345 while ((c = fgetc(iface->file))) {
347 /* check if this is the start of a message */
348 if (!start) {
349 if (c == 'M')
350 start = 1;
352 else { /* Determine if this is a MD or MWI message */
353 if(c == 'D') { /* MD message */
354 start = 0;
356 if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
357 ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
358 return NULL;
361 ASTOBJ_INIT(md_msg);
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)) == ' ') {
382 *cp = '\0';
383 break;
386 /* store c in md_msg->fwd_st */
387 if( i >= iface->msdstrip)
388 *cp++ = c;
391 /* make sure the value is null terminated, even if this truncates it */
392 md_msg->fwd_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
393 cp = NULL;
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)))) {
399 *cp = '\0';
400 break;
403 /* store c in md_msg->calling_st */
404 if (i >= iface->msdstrip)
405 *cp++ = c;
408 /* make sure the value is null terminated, even if this truncates it */
409 md_msg->calling_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
410 cp = NULL;
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 */
420 start = 0;
422 if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
423 ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
424 return NULL;
427 ASTOBJ_INIT(mwi_msg);
429 /* discard the 'I' (from 'MWI') */
430 fgetc(iface->file);
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)) == ' ') {
436 *cp = '\0';
437 break;
440 /* store c in md_msg->fwd_st */
441 if (i >= iface->msdstrip)
442 *cp++ = c;
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';
447 cp = NULL;
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);
461 } else {
462 ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c);
463 start = 0;
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);
470 return NULL;
473 /*! \brief ast_smdi_md_message destructor. */
474 void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg)
476 free(msg);
479 /*! \brief ast_smdi_mwi_message destructor. */
480 void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
482 free(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;
495 if(iface->file)
496 fclose(iface->file);
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);
502 free(iface);
504 ast_module_unref(ast_module_info->self);
508 * \internal
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;
522 int res = 0;
524 /* Config options */
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);
535 if (!conf) {
536 if (reload)
537 ast_log(LOG_NOTICE, "Unable to reload config %s: SMDI untouched\n", config_file);
538 else
539 ast_log(LOG_NOTICE, "Unable to load config %s: SMDI disabled\n", config_file);
540 return 1;
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
546 * be stopped.
548 if (reload)
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"))
554 baud_rate = B9600;
555 else if(!strcasecmp(v->value, "4800"))
556 baud_rate = B4800;
557 else if(!strcasecmp(v->value, "2400"))
558 baud_rate = B2400;
559 else if(!strcasecmp(v->value, "1200"))
560 baud_rate = B1200;
561 else {
562 ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno);
563 baud_rate = B9600;
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);
568 msdstrip = 0;
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);
571 msdstrip = 0;
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"))
580 paritybit = PARENB;
581 else if (!strcasecmp(v->value, "odd"))
582 paritybit = PARENB | PARODD;
583 else if (!strcasecmp(v->value, "none"))
584 paritybit = ~PARENB;
585 else {
586 ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno);
587 paritybit = PARENB;
589 } else if (!strcasecmp(v->name, "charsize")) {
590 if (!strcasecmp(v->value, "7"))
591 charsize = CS7;
592 else if (!strcasecmp(v->value, "8"))
593 charsize = CS8;
594 else {
595 ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno);
596 charsize = CS7;
598 } else if (!strcasecmp(v->name, "twostopbits")) {
599 stopbits = ast_true(v->name);
600 } else if (!strcasecmp(v->name, "smdiport")) {
601 if (reload) {
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
611 * queue. */
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);
616 continue;
620 if (!(iface = ast_calloc(1, sizeof(*iface))))
621 continue;
623 ASTOBJ_INIT(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);
632 continue;
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);
643 continue;
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);
650 continue;
653 /* set the stop bits */
654 if (stopbits)
655 iface->mode.c_cflag = iface->mode.c_cflag | CSTOPB; /* set two stop bits */
656 else
657 iface->mode.c_cflag = iface->mode.c_cflag & ~CSTOPB; /* set one stop bit */
659 /* set the parity */
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);
669 continue;
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);
684 continue;
687 ASTOBJ_CONTAINER_LINK(&smdi_ifaces, iface);
688 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
689 ast_module_ref(ast_module_info->self);
690 } else {
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. */
697 if (reload)
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)
703 res = 1;
704 ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces);
706 return res;
709 static int load_module(void)
711 int res;
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*/
718 res = smdi_load(0);
719 if (res < 0) {
720 return res;
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;;
724 } else
725 return 0;
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);
734 return 0;
737 static int reload(void)
739 int res;
741 res = smdi_load(1);
743 if (res < 0) {
744 return res;
745 } else if (res == 1) {
746 ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n");
747 return 0;
748 } else
749 return 0;
752 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Simplified Message Desk Interface (SMDI) Resource",
753 .load = load_module,
754 .unload = unload_module,
755 .reload = reload,