Thu Apr 28 2011 17:15:13

Asterisk developer's documentation


app_alarmreceiver.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C)  2004 - 2005 Steve Rodgers
00005  *
00006  * Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  * \brief Central Station Alarm receiver for Ademco Contact ID
00021  * \author Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
00022  *
00023  * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
00024  *
00025  * Use at your own risk. Please consult the GNU GPL license document included with Asterisk.         *
00026  *
00027  * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
00028  *
00029  * \ingroup applications
00030  */
00031 
00032 #include "asterisk.h"
00033 
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211580 $")
00035 
00036 #include <math.h>
00037 #include <sys/wait.h>
00038 #include <sys/time.h>
00039 
00040 #include "asterisk/lock.h"
00041 #include "asterisk/file.h"
00042 #include "asterisk/channel.h"
00043 #include "asterisk/pbx.h"
00044 #include "asterisk/module.h"
00045 #include "asterisk/translate.h"
00046 #include "asterisk/ulaw.h"
00047 #include "asterisk/app.h"
00048 #include "asterisk/dsp.h"
00049 #include "asterisk/config.h"
00050 #include "asterisk/localtime.h"
00051 #include "asterisk/callerid.h"
00052 #include "asterisk/astdb.h"
00053 #include "asterisk/utils.h"
00054 
00055 #define ALMRCV_CONFIG "alarmreceiver.conf"
00056 #define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID"
00057 
00058 struct event_node{
00059    char data[17];
00060    struct event_node *next;
00061 };
00062 
00063 typedef struct event_node event_node_t;
00064 
00065 static char *app = "AlarmReceiver";
00066 /*** DOCUMENTATION
00067    <application name="AlarmReceiver" language="en_US">
00068       <synopsis>
00069          Provide support for receiving alarm reports from a burglar or fire alarm panel.
00070       </synopsis>
00071       <syntax />
00072       <description>
00073          <para>This application should be called whenever there is an alarm panel calling in to dump its events.
00074          The application will handshake with the alarm panel, and receive events, validate them, handshake them,
00075          and store them until the panel hangs up. Once the panel hangs up, the application will run the system
00076          command specified by the eventcmd setting in <filename>alarmreceiver.conf</filename> and pipe the
00077          events to the standard input of the application.
00078          The configuration file also contains settings for DTMF timing, and for the loudness of the
00079          acknowledgement tones.</para>
00080          <note><para>Only 1 signalling format is supported at this time: Ademco Contact ID.</para></note>
00081       </description>
00082       <see-also>
00083          <ref type="filename">alarmreceiver.conf</ref>
00084       </see-also>
00085    </application>
00086  ***/
00087 
00088 /* Config Variables */
00089 static int fdtimeout = 2000;
00090 static int sdtimeout = 200;
00091 static int toneloudness = 4096;
00092 static int log_individual_events = 0;
00093 static char event_spool_dir[128] = {'\0'};
00094 static char event_app[128] = {'\0'};
00095 static char db_family[128] = {'\0'};
00096 static char time_stamp_format[128] = {"%a %b %d, %Y @ %H:%M:%S %Z"};
00097 
00098 /* Misc variables */
00099 static char event_file[14] = "/event-XXXXXX";
00100 
00101 /*
00102 * Attempt to access a database variable and increment it,
00103 * provided that the user defined db-family in alarmreceiver.conf
00104 * The alarmreceiver app will write statistics to a few variables
00105 * in this family if it is defined. If the new key doesn't exist in the
00106 * family, then create it and set its value to 1.
00107 */
00108 static void database_increment( char *key )
00109 {
00110    int res = 0;
00111    unsigned v;
00112    char value[16];
00113    
00114    
00115    if (ast_strlen_zero(db_family))
00116       return; /* If not defined, don't do anything */
00117    
00118    res = ast_db_get(db_family, key, value, sizeof(value) - 1);
00119    
00120    if (res) {
00121       ast_verb(4, "AlarmReceiver: Creating database entry %s and setting to 1\n", key);
00122       /* Guess we have to create it */
00123       res = ast_db_put(db_family, key, "1");
00124       return;
00125    }
00126    
00127    sscanf(value, "%30u", &v);
00128    v++;
00129 
00130    ast_verb(4, "AlarmReceiver: New value for %s: %u\n", key, v);
00131 
00132    snprintf(value, sizeof(value), "%u", v);
00133 
00134    res = ast_db_put(db_family, key, value);
00135 
00136    if (res)
00137       ast_verb(4, "AlarmReceiver: database_increment write error\n");
00138 
00139    return;
00140 }
00141 
00142 
00143 /*
00144 * Build a MuLaw data block for a single frequency tone
00145 */
00146 static void make_tone_burst(unsigned char *data, float freq, float loudness, int len, int *x)
00147 {
00148    int     i;
00149    float   val;
00150 
00151    for (i = 0; i < len; i++) {
00152       val = loudness * sin((freq * 2.0 * M_PI * (*x)++)/8000.0);
00153       data[i] = AST_LIN2MU((int)val);
00154    }
00155 
00156    /* wrap back around from 8000 */
00157 
00158    if (*x >= 8000)
00159       *x = 0;
00160    return;
00161 }
00162 
00163 /*
00164 * Send a single tone burst for a specifed duration and frequency.
00165 * Returns 0 if successful
00166 */
00167 static int send_tone_burst(struct ast_channel *chan, float freq, int duration, int tldn)
00168 {
00169    int res = 0;
00170    int i = 0;
00171    int x = 0;
00172    struct ast_frame *f, wf;
00173    
00174    struct {
00175       unsigned char offset[AST_FRIENDLY_OFFSET];
00176       unsigned char buf[640];
00177    } tone_block;
00178 
00179    for (;;) {
00180 
00181       if (ast_waitfor(chan, -1) < 0) {
00182          res = -1;
00183          break;
00184       }
00185 
00186       f = ast_read(chan);
00187       if (!f) {
00188          res = -1;
00189          break;
00190       }
00191 
00192       if (f->frametype == AST_FRAME_VOICE) {
00193          wf.frametype = AST_FRAME_VOICE;
00194          wf.subclass = AST_FORMAT_ULAW;
00195          wf.offset = AST_FRIENDLY_OFFSET;
00196          wf.mallocd = 0;
00197          wf.data.ptr = tone_block.buf;
00198          wf.datalen = f->datalen;
00199          wf.samples = wf.datalen;
00200          
00201          make_tone_burst(tone_block.buf, freq, (float) tldn, wf.datalen, &x);
00202 
00203          i += wf.datalen / 8;
00204          if (i > duration) {
00205             ast_frfree(f);
00206             break;
00207          }
00208          if (ast_write(chan, &wf)) {
00209             ast_verb(4, "AlarmReceiver: Failed to write frame on %s\n", chan->name);
00210             ast_log(LOG_WARNING, "AlarmReceiver Failed to write frame on %s\n",chan->name);
00211             res = -1;
00212             ast_frfree(f);
00213             break;
00214          }
00215       }
00216 
00217       ast_frfree(f);
00218    }
00219    return res;
00220 }
00221 
00222 /*
00223 * Receive a string of DTMF digits where the length of the digit string is known in advance. Do not give preferential
00224 * treatment to any digit value, and allow separate time out values to be specified for the first digit and all subsequent
00225 * digits.
00226 *
00227 * Returns 0 if all digits successfully received.
00228 * Returns 1 if a digit time out occurred
00229 * Returns -1 if the caller hung up or there was a channel error.
00230 *
00231 */
00232 static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int length, int fdto, int sdto)
00233 {
00234    int res = 0;
00235    int i = 0;
00236    int r;
00237    struct ast_frame *f;
00238    struct timeval lastdigittime;
00239 
00240    lastdigittime = ast_tvnow();
00241    for (;;) {
00242       /* if outa time, leave */
00243       if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((i > 0) ? sdto : fdto)) {
00244          ast_verb(4, "AlarmReceiver: DTMF Digit Timeout on %s\n", chan->name);
00245          ast_debug(1,"AlarmReceiver: DTMF timeout on chan %s\n",chan->name);
00246          res = 1;
00247          break;
00248       }
00249 
00250       if ((r = ast_waitfor(chan, -1) < 0)) {
00251          ast_debug(1, "Waitfor returned %d\n", r);
00252          continue;
00253       }
00254 
00255       f = ast_read(chan);
00256 
00257       if (f == NULL) {
00258          res = -1;
00259          break;
00260       }
00261 
00262       /* If they hung up, leave */
00263       if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
00264          if (f->data.uint32) {
00265             chan->hangupcause = f->data.uint32;
00266          }
00267          ast_frfree(f);
00268          res = -1;
00269          break;
00270       }
00271 
00272       /* if not DTMF, just do it again */
00273       if (f->frametype != AST_FRAME_DTMF) {
00274          ast_frfree(f);
00275          continue;
00276       }
00277 
00278       digit_string[i++] = f->subclass;  /* save digit */
00279 
00280       ast_frfree(f);
00281 
00282       /* If we have all the digits we expect, leave */
00283       if(i >= length)
00284          break;
00285 
00286       lastdigittime = ast_tvnow();
00287    }
00288 
00289    digit_string[i] = '\0'; /* Nul terminate the end of the digit string */
00290    return res;
00291 }
00292 
00293 /*
00294 * Write the metadata to the log file
00295 */
00296 static int write_metadata( FILE *logfile, char *signalling_type, struct ast_channel *chan)
00297 {
00298    int res = 0;
00299    struct timeval t;
00300    struct ast_tm now;
00301    char *cl,*cn;
00302    char workstring[80];
00303    char timestamp[80];
00304    
00305    /* Extract the caller ID location */
00306    if (chan->cid.cid_num)
00307       ast_copy_string(workstring, chan->cid.cid_num, sizeof(workstring));
00308    workstring[sizeof(workstring) - 1] = '\0';
00309 
00310    ast_callerid_parse(workstring, &cn, &cl);
00311    if (cl)
00312       ast_shrink_phone_number(cl);
00313 
00314    /* Get the current time */
00315    t = ast_tvnow();
00316    ast_localtime(&t, &now, NULL);
00317 
00318    /* Format the time */
00319    ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
00320 
00321    res = fprintf(logfile, "\n\n[metadata]\n\n");
00322 
00323    if (res >= 0)
00324       res = fprintf(logfile, "PROTOCOL=%s\n", signalling_type);
00325 
00326    if (res >= 0)
00327       res = fprintf(logfile, "CALLINGFROM=%s\n", (!cl) ? "<unknown>" : cl);
00328 
00329    if (res >- 0)
00330       res = fprintf(logfile, "CALLERNAME=%s\n", (!cn) ? "<unknown>" : cn);
00331 
00332    if (res >= 0)
00333       res = fprintf(logfile, "TIMESTAMP=%s\n\n", timestamp);
00334 
00335    if (res >= 0)
00336       res = fprintf(logfile, "[events]\n\n");
00337 
00338    if (res < 0) {
00339       ast_verb(3, "AlarmReceiver: can't write metadata\n");
00340       ast_debug(1,"AlarmReceiver: can't write metadata\n");
00341    } else
00342       res = 0;
00343 
00344    return res;
00345 }
00346 
00347 /*
00348 * Write a single event to the log file
00349 */
00350 static int write_event( FILE *logfile,  event_node_t *event)
00351 {
00352    int res = 0;
00353 
00354    if (fprintf(logfile, "%s\n", event->data) < 0)
00355       res = -1;
00356 
00357    return res;
00358 }
00359 
00360 /*
00361 * If we are configured to log events, do so here.
00362 *
00363 */
00364 static int log_events(struct ast_channel *chan,  char *signalling_type, event_node_t *event)
00365 {
00366 
00367    int res = 0;
00368    char workstring[sizeof(event_spool_dir)+sizeof(event_file)] = "";
00369    int fd;
00370    FILE *logfile;
00371    event_node_t *elp = event;
00372    
00373    if (!ast_strlen_zero(event_spool_dir)) {
00374       
00375       /* Make a template */
00376       ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
00377       strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
00378       
00379       /* Make the temporary file */
00380       fd = mkstemp(workstring);
00381       
00382       if (fd == -1) {
00383          ast_verb(3, "AlarmReceiver: can't make temporary file\n");
00384          ast_debug(1,"AlarmReceiver: can't make temporary file\n");
00385          res = -1;
00386       }
00387 
00388       if (!res) {
00389          logfile = fdopen(fd, "w");
00390          if (logfile) {
00391             /* Write the file */
00392             res = write_metadata(logfile, signalling_type, chan);
00393             if (!res)
00394                while ((!res) && (elp != NULL)) {
00395                   res = write_event(logfile, elp);
00396                   elp = elp->next;
00397                }
00398             if (!res) {
00399                if (fflush(logfile) == EOF)
00400                   res = -1;
00401                if (!res) {
00402                   if (fclose(logfile) == EOF)
00403                      res = -1;
00404                }
00405             }
00406          } else
00407             res = -1;
00408       }
00409    }
00410 
00411    return res;
00412 }
00413 
00414 /*
00415 * This function implements the logic to receive the Ademco contact ID  format.
00416 *
00417 * The function will return 0 when the caller hangs up, else a -1 if there was a problem.
00418 */
00419 static int receive_ademco_contact_id( struct ast_channel *chan, void *data, int fdto, int sdto, int tldn, event_node_t **ehead)
00420 {
00421    int i, j;
00422    int res = 0;
00423    int checksum;
00424    char event[17];
00425    event_node_t *enew, *elp;
00426    int got_some_digits = 0;
00427    int events_received = 0;
00428    int ack_retries = 0;
00429    
00430    static char digit_map[15] = "0123456789*#ABC";
00431    static unsigned char digit_weights[15] = {10,1,2,3,4,5,6,7,8,9,11,12,13,14,15};
00432 
00433    database_increment("calls-received");
00434 
00435    /* Wait for first event */
00436    ast_verb(4, "AlarmReceiver: Waiting for first event from panel\n");
00437 
00438    while (res >= 0) {
00439       if (got_some_digits == 0) {
00440          /* Send ACK tone sequence */
00441          ast_verb(4, "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
00442          res = send_tone_burst(chan, 1400.0, 100, tldn);
00443          if (!res)
00444             res = ast_safe_sleep(chan, 100);
00445          if (!res) {
00446             ast_verb(4, "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
00447             res = send_tone_burst(chan, 2300.0, 100, tldn);
00448          }
00449       }
00450       if ( res >= 0)
00451          res = receive_dtmf_digits(chan, event, sizeof(event) - 1, fdto, sdto);
00452       if (res < 0) {
00453          if (events_received == 0) {
00454             /* Hangup with no events received should be logged in the DB */
00455             database_increment("no-events-received");
00456          } else {
00457             if (ack_retries) {
00458                ast_verb(4, "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
00459                database_increment("ack-retries");
00460             }
00461          }
00462          ast_verb(4, "AlarmReceiver: App exiting...\n");
00463          res = -1;
00464          break;
00465       }
00466 
00467       if (res != 0) {
00468          /* Didn't get all of the digits */
00469          ast_verb(2, "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
00470 
00471          if (!got_some_digits) {
00472             got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
00473             ack_retries++;
00474          }
00475          continue;
00476       }
00477 
00478       got_some_digits = 1;
00479 
00480       ast_verb(2, "AlarmReceiver: Received Event %s\n", event);
00481       ast_debug(1, "AlarmReceiver: Received event: %s\n", event);
00482 
00483       /* Calculate checksum */
00484 
00485       for (j = 0, checksum = 0; j < 16; j++) {
00486          for (i = 0; i < sizeof(digit_map); i++) {
00487             if (digit_map[i] == event[j])
00488                break;
00489          }
00490 
00491          if (i == 16)
00492             break;
00493 
00494          checksum += digit_weights[i];
00495       }
00496       if (i == 16) {
00497          ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
00498          continue; /* Bad character */
00499       }
00500 
00501       /* Checksum is mod(15) of the total */
00502 
00503       checksum = checksum % 15;
00504 
00505       if (checksum) {
00506          database_increment("checksum-errors");
00507          ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
00508          ast_debug(1, "AlarmReceiver: Nonzero checksum\n");
00509          continue;
00510       }
00511 
00512       /* Check the message type for correctness */
00513 
00514       if (strncmp(event + 4, "18", 2)) {
00515          if (strncmp(event + 4, "98", 2)) {
00516             database_increment("format-errors");
00517             ast_verb(2, "AlarmReceiver: Wrong message type\n");
00518             ast_debug(1, "AlarmReceiver: Wrong message type\n");
00519          continue;
00520          }
00521       }
00522 
00523       events_received++;
00524 
00525       /* Queue the Event */
00526       if (!(enew = ast_calloc(1, sizeof(*enew)))) {
00527          res = -1;
00528          break;
00529       }
00530 
00531       enew->next = NULL;
00532       ast_copy_string(enew->data, event, sizeof(enew->data));
00533 
00534       /*
00535       * Insert event onto end of list
00536       */
00537       if (*ehead == NULL)
00538          *ehead = enew;
00539       else {
00540          for(elp = *ehead; elp->next != NULL; elp = elp->next)
00541          ;
00542          elp->next = enew;
00543       }
00544 
00545       if (res > 0)
00546          res = 0;
00547 
00548       /* Let the user have the option of logging the single event before sending the kissoff tone */
00549       if ((res == 0) && (log_individual_events))
00550          res = log_events(chan, ADEMCO_CONTACT_ID, enew);
00551       /* Wait 200 msec before sending kissoff */
00552       if (res == 0)
00553          res = ast_safe_sleep(chan, 200);
00554 
00555       /* Send the kissoff tone */
00556       if (res == 0)
00557          res = send_tone_burst(chan, 1400.0, 900, tldn);
00558    }
00559 
00560    return res;
00561 }
00562 
00563 /*
00564 * This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
00565 * This function will always return 0.
00566 */
00567 static int alarmreceiver_exec(struct ast_channel *chan, void *data)
00568 {
00569    int res = 0;
00570    event_node_t *elp, *efree;
00571    char signalling_type[64] = "";
00572    event_node_t *event_head = NULL;
00573 
00574    /* Set write and read formats to ULAW */
00575    ast_verb(4, "AlarmReceiver: Setting read and write formats to ULAW\n");
00576 
00577    if (ast_set_write_format(chan,AST_FORMAT_ULAW)) {
00578       ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",chan->name);
00579       return -1;
00580    }
00581 
00582    if (ast_set_read_format(chan,AST_FORMAT_ULAW)) {
00583       ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",chan->name);
00584       return -1;
00585    }
00586 
00587    /* Set default values for this invocation of the application */
00588    ast_copy_string(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type));
00589 
00590    /* Answer the channel if it is not already */
00591    ast_verb(4, "AlarmReceiver: Answering channel\n");
00592    if (chan->_state != AST_STATE_UP) {
00593       if ((res = ast_answer(chan)))
00594          return -1;
00595    }
00596 
00597    /* Wait for the connection to settle post-answer */
00598    ast_verb(4, "AlarmReceiver: Waiting for connection to stabilize\n");
00599    res = ast_safe_sleep(chan, 1250);
00600 
00601    /* Attempt to receive the events */
00602    if (!res) {
00603       /* Determine the protocol to receive in advance */
00604       /* Note: Ademco contact is the only one supported at this time */
00605       /* Others may be added later */
00606       if(!strcmp(signalling_type, ADEMCO_CONTACT_ID))
00607          receive_ademco_contact_id(chan, data, fdtimeout, sdtimeout, toneloudness, &event_head);
00608       else
00609          res = -1;
00610    }
00611 
00612    /* Events queued by receiver, write them all out here if so configured */
00613    if ((!res) && (log_individual_events == 0))
00614       res = log_events(chan, signalling_type, event_head);
00615 
00616    /*
00617    * Do we exec a command line at the end?
00618    */
00619    if ((!res) && (!ast_strlen_zero(event_app)) && (event_head)) {
00620       ast_debug(1,"Alarmreceiver: executing: %s\n", event_app);
00621       ast_safe_system(event_app);
00622    }
00623 
00624    /*
00625    * Free up the data allocated in our linked list
00626    */
00627    for (elp = event_head; (elp != NULL);) {
00628       efree = elp;
00629       elp = elp->next;
00630       ast_free(efree);
00631    }
00632 
00633    return 0;
00634 }
00635 
00636 /*
00637 * Load the configuration from the configuration file
00638 */
00639 static int load_config(void)
00640 {
00641    struct ast_config *cfg;
00642    const char *p;
00643    struct ast_flags config_flags = { 0 };
00644 
00645    /* Read in the config file */
00646    cfg = ast_config_load(ALMRCV_CONFIG, config_flags);
00647 
00648    if (!cfg) {
00649       ast_verb(4, "AlarmReceiver: No config file\n");
00650       return 0;
00651    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00652       ast_log(LOG_ERROR, "Config file %s is in an invalid format.  Aborting.\n", ALMRCV_CONFIG);
00653       return 0;
00654    } else {
00655       p = ast_variable_retrieve(cfg, "general", "eventcmd");
00656       if (p) {
00657          ast_copy_string(event_app, p, sizeof(event_app));
00658          event_app[sizeof(event_app) - 1] = '\0';
00659       }
00660       p = ast_variable_retrieve(cfg, "general", "loudness");
00661       if (p) {
00662          toneloudness = atoi(p);
00663          if(toneloudness < 100)
00664             toneloudness = 100;
00665          if(toneloudness > 8192)
00666             toneloudness = 8192;
00667       }
00668       p = ast_variable_retrieve(cfg, "general", "fdtimeout");
00669       if (p) {
00670          fdtimeout = atoi(p);
00671          if(fdtimeout < 1000)
00672             fdtimeout = 1000;
00673          if(fdtimeout > 10000)
00674             fdtimeout = 10000;
00675       }
00676 
00677       p = ast_variable_retrieve(cfg, "general", "sdtimeout");
00678       if (p) {
00679          sdtimeout = atoi(p);
00680          if(sdtimeout < 110)
00681             sdtimeout = 110;
00682          if(sdtimeout > 4000)
00683             sdtimeout = 4000;
00684       }
00685 
00686       p = ast_variable_retrieve(cfg, "general", "logindividualevents");
00687       if (p)
00688          log_individual_events = ast_true(p);
00689 
00690       p = ast_variable_retrieve(cfg, "general", "eventspooldir");
00691       if (p) {
00692          ast_copy_string(event_spool_dir, p, sizeof(event_spool_dir));
00693          event_spool_dir[sizeof(event_spool_dir) - 1] = '\0';
00694       }
00695 
00696       p = ast_variable_retrieve(cfg, "general", "timestampformat");
00697       if (p) {
00698          ast_copy_string(time_stamp_format, p, sizeof(time_stamp_format));
00699          time_stamp_format[sizeof(time_stamp_format) - 1] = '\0';
00700       }
00701 
00702       p = ast_variable_retrieve(cfg, "general", "db-family");
00703       if (p) {
00704          ast_copy_string(db_family, p, sizeof(db_family));
00705          db_family[sizeof(db_family) - 1] = '\0';
00706       }
00707       ast_config_destroy(cfg);
00708    }
00709    return 1;
00710 }
00711 
00712 /*
00713 * These functions are required to implement an Asterisk App.
00714 */
00715 static int unload_module(void)
00716 {
00717    return ast_unregister_application(app);
00718 }
00719 
00720 static int load_module(void)
00721 {
00722    if (load_config()) {
00723       if (ast_register_application_xml(app, alarmreceiver_exec))
00724          return AST_MODULE_LOAD_FAILURE;
00725       return AST_MODULE_LOAD_SUCCESS;
00726    } else
00727       return AST_MODULE_LOAD_DECLINE;
00728 }
00729 
00730 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk");