Wed Aug 18 22:33:40 2010

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

Generated on Wed Aug 18 22:33:40 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7