Sat Aug 6 00:39:34 2011

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 <sys/capability.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 = "361d7bb937402d51e4658efb5b4d76e4" , .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 78 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 601 of file app_externalivr.c.

static void __unreg_module ( void   )  [static]

Definition at line 601 of file app_externalivr.c.

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

Definition at line 250 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().

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

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

Definition at line 117 of file app_externalivr.c.

References ast_calloc, and gen_state::u.

00118 {
00119    struct ivr_localuser *u = params;
00120    struct gen_state *state;
00121    
00122    if (!(state = ast_calloc(1, sizeof(*state))))
00123       return NULL;
00124 
00125    state->u = u;
00126 
00127    return state;
00128 }

static void gen_closestream ( struct gen_state state  )  [static]

Definition at line 130 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().

00131 {
00132    if (!state->stream)
00133       return;
00134 
00135    ast_closestream(state->stream);
00136    state->u->chan->stream = NULL;
00137    state->stream = NULL;
00138 }

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

Definition at line 207 of file app_externalivr.c.

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

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

static int gen_nextfile ( struct gen_state state  )  [static]

Definition at line 149 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().

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

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

Definition at line 180 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().

00181 {
00182    struct ast_frame *f = NULL;
00183    struct ivr_localuser *u = state->u;
00184    
00185    if (u->abort_current_sound ||
00186        (u->playing_silence && AST_LIST_FIRST(&u->playlist))) {
00187       gen_closestream(state);
00188       AST_LIST_LOCK(&u->playlist);
00189       gen_nextfile(state);
00190       AST_LIST_UNLOCK(&u->playlist);
00191    }
00192 
00193    if (!(state->stream && (f = ast_readframe(state->stream)))) {
00194       if (state->current) {
00195          AST_LIST_LOCK(&u->finishlist);
00196          AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
00197          AST_LIST_UNLOCK(&u->finishlist);
00198          state->current = NULL;
00199       }
00200       if (!gen_nextfile(state))
00201          f = ast_readframe(state->stream);
00202    }
00203 
00204    return f;
00205 }

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

Definition at line 140 of file app_externalivr.c.

References free, and gen_closestream().

00141 {
00142    struct gen_state *state = data;
00143 
00144    gen_closestream(state);
00145    free(data);
00146 }

static int load_module ( void   )  [static]

Definition at line 596 of file app_externalivr.c.

References app_exec, and ast_register_application().

00597 {
00598    return ast_register_application(app, app_exec, synopsis, descrip);
00599 }

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

Definition at line 238 of file app_externalivr.c.

References ast_calloc.

Referenced by app_exec().

00239 {
00240    struct playlist_entry *entry;
00241    
00242    if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
00243       return NULL;
00244 
00245    strcpy(entry->filename, filename);
00246 
00247    return entry;
00248 }

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

Definition at line 102 of file app_externalivr.c.

References ast_chan_log, and LOG_DEBUG.

Referenced by app_exec().

00104 {
00105    char tmp[256];
00106 
00107    if (!data) {
00108       snprintf(tmp, sizeof(tmp), "%c,%10d", event, (int)time(NULL));
00109    } else {
00110       snprintf(tmp, sizeof(tmp), "%c,%10d,%s", event, (int)time(NULL), data);
00111    }
00112 
00113    fprintf(handle, "%s\n", tmp);
00114    ast_chan_log(LOG_DEBUG, chan, "sent '%s'\n", tmp);
00115 }

static int unload_module ( void   )  [static]

Definition at line 585 of file app_externalivr.c.

References ast_module_user_hangup_all, and ast_unregister_application().

00586 {
00587    int res;
00588 
00589    res = ast_unregister_application(app);
00590 
00591    ast_module_user_hangup_all();
00592 
00593    return res;
00594 }


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 = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, } [static]

Definition at line 601 of file app_externalivr.c.

const char* app = "ExternalIVR" [static]

Definition at line 63 of file app_externalivr.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 601 of file app_externalivr.c.

const char* descrip [static]

Definition at line 67 of file app_externalivr.c.

struct ast_generator gen [static]

Definition at line 231 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 65 of file app_externalivr.c.


Generated on Sat Aug 6 00:39:34 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7