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