Mon Jun 27 16:50:46 2011

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: 276347 $")
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 const 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.codec = 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.integer == 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.integer;  /* 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;
00302    char *cn;
00303    char workstring[80];
00304    char timestamp[80];
00305    
00306    /* Extract the caller ID location */
00307    ast_copy_string(workstring,
00308       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""),
00309       sizeof(workstring));
00310    ast_shrink_phone_number(workstring);
00311    if (ast_strlen_zero(workstring)) {
00312       cl = "<unknown>";
00313    } else {
00314       cl = workstring;
00315    }
00316    cn = S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, "<unknown>");
00317 
00318    /* Get the current time */
00319    t = ast_tvnow();
00320    ast_localtime(&t, &now, NULL);
00321 
00322    /* Format the time */
00323    ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
00324 
00325    res = fprintf(logfile, "\n\n[metadata]\n\n");
00326    if (res >= 0) {
00327       res = fprintf(logfile, "PROTOCOL=%s\n", signalling_type);
00328    }
00329    if (res >= 0) {
00330       res = fprintf(logfile, "CALLINGFROM=%s\n", cl);
00331    }
00332    if (res >= 0) {
00333       res = fprintf(logfile, "CALLERNAME=%s\n", cn);
00334    }
00335    if (res >= 0) {
00336       res = fprintf(logfile, "TIMESTAMP=%s\n\n", timestamp);
00337    }
00338    if (res >= 0) {
00339       res = fprintf(logfile, "[events]\n\n");
00340    }
00341    if (res < 0) {
00342       ast_verb(3, "AlarmReceiver: can't write metadata\n");
00343       ast_debug(1,"AlarmReceiver: can't write metadata\n");
00344    } else {
00345       res = 0;
00346    }
00347 
00348    return res;
00349 }
00350 
00351 /*
00352 * Write a single event to the log file
00353 */
00354 static int write_event( FILE *logfile,  event_node_t *event)
00355 {
00356    int res = 0;
00357 
00358    if (fprintf(logfile, "%s\n", event->data) < 0)
00359       res = -1;
00360 
00361    return res;
00362 }
00363 
00364 /*
00365 * If we are configured to log events, do so here.
00366 *
00367 */
00368 static int log_events(struct ast_channel *chan,  char *signalling_type, event_node_t *event)
00369 {
00370 
00371    int res = 0;
00372    char workstring[sizeof(event_spool_dir)+sizeof(event_file)] = "";
00373    int fd;
00374    FILE *logfile;
00375    event_node_t *elp = event;
00376    
00377    if (!ast_strlen_zero(event_spool_dir)) {
00378       
00379       /* Make a template */
00380       ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
00381       strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
00382       
00383       /* Make the temporary file */
00384       fd = mkstemp(workstring);
00385       
00386       if (fd == -1) {
00387          ast_verb(3, "AlarmReceiver: can't make temporary file\n");
00388          ast_debug(1,"AlarmReceiver: can't make temporary file\n");
00389          res = -1;
00390       }
00391 
00392       if (!res) {
00393          logfile = fdopen(fd, "w");
00394          if (logfile) {
00395             /* Write the file */
00396             res = write_metadata(logfile, signalling_type, chan);
00397             if (!res)
00398                while ((!res) && (elp != NULL)) {
00399                   res = write_event(logfile, elp);
00400                   elp = elp->next;
00401                }
00402             if (!res) {
00403                if (fflush(logfile) == EOF)
00404                   res = -1;
00405                if (!res) {
00406                   if (fclose(logfile) == EOF)
00407                      res = -1;
00408                }
00409             }
00410          } else
00411             res = -1;
00412       }
00413    }
00414 
00415    return res;
00416 }
00417 
00418 /*
00419 * This function implements the logic to receive the Ademco contact ID  format.
00420 *
00421 * The function will return 0 when the caller hangs up, else a -1 if there was a problem.
00422 */
00423 static int receive_ademco_contact_id(struct ast_channel *chan, const void *data, int fdto, int sdto, int tldn, event_node_t **ehead)
00424 {
00425    int i, j;
00426    int res = 0;
00427    int checksum;
00428    char event[17];
00429    event_node_t *enew, *elp;
00430    int got_some_digits = 0;
00431    int events_received = 0;
00432    int ack_retries = 0;
00433    
00434    static char digit_map[15] = "0123456789*#ABC";
00435    static unsigned char digit_weights[15] = {10,1,2,3,4,5,6,7,8,9,11,12,13,14,15};
00436 
00437    database_increment("calls-received");
00438 
00439    /* Wait for first event */
00440    ast_verb(4, "AlarmReceiver: Waiting for first event from panel\n");
00441 
00442    while (res >= 0) {
00443       if (got_some_digits == 0) {
00444          /* Send ACK tone sequence */
00445          ast_verb(4, "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
00446          res = send_tone_burst(chan, 1400.0, 100, tldn);
00447          if (!res)
00448             res = ast_safe_sleep(chan, 100);
00449          if (!res) {
00450             ast_verb(4, "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
00451             res = send_tone_burst(chan, 2300.0, 100, tldn);
00452          }
00453       }
00454       if ( res >= 0)
00455          res = receive_dtmf_digits(chan, event, sizeof(event) - 1, fdto, sdto);
00456       if (res < 0) {
00457          if (events_received == 0) {
00458             /* Hangup with no events received should be logged in the DB */
00459             database_increment("no-events-received");
00460          } else {
00461             if (ack_retries) {
00462                ast_verb(4, "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
00463                database_increment("ack-retries");
00464             }
00465          }
00466          ast_verb(4, "AlarmReceiver: App exiting...\n");
00467          res = -1;
00468          break;
00469       }
00470 
00471       if (res != 0) {
00472          /* Didn't get all of the digits */
00473          ast_verb(2, "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
00474 
00475          if (!got_some_digits) {
00476             got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
00477             ack_retries++;
00478          }
00479          continue;
00480       }
00481 
00482       got_some_digits = 1;
00483 
00484       ast_verb(2, "AlarmReceiver: Received Event %s\n", event);
00485       ast_debug(1, "AlarmReceiver: Received event: %s\n", event);
00486 
00487       /* Calculate checksum */
00488 
00489       for (j = 0, checksum = 0; j < 16; j++) {
00490          for (i = 0; i < sizeof(digit_map); i++) {
00491             if (digit_map[i] == event[j])
00492                break;
00493          }
00494 
00495          if (i == 16)
00496             break;
00497 
00498          checksum += digit_weights[i];
00499       }
00500       if (i == 16) {
00501          ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
00502          continue; /* Bad character */
00503       }
00504 
00505       /* Checksum is mod(15) of the total */
00506 
00507       checksum = checksum % 15;
00508 
00509       if (checksum) {
00510          database_increment("checksum-errors");
00511          ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
00512          ast_debug(1, "AlarmReceiver: Nonzero checksum\n");
00513          continue;
00514       }
00515 
00516       /* Check the message type for correctness */
00517 
00518       if (strncmp(event + 4, "18", 2)) {
00519          if (strncmp(event + 4, "98", 2)) {
00520             database_increment("format-errors");
00521             ast_verb(2, "AlarmReceiver: Wrong message type\n");
00522             ast_debug(1, "AlarmReceiver: Wrong message type\n");
00523          continue;
00524          }
00525       }
00526 
00527       events_received++;
00528 
00529       /* Queue the Event */
00530       if (!(enew = ast_calloc(1, sizeof(*enew)))) {
00531          res = -1;
00532          break;
00533       }
00534 
00535       enew->next = NULL;
00536       ast_copy_string(enew->data, event, sizeof(enew->data));
00537 
00538       /*
00539       * Insert event onto end of list
00540       */
00541       if (*ehead == NULL)
00542          *ehead = enew;
00543       else {
00544          for(elp = *ehead; elp->next != NULL; elp = elp->next)
00545          ;
00546          elp->next = enew;
00547       }
00548 
00549       if (res > 0)
00550          res = 0;
00551 
00552       /* Let the user have the option of logging the single event before sending the kissoff tone */
00553       if ((res == 0) && (log_individual_events))
00554          res = log_events(chan, ADEMCO_CONTACT_ID, enew);
00555       /* Wait 200 msec before sending kissoff */
00556       if (res == 0)
00557          res = ast_safe_sleep(chan, 200);
00558 
00559       /* Send the kissoff tone */
00560       if (res == 0)
00561          res = send_tone_burst(chan, 1400.0, 900, tldn);
00562    }
00563 
00564    return res;
00565 }
00566 
00567 /*
00568 * This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
00569 * This function will always return 0.
00570 */
00571 static int alarmreceiver_exec(struct ast_channel *chan, const char *data)
00572 {
00573    int res = 0;
00574    event_node_t *elp, *efree;
00575    char signalling_type[64] = "";
00576    event_node_t *event_head = NULL;
00577 
00578    /* Set write and read formats to ULAW */
00579    ast_verb(4, "AlarmReceiver: Setting read and write formats to ULAW\n");
00580 
00581    if (ast_set_write_format(chan,AST_FORMAT_ULAW)) {
00582       ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",chan->name);
00583       return -1;
00584    }
00585 
00586    if (ast_set_read_format(chan,AST_FORMAT_ULAW)) {
00587       ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",chan->name);
00588       return -1;
00589    }
00590 
00591    /* Set default values for this invocation of the application */
00592    ast_copy_string(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type));
00593 
00594    /* Answer the channel if it is not already */
00595    ast_verb(4, "AlarmReceiver: Answering channel\n");
00596    if (chan->_state != AST_STATE_UP) {
00597       if ((res = ast_answer(chan)))
00598          return -1;
00599    }
00600 
00601    /* Wait for the connection to settle post-answer */
00602    ast_verb(4, "AlarmReceiver: Waiting for connection to stabilize\n");
00603    res = ast_safe_sleep(chan, 1250);
00604 
00605    /* Attempt to receive the events */
00606    if (!res) {
00607       /* Determine the protocol to receive in advance */
00608       /* Note: Ademco contact is the only one supported at this time */
00609       /* Others may be added later */
00610       if(!strcmp(signalling_type, ADEMCO_CONTACT_ID))
00611          receive_ademco_contact_id(chan, data, fdtimeout, sdtimeout, toneloudness, &event_head);
00612       else
00613          res = -1;
00614    }
00615 
00616    /* Events queued by receiver, write them all out here if so configured */
00617    if ((!res) && (log_individual_events == 0))
00618       res = log_events(chan, signalling_type, event_head);
00619 
00620    /*
00621    * Do we exec a command line at the end?
00622    */
00623    if ((!res) && (!ast_strlen_zero(event_app)) && (event_head)) {
00624       ast_debug(1,"Alarmreceiver: executing: %s\n", event_app);
00625       ast_safe_system(event_app);
00626    }
00627 
00628    /*
00629    * Free up the data allocated in our linked list
00630    */
00631    for (elp = event_head; (elp != NULL);) {
00632       efree = elp;
00633       elp = elp->next;
00634       ast_free(efree);
00635    }
00636 
00637    return 0;
00638 }
00639 
00640 /*
00641 * Load the configuration from the configuration file
00642 */
00643 static int load_config(void)
00644 {
00645    struct ast_config *cfg;
00646    const char *p;
00647    struct ast_flags config_flags = { 0 };
00648 
00649    /* Read in the config file */
00650    cfg = ast_config_load(ALMRCV_CONFIG, config_flags);
00651 
00652    if (!cfg) {
00653       ast_verb(4, "AlarmReceiver: No config file\n");
00654       return 0;
00655    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00656       ast_log(LOG_ERROR, "Config file %s is in an invalid format.  Aborting.\n", ALMRCV_CONFIG);
00657       return 0;
00658    } else {
00659       p = ast_variable_retrieve(cfg, "general", "eventcmd");
00660       if (p) {
00661          ast_copy_string(event_app, p, sizeof(event_app));
00662          event_app[sizeof(event_app) - 1] = '\0';
00663       }
00664       p = ast_variable_retrieve(cfg, "general", "loudness");
00665       if (p) {
00666          toneloudness = atoi(p);
00667          if(toneloudness < 100)
00668             toneloudness = 100;
00669          if(toneloudness > 8192)
00670             toneloudness = 8192;
00671       }
00672       p = ast_variable_retrieve(cfg, "general", "fdtimeout");
00673       if (p) {
00674          fdtimeout = atoi(p);
00675          if(fdtimeout < 1000)
00676             fdtimeout = 1000;
00677          if(fdtimeout > 10000)
00678             fdtimeout = 10000;
00679       }
00680 
00681       p = ast_variable_retrieve(cfg, "general", "sdtimeout");
00682       if (p) {
00683          sdtimeout = atoi(p);
00684          if(sdtimeout < 110)
00685             sdtimeout = 110;
00686          if(sdtimeout > 4000)
00687             sdtimeout = 4000;
00688       }
00689 
00690       p = ast_variable_retrieve(cfg, "general", "logindividualevents");
00691       if (p)
00692          log_individual_events = ast_true(p);
00693 
00694       p = ast_variable_retrieve(cfg, "general", "eventspooldir");
00695       if (p) {
00696          ast_copy_string(event_spool_dir, p, sizeof(event_spool_dir));
00697          event_spool_dir[sizeof(event_spool_dir) - 1] = '\0';
00698       }
00699 
00700       p = ast_variable_retrieve(cfg, "general", "timestampformat");
00701       if (p) {
00702          ast_copy_string(time_stamp_format, p, sizeof(time_stamp_format));
00703          time_stamp_format[sizeof(time_stamp_format) - 1] = '\0';
00704       }
00705 
00706       p = ast_variable_retrieve(cfg, "general", "db-family");
00707       if (p) {
00708          ast_copy_string(db_family, p, sizeof(db_family));
00709          db_family[sizeof(db_family) - 1] = '\0';
00710       }
00711       ast_config_destroy(cfg);
00712    }
00713    return 1;
00714 }
00715 
00716 /*
00717 * These functions are required to implement an Asterisk App.
00718 */
00719 static int unload_module(void)
00720 {
00721    return ast_unregister_application(app);
00722 }
00723 
00724 static int load_module(void)
00725 {
00726    if (load_config()) {
00727       if (ast_register_application_xml(app, alarmreceiver_exec))
00728          return AST_MODULE_LOAD_FAILURE;
00729       return AST_MODULE_LOAD_SUCCESS;
00730    } else
00731       return AST_MODULE_LOAD_DECLINE;
00732 }
00733 
00734 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk");

Generated on Mon Jun 27 16:50:46 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7