Mon Mar 20 08:20:15 2006

Asterisk developer's documentation


Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

app_externalivr.c File Reference

External IVR application interface. More...

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "asterisk.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"

Go to the source code of this file.

Data Structures

struct  gen_state
struct  localuser
 We define a custom "local user" structure because we use it not only for keeping track of what is in use but also for keeping track of who we're dialing. More...
struct  playlist_entry

Defines

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

Functions

int app_exec (struct ast_channel *chan, void *data)
char * description (void)
 Provides a description of the module.
void * gen_alloc (struct ast_channel *chan, void *params)
void gen_closestream (struct gen_state *state)
int gen_generate (struct ast_channel *chan, void *data, int len, int samples)
int gen_nextfile (struct gen_state *state)
ast_framegen_readframe (struct gen_state *state)
void gen_release (struct ast_channel *chan, void *data)
char * key ()
 Returns the ASTERISK_GPL_KEY.
int load_module (void)
 Initialize the module.
playlist_entrymake_entry (const char *filename)
void send_child_event (FILE *handle, const char event, const char *data, const struct ast_channel *chan)
int unload_module (void)
 Cleanup all module structures, sockets, etc.
int usecount (void)
 Provides a usecount.

Variables

const char * app = "ExternalIVR"
const char * descrip
ast_generator gen
 LOCAL_USER_DECL
const char * synopsis = "Interfaces with an external IVR application"
const char * tdesc = "External IVR Interface Application"


Detailed Description

External IVR application interface.

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 65 of file app_externalivr.c.

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


Function Documentation

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

Definition at line 243 of file app_externalivr.c.

References ast_channel::_state, ast_activate_generator(), ast_answer(), ast_app_separate_args(), ast_chan_log, ast_check_hangup(), ast_deactivate_generator(), ast_fileexists(), AST_FLAG_ZOMBIE, ast_frfree(), AST_LIST_EMPTY, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_log(), ast_read(), ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_waitfor_nandfds(), ast_frame::frametype, free, gen, LOCAL_USER_ADD, LOCAL_USER_REMOVE, LOG_DEBUG, LOG_ERROR, LOG_NOTICE, LOG_WARNING, make_entry(), send_child_event(), and ast_frame::subclass.

Referenced by load_module().

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

char* description void   ) 
 

Provides a description of the module.

Returns:
a short description of your module

Definition at line 566 of file app_externalivr.c.

00567 {
00568    return (char *) tdesc;
00569 }

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

Definition at line 106 of file app_externalivr.c.

References calloc, and gen_state::u.

00107 {
00108    struct localuser *u = params;
00109    struct gen_state *state;
00110 
00111    state = calloc(1, sizeof(*state));
00112 
00113    if (!state)
00114       return NULL;
00115 
00116    state->u = u;
00117 
00118    return state;
00119 }

void gen_closestream struct gen_state state  )  [static]
 

Definition at line 121 of file app_externalivr.c.

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

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

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

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

Definition at line 198 of file app_externalivr.c.

References ast_chan_log, ast_frfree(), ast_write(), gen_readframe(), LOG_WARNING, gen_state::sample_queue, and ast_frame::samples.

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

int gen_nextfile struct gen_state state  )  [static]
 

Definition at line 140 of file app_externalivr.c.

References ast_chan_log, AST_LIST_REMOVE_HEAD, ast_openstream_full(), localuser::chan, gen_state::current, gen_closestream(), ast_channel::language, LOG_WARNING, gen_state::stream, and gen_state::u.

Referenced by gen_readframe().

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

struct ast_frame* gen_readframe struct gen_state state  )  [static]
 

Definition at line 171 of file app_externalivr.c.

References AST_LIST_FIRST, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_readframe(), gen_state::current, gen_closestream(), gen_nextfile(), gen_state::stream, and gen_state::u.

Referenced by gen_generate().

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

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

Definition at line 131 of file app_externalivr.c.

References free, and gen_closestream().

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

char* key void   ) 
 

Returns the ASTERISK_GPL_KEY.

This returns the ASTERISK_GPL_KEY, signifiying that you agree to the terms of the GPL stated in the ASTERISK_GPL_KEY. Your module will not load if it does not return the EXACT message:

 char *key(void) {
         return ASTERISK_GPL_KEY;
 }

Returns:
ASTERISK_GPL_KEY

Definition at line 580 of file app_externalivr.c.

00581 {
00582    return ASTERISK_GPL_KEY;
00583 }

int load_module void   ) 
 

Initialize the module.

Initialize the Agents module. This function is being called by Asterisk when loading the module. Among other thing it registers applications, cli commands and reads the cofiguration file.

Returns:
int Always 0.

Definition at line 561 of file app_externalivr.c.

References app, app_exec(), ast_register_application(), descrip, and synopsis.

00562 {
00563    return ast_register_application(app, app_exec, synopsis, descrip);
00564 }

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

Definition at line 229 of file app_externalivr.c.

References calloc.

Referenced by app_exec().

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

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

Definition at line 91 of file app_externalivr.c.

References ast_chan_log, and LOG_DEBUG.

Referenced by app_exec().

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

int unload_module void   ) 
 

Cleanup all module structures, sockets, etc.

This is called at exit. Any registrations and memory allocations need to be unregistered and free'd here. Nothing else will do these for you (until exit).

Returns:
Zero on success, or non-zero on error.

Definition at line 550 of file app_externalivr.c.

References app, and ast_unregister_application().

00551 {
00552    int res;
00553 
00554    res = ast_unregister_application(app);
00555 
00556    STANDARD_HANGUP_LOCALUSERS;
00557 
00558    return res;
00559 }

int usecount void   ) 
 

Provides a usecount.

This function will be called by various parts of asterisk. Basically, all it has to do is to return a usecount when called. You will need to maintain your usecount within the module somewhere. The usecount should be how many channels provided by this module are in use.

Returns:
The module's usecount.

Definition at line 571 of file app_externalivr.c.

References STANDARD_USECOUNT.

00572 {
00573    int res;
00574 
00575    STANDARD_USECOUNT(res);
00576 
00577    return res;
00578 }


Variable Documentation

const char* app = "ExternalIVR" [static]
 

Definition at line 50 of file app_externalivr.c.

Referenced by load_module(), and unload_module().

const char* descrip [static]
 

Definition at line 54 of file app_externalivr.c.

Referenced by load_module().

struct ast_generator gen [static]
 

Definition at line 222 of file app_externalivr.c.

Referenced by app_exec().

LOCAL_USER_DECL
 

Definition at line 82 of file app_externalivr.c.

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

Definition at line 52 of file app_externalivr.c.

Referenced by load_module().

const char* tdesc = "External IVR Interface Application" [static]
 

Definition at line 48 of file app_externalivr.c.


Generated on Mon Mar 20 08:20:15 2006 for Asterisk - the Open Source PBX by  doxygen 1.3.9.1