#include "asterisk.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/linkedlists.h"
#include "asterisk/app.h"
#include "asterisk/utils.h"
#include "asterisk/options.h"
Go to the source code of this file.
Data Structures | |
struct | gen_state |
struct | ivr_localuser |
struct | 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) |
AST_MODULE_INFO_STANDARD (ASTERISK_GPL_KEY,"External IVR Interface Application") | |
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) |
static int | load_module (void) |
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) |
static int | unload_module (void) |
Variables | |
static const char * | app = "ExternalIVR" |
static const char * | descrip |
static struct ast_generator | gen |
static const char * | synopsis = "Interfaces with an external IVR 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 71 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 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_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_strlen_zero(), ast_test_flag, ast_waitfor_nandfds(), ivr_localuser::chan, errno, f, ast_frame::frametype, free, gen, input(), LOG_DEBUG, LOG_NOTICE, LOG_WARNING, make_entry(), send_child_event(), and ast_frame::subclass.
00244 { 00245 struct ast_module_user *lu; 00246 struct playlist_entry *entry; 00247 const char *args = data; 00248 int child_stdin[2] = { 0,0 }; 00249 int child_stdout[2] = { 0,0 }; 00250 int child_stderr[2] = { 0,0 }; 00251 int res = -1; 00252 int 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 struct ivr_localuser foo = { 00261 .playlist = AST_LIST_HEAD_INIT_VALUE, 00262 .finishlist = AST_LIST_HEAD_INIT_VALUE, 00263 }; 00264 struct ivr_localuser *u = &foo; 00265 sigset_t fullset, oldset; 00266 00267 lu = ast_module_user_add(chan); 00268 00269 sigfillset(&fullset); 00270 pthread_sigmask(SIG_BLOCK, &fullset, &oldset); 00271 00272 u->abort_current_sound = 0; 00273 u->chan = chan; 00274 00275 if (ast_strlen_zero(args)) { 00276 ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n"); 00277 ast_module_user_remove(lu); 00278 return -1; 00279 } 00280 00281 buf = ast_strdupa(data); 00282 00283 argc = ast_app_separate_args(buf, '|', argv, sizeof(argv) / sizeof(argv[0])); 00284 00285 if (pipe(child_stdin)) { 00286 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno)); 00287 goto exit; 00288 } 00289 00290 if (pipe(child_stdout)) { 00291 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno)); 00292 goto exit; 00293 } 00294 00295 if (pipe(child_stderr)) { 00296 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno)); 00297 goto exit; 00298 } 00299 00300 if (chan->_state != AST_STATE_UP) { 00301 ast_answer(chan); 00302 } 00303 00304 if (ast_activate_generator(chan, &gen, u) < 0) { 00305 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n"); 00306 goto exit; 00307 } else 00308 gen_active = 1; 00309 00310 pid = fork(); 00311 if (pid < 0) { 00312 ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno)); 00313 goto exit; 00314 } 00315 00316 if (!pid) { 00317 /* child process */ 00318 int i; 00319 00320 signal(SIGPIPE, SIG_DFL); 00321 pthread_sigmask(SIG_UNBLOCK, &fullset, NULL); 00322 00323 if (ast_opt_high_priority) 00324 ast_set_priority(0); 00325 00326 dup2(child_stdin[0], STDIN_FILENO); 00327 dup2(child_stdout[1], STDOUT_FILENO); 00328 dup2(child_stderr[1], STDERR_FILENO); 00329 for (i = STDERR_FILENO + 1; i < 1024; i++) 00330 close(i); 00331 execv(argv[0], argv); 00332 fprintf(stderr, "Failed to execute '%s': %s\n", argv[0], strerror(errno)); 00333 _exit(1); 00334 } else { 00335 /* parent process */ 00336 int child_events_fd = child_stdin[1]; 00337 int child_commands_fd = child_stdout[0]; 00338 int child_errors_fd = child_stderr[0]; 00339 struct ast_frame *f; 00340 int ms; 00341 int exception; 00342 int ready_fd; 00343 int waitfds[2] = { child_errors_fd, child_commands_fd }; 00344 struct ast_channel *rchan; 00345 00346 pthread_sigmask(SIG_SETMASK, &oldset, NULL); 00347 00348 close(child_stdin[0]); 00349 child_stdin[0] = 0; 00350 close(child_stdout[1]); 00351 child_stdout[1] = 0; 00352 close(child_stderr[1]); 00353 child_stderr[1] = 0; 00354 00355 if (!(child_events = fdopen(child_events_fd, "w"))) { 00356 ast_chan_log(LOG_WARNING, chan, "Could not open stream for child events\n"); 00357 goto exit; 00358 } 00359 00360 if (!(child_commands = fdopen(child_commands_fd, "r"))) { 00361 ast_chan_log(LOG_WARNING, chan, "Could not open stream for child commands\n"); 00362 goto exit; 00363 } 00364 00365 if (!(child_errors = fdopen(child_errors_fd, "r"))) { 00366 ast_chan_log(LOG_WARNING, chan, "Could not open stream for child errors\n"); 00367 goto exit; 00368 } 00369 00370 setvbuf(child_events, NULL, _IONBF, 0); 00371 setvbuf(child_commands, NULL, _IONBF, 0); 00372 setvbuf(child_errors, NULL, _IONBF, 0); 00373 00374 res = 0; 00375 00376 while (1) { 00377 if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) { 00378 ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n"); 00379 res = -1; 00380 break; 00381 } 00382 00383 if (ast_check_hangup(chan)) { 00384 ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n"); 00385 send_child_event(child_events, 'H', NULL, chan); 00386 res = -1; 00387 break; 00388 } 00389 00390 ready_fd = 0; 00391 ms = 100; 00392 errno = 0; 00393 exception = 0; 00394 00395 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, 2, &exception, &ready_fd, &ms); 00396 00397 if (!AST_LIST_EMPTY(&u->finishlist)) { 00398 AST_LIST_LOCK(&u->finishlist); 00399 while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) { 00400 send_child_event(child_events, 'F', entry->filename, chan); 00401 free(entry); 00402 } 00403 AST_LIST_UNLOCK(&u->finishlist); 00404 } 00405 00406 if (rchan) { 00407 /* the channel has something */ 00408 f = ast_read(chan); 00409 if (!f) { 00410 ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n"); 00411 send_child_event(child_events, 'H', NULL, chan); 00412 res = -1; 00413 break; 00414 } 00415 00416 if (f->frametype == AST_FRAME_DTMF) { 00417 send_child_event(child_events, f->subclass, NULL, chan); 00418 if (u->option_autoclear) { 00419 if (!u->abort_current_sound && !u->playing_silence) 00420 send_child_event(child_events, 'T', NULL, chan); 00421 AST_LIST_LOCK(&u->playlist); 00422 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00423 send_child_event(child_events, 'D', entry->filename, chan); 00424 free(entry); 00425 } 00426 if (!u->playing_silence) 00427 u->abort_current_sound = 1; 00428 AST_LIST_UNLOCK(&u->playlist); 00429 } 00430 } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) { 00431 ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n"); 00432 send_child_event(child_events, 'H', NULL, chan); 00433 ast_frfree(f); 00434 res = -1; 00435 break; 00436 } 00437 ast_frfree(f); 00438 } else if (ready_fd == child_commands_fd) { 00439 char input[1024]; 00440 00441 if (exception || feof(child_commands)) { 00442 ast_chan_log(LOG_WARNING, chan, "Child process went away\n"); 00443 res = -1; 00444 break; 00445 } 00446 00447 if (!fgets(input, sizeof(input), child_commands)) 00448 continue; 00449 00450 command = ast_strip(input); 00451 00452 ast_chan_log(LOG_DEBUG, chan, "got command '%s'\n", input); 00453 00454 if (strlen(input) < 4) 00455 continue; 00456 00457 if (input[0] == 'S') { 00458 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) { 00459 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); 00460 send_child_event(child_events, 'Z', NULL, chan); 00461 strcpy(&input[2], "exception"); 00462 } 00463 if (!u->abort_current_sound && !u->playing_silence) 00464 send_child_event(child_events, 'T', NULL, chan); 00465 AST_LIST_LOCK(&u->playlist); 00466 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00467 send_child_event(child_events, 'D', entry->filename, chan); 00468 free(entry); 00469 } 00470 if (!u->playing_silence) 00471 u->abort_current_sound = 1; 00472 entry = make_entry(&input[2]); 00473 if (entry) 00474 AST_LIST_INSERT_TAIL(&u->playlist, entry, list); 00475 AST_LIST_UNLOCK(&u->playlist); 00476 } else if (input[0] == 'A') { 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 entry = make_entry(&input[2]); 00483 if (entry) { 00484 AST_LIST_LOCK(&u->playlist); 00485 AST_LIST_INSERT_TAIL(&u->playlist, entry, list); 00486 AST_LIST_UNLOCK(&u->playlist); 00487 } 00488 } else if (input[0] == 'H') { 00489 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]); 00490 send_child_event(child_events, 'H', NULL, chan); 00491 break; 00492 } else if (input[0] == 'O') { 00493 if (!strcasecmp(&input[2], "autoclear")) 00494 u->option_autoclear = 1; 00495 else if (!strcasecmp(&input[2], "noautoclear")) 00496 u->option_autoclear = 0; 00497 else 00498 ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]); 00499 } 00500 } else if (ready_fd == child_errors_fd) { 00501 char input[1024]; 00502 00503 if (exception || feof(child_errors)) { 00504 ast_chan_log(LOG_WARNING, chan, "Child process went away\n"); 00505 res = -1; 00506 break; 00507 } 00508 00509 if (fgets(input, sizeof(input), child_errors)) { 00510 command = ast_strip(input); 00511 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command); 00512 } 00513 } else if ((ready_fd < 0) && ms) { 00514 if (errno == 0 || errno == EINTR) 00515 continue; 00516 00517 ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno)); 00518 break; 00519 } 00520 } 00521 } 00522 00523 exit: 00524 if (gen_active) 00525 ast_deactivate_generator(chan); 00526 00527 if (child_events) 00528 fclose(child_events); 00529 00530 if (child_commands) 00531 fclose(child_commands); 00532 00533 if (child_errors) 00534 fclose(child_errors); 00535 00536 if (child_stdin[0]) 00537 close(child_stdin[0]); 00538 00539 if (child_stdin[1]) 00540 close(child_stdin[1]); 00541 00542 if (child_stdout[0]) 00543 close(child_stdout[0]); 00544 00545 if (child_stdout[1]) 00546 close(child_stdout[1]); 00547 00548 if (child_stderr[0]) 00549 close(child_stderr[0]); 00550 00551 if (child_stderr[1]) 00552 close(child_stderr[1]); 00553 00554 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) 00555 free(entry); 00556 00557 ast_module_user_remove(lu); 00558 00559 return res; 00560 }
AST_MODULE_INFO_STANDARD | ( | ASTERISK_GPL_KEY | , | |
"External IVR Interface Application" | ||||
) |
static void* gen_alloc | ( | struct ast_channel * | chan, | |
void * | params | |||
) | [static] |
Definition at line 110 of file app_externalivr.c.
References ast_calloc, and gen_state::u.
00111 { 00112 struct ivr_localuser *u = params; 00113 struct gen_state *state; 00114 00115 if (!(state = ast_calloc(1, sizeof(*state)))) 00116 return NULL; 00117 00118 state->u = u; 00119 00120 return state; 00121 }
static void gen_closestream | ( | struct gen_state * | state | ) | [static] |
Definition at line 123 of file app_externalivr.c.
References ast_closestream(), ivr_localuser::chan, ast_channel::stream, gen_state::stream, and gen_state::u.
Referenced by gen_nextfile(), gen_readframe(), and gen_release().
00124 { 00125 if (!state->stream) 00126 return; 00127 00128 ast_closestream(state->stream); 00129 state->u->chan->stream = NULL; 00130 state->stream = NULL; 00131 }
static int gen_generate | ( | struct ast_channel * | chan, | |
void * | data, | |||
int | len, | |||
int | samples | |||
) | [static] |
Definition at line 200 of file app_externalivr.c.
References ast_chan_log, ast_frfree, ast_write(), errno, f, gen_readframe(), LOG_WARNING, gen_state::sample_queue, and ast_frame::samples.
00201 { 00202 struct gen_state *state = data; 00203 struct ast_frame *f = NULL; 00204 int res = 0; 00205 00206 state->sample_queue += samples; 00207 00208 while (state->sample_queue > 0) { 00209 if (!(f = gen_readframe(state))) 00210 return -1; 00211 00212 res = ast_write(chan, f); 00213 ast_frfree(f); 00214 if (res < 0) { 00215 ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno)); 00216 return -1; 00217 } 00218 state->sample_queue -= f->samples; 00219 } 00220 00221 return res; 00222 }
static int gen_nextfile | ( | struct gen_state * | state | ) | [static] |
Definition at line 142 of file app_externalivr.c.
References ast_chan_log, AST_LIST_REMOVE_HEAD, ast_openstream_full(), ivr_localuser::chan, gen_state::current, errno, gen_closestream(), LOG_WARNING, gen_state::stream, and gen_state::u.
Referenced by gen_readframe().
00143 { 00144 struct ivr_localuser *u = state->u; 00145 char *file_to_stream; 00146 00147 u->abort_current_sound = 0; 00148 u->playing_silence = 0; 00149 gen_closestream(state); 00150 00151 while (!state->stream) { 00152 state->current = AST_LIST_REMOVE_HEAD(&u->playlist, list); 00153 if (state->current) { 00154 file_to_stream = state->current->filename; 00155 } else { 00156 file_to_stream = "silence/10"; 00157 u->playing_silence = 1; 00158 } 00159 00160 if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) { 00161 ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno)); 00162 if (!u->playing_silence) { 00163 continue; 00164 } else { 00165 break; 00166 } 00167 } 00168 } 00169 00170 return (!state->stream); 00171 }
Definition at line 173 of file app_externalivr.c.
References AST_LIST_FIRST, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_readframe(), gen_state::current, f, gen_closestream(), gen_nextfile(), gen_state::stream, and gen_state::u.
Referenced by gen_generate().
00174 { 00175 struct ast_frame *f = NULL; 00176 struct ivr_localuser *u = state->u; 00177 00178 if (u->abort_current_sound || 00179 (u->playing_silence && AST_LIST_FIRST(&u->playlist))) { 00180 gen_closestream(state); 00181 AST_LIST_LOCK(&u->playlist); 00182 gen_nextfile(state); 00183 AST_LIST_UNLOCK(&u->playlist); 00184 } 00185 00186 if (!(state->stream && (f = ast_readframe(state->stream)))) { 00187 if (state->current) { 00188 AST_LIST_LOCK(&u->finishlist); 00189 AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list); 00190 AST_LIST_UNLOCK(&u->finishlist); 00191 state->current = NULL; 00192 } 00193 if (!gen_nextfile(state)) 00194 f = ast_readframe(state->stream); 00195 } 00196 00197 return f; 00198 }
static void gen_release | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 133 of file app_externalivr.c.
References free, and gen_closestream().
00134 { 00135 struct gen_state *state = data; 00136 00137 gen_closestream(state); 00138 free(data); 00139 }
static int load_module | ( | void | ) | [static] |
Definition at line 573 of file app_externalivr.c.
References app_exec, and ast_register_application().
00574 { 00575 return ast_register_application(app, app_exec, synopsis, descrip); 00576 }
static struct playlist_entry* make_entry | ( | const char * | filename | ) | [static, read] |
Definition at line 231 of file app_externalivr.c.
References ast_calloc.
Referenced by app_exec().
00232 { 00233 struct playlist_entry *entry; 00234 00235 if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */ 00236 return NULL; 00237 00238 strcpy(entry->filename, filename); 00239 00240 return entry; 00241 }
static void send_child_event | ( | FILE * | handle, | |
const char | event, | |||
const char * | data, | |||
const struct ast_channel * | chan | |||
) | [static] |
Definition at line 95 of file app_externalivr.c.
References ast_chan_log, and LOG_DEBUG.
Referenced by app_exec().
00097 { 00098 char tmp[256]; 00099 00100 if (!data) { 00101 snprintf(tmp, sizeof(tmp), "%c,%10d", event, (int)time(NULL)); 00102 } else { 00103 snprintf(tmp, sizeof(tmp), "%c,%10d,%s", event, (int)time(NULL), data); 00104 } 00105 00106 fprintf(handle, "%s\n", tmp); 00107 ast_chan_log(LOG_DEBUG, chan, "sent '%s'\n", tmp); 00108 }
static int unload_module | ( | void | ) | [static] |
Definition at line 562 of file app_externalivr.c.
References ast_module_user_hangup_all, and ast_unregister_application().
00563 { 00564 int res; 00565 00566 res = ast_unregister_application(app); 00567 00568 ast_module_user_hangup_all(); 00569 00570 return res; 00571 }
const char* app = "ExternalIVR" [static] |
Definition at line 56 of file app_externalivr.c.
const char* descrip [static] |
Definition at line 60 of file app_externalivr.c.
struct ast_generator gen [static] |
Definition at line 224 of file app_externalivr.c.
Referenced by app_exec(), reload_config(), and set_config().
const char* synopsis = "Interfaces with an external IVR application" [static] |
Definition at line 58 of file app_externalivr.c.