2f8515440c84f20b11c028a398b1a3b36e3f8b2f
[asterisk/asterisk.git] / res / res_smdi.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Copyright (C) 2005-2006, Digium, Inc.
5  *
6  * Matthew A. Nicholson <mnicholson@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*!
20  * \file
21  * \brief SMDI support for Asterisk.
22  * \author Matthew A. Nicholson <mnicholson@digium.com>
23  */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <termios.h>
29 #include <sys/time.h>
30 #include <time.h>
31 #include <ctype.h>
32
33 #include "asterisk.h"
34
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36
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"
47
48 /* Message expiry time in milliseconds */
49 #define SMDI_MSG_EXPIRY_TIME    30000 /* 30 seconds */
50
51 static const char config_file[] = "smdi.conf";
52
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);
55
56 static void *smdi_read(void *iface_p);
57 static int smdi_load(int reload);
58
59 struct module_symbols *me;      /* initialized in load_module() */
60
61 /*! \brief SMDI interface container. */
62 struct ast_smdi_interface_container {
63         ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface);
64 } smdi_ifaces;
65
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.
71  */
72 static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
73 {
74         ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg);
75 }
76
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.
82  */
83 static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
84 {
85         ASTOBJ_CONTAINER_LINK_END(&iface->mwi_q, mwi_msg);
86 }
87
88 /*!
89  * \brief Set the MWI indicator for a mailbox.
90  * \param iface the interface to use.
91  * \param mailbox the mailbox to use.
92  */
93 int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
94 {
95         FILE *file;
96         int i;
97         
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;
102         }       
103
104         ASTOBJ_WRLOCK(iface);
105
106         fprintf(file, "OP:MWI ");
107
108         for(i = 0; i < iface->msdstrip; i++)
109            fprintf(file, "0");
110
111         fprintf(file, "%s!\x04", mailbox);
112         fclose(file);
113
114         ASTOBJ_UNLOCK(iface);
115         ast_log(LOG_DEBUG, "Sent MWI set message for %s on %s\n", mailbox, iface->name);
116         return 0;
117 }
118
119 /*! 
120  * \brief Unset the MWI indicator for a mailbox.
121  * \param iface the interface to use.
122  * \param mailbox the mailbox to use.
123  */
124 int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
125 {
126         FILE *file;
127         int i;
128         
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;
133         }       
134
135         ASTOBJ_WRLOCK(iface);
136
137         fprintf(file, "RMV:MWI ");
138
139         for(i = 0; i < iface->msdstrip; i++)
140            fprintf(file, "0");
141
142         fprintf(file, "%s!\x04", mailbox);
143         fclose(file);
144
145         ASTOBJ_UNLOCK(iface);
146         ast_log(LOG_DEBUG, "Sent MWI unset message for %s on %s", mailbox, iface->name);
147         return 0;
148 }
149
150 /*!
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 msg a pointer to the message to use.
154  *
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.
158  */
159 void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
160 {
161         ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg);
162 }
163
164 /*!
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 msg a pointer to the message to use.
168  *
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.
172  */
173 void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
174 {
175         ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg);
176 }
177
178 /*! 
179  * \brief Get the next SMDI message from the queue.
180  * \param iface a pointer to the interface to use.
181  *
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.
185  *
186  * \return the next SMDI message, or NULL if there were no pending messages.
187  */
188 struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
189 {
190         struct ast_smdi_md_message *md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
191         struct timeval now;
192         long elapsed = 0;
193
194         /* purge old messages */
195         now = ast_tvnow();
196         while (md_msg) {
197                 elapsed = ast_tvdiff_ms(now, md_msg->timestamp);
198
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.",
203                                 iface->name, elapsed - iface->msg_expiry);
204                         md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
205                 }
206                 else {
207                         /* good message, return it */
208                         break;
209                 }
210         }
211
212         return md_msg;
213 }
214
215 /*!
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.
219  *
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.
223  *
224  * \return the next SMDI message, or NULL if there were no pending messages and
225  * the timeout has expired.
226  */
227 extern struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
228 {
229         struct timeval start;
230         long diff = 0;
231         struct ast_smdi_md_message *msg;
232
233         start = ast_tvnow();
234         while (diff < timeout) {
235
236                 if ((msg = ast_smdi_md_message_pop(iface)))
237                         return msg;
238
239                 /* check timeout */
240                 diff = ast_tvdiff_ms(ast_tvnow(), start);
241         }
242
243         return (ast_smdi_md_message_pop(iface));
244 }
245
246 /*!
247  * \brief Get the next SMDI message from the queue.
248  * \param iface a pointer to the interface to use.
249  *
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.
253  *
254  * \return the next SMDI message, or NULL if there were no pending messages.
255  */
256 extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
257 {
258         struct ast_smdi_mwi_message *mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
259         struct timeval now;
260         long elapsed = 0;
261
262         /* purge old messages */
263         now = ast_tvnow();
264         while (mwi_msg) {
265                 elapsed = ast_tvdiff_ms(now, mwi_msg->timestamp);
266
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.",
271                                 iface->name, elapsed - iface->msg_expiry);
272                         mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
273                 }
274                 else {
275                         /* good message, return it */
276                         break;
277                 }
278         }
279
280         return mwi_msg;
281 }
282
283 /*!
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.
287  *
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.
291  *
292  * \return the next SMDI message, or NULL if there were no pending messages and
293  * the timeout has expired.
294  */
295 extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
296 {
297         struct timeval start;
298         long diff = 0;
299         struct ast_smdi_mwi_message *msg;
300
301         start = ast_tvnow();
302         while (diff < timeout) {
303
304                 if ((msg = ast_smdi_mwi_message_pop(iface)))
305                         return msg;
306
307                 /* check timeout */
308                 diff = ast_tvdiff_ms(ast_tvnow(), start);
309         }
310
311         return (ast_smdi_mwi_message_pop(iface));
312 }
313
314 /*!
315  * \brief Find an SMDI interface with the specified name.
316  * \param iface_name the name/port of the interface to search for.
317  *
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).
321  */
322 extern struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
323 {
324         return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name));
325 }
326
327 /*! \brief Read an SMDI message.
328  *
329  * \param iface the SMDI interface to read from.
330  *
331  * This function loops and reads from and SMDI interface.  It must be stopped
332  * using pthread_cancel().
333  */
334 static void *smdi_read(void *iface_p)
335 {
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;
343                 
344         /* read an smdi message */
345         while ((c = fgetc(iface->file))) {
346
347                 /* check if this is the start of a message */
348                 if (!start) {
349                         if (c == 'M')
350                                 start = 1;
351                 }
352                 else { /* Determine if this is a MD or MWI message */
353                         if(c == 'D') { /* MD message */
354                                 start = 0;
355
356                                 if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
357                                         ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
358                                         return NULL;
359                                 }
360                                 
361                                 ASTOBJ_INIT(md_msg);
362
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);
366
367                                 md_msg->mesg_desk_num[SMDI_MESG_DESK_NUM_LEN] = '\0';
368
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);
372
373                                 md_msg->mesg_desk_term[SMDI_MESG_DESK_TERM_LEN] = '\0';
374
375                                 /* read the message type */
376                                 md_msg->type = fgetc(iface->file);
377                            
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;
384                                         }
385
386                                         /* store c in md_msg->fwd_st */
387                                         if( i >= iface->msdstrip)
388                                                 *cp++ = c;
389                                 }
390
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;
394                                 
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;
401                                         }
402
403                                         /* store c in md_msg->calling_st */
404                                         if (i >= iface->msdstrip)
405                                                 *cp++ = c;
406                                 }
407
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;
411
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);
416                                 
417                                 ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
418
419                         } else if(c == 'W') { /* MWI message */
420                                 start = 0;
421
422                                 if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
423                                         ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
424                                         return NULL;
425                                 }
426
427                                 ASTOBJ_INIT(mwi_msg);
428
429                                 /* discard the 'I' (from 'MWI') */
430                                 fgetc(iface->file);
431                                 
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;
438                                         }
439
440                                         /* store c in md_msg->fwd_st */
441                                         if (i >= iface->msdstrip)
442                                                 *cp++ = c;
443                                 }
444
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;
448                                 
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);
452
453                                 mwi_msg->cause[SMDI_MWI_FAIL_CAUSE_LEN] = '\0';
454
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);
459                                 
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;
464                         }
465                 }
466         }
467
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;
471 }
472
473 /*! \brief ast_smdi_md_message destructor. */
474 void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg)
475 {
476         free(msg);
477 }
478
479 /*! \brief ast_smdi_mwi_message destructor. */
480 void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
481 {
482         free(msg);
483 }
484
485 /*! \brief ast_smdi_interface destructor. */
486 void ast_smdi_interface_destroy(struct ast_smdi_interface *iface)
487 {
488         if (iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) {
489                 pthread_cancel(iface->thread);
490                 pthread_join(iface->thread, NULL);
491         }
492         
493         iface->thread = AST_PTHREADT_STOP;
494         
495         if(iface->file) 
496                 fclose(iface->file);
497         
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);
503
504         ast_atomic_fetchadd_int(&me->usecnt, -1);
505 }
506
507 /*!
508  * \internal
509  * \brief Load and reload SMDI configuration.
510  * \param reload this should be 1 if we are reloading and 0 if not.
511  *
512  * This function loads/reloads the SMDI configuration and starts and stops
513  * interfaces accordingly.
514  *
515  * \return zero on success, -1 on failure, and 1 if no smdi interfaces were started.
516  */
517 static int smdi_load(int reload)
518 {
519         struct ast_config *conf;
520         struct ast_variable *v;
521         struct ast_smdi_interface *iface = NULL;
522         int res = 0;
523
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 */
529         
530         int msdstrip = 0;              /* strip zero digits */
531         long msg_expiry = SMDI_MSG_EXPIRY_TIME;
532         
533         conf = ast_config_load(config_file);
534
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;
541         }
542
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.
547          */
548         if (reload)
549                 ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces);
550
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;
564                         }
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;
572                         }
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;
577                         }
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;
588                         }
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;
597                         }
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;
617                                 }
618                         }
619                                                         
620                         iface = ast_calloc(1, sizeof(*iface));
621
622                         ASTOBJ_INIT(iface);
623                         ASTOBJ_CONTAINER_INIT(&iface->md_q);
624                         ASTOBJ_CONTAINER_INIT(&iface->mwi_q);
625
626                         ast_copy_string(iface->name, v->value, sizeof(iface->name));
627
628                         if (!(iface->file = fopen(iface->name, "r"))) {
629                                 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s)\n", iface->name, strerror(errno));
630                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
631                                 continue;
632                         }
633
634                         iface->fd = fileno(iface->file);
635
636                         /* Set the proper attributes for our serial port. */
637
638                         /* get the current attributes from the port */
639                         if (tcgetattr(iface->fd, &iface->mode)) {
640                                 ast_log(LOG_ERROR, "Error getting atributes of %s (%s)\n", iface->name, strerror(errno));
641                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
642                                 continue;
643                         }
644
645                         /* set the desired speed */
646                         if (cfsetispeed(&iface->mode, baud_rate) || cfsetospeed(&iface->mode, baud_rate)) {
647                                 ast_log(LOG_ERROR, "Error setting baud rate on %s (%s)\n", iface->name, strerror(errno));
648                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
649                                 continue;
650                         }
651                         
652                         /* set the stop bits */
653                         if (stopbits)
654                            iface->mode.c_cflag = iface->mode.c_cflag | CSTOPB;   /* set two stop bits */
655                         else
656                            iface->mode.c_cflag = iface->mode.c_cflag & ~CSTOPB;  /* set one stop bit */
657
658                         /* set the parity */
659                         iface->mode.c_cflag = (iface->mode.c_cflag & ~PARENB & ~PARODD) | paritybit;
660
661                         /* set the character size */
662                         iface->mode.c_cflag = (iface->mode.c_cflag & ~CSIZE) | charsize;
663                         
664                         /* commit the desired attributes */
665                         if (tcsetattr(iface->fd, TCSAFLUSH, &iface->mode)) {
666                                 ast_log(LOG_ERROR, "Error setting attributes on %s (%s)\n", iface->name, strerror(errno));
667                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
668                                 continue;
669                         }
670
671                         /* set the msdstrip */
672                         iface->msdstrip = msdstrip;
673
674                         /* set the message expiry time */
675                         iface->msg_expiry = msg_expiry;
676
677                         /* start the listner thread */
678                         if (option_verbose > 2)
679                                 ast_verbose(VERBOSE_PREFIX_3 "Starting SMDI monitor thread for %s\n", iface->name);
680                         if (ast_pthread_create(&iface->thread, NULL, smdi_read, iface)) {
681                                 ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name);
682                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
683                                 continue;
684                         }
685
686                         ASTOBJ_CONTAINER_LINK(&smdi_ifaces, iface);
687                         ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
688                         ast_atomic_fetchadd_int(&me->usecnt, +1);
689                 } else {
690                         ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file);
691                 }
692         }
693         ast_config_destroy(conf);
694
695         /* Prune any interfaces we should no longer monitor. */
696         if (reload)
697                 ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces, ast_smdi_interface_destroy);
698         
699         ASTOBJ_CONTAINER_RDLOCK(&smdi_ifaces);
700         /* TODO: this is bad, we need an ASTOBJ method for this! */
701         if (!smdi_ifaces.head)
702                 res = 1;
703         ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces);
704                         
705         return res;
706 }
707
708
709 static const char *description(void)
710 {
711         return "Asterisk Simplified Message Desk Interface (SMDI) Module";
712 }
713
714 static int load_module(void *mod)
715 {
716         int res;
717
718         me = mod;
719         /* initialize our containers */
720         memset(&smdi_ifaces, 0, sizeof(smdi_ifaces));
721         ASTOBJ_CONTAINER_INIT(&smdi_ifaces);
722
723         /* load the config and start the listener threads*/
724         res = smdi_load(0);
725         if (res < 0) {
726                 return res;
727         } else if (res == 1) {
728                 ast_log(LOG_WARNING, "No SMDI interfaces are available to listen on, not starting SDMI listener.\n");
729                 return 0;
730         } else
731                 return 0;
732 }
733
734 static int unload_module(void *mod)
735 {
736         /* this destructor stops any running smdi_read threads */
737         ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces, ast_smdi_interface_destroy);
738         ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces);
739
740         return 0;
741 }
742
743 static int reload(void *mod)
744 {
745         int res;
746
747         res = smdi_load(1);
748
749         if (res < 0) {
750                 return res;
751         } else if (res == 1) {
752                 ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n");
753                 return 0;
754         } else
755                 return 0;
756 }
757
758 static const char *key(void)
759 {
760         return ASTERISK_GPL_KEY;
761 }
762
763 STD_MOD(MOD_0, reload, NULL, NULL);