Wed Feb 11 12:00:02 2009

Asterisk developer's documentation


app_externalivr.c File Reference

External IVR application interface. More...

#include "asterisk.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/linkedlists.h"
#include "asterisk/app.h"
#include "asterisk/utils.h"
#include "asterisk/options.h"

Go to the source code of this file.

Data Structures

struct  gen_state
struct  ivr_localuser
struct  ivr_localuser::finishlist
struct  ivr_localuser::playlist
struct  playlist_entry

Defines

#define ast_chan_log(level, channel, format,)   ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__)

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int app_exec (struct ast_channel *chan, void *data)
static void * gen_alloc (struct ast_channel *chan, void *params)
static void gen_closestream (struct gen_state *state)
static int gen_generate (struct ast_channel *chan, void *data, int len, int samples)
static int gen_nextfile (struct gen_state *state)
static struct ast_framegen_readframe (struct gen_state *state)
static void gen_release (struct ast_channel *chan, void *data)
static int load_module (void)
static struct playlist_entrymake_entry (const char *filename)
static void send_child_event (FILE *handle, const char event, const char *data, const struct ast_channel *chan)
static int unload_module (void)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "External IVR Interface Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "f450f61f60e761b3aa089ebed76ca8a5" , .load = load_module, .unload = unload_module, }
static const char * app = "ExternalIVR"
static const struct ast_module_infoast_module_info = &__mod_info
static const char * descrip
static struct ast_generator gen
static const char * synopsis = "Interfaces with an external IVR application"


Detailed Description

External IVR application interface.

Author:
Kevin P. Fleming <kpfleming@digium.com>
Note:
Portions taken from the file-based music-on-hold work created by Anthony Minessale II in res_musiconhold.c

Definition in file app_externalivr.c.


Define Documentation

#define ast_chan_log ( level,
channel,
format   )     ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__)

Definition at line 71 of file app_externalivr.c.

Referenced by app_exec(), gen_generate(), gen_nextfile(), and send_child_event().


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 585 of file app_externalivr.c.

static void __unreg_module ( void   )  [static]

Definition at line 585 of file app_externalivr.c.

static int app_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 243 of file app_externalivr.c.

References ast_channel::_state, ivr_localuser::abort_current_sound, ast_activate_generator(), ast_answer(), ast_app_separate_args(), ast_chan_log, ast_check_hangup(), AST_CONTROL_HANGUP, ast_deactivate_generator(), ast_fileexists(), AST_FLAG_ZOMBIE, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_frfree, AST_LIST_EMPTY, AST_LIST_HEAD_INIT_VALUE, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_high_priority, ast_read(), ast_set_priority(), AST_STATE_UP, ast_strdupa, ast_strip(), ast_strlen_zero(), ast_test_flag, ast_waitfor_nandfds(), ivr_localuser::chan, errno, f, playlist_entry::filename, ivr_localuser::finishlist, free, gen, input(), ast_channel::language, playlist_entry::list, LOG_DEBUG, LOG_NOTICE, LOG_WARNING, make_entry(), ivr_localuser::option_autoclear, ivr_localuser::playing_silence, ivr_localuser::playlist, and send_child_event().

00244 {
00245    struct ast_module_user *lu;
00246    struct playlist_entry *entry;
00247    const char *args = data;
00248    int child_stdin[2] = { 0,0 };
00249    int child_stdout[2] = { 0,0 };
00250    int child_stderr[2] = { 0,0 };
00251    int res = -1;
00252    int test_available_fd = -1;
00253    int gen_active = 0;
00254    int pid;
00255    char *argv[32];
00256    int argc = 1;
00257    char *buf, *command;
00258    FILE *child_commands = NULL;
00259    FILE *child_errors = NULL;
00260    FILE *child_events = NULL;
00261    struct ivr_localuser foo = {
00262       .playlist = AST_LIST_HEAD_INIT_VALUE,
00263       .finishlist = AST_LIST_HEAD_INIT_VALUE,
00264    };
00265    struct ivr_localuser *u = &foo;
00266    sigset_t fullset, oldset;
00267 
00268    lu = ast_module_user_add(chan);
00269 
00270    sigfillset(&fullset);
00271    pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
00272 
00273    u->abort_current_sound = 0;
00274    u->chan = chan;
00275    
00276    if (ast_strlen_zero(args)) {
00277       ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
00278       ast_module_user_remove(lu);
00279       return -1;  
00280    }
00281 
00282    buf = ast_strdupa(data);
00283 
00284    argc = ast_app_separate_args(buf, '|', argv, sizeof(argv) / sizeof(argv[0]));
00285 
00286    if (pipe(child_stdin)) {
00287       ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno));
00288       goto exit;
00289    }
00290 
00291    if (pipe(child_stdout)) {
00292       ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno));
00293       goto exit;
00294    }
00295 
00296    if (pipe(child_stderr)) {
00297       ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
00298       goto exit;
00299    }
00300 
00301    if (chan->_state != AST_STATE_UP) {
00302       ast_answer(chan);
00303    }
00304 
00305    if (ast_activate_generator(chan, &gen, u) < 0) {
00306       ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
00307       goto exit;
00308    } else
00309       gen_active = 1;
00310 
00311    pid = fork();
00312    if (pid < 0) {
00313       ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
00314       goto exit;
00315    }
00316 
00317    if (!pid) {
00318       /* child process */
00319       int i;
00320 
00321       signal(SIGPIPE, SIG_DFL);
00322       pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
00323 
00324       if (ast_opt_high_priority)
00325          ast_set_priority(0);
00326 
00327       dup2(child_stdin[0], STDIN_FILENO);
00328       dup2(child_stdout[1], STDOUT_FILENO);
00329       dup2(child_stderr[1], STDERR_FILENO);
00330       for (i = STDERR_FILENO + 1; i < 1024; i++)
00331          close(i);
00332       execv(argv[0], argv);
00333       fprintf(stderr, "Failed to execute '%s': %s\n", argv[0], strerror(errno));
00334       _exit(1);
00335    } else {
00336       /* parent process */
00337       int child_events_fd = child_stdin[1];
00338       int child_commands_fd = child_stdout[0];
00339       int child_errors_fd = child_stderr[0];
00340       struct ast_frame *f;
00341       int ms;
00342       int exception;
00343       int ready_fd;
00344       int waitfds[2] = { child_errors_fd, child_commands_fd };
00345       struct ast_channel *rchan;
00346 
00347       pthread_sigmask(SIG_SETMASK, &oldset, NULL);
00348 
00349       close(child_stdin[0]);
00350       child_stdin[0] = 0;
00351       close(child_stdout[1]);
00352       child_stdout[1] = 0;
00353       close(child_stderr[1]);
00354       child_stderr[1] = 0;
00355 
00356       if (!(child_events = fdopen(child_events_fd, "w"))) {
00357          ast_chan_log(LOG_WARNING, chan, "Could not open stream for child events\n");
00358          goto exit;
00359       }
00360 
00361       if (!(child_commands = fdopen(child_commands_fd, "r"))) {
00362          ast_chan_log(LOG_WARNING, chan, "Could not open stream for child commands\n");
00363          goto exit;
00364       }
00365 
00366       if (!(child_errors = fdopen(child_errors_fd, "r"))) {
00367          ast_chan_log(LOG_WARNING, chan, "Could not open stream for child errors\n");
00368          goto exit;
00369       }
00370 
00371       test_available_fd = open("/dev/null", O_RDONLY);
00372 
00373       setvbuf(child_events, NULL, _IONBF, 0);
00374       setvbuf(child_commands, NULL, _IONBF, 0);
00375       setvbuf(child_errors, NULL, _IONBF, 0);
00376 
00377       res = 0;
00378 
00379       while (1) {
00380          if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
00381             ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n");
00382             res = -1;
00383             break;
00384          }
00385 
00386          if (ast_check_hangup(chan)) {
00387             ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n");
00388             send_child_event(child_events, 'H', NULL, chan);
00389             res = -1;
00390             break;
00391          }
00392 
00393          ready_fd = 0;
00394          ms = 100;
00395          errno = 0;
00396          exception = 0;
00397 
00398          rchan = ast_waitfor_nandfds(&chan, 1, waitfds, 2, &exception, &ready_fd, &ms);
00399 
00400          if (!AST_LIST_EMPTY(&u->finishlist)) {
00401             AST_LIST_LOCK(&u->finishlist);
00402             while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
00403                send_child_event(child_events, 'F', entry->filename, chan);
00404                free(entry);
00405             }
00406             AST_LIST_UNLOCK(&u->finishlist);
00407          }
00408 
00409          if (rchan) {
00410             /* the channel has something */
00411             f = ast_read(chan);
00412             if (!f) {
00413                ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n");
00414                send_child_event(child_events, 'H', NULL, chan);
00415                res = -1;
00416                break;
00417             }
00418 
00419             if (f->frametype == AST_FRAME_DTMF) {
00420                send_child_event(child_events, f->subclass, NULL, chan);
00421                if (u->option_autoclear) {
00422                   if (!u->abort_current_sound && !u->playing_silence)
00423                      send_child_event(child_events, 'T', NULL, chan);
00424                   AST_LIST_LOCK(&u->playlist);
00425                   while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00426                      send_child_event(child_events, 'D', entry->filename, chan);
00427                      free(entry);
00428                   }
00429                   if (!u->playing_silence)
00430                      u->abort_current_sound = 1;
00431                   AST_LIST_UNLOCK(&u->playlist);
00432                }
00433             } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
00434                ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n");
00435                send_child_event(child_events, 'H', NULL, chan);
00436                ast_frfree(f);
00437                res = -1;
00438                break;
00439             }
00440             ast_frfree(f);
00441          } else if (ready_fd == child_commands_fd) {
00442             char input[1024];
00443 
00444             if (exception || feof(child_commands)) {
00445                ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
00446                res = -1;
00447                break;
00448             }
00449 
00450             if (!fgets(input, sizeof(input), child_commands))
00451                continue;
00452 
00453             command = ast_strip(input);
00454 
00455             ast_chan_log(LOG_DEBUG, chan, "got command '%s'\n", input);
00456 
00457             if (strlen(input) < 4)
00458                continue;
00459 
00460             if (input[0] == 'S') {
00461                if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
00462                   ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
00463                   send_child_event(child_events, 'Z', NULL, chan);
00464                   strcpy(&input[2], "exception");
00465                }
00466                if (!u->abort_current_sound && !u->playing_silence)
00467                   send_child_event(child_events, 'T', NULL, chan);
00468                AST_LIST_LOCK(&u->playlist);
00469                while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00470                   send_child_event(child_events, 'D', entry->filename, chan);
00471                   free(entry);
00472                }
00473                if (!u->playing_silence)
00474                   u->abort_current_sound = 1;
00475                entry = make_entry(&input[2]);
00476                if (entry)
00477                   AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
00478                AST_LIST_UNLOCK(&u->playlist);
00479             } else if (input[0] == 'A') {
00480                if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
00481                   ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
00482                   send_child_event(child_events, 'Z', NULL, chan);
00483                   strcpy(&input[2], "exception");
00484                }
00485                entry = make_entry(&input[2]);
00486                if (entry) {
00487                   AST_LIST_LOCK(&u->playlist);
00488                   AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
00489                   AST_LIST_UNLOCK(&u->playlist);
00490                }
00491             } else if (input[0] == 'H') {
00492                ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
00493                send_child_event(child_events, 'H', NULL, chan);
00494                break;
00495             } else if (input[0] == 'O') {
00496                if (!strcasecmp(&input[2], "autoclear"))
00497                   u->option_autoclear = 1;
00498                else if (!strcasecmp(&input[2], "noautoclear"))
00499                   u->option_autoclear = 0;
00500                else
00501                   ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]);
00502             }
00503          } else if (ready_fd == child_errors_fd) {
00504             char input[1024];
00505 
00506             if (exception || (dup2(child_commands_fd, test_available_fd) == -1) || feof(child_errors)) {
00507                ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
00508                res = -1;
00509                break;
00510             }
00511 
00512             if (fgets(input, sizeof(input), child_errors)) {
00513                command = ast_strip(input);
00514                ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command);
00515             }
00516          } else if ((ready_fd < 0) && ms) { 
00517             if (errno == 0 || errno == EINTR)
00518                continue;
00519 
00520             ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno));
00521             break;
00522          }
00523       }
00524    }
00525 
00526  exit:
00527    if (gen_active)
00528       ast_deactivate_generator(chan);
00529 
00530    if (child_events)
00531       fclose(child_events);
00532 
00533    if (child_commands)
00534       fclose(child_commands);
00535 
00536    if (child_errors)
00537       fclose(child_errors);
00538 
00539    if (test_available_fd > -1) {
00540       close(test_available_fd);
00541    }
00542 
00543    if (child_stdin[0])
00544       close(child_stdin[0]);
00545 
00546    if (child_stdin[1])
00547       close(child_stdin[1]);
00548 
00549    if (child_stdout[0])
00550       close(child_stdout[0]);
00551 
00552    if (child_stdout[1])
00553       close(child_stdout[1]);
00554 
00555    if (child_stderr[0])
00556       close(child_stderr[0]);
00557 
00558    if (child_stderr[1])
00559       close(child_stderr[1]);
00560 
00561    while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list)))
00562       free(entry);
00563 
00564    ast_module_user_remove(lu);
00565 
00566    return res;
00567 }

static void* gen_alloc ( struct ast_channel chan,
void *  params 
) [static]

Definition at line 110 of file app_externalivr.c.

References ast_calloc, and gen_state::u.

00111 {
00112    struct ivr_localuser *u = params;
00113    struct gen_state *state;
00114    
00115    if (!(state = ast_calloc(1, sizeof(*state))))
00116       return NULL;
00117 
00118    state->u = u;
00119 
00120    return state;
00121 }

static void gen_closestream ( struct gen_state state  )  [static]

Definition at line 123 of file app_externalivr.c.

References ast_closestream(), ivr_localuser::chan, ast_channel::stream, gen_state::stream, and gen_state::u.

Referenced by gen_nextfile(), gen_readframe(), and gen_release().

00124 {
00125    if (!state->stream)
00126       return;
00127 
00128    ast_closestream(state->stream);
00129    state->u->chan->stream = NULL;
00130    state->stream = NULL;
00131 }

static int gen_generate ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
) [static]

Definition at line 200 of file app_externalivr.c.

References ast_chan_log, ast_frfree, ast_write(), errno, f, gen_readframe(), LOG_WARNING, and gen_state::sample_queue.

00201 {
00202    struct gen_state *state = data;
00203    struct ast_frame *f = NULL;
00204    int res = 0;
00205 
00206    state->sample_queue += samples;
00207 
00208    while (state->sample_queue > 0) {
00209       if (!(f = gen_readframe(state)))
00210          return -1;
00211 
00212       res = ast_write(chan, f);
00213       ast_frfree(f);
00214       if (res < 0) {
00215          ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
00216          return -1;
00217       }
00218       state->sample_queue -= f->samples;
00219    }
00220 
00221    return res;
00222 }

static int gen_nextfile ( struct gen_state state  )  [static]

Definition at line 142 of file app_externalivr.c.

References ivr_localuser::abort_current_sound, ast_chan_log, AST_LIST_REMOVE_HEAD, ast_openstream_full(), ivr_localuser::chan, gen_state::current, errno, playlist_entry::filename, gen_closestream(), ast_channel::language, playlist_entry::list, LOG_WARNING, ivr_localuser::playing_silence, ivr_localuser::playlist, gen_state::stream, and gen_state::u.

Referenced by gen_readframe().

00143 {
00144    struct ivr_localuser *u = state->u;
00145    char *file_to_stream;
00146    
00147    u->abort_current_sound = 0;
00148    u->playing_silence = 0;
00149    gen_closestream(state);
00150 
00151    while (!state->stream) {
00152       state->current = AST_LIST_REMOVE_HEAD(&u->playlist, list);
00153       if (state->current) {
00154          file_to_stream = state->current->filename;
00155       } else {
00156          file_to_stream = "silence/10";
00157          u->playing_silence = 1;
00158       }
00159 
00160       if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) {
00161          ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
00162          if (!u->playing_silence) {
00163             continue;
00164          } else { 
00165             break;
00166          }
00167       }
00168    }
00169 
00170    return (!state->stream);
00171 }

static struct ast_frame* gen_readframe ( struct gen_state state  )  [static]

Definition at line 173 of file app_externalivr.c.

References ivr_localuser::abort_current_sound, AST_LIST_FIRST, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_readframe(), gen_state::current, f, ivr_localuser::finishlist, gen_closestream(), gen_nextfile(), playlist_entry::list, ivr_localuser::playing_silence, ivr_localuser::playlist, gen_state::stream, and gen_state::u.

Referenced by gen_generate().

00174 {
00175    struct ast_frame *f = NULL;
00176    struct ivr_localuser *u = state->u;
00177    
00178    if (u->abort_current_sound ||
00179        (u->playing_silence && AST_LIST_FIRST(&u->playlist))) {
00180       gen_closestream(state);
00181       AST_LIST_LOCK(&u->playlist);
00182       gen_nextfile(state);
00183       AST_LIST_UNLOCK(&u->playlist);
00184    }
00185 
00186    if (!(state->stream && (f = ast_readframe(state->stream)))) {
00187       if (state->current) {
00188          AST_LIST_LOCK(&u->finishlist);
00189          AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
00190          AST_LIST_UNLOCK(&u->finishlist);
00191          state->current = NULL;
00192       }
00193       if (!gen_nextfile(state))
00194          f = ast_readframe(state->stream);
00195    }
00196 
00197    return f;
00198 }

static void gen_release ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 133 of file app_externalivr.c.

References free, and gen_closestream().

00134 {
00135    struct gen_state *state = data;
00136 
00137    gen_closestream(state);
00138    free(data);
00139 }

static int load_module ( void   )  [static]

Definition at line 580 of file app_externalivr.c.

References app_exec, and ast_register_application().

00581 {
00582    return ast_register_application(app, app_exec, synopsis, descrip);
00583 }

static struct playlist_entry* make_entry ( const char *  filename  )  [static]

Definition at line 231 of file app_externalivr.c.

References ast_calloc.

Referenced by app_exec().

00232 {
00233    struct playlist_entry *entry;
00234    
00235    if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
00236       return NULL;
00237 
00238    strcpy(entry->filename, filename);
00239 
00240    return entry;
00241 }

static void send_child_event ( FILE *  handle,
const char  event,
const char *  data,
const struct ast_channel chan 
) [static]

Definition at line 95 of file app_externalivr.c.

References ast_chan_log, and LOG_DEBUG.

Referenced by app_exec().

00097 {
00098    char tmp[256];
00099 
00100    if (!data) {
00101       snprintf(tmp, sizeof(tmp), "%c,%10d", event, (int)time(NULL));
00102    } else {
00103       snprintf(tmp, sizeof(tmp), "%c,%10d,%s", event, (int)time(NULL), data);
00104    }
00105 
00106    fprintf(handle, "%s\n", tmp);
00107    ast_chan_log(LOG_DEBUG, chan, "sent '%s'\n", tmp);
00108 }

static int unload_module ( void   )  [static]

Definition at line 569 of file app_externalivr.c.

References ast_module_user_hangup_all, and ast_unregister_application().

00570 {
00571    int res;
00572 
00573    res = ast_unregister_application(app);
00574 
00575    ast_module_user_hangup_all();
00576 
00577    return res;
00578 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "External IVR Interface Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "f450f61f60e761b3aa089ebed76ca8a5" , .load = load_module, .unload = unload_module, } [static]

Definition at line 585 of file app_externalivr.c.

const char* app = "ExternalIVR" [static]

Definition at line 56 of file app_externalivr.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 585 of file app_externalivr.c.

const char* descrip [static]

Definition at line 60 of file app_externalivr.c.

struct ast_generator gen [static]

Definition at line 224 of file app_externalivr.c.

Referenced by app_exec(), ast_activate_generator(), reload_config(), and set_config().

const char* synopsis = "Interfaces with an external IVR application" [static]

Definition at line 58 of file app_externalivr.c.


Generated on Wed Feb 11 12:00:02 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7