Use defined return values in load_module in more places.
[asterisk/asterisk.git] / apps / app_alarmreceiver.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C)  2004 - 2005 Steve Rodgers
5  *
6  * Steve Rodgers <hwstar@rodgers.sdcoxmail.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 /*! \file
20  * \brief Central Station Alarm receiver for Ademco Contact ID  
21  * \author Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
22  * 
23  * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** 
24  *
25  * Use at your own risk. Please consult the GNU GPL license document included with Asterisk.         *
26  *
27  * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
28  *
29  * \ingroup applications
30  */ 
31  
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include <math.h>
37 #include <sys/wait.h>
38 #include <sys/time.h>
39
40 #include "asterisk/lock.h"
41 #include "asterisk/file.h"
42 #include "asterisk/channel.h"
43 #include "asterisk/pbx.h"
44 #include "asterisk/module.h"
45 #include "asterisk/translate.h"
46 #include "asterisk/ulaw.h"
47 #include "asterisk/app.h"
48 #include "asterisk/dsp.h"
49 #include "asterisk/config.h"
50 #include "asterisk/localtime.h"
51 #include "asterisk/callerid.h"
52 #include "asterisk/astdb.h"
53 #include "asterisk/utils.h"
54
55 #define ALMRCV_CONFIG "alarmreceiver.conf"
56 #define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID"
57
58 struct event_node{
59         char data[17];
60         struct event_node *next;
61 };
62
63 typedef struct event_node event_node_t;
64
65 static char *app = "AlarmReceiver";
66
67 static char *synopsis = "Provide support for receiving alarm reports from a burglar or fire alarm panel";
68 static char *descrip =
69 "  AlarmReceiver(): Only 1 signalling format is supported at this time: Ademco\n"
70 "Contact ID. This application should be called whenever there is an alarm\n"
71 "panel calling in to dump its events. The application will handshake with the\n"
72 "alarm panel, and receive events, validate them, handshake them, and store them\n"
73 "until the panel hangs up. Once the panel hangs up, the application will run the\n"
74 "system command specified by the eventcmd setting in alarmreceiver.conf and pipe\n"
75 "the events to the standard input of the application. The configuration file also\n"
76 "contains settings for DTMF timing, and for the loudness of the acknowledgement\n" 
77 "tones.\n";
78
79 /* Config Variables */
80
81 static int fdtimeout = 2000;
82 static int sdtimeout = 200; 
83 static int toneloudness = 4096;
84 static int log_individual_events = 0;
85 static char event_spool_dir[128] = {'\0'};
86 static char event_app[128] = {'\0'};
87 static char db_family[128] = {'\0'};
88 static char time_stamp_format[128] = {"%a %b %d, %Y @ %H:%M:%S %Z"};
89
90 /* Misc variables */
91         
92 static char event_file[14] = "/event-XXXXXX";
93
94 /*
95 * Attempt to access a database variable and increment it,
96 * provided that the user defined db-family in alarmreceiver.conf
97 * The alarmreceiver app will write statistics to a few variables
98 * in this family if it is defined. If the new key doesn't exist in the
99 * family, then create it and set its value to 1.
100 */
101
102 static void database_increment( char *key )
103 {
104         int res = 0;
105         unsigned v;
106         char value[16];
107         
108         
109         if (ast_strlen_zero(db_family))
110                 return; /* If not defined, don't do anything */
111         
112         res = ast_db_get(db_family, key, value, sizeof(value) - 1);
113         
114         if(res){
115                 ast_verb(4, "AlarmReceiver: Creating database entry %s and setting to 1\n", key);
116                 /* Guess we have to create it */
117                 res = ast_db_put(db_family, key, "1");
118                 return;
119         }
120         
121         sscanf(value, "%u", &v);
122         v++;
123         
124         if(option_verbose >= 4)
125                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: New value for %s: %u\n", key, v);
126                 
127         snprintf(value, sizeof(value), "%u", v);
128         
129         res = ast_db_put(db_family, key, value);
130         
131         if((res)&&(option_verbose >= 4))
132                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: database_increment write error\n");
133         
134         return; 
135 }
136
137
138 /*
139 * Build a MuLaw data block for a single frequency tone
140 */
141
142 static void make_tone_burst(unsigned char *data, float freq, float loudness, int len, int *x)
143 {
144         int     i;
145         float   val;
146                                                                                                                                     
147         for(i = 0; i < len; i++){
148                 val = loudness * sin((freq * 2.0 * M_PI * (*x)++)/8000.0);
149                 data[i] = AST_LIN2MU((int)val);
150         }
151
152         /* wrap back around from 8000 */
153
154         if (*x >= 8000) *x = 0;
155         return;
156 }
157
158 /*
159 * Send a single tone burst for a specifed duration and frequency. 
160 * Returns 0 if successful
161 */
162
163 static int send_tone_burst(struct ast_channel *chan, float freq, int duration, int tldn)
164 {
165         int res = 0;
166         int i = 0;
167         int x = 0;
168         struct ast_frame *f, wf;
169         
170         struct {
171                 unsigned char offset[AST_FRIENDLY_OFFSET];
172                 unsigned char buf[640];
173         } tone_block;
174
175         for(;;)
176         {
177         
178                 if (ast_waitfor(chan, -1) < 0){
179                         res = -1;
180                         break;
181                 }
182                 
183                 f = ast_read(chan);
184                 if (!f){
185                         res = -1;
186                         break;
187                 }
188                 
189                 if (f->frametype == AST_FRAME_VOICE) {
190                         wf.frametype = AST_FRAME_VOICE;
191                         wf.subclass = AST_FORMAT_ULAW;
192                         wf.offset = AST_FRIENDLY_OFFSET;
193                         wf.mallocd = 0;
194                         wf.data = tone_block.buf;
195                         wf.datalen = f->datalen;
196                         wf.samples = wf.datalen;
197                         
198                         make_tone_burst(tone_block.buf, freq, (float) tldn, wf.datalen, &x);
199
200                         i += wf.datalen / 8;
201                         if (i > duration) {
202                                 ast_frfree(f);
203                                 break;
204                         }
205                         if (ast_write(chan, &wf)){
206                                 ast_verb(4, "AlarmReceiver: Failed to write frame on %s\n", chan->name);
207                                 ast_log(LOG_WARNING, "AlarmReceiver Failed to write frame on %s\n",chan->name);
208                                 res = -1;
209                                 ast_frfree(f);
210                                 break;
211                         }
212                 }
213                 
214                 ast_frfree(f);
215         }
216         return res;
217 }
218
219 /*
220 * Receive a string of DTMF digits where the length of the digit string is known in advance. Do not give preferential
221 * treatment to any digit value, and allow separate time out values to be specified for the first digit and all subsequent
222 * digits.
223 *
224 * Returns 0 if all digits successfully received.
225 * Returns 1 if a digit time out occurred
226 * Returns -1 if the caller hung up or there was a channel error.
227 *
228 */
229
230 static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int length, int fdto, int sdto)
231 {
232         int res = 0;
233         int i = 0;
234         int r;
235         struct ast_frame *f;
236         struct timeval lastdigittime;
237         
238         lastdigittime = ast_tvnow();
239         for(;;){
240                   /* if outa time, leave */
241                 if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) >
242                     ((i > 0) ? sdto : fdto)){
243                         if(option_verbose >= 4)
244                                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: DTMF Digit Timeout on %s\n", chan->name);
245                                 
246                         ast_debug(1,"AlarmReceiver: DTMF timeout on chan %s\n",chan->name);
247                                 
248                         res = 1;
249                         break;
250                 }
251                 
252                 if ((r = ast_waitfor(chan, -1) < 0)) {
253                         ast_debug(1, "Waitfor returned %d\n", r);
254                         continue;
255                 }
256                         
257                 f = ast_read(chan);
258                 
259                 if (f == NULL){
260                         res = -1;
261                         break;
262                 }
263                 
264                 /* If they hung up, leave */
265                 if ((f->frametype == AST_FRAME_CONTROL) &&
266                     (f->subclass == AST_CONTROL_HANGUP)){
267                         ast_frfree(f);
268                         res = -1;
269                         break;
270                 }
271                 
272                 /* if not DTMF, just do it again */
273                 if (f->frametype != AST_FRAME_DTMF){
274                         ast_frfree(f);
275                         continue;
276                 }
277
278                 digit_string[i++] = f->subclass;  /* save digit */
279                 
280                 ast_frfree(f);
281                 
282                 /* If we have all the digits we expect, leave */
283                 if(i >= length)
284                         break;
285                 
286                 lastdigittime = ast_tvnow();
287         }
288         
289         digit_string[i] = '\0'; /* Nul terminate the end of the digit string */
290         return res;
291
292 }
293
294 /*
295 * Write the metadata to the log file
296 */
297
298 static int write_metadata( FILE *logfile, char *signalling_type, struct ast_channel *chan)
299 {
300         int res = 0;
301         struct timeval t;
302         struct ast_tm now;
303         char *cl,*cn;
304         char workstring[80];
305         char timestamp[80];
306         
307         /* Extract the caller ID location */
308         if (chan->cid.cid_num)
309                 ast_copy_string(workstring, chan->cid.cid_num, sizeof(workstring));
310         workstring[sizeof(workstring) - 1] = '\0';
311         
312         ast_callerid_parse(workstring, &cn, &cl);
313         if (cl) 
314                 ast_shrink_phone_number(cl);
315                 
316
317         /* Get the current time */
318                 
319         t = ast_tvnow();
320         ast_localtime(&t, &now, NULL);
321         
322         /* Format the time */
323         
324         ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now); 
325
326         
327         res = fprintf(logfile, "\n\n[metadata]\n\n");
328         
329         if(res >= 0)
330                 res = fprintf(logfile, "PROTOCOL=%s\n", signalling_type);
331                 
332         if(res >= 0)    
333                 res = fprintf(logfile, "CALLINGFROM=%s\n", (!cl) ? "<unknown>" : cl);
334                 
335         if(res >- 0)
336                 res = fprintf(logfile, "CALLERNAME=%s\n", (!cn) ? "<unknown>" : cn);
337                 
338         if(res >= 0)
339                 res = fprintf(logfile, "TIMESTAMP=%s\n\n", timestamp);
340         
341         if(res >= 0)
342                 res = fprintf(logfile, "[events]\n\n");
343         
344         if(res < 0){
345                 if (option_verbose >= 3 )
346                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: can't write metadata\n");  
347                 
348                 ast_debug(1,"AlarmReceiver: can't write metadata\n");
349         }
350         else
351                 res = 0;
352
353         return res;
354 }
355
356 /*
357 * Write a single event to the log file
358 */
359
360 static int write_event( FILE *logfile,  event_node_t *event)
361 {
362         int res = 0;
363
364         if( fprintf(logfile, "%s\n", event->data) < 0)
365                 res = -1;
366                         
367         return res;
368 }
369
370
371 /*
372 * If we are configured to log events, do so here.
373 *
374 */
375
376 static int log_events(struct ast_channel *chan,  char *signalling_type, event_node_t *event)
377 {
378
379         int res = 0;
380         char workstring[sizeof(event_spool_dir)+sizeof(event_file)] = "";
381         int fd;
382         FILE *logfile;
383         event_node_t *elp = event;
384         
385         if (!ast_strlen_zero(event_spool_dir)) {
386                 
387                 /* Make a template */
388                 
389                 ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
390                 strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
391                 
392                 /* Make the temporary file */
393                 
394                 fd = mkstemp(workstring);
395                 
396                 if(fd == -1) {
397                         if (option_verbose >= 3)
398                                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: can't make temporary file\n");     
399                         ast_debug(1,"AlarmReceiver: can't make temporary file\n");
400                         res = -1;
401                 }
402                 
403                 if(!res){
404                         logfile = fdopen(fd, "w");
405                         if(logfile){
406                                 /* Write the file */
407                                 res = write_metadata(logfile, signalling_type, chan);
408                                 if(!res)
409                                         while((!res) && (elp != NULL)){
410                                                 res = write_event(logfile, elp);
411                                                 elp = elp->next;
412                                         }
413                                 if(!res){
414                                         if(fflush(logfile) == EOF)
415                                                 res = -1;
416                                         if(!res){
417                                                 if(fclose(logfile) == EOF)
418                                                         res = -1;
419                                         }                               
420                                 }
421                         }
422                         else
423                                 res = -1;
424                 }
425         }
426
427         return res;     
428 }
429
430 /*
431 * This function implements the logic to receive the Ademco contact ID  format.
432 *
433 * The function will return 0 when the caller hangs up, else a -1 if there was a problem.
434 */
435
436 static int receive_ademco_contact_id( struct ast_channel *chan, void *data, int fdto, int sdto, int tldn, event_node_t **ehead)
437 {
438         int i,j;
439         int res = 0;
440         int checksum;
441         char event[17];
442         event_node_t *enew, *elp;
443         int got_some_digits = 0;
444         int events_received = 0;
445         int ack_retries = 0;
446         
447         static char digit_map[15] = "0123456789*#ABC";
448         static unsigned char digit_weights[15] = {10,1,2,3,4,5,6,7,8,9,11,12,13,14,15};
449                                                                                                                       
450         database_increment("calls-received");
451
452         /* Wait for first event */
453
454         if(option_verbose >= 4)
455                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Waiting for first event from panel\n");
456
457         while(res >= 0){
458
459                 if(got_some_digits == 0){
460
461                         /* Send ACK tone sequence */
462                         
463                                                                                                                                     
464                         if(option_verbose >= 4)
465                                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
466                                                                                                                                             
467                                                                                                                                             
468                         res = send_tone_burst(chan, 1400.0, 100, tldn);
469                                                                                                                                             
470                         if(!res)
471                                 res = ast_safe_sleep(chan, 100);
472                                                                                                                                             
473                         if(!res){
474                                 if(option_verbose >= 4)
475                                         ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
476                                                                                                                                             
477                                 res = send_tone_burst(chan, 2300.0, 100, tldn);
478                         }
479                                                                                                                                             
480                 }
481
482                 if( res >= 0)
483                         res = receive_dtmf_digits(chan, event, sizeof(event) - 1, fdto, sdto);
484                 
485                 if (res < 0){
486                 
487                         if(events_received == 0)
488                                 /* Hangup with no events received should be logged in the DB */
489                                 database_increment("no-events-received");
490                         else{
491                                 if(ack_retries){
492                                         if(option_verbose >= 4)
493                                                 ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
494                                         
495                                         database_increment("ack-retries");
496                                 }
497                         }
498                         if(option_verbose >= 4)
499                                 ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: App exiting...\n");
500                         res = -1;
501                         break;
502                 }
503                 
504                 if(res != 0){
505                          /* Didn't get all of the digits */
506                         if(option_verbose >= 2)
507                                 ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
508
509                         if(!got_some_digits){
510                                 got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
511                                 ack_retries++;
512                         }
513                         continue;       
514                 }               
515                 
516                 got_some_digits = 1;
517
518                 ast_verb(2, "AlarmReceiver: Received Event %s\n", event);
519                 ast_debug(1, "AlarmReceiver: Received event: %s\n", event);
520                 
521                 /* Calculate checksum */
522                 
523                 for(j = 0, checksum = 0; j < 16; j++){
524                         for(i = 0 ; i < sizeof(digit_map) ; i++){
525                                 if(digit_map[i] == event[j])
526                                         break;
527                         }
528                         
529                         if(i == 16)
530                                 break;
531                                 
532                         checksum += digit_weights[i];
533                 }
534                 
535                 if(i == 16){
536                         ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
537                         continue; /* Bad character */
538                 }
539
540                 /* Checksum is mod(15) of the total */
541
542                 checksum = checksum % 15;
543
544                 if (checksum) {
545                         database_increment("checksum-errors");
546                         ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
547                         ast_debug(1, "AlarmReceiver: Nonzero checksum\n");
548                         continue;
549                 }
550
551                 /* Check the message type for correctness */
552
553                 if(strncmp(event + 4, "18", 2)){
554                         if(strncmp(event + 4, "98", 2)){
555                                 database_increment("format-errors");
556                                 ast_verb(2, "AlarmReceiver: Wrong message type\n");
557                                 ast_debug(1, "AlarmReceiver: Wrong message type\n");
558                         continue;
559                         }
560                 }
561
562                 events_received++;
563                 
564                 /* Queue the Event */           
565                 if (!(enew = ast_calloc(1, sizeof(*enew)))) {
566                         res = -1;
567                         break;
568                 }
569                 
570                 enew->next = NULL;
571                 ast_copy_string(enew->data, event, sizeof(enew->data));
572
573                 /*
574                 * Insert event onto end of list
575                 */
576                 
577                 if(*ehead == NULL){
578                         *ehead = enew;
579                 }
580                 else{
581                         for(elp = *ehead; elp->next != NULL; elp = elp->next)
582                         ;
583                         
584                         elp->next = enew;
585                 }
586                 
587                 if(res > 0)
588                         res = 0;
589                 
590                 /* Let the user have the option of logging the single event before sending the kissoff tone */
591
592                 if((res == 0) && (log_individual_events))
593                         res = log_events(chan, ADEMCO_CONTACT_ID, enew);
594         
595                 /* Wait 200 msec before sending kissoff */      
596                         
597                 if(res == 0)    
598                         res = ast_safe_sleep(chan, 200);
599
600                 /* Send the kissoff tone */
601
602                 if(res == 0)            
603                         res = send_tone_burst(chan, 1400.0, 900, tldn);
604         }
605
606         
607         return res;
608 }
609
610
611 /*
612 * This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
613 * This function will always return 0.
614 */
615
616 static int alarmreceiver_exec(struct ast_channel *chan, void *data)
617 {
618         int res = 0;
619         event_node_t *elp, *efree;
620         char signalling_type[64] = "";
621
622         event_node_t *event_head = NULL;
623
624         /* Set write and read formats to ULAW */
625
626         ast_verb(4, "AlarmReceiver: Setting read and write formats to ULAW\n");
627
628         if (ast_set_write_format(chan,AST_FORMAT_ULAW)){
629                 ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",chan->name);
630                 return -1;
631         }
632         
633         if (ast_set_read_format(chan,AST_FORMAT_ULAW)){
634                 ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",chan->name);
635                 return -1;
636         }
637
638         /* Set default values for this invocation of the application */
639         
640         ast_copy_string(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type));
641
642
643         /* Answer the channel if it is not already */
644
645         ast_verb(4, "AlarmReceiver: Answering channel\n");
646
647         if (chan->_state != AST_STATE_UP) {
648                 if ((res = ast_answer(chan)))
649                         return -1;
650         }
651
652         /* Wait for the connection to settle post-answer */
653
654         ast_verb(4, "AlarmReceiver: Waiting for connection to stabilize\n");
655
656         res = ast_safe_sleep(chan, 1250);
657
658         /* Attempt to receive the events */
659
660         if(!res){
661         
662                 /* Determine the protocol to receive in advance */
663                 /* Note: Ademco contact is the only one supported at this time */
664                 /* Others may be added later */
665                 
666                 if(!strcmp(signalling_type, ADEMCO_CONTACT_ID))
667                         receive_ademco_contact_id(chan, data, fdtimeout, sdtimeout, toneloudness, &event_head);
668                 else
669                         res = -1;
670         }
671         
672                 
673         
674         /* Events queued by receiver, write them all out here if so configured */
675
676         if((!res) && (log_individual_events == 0)){
677                 res = log_events(chan, signalling_type, event_head);
678
679         }
680
681         /*
682         * Do we exec a command line at the end?
683         */
684         
685         if((!res) && (!ast_strlen_zero(event_app)) && (event_head)){
686                 ast_debug(1,"Alarmreceiver: executing: %s\n", event_app);
687                 ast_safe_system(event_app);
688         }
689
690         /*
691         * Free up the data allocated in our linked list
692         */
693                 
694         for(elp = event_head; (elp != NULL);){
695                 efree = elp;
696                 elp = elp->next;
697                 ast_free(efree);
698         }
699
700         return 0;
701 }
702
703 /* 
704 * Load the configuration from the configuration file
705 */
706
707 static int load_config(void)
708 {
709         struct ast_config *cfg;
710         const char *p;
711         struct ast_flags config_flags = { 0 };
712
713         /* Read in the config file */
714
715         cfg = ast_config_load(ALMRCV_CONFIG, config_flags);
716                                                                                                                                   
717         if(!cfg){
718         
719                 if(option_verbose >= 4)
720                         ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: No config file\n");
721                 return 0;
722         }
723         else{
724
725                 
726                 p = ast_variable_retrieve(cfg, "general", "eventcmd");
727                 
728                 if(p){
729                         ast_copy_string(event_app, p, sizeof(event_app));
730                         event_app[sizeof(event_app) - 1] = '\0';
731                 }
732                 
733                 p = ast_variable_retrieve(cfg, "general", "loudness");
734                 if(p){
735                         toneloudness = atoi(p);
736                         if(toneloudness < 100)
737                                 toneloudness = 100;
738                         if(toneloudness > 8192)
739                                 toneloudness = 8192;
740                 }
741                 p = ast_variable_retrieve(cfg, "general", "fdtimeout");
742                 if(p){
743                         fdtimeout = atoi(p);
744                         if(fdtimeout < 1000)
745                                 fdtimeout = 1000;
746                         if(fdtimeout > 10000)
747                                 fdtimeout = 10000;      
748                 }
749                 
750                 p = ast_variable_retrieve(cfg, "general", "sdtimeout");
751                 if(p){
752                         sdtimeout = atoi(p);
753                         if(sdtimeout < 110)
754                                 sdtimeout = 110;
755                         if(sdtimeout > 4000)
756                                 sdtimeout = 4000;                       
757
758                 }
759                 
760                 p = ast_variable_retrieve(cfg, "general", "logindividualevents");
761                 if(p){
762                         log_individual_events = ast_true(p);
763
764                 }
765                 
766                 p = ast_variable_retrieve(cfg, "general", "eventspooldir");
767                         
768                 if(p){
769                         ast_copy_string(event_spool_dir, p, sizeof(event_spool_dir));
770                         event_spool_dir[sizeof(event_spool_dir) - 1] = '\0';
771                 }
772                 
773                 p = ast_variable_retrieve(cfg, "general", "timestampformat");
774                         
775                 if(p){
776                         ast_copy_string(time_stamp_format, p, sizeof(time_stamp_format));
777                         time_stamp_format[sizeof(time_stamp_format) - 1] = '\0';
778                 }
779
780                 p = ast_variable_retrieve(cfg, "general", "db-family");
781                                                                                                                                             
782                 if(p){
783                         ast_copy_string(db_family, p, sizeof(db_family));
784                         db_family[sizeof(db_family) - 1] = '\0';
785                 }
786                 ast_config_destroy(cfg);
787         }
788         return 1;
789
790 }
791
792 /*
793 * These functions are required to implement an Asterisk App.
794 */
795
796
797 static int unload_module(void)
798 {
799         return ast_unregister_application(app);
800 }
801
802 static int load_module(void)
803 {
804         if(load_config()) {
805                 if (ast_register_application(app, alarmreceiver_exec, synopsis, descrip))
806                         return AST_MODULE_LOAD_FAILURE;
807                 return AST_MODULE_LOAD_SUCCESS;
808         }       
809         else
810                 return AST_MODULE_LOAD_DECLINE;
811 }
812
813 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk");