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