Thu Jul 9 13:40:17 2009

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

Generated on Thu Jul 9 13:40:17 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7