#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"
Include dependency graph for app_externalivr.c:
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_frame * | gen_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_entry * | make_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" |
Definition in file app_externalivr.c.
#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().
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.
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 }
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; }
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.
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).
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.
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 }
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().
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.