#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 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.
|
Definition at line 65 of file app_externalivr.c. Referenced by app_exec(), gen_generate(), gen_nextfile(), and send_child_event(). |
|
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, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_log(), ast_read(), 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(), send_child_event(), and ast_frame::subclass. 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 }
|
|
Provides a description of the module.
Definition at line 566 of file app_externalivr.c. 00567 { 00568 return (char *) tdesc; 00569 }
|
|
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 }
|
|
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 }
|
|
Definition at line 198 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. 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 }
|
|
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, list, 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 }
|
|
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(), list, 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 }
|
|
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 }
|
|
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 580 of file app_externalivr.c. References ASTERISK_GPL_KEY. 00581 { 00582 return ASTERISK_GPL_KEY; 00583 }
|
|
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 561 of file app_externalivr.c. References app_exec, and ast_register_application(). 00562 { 00563 return ast_register_application(app, app_exec, synopsis, descrip); 00564 }
|
|
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 }
|
|
Definition at line 91 of file app_externalivr.c. References ast_chan_log, localuser::chan, 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 }
|
|
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 550 of file app_externalivr.c. References ast_unregister_application(), and STANDARD_HANGUP_LOCALUSERS. 00551 { 00552 int res; 00553 00554 res = ast_unregister_application(app); 00555 00556 STANDARD_HANGUP_LOCALUSERS; 00557 00558 return res; 00559 }
|
|
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 571 of file app_externalivr.c. References STANDARD_USECOUNT. 00572 { 00573 int res; 00574 00575 STANDARD_USECOUNT(res); 00576 00577 return res; 00578 }
|
|
Definition at line 50 of file app_externalivr.c. |
|
Definition at line 54 of file app_externalivr.c. |
|
Definition at line 222 of file app_externalivr.c. Referenced by app_exec(), and ast_activate_generator(). |
|
Definition at line 82 of file app_externalivr.c. |
|
Definition at line 52 of file app_externalivr.c. |
|
Definition at line 48 of file app_externalivr.c. |