revert loader changes that have clearly not undergone adequate testing before commit
[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 tdesc[] = "Asterisk Simplified Message Desk Interface (SMDI) Module";
52 static const char config_file[] = "smdi.conf";
53
54 static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *msg);
55 static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *msg);
56
57 static void *smdi_read(void *iface_p);
58 static int smdi_load(int reload);
59
60 /* Use count stuff */
61
62 AST_MUTEX_DEFINE_STATIC(localuser_lock);
63 static int localusecnt = 0;
64
65 /*! \brief SMDI interface container. */
66 struct ast_smdi_interface_container {
67         ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface);
68 } smdi_ifaces;
69
70 /*! 
71  * \internal
72  * \brief Push an SMDI message to the back of an interface's message queue.
73  * \param iface a pointer to the interface to use.
74  * \param md_msg a pointer to the message to use.
75  */
76 static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
77 {
78         ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg);
79 }
80
81 /*!
82  * \internal
83  * \brief Push an SMDI message to the back of an interface's message queue.
84  * \param iface a pointer to the interface to use.
85  * \param mwi_msg a pointer to the message to use.
86  */
87 static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
88 {
89         ASTOBJ_CONTAINER_LINK_END(&iface->mwi_q, mwi_msg);
90 }
91
92 /*!
93  * \brief Set the MWI indicator for a mailbox.
94  * \param iface the interface to use.
95  * \param mailbox the mailbox to use.
96  */
97 int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
98 {
99         FILE *file;
100         int i;
101         
102         file = fopen(iface->name, "w");
103         if(!file) {
104                 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
105                 return 1;
106         }       
107
108         ASTOBJ_WRLOCK(iface);
109
110         fprintf(file, "OP:MWI ");
111
112         for(i = 0; i < iface->msdstrip; i++)
113            fprintf(file, "0");
114
115         fprintf(file, "%s!\x04", mailbox);
116         fclose(file);
117
118         ASTOBJ_UNLOCK(iface);
119         ast_log(LOG_DEBUG, "Sent MWI set message for %s on %s\n", mailbox, iface->name);
120         return 0;
121 }
122
123 /*! 
124  * \brief Unset the MWI indicator for a mailbox.
125  * \param iface the interface to use.
126  * \param mailbox the mailbox to use.
127  */
128 int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
129 {
130         FILE *file;
131         int i;
132         
133         file = fopen(iface->name, "w");
134         if(!file) {
135                 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
136                 return 1;
137         }       
138
139         ASTOBJ_WRLOCK(iface);
140
141         fprintf(file, "RMV:MWI ");
142
143         for(i = 0; i < iface->msdstrip; i++)
144            fprintf(file, "0");
145
146         fprintf(file, "%s!\x04", mailbox);
147         fclose(file);
148
149         ASTOBJ_UNLOCK(iface);
150         ast_log(LOG_DEBUG, "Sent MWI unset message for %s on %s", mailbox, iface->name);
151         return 0;
152 }
153
154 /*!
155  * \brief Put an SMDI message back in the front of the queue.
156  * \param iface a pointer to the interface to use.
157  * \param msg a pointer to the message to use.
158  *
159  * This function puts a message back in the front of the specified queue.  It
160  * should be used if a message was popped but is not going to be processed for
161  * some reason, and the message needs to be returned to the queue.
162  */
163 void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
164 {
165         ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg);
166 }
167
168 /*!
169  * \brief Put an SMDI message back in the front of the queue.
170  * \param iface a pointer to the interface to use.
171  * \param msg a pointer to the message to use.
172  *
173  * This function puts a message back in the front of the specified queue.  It
174  * should be used if a message was popped but is not going to be processed for
175  * some reason, and the message needs to be returned to the queue.
176  */
177 void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
178 {
179         ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg);
180 }
181
182 /*! 
183  * \brief Get the next SMDI message from the queue.
184  * \param iface a pointer to the interface to use.
185  *
186  * This function pulls the first unexpired message from the SMDI message queue
187  * on the specified interface.  It will purge all expired SMDI messages before
188  * returning.
189  *
190  * \return the next SMDI message, or NULL if there were no pending messages.
191  */
192 struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
193 {
194         struct ast_smdi_md_message *md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
195         struct timeval now;
196         long elapsed = 0;
197
198         /* purge old messages */
199         now = ast_tvnow();
200         while (md_msg) {
201                 elapsed = ast_tvdiff_ms(now, md_msg->timestamp);
202
203                 if (elapsed > iface->msg_expiry) {
204                         /* found an expired message */
205                         ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
206                         ast_log(LOG_NOTICE, "Purged expired message from %s SMDI MD message queue.  Message was %ld milliseconds too old.",
207                                 iface->name, elapsed - iface->msg_expiry);
208                         md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
209                 }
210                 else {
211                         /* good message, return it */
212                         break;
213                 }
214         }
215
216         return md_msg;
217 }
218
219 /*!
220  * \brief Get the next SMDI message from the queue.
221  * \param iface a pointer to the interface to use.
222  * \param timeout the time to wait before returning in milliseconds.
223  *
224  * This function pulls a message from the SMDI message queue on the specified
225  * interface.  If no message is available this function will wait the specified
226  * amount of time before returning.
227  *
228  * \return the next SMDI message, or NULL if there were no pending messages and
229  * the timeout has expired.
230  */
231 extern struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
232 {
233         struct timeval start;
234         long diff = 0;
235         struct ast_smdi_md_message *msg;
236
237         start = ast_tvnow();
238         while (diff < timeout) {
239
240                 if ((msg = ast_smdi_md_message_pop(iface)))
241                         return msg;
242
243                 /* check timeout */
244                 diff = ast_tvdiff_ms(ast_tvnow(), start);
245         }
246
247         return (ast_smdi_md_message_pop(iface));
248 }
249
250 /*!
251  * \brief Get the next SMDI message from the queue.
252  * \param iface a pointer to the interface to use.
253  *
254  * This function pulls the first unexpired message from the SMDI message queue
255  * on the specified interface.  It will purge all expired SMDI messages before
256  * returning.
257  *
258  * \return the next SMDI message, or NULL if there were no pending messages.
259  */
260 extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
261 {
262         struct ast_smdi_mwi_message *mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
263         struct timeval now;
264         long elapsed = 0;
265
266         /* purge old messages */
267         now = ast_tvnow();
268         while (mwi_msg) {
269                 elapsed = ast_tvdiff_ms(now, mwi_msg->timestamp);
270
271                 if (elapsed > iface->msg_expiry) {
272                         /* found an expired message */
273                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
274                         ast_log(LOG_NOTICE, "Purged expired message from %s SMDI MWI message queue.  Message was %ld milliseconds too old.",
275                                 iface->name, elapsed - iface->msg_expiry);
276                         mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
277                 }
278                 else {
279                         /* good message, return it */
280                         break;
281                 }
282         }
283
284         return mwi_msg;
285 }
286
287 /*!
288  * \brief Get the next SMDI message from the queue.
289  * \param iface a pointer to the interface to use.
290  * \param timeout the time to wait before returning in milliseconds.
291  *
292  * This function pulls a message from the SMDI message queue on the specified
293  * interface.  If no message is available this function will wait the specified
294  * amount of time before returning.
295  *
296  * \return the next SMDI message, or NULL if there were no pending messages and
297  * the timeout has expired.
298  */
299 extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
300 {
301         struct timeval start;
302         long diff = 0;
303         struct ast_smdi_mwi_message *msg;
304
305         start = ast_tvnow();
306         while (diff < timeout) {
307
308                 if ((msg = ast_smdi_mwi_message_pop(iface)))
309                         return msg;
310
311                 /* check timeout */
312                 diff = ast_tvdiff_ms(ast_tvnow(), start);
313         }
314
315         return (ast_smdi_mwi_message_pop(iface));
316 }
317
318 /*!
319  * \brief Find an SMDI interface with the specified name.
320  * \param iface_name the name/port of the interface to search for.
321  *
322  * \return a pointer to the interface located or NULL if none was found.  This
323  * actually returns an ASTOBJ reference and should be released using
324  * #ASTOBJ_UNREF(iface, ast_smdi_interface_destroy).
325  */
326 extern struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
327 {
328         return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name));
329 }
330
331 /*! \brief Read an SMDI message.
332  *
333  * \param iface the SMDI interface to read from.
334  *
335  * This function loops and reads from and SMDI interface.  It must be stopped
336  * using pthread_cancel().
337  */
338 static void *smdi_read(void *iface_p)
339 {
340         struct ast_smdi_interface *iface = iface_p;
341         struct ast_smdi_md_message *md_msg;
342         struct ast_smdi_mwi_message *mwi_msg;
343         char c = '\0';
344         char *cp = NULL;
345         int i;
346         int start = 0;
347                 
348         /* read an smdi message */
349         while ((c = fgetc(iface->file))) {
350
351                 /* check if this is the start of a message */
352                 if (!start) {
353                         if (c == 'M')
354                                 start = 1;
355                 }
356                 else { /* Determine if this is a MD or MWI message */
357                         if(c == 'D') { /* MD message */
358                                 start = 0;
359
360                                 if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
361                                         ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
362                                         return NULL;
363                                 }
364                                 
365                                 ASTOBJ_INIT(md_msg);
366
367                                 /* read the message desk number */
368                                 for(i = 0; i < SMDI_MESG_DESK_NUM_LEN; i++)
369                                         md_msg->mesg_desk_num[i] = fgetc(iface->file);
370
371                                 md_msg->mesg_desk_num[SMDI_MESG_DESK_NUM_LEN] = '\0';
372
373                                 /* read the message desk terminal number */
374                                 for(i = 0; i < SMDI_MESG_DESK_TERM_LEN; i++)
375                                         md_msg->mesg_desk_term[i] = fgetc(iface->file);
376
377                                 md_msg->mesg_desk_term[SMDI_MESG_DESK_TERM_LEN] = '\0';
378
379                                 /* read the message type */
380                                 md_msg->type = fgetc(iface->file);
381                            
382                                 /* read the forwarding station number (may be blank) */
383                                 cp = &md_msg->fwd_st[0];
384                                 for (i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
385                                         if((c = fgetc(iface->file)) == ' ') {
386                                                 *cp = '\0';
387                                                 break;
388                                         }
389
390                                         /* store c in md_msg->fwd_st */
391                                         if( i >= iface->msdstrip)
392                                                 *cp++ = c;
393                                 }
394
395                                 /* make sure the value is null terminated, even if this truncates it */
396                                 md_msg->fwd_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
397                                 cp = NULL;
398                                 
399                                 /* read the calling station number (may be blank) */
400                                 cp = &md_msg->calling_st[0];
401                                 for (i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
402                                         if (!isdigit((c = fgetc(iface->file)))) {
403                                                 *cp = '\0';
404                                                 break;
405                                         }
406
407                                         /* store c in md_msg->calling_st */
408                                         if (i >= iface->msdstrip)
409                                                 *cp++ = c;
410                                 }
411
412                                 /* make sure the value is null terminated, even if this truncates it */
413                                 md_msg->calling_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
414                                 cp = NULL;
415
416                                 /* add the message to the message queue */
417                                 md_msg->timestamp = ast_tvnow();
418                                 ast_smdi_md_message_push(iface, md_msg);
419                                 ast_log(LOG_DEBUG, "Recieved SMDI MD message on %s\n", iface->name);
420                                 
421                                 ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
422
423                         } else if(c == 'W') { /* MWI message */
424                                 start = 0;
425
426                                 if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
427                                         ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
428                                         return NULL;
429                                 }
430
431                                 ASTOBJ_INIT(mwi_msg);
432
433                                 /* discard the 'I' (from 'MWI') */
434                                 fgetc(iface->file);
435                                 
436                                 /* read the forwarding station number (may be blank) */
437                                 cp = &mwi_msg->fwd_st[0];
438                                 for (i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
439                                         if ((c = fgetc(iface->file)) == ' ') {
440                                                 *cp = '\0';
441                                                 break;
442                                         }
443
444                                         /* store c in md_msg->fwd_st */
445                                         if (i >= iface->msdstrip)
446                                                 *cp++ = c;
447                                 }
448
449                                 /* make sure the station number is null terminated, even if this will truncate it */
450                                 mwi_msg->fwd_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
451                                 cp = NULL;
452                                 
453                                 /* read the mwi failure cause */
454                                 for (i = 0; i < SMDI_MWI_FAIL_CAUSE_LEN; i++)
455                                         mwi_msg->cause[i] = fgetc(iface->file);
456
457                                 mwi_msg->cause[SMDI_MWI_FAIL_CAUSE_LEN] = '\0';
458
459                                 /* add the message to the message queue */
460                                 mwi_msg->timestamp = ast_tvnow();
461                                 ast_smdi_mwi_message_push(iface, mwi_msg);
462                                 ast_log(LOG_DEBUG, "Recieved SMDI MWI message on %s\n", iface->name);
463                                 
464                                 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
465                         } else {
466                                 ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c);
467                                 start = 0;
468                         }
469                 }
470         }
471
472         ast_log(LOG_ERROR, "Error reading from SMDI interface %s, stopping listener thread\n", iface->name);
473         ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
474         return NULL;
475 }
476
477 /*! \brief ast_smdi_md_message destructor. */
478 void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg)
479 {
480         free(msg);
481 }
482
483 /*! \brief ast_smdi_mwi_message destructor. */
484 void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
485 {
486         free(msg);
487 }
488
489 /*! \brief ast_smdi_interface destructor. */
490 void ast_smdi_interface_destroy(struct ast_smdi_interface *iface)
491 {
492         if (iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) {
493                 pthread_cancel(iface->thread);
494                 pthread_join(iface->thread, NULL);
495         }
496         
497         iface->thread = AST_PTHREADT_STOP;
498         
499         if(iface->file) 
500                 fclose(iface->file);
501         
502         ASTOBJ_CONTAINER_DESTROYALL(&iface->md_q, ast_smdi_md_message_destroy);
503         ASTOBJ_CONTAINER_DESTROYALL(&iface->mwi_q, ast_smdi_mwi_message_destroy);
504         ASTOBJ_CONTAINER_DESTROY(&iface->md_q);
505         ASTOBJ_CONTAINER_DESTROY(&iface->mwi_q);
506         free(iface);
507
508         ast_mutex_lock(&localuser_lock);
509         localusecnt--;
510         ast_mutex_unlock(&localuser_lock);
511         ast_update_use_count();
512 }
513
514 /*!
515  * \internal
516  * \brief Load and reload SMDI configuration.
517  * \param reload this should be 1 if we are reloading and 0 if not.
518  *
519  * This function loads/reloads the SMDI configuration and starts and stops
520  * interfaces accordingly.
521  *
522  * \return zero on success, -1 on failure, and 1 if no smdi interfaces were started.
523  */
524 static int smdi_load(int reload)
525 {
526         struct ast_config *conf;
527         struct ast_variable *v;
528         struct ast_smdi_interface *iface = NULL;
529         int res = 0;
530
531         /* Config options */
532         speed_t baud_rate = B9600;     /* 9600 baud rate */
533         tcflag_t paritybit = PARENB;   /* even parity checking */
534         tcflag_t charsize = CS7;       /* seven bit characters */
535         int stopbits = 0;              /* One stop bit */
536         
537         int msdstrip = 0;              /* strip zero digits */
538         long msg_expiry = SMDI_MSG_EXPIRY_TIME;
539         
540         conf = ast_config_load(config_file);
541
542         if (!conf) {
543                 if (reload)
544                         ast_log(LOG_NOTICE, "Unable to reload config %s: SMDI untouched\n", config_file);
545                 else
546                         ast_log(LOG_NOTICE, "Unable to load config %s: SMDI disabled\n", config_file);
547                 return 1;
548         }
549
550         /* Mark all interfaces that we are listening on.  We will unmark them
551          * as we find them in the config file, this way we know any interfaces
552          * still marked after we have finished parsing the config file should
553          * be stopped.
554          */
555         if (reload)
556                 ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces);
557
558         for (v = ast_variable_browse(conf, "interfaces"); v; v = v->next) {
559                 if (!strcasecmp(v->name, "baudrate")) {
560                         if (!strcasecmp(v->value, "9600"))
561                                 baud_rate = B9600;
562                         else if(!strcasecmp(v->value, "4800"))
563                                 baud_rate = B4800;
564                         else if(!strcasecmp(v->value, "2400"))
565                                 baud_rate = B2400;
566                         else if(!strcasecmp(v->value, "1200"))
567                                 baud_rate = B1200;
568                         else {
569                                 ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno);
570                                 baud_rate = B9600;
571                         }
572                 } else if (!strcasecmp(v->name, "msdstrip")) {
573                         if (!sscanf(v->value, "%d", &msdstrip)) {
574                                 ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
575                                 msdstrip = 0;
576                         } else if (0 > msdstrip || msdstrip > 9) {
577                                 ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
578                                 msdstrip = 0;
579                         }
580                 } else if (!strcasecmp(v->name, "msgexpirytime")) {
581                         if (!sscanf(v->value, "%ld", &msg_expiry)) {
582                                 ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno);
583                                 msg_expiry = SMDI_MSG_EXPIRY_TIME;
584                         }
585                 } else if (!strcasecmp(v->name, "paritybit")) {
586                         if (!strcasecmp(v->value, "even"))
587                                 paritybit = PARENB;
588                         else if (!strcasecmp(v->value, "odd"))
589                                 paritybit = PARENB | PARODD;
590                         else if (!strcasecmp(v->value, "none"))
591                                 paritybit = ~PARENB;
592                         else {
593                                 ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno);
594                                 paritybit = PARENB;
595                         }
596                 } else if (!strcasecmp(v->name, "charsize")) {
597                         if (!strcasecmp(v->value, "7"))
598                                 charsize = CS7;
599                         else if (!strcasecmp(v->value, "8"))
600                                 charsize = CS8;
601                         else {
602                                 ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno);
603                                 charsize = CS7;
604                         }
605                 } else if (!strcasecmp(v->name, "twostopbits")) {
606                         stopbits = ast_true(v->name);
607                 } else if (!strcasecmp(v->name, "smdiport")) {
608                         if (reload) {
609                                 /* we are reloading, check if we are already
610                                  * monitoring this interface, if we are we do
611                                  * not want to start it again.  This also has
612                                  * the side effect of not updating different
613                                  * setting for the serial port, but it should
614                                  * be trivial to rewrite this section so that
615                                  * options on the port are changed without
616                                  * restarting the interface.  Or the interface
617                                  * could be restarted with out emptying the
618                                  * queue. */
619                                 if ((iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
620                                         ast_log(LOG_NOTICE, "SMDI interface %s already running, not restarting\n", iface->name);
621                                         ASTOBJ_UNMARK(iface);
622                                         ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
623                                         continue;
624                                 }
625                         }
626                                                         
627                         iface = ast_calloc(1, sizeof(*iface));
628
629                         ASTOBJ_INIT(iface);
630                         ASTOBJ_CONTAINER_INIT(&iface->md_q);
631                         ASTOBJ_CONTAINER_INIT(&iface->mwi_q);
632
633                         ast_copy_string(iface->name, v->value, sizeof(iface->name));
634
635                         if (!(iface->file = fopen(iface->name, "r"))) {
636                                 ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s)\n", iface->name, strerror(errno));
637                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
638                                 continue;
639                         }
640
641                         iface->fd = fileno(iface->file);
642
643                         /* Set the proper attributes for our serial port. */
644
645                         /* get the current attributes from the port */
646                         if (tcgetattr(iface->fd, &iface->mode)) {
647                                 ast_log(LOG_ERROR, "Error getting atributes of %s (%s)\n", iface->name, strerror(errno));
648                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
649                                 continue;
650                         }
651
652                         /* set the desired speed */
653                         if (cfsetispeed(&iface->mode, baud_rate) || cfsetospeed(&iface->mode, baud_rate)) {
654                                 ast_log(LOG_ERROR, "Error setting baud rate on %s (%s)\n", iface->name, strerror(errno));
655                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
656                                 continue;
657                         }
658                         
659                         /* set the stop bits */
660                         if (stopbits)
661                            iface->mode.c_cflag = iface->mode.c_cflag | CSTOPB;   /* set two stop bits */
662                         else
663                            iface->mode.c_cflag = iface->mode.c_cflag & ~CSTOPB;  /* set one stop bit */
664
665                         /* set the parity */
666                         iface->mode.c_cflag = (iface->mode.c_cflag & ~PARENB & ~PARODD) | paritybit;
667
668                         /* set the character size */
669                         iface->mode.c_cflag = (iface->mode.c_cflag & ~CSIZE) | charsize;
670                         
671                         /* commit the desired attributes */
672                         if (tcsetattr(iface->fd, TCSAFLUSH, &iface->mode)) {
673                                 ast_log(LOG_ERROR, "Error setting attributes on %s (%s)\n", iface->name, strerror(errno));
674                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
675                                 continue;
676                         }
677
678                         /* set the msdstrip */
679                         iface->msdstrip = msdstrip;
680
681                         /* set the message expiry time */
682                         iface->msg_expiry = msg_expiry;
683
684                         /* start the listner thread */
685                         if (option_verbose > 2)
686                                 ast_verbose(VERBOSE_PREFIX_3 "Starting SMDI monitor thread for %s\n", iface->name);
687                         if (ast_pthread_create(&iface->thread, NULL, smdi_read, iface)) {
688                                 ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name);
689                                 ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
690                                 continue;
691                         }
692
693                         ASTOBJ_CONTAINER_LINK(&smdi_ifaces, iface);
694                         ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
695                         ast_mutex_lock(&localuser_lock);
696                         localusecnt++;
697                         ast_mutex_unlock(&localuser_lock);
698                         ast_update_use_count();
699                 } else {
700                         ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file);
701                 }
702                 v = v->next;
703         }
704         ast_config_destroy(conf);
705
706         /* Prune any interfaces we should no longer monitor. */
707         if (reload)
708                 ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces, ast_smdi_interface_destroy);
709         
710         ASTOBJ_CONTAINER_RDLOCK(&smdi_ifaces);
711         /* TODO: this is bad, we need an ASTOBJ method for this! */
712         if (!smdi_ifaces.head)
713                 res = 1;
714         ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces);
715                         
716         return res;
717 }
718
719
720 char *description(void)
721 {
722         return (char *) tdesc;
723 }
724
725 int load_module(void)
726 {
727         int res;
728
729         /* initialize our containers */
730         memset(&smdi_ifaces, 0, sizeof(smdi_ifaces));
731         ASTOBJ_CONTAINER_INIT(&smdi_ifaces);
732
733         /* load the config and start the listener threads*/
734         res = smdi_load(0);
735         if (res < 0) {
736                 return res;
737         } else if (res == 1) {
738                 ast_log(LOG_WARNING, "No SMDI interfaces are available to listen on, not starting SDMI listener.\n");
739                 return 0;
740         } else
741                 return 0;
742 }
743
744 int unload_module(void)
745 {
746         /* this destructor stops any running smdi_read threads */
747         ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces, ast_smdi_interface_destroy);
748         ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces);
749
750         return 0;
751 }
752
753 int reload(void)
754 {
755         int res;
756
757         res = smdi_load(1);
758
759         if (res < 0) {
760                 return res;
761         } else if (res == 1) {
762                 ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n");
763                 return 0;
764         } else
765                 return 0;
766 }
767
768 int usecount(void)
769 {
770         int res;
771
772         STANDARD_USECOUNT(res);
773
774         return res;
775 }
776
777 char *key()
778 {
779         return ASTERISK_GPL_KEY;
780 }