Sat Sep 16 05:47:49 2006

Asterisk developer's documentation


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"
#include "asterisk/options.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

static int app_exec (struct ast_channel *chan, void *data)
char * description (void)
 Provides a description of the module.
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)
char * key ()
 Returns the ASTERISK_GPL_KEY.
int load_module (void)
 Initialize the module.
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)
int unload_module (void)
 Cleanup all module structures, sockets, etc.
int usecount (void)
 Provides a usecount.

Variables

static const char * app = "ExternalIVR"
static const char * descrip
static struct ast_generator gen
 LOCAL_USER_DECL
static const char * synopsis = "Interfaces with an external IVR application"
static 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 66 of file app_externalivr.c.

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


Function Documentation

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

Definition at line 244 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_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, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_log(), ast_read(), ast_set_priority(), AST_STATE_UP, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_waitfor_nandfds(), localuser::chan, ast_frame::frametype, free, gen, input(), list, LOCAL_USER_ADD, LOCAL_USER_REMOVE, LOG_DEBUG, LOG_ERROR, LOG_NOTICE, LOG_WARNING, make_entry(), option_highpriority, send_child_event(), and ast_frame::subclass.

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

char* description ( void   ) 

Provides a description of the module.

Returns:
a short description of your module

Definition at line 570 of file app_externalivr.c.

00571 {
00572    return (char *) tdesc;
00573 }

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

Definition at line 107 of file app_externalivr.c.

References calloc, and gen_state::u.

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

static void gen_closestream ( struct gen_state state  )  [static]

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

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

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

Definition at line 199 of file app_externalivr.c.

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

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

static int gen_nextfile ( struct gen_state state  )  [static]

Definition at line 141 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, list, LOG_WARNING, gen_state::stream, and gen_state::u.

Referenced by gen_readframe().

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

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

Definition at line 172 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(), list, gen_state::stream, and gen_state::u.

Referenced by gen_generate().

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

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

Definition at line 132 of file app_externalivr.c.

References free, and gen_closestream().

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

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

References ASTERISK_GPL_KEY.

00585 {
00586    return ASTERISK_GPL_KEY;
00587 }

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

References app_exec, and ast_register_application().

00566 {
00567    return ast_register_application(app, app_exec, synopsis, descrip);
00568 }

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

Definition at line 230 of file app_externalivr.c.

References calloc.

Referenced by app_exec().

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

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

Definition at line 92 of file app_externalivr.c.

References ast_chan_log, localuser::chan, and LOG_DEBUG.

Referenced by app_exec().

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

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

References ast_unregister_application(), and STANDARD_HANGUP_LOCALUSERS.

00555 {
00556    int res;
00557 
00558    res = ast_unregister_application(app);
00559 
00560    STANDARD_HANGUP_LOCALUSERS;
00561 
00562    return res;
00563 }

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

References STANDARD_USECOUNT.

00576 {
00577    int res;
00578 
00579    STANDARD_USECOUNT(res);
00580 
00581    return res;
00582 }


Variable Documentation

const char* app = "ExternalIVR" [static]

Definition at line 51 of file app_externalivr.c.

const char* descrip [static]

Definition at line 55 of file app_externalivr.c.

struct ast_generator gen [static]

Definition at line 223 of file app_externalivr.c.

Referenced by app_exec(), and ast_activate_generator().

LOCAL_USER_DECL

Definition at line 83 of file app_externalivr.c.

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

Definition at line 53 of file app_externalivr.c.

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

Definition at line 49 of file app_externalivr.c.


Generated on Sat Sep 16 05:47:49 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7