#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.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 67 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 245 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(), ast_channel::language, 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.
00246 { 00247 struct localuser *u = NULL; 00248 struct playlist_entry *entry; 00249 const char *args = data; 00250 int child_stdin[2] = { 0,0 }; 00251 int child_stdout[2] = { 0,0 }; 00252 int child_stderr[2] = { 0,0 }; 00253 int res = -1; 00254 int gen_active = 0; 00255 int pid; 00256 char *argv[32]; 00257 int argc = 1; 00258 char *buf, *command; 00259 FILE *child_commands = NULL; 00260 FILE *child_errors = NULL; 00261 FILE *child_events = NULL; 00262 sigset_t fullset, oldset; 00263 00264 LOCAL_USER_ADD(u); 00265 00266 sigfillset(&fullset); 00267 pthread_sigmask(SIG_BLOCK, &fullset, &oldset); 00268 00269 AST_LIST_HEAD_INIT(&u->playlist); 00270 AST_LIST_HEAD_INIT(&u->finishlist); 00271 u->abort_current_sound = 0; 00272 00273 if (ast_strlen_zero(args)) { 00274 ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n"); 00275 goto exit; 00276 } 00277 00278 buf = ast_strdupa(data); 00279 if (!buf) { 00280 ast_log(LOG_ERROR, "Out of memory!\n"); 00281 LOCAL_USER_REMOVE(u); 00282 return -1; 00283 } 00284 00285 argc = ast_app_separate_args(buf, '|', argv, sizeof(argv) / sizeof(argv[0])); 00286 00287 if (pipe(child_stdin)) { 00288 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno)); 00289 goto exit; 00290 } 00291 00292 if (pipe(child_stdout)) { 00293 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno)); 00294 goto exit; 00295 } 00296 00297 if (pipe(child_stderr)) { 00298 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno)); 00299 goto exit; 00300 } 00301 00302 if (chan->_state != AST_STATE_UP) { 00303 ast_answer(chan); 00304 } 00305 00306 if (ast_activate_generator(chan, &gen, u) < 0) { 00307 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n"); 00308 goto exit; 00309 } else 00310 gen_active = 1; 00311 00312 pid = fork(); 00313 if (pid < 0) { 00314 ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno)); 00315 goto exit; 00316 } 00317 00318 if (!pid) { 00319 /* child process */ 00320 int i; 00321 00322 signal(SIGPIPE, SIG_DFL); 00323 pthread_sigmask(SIG_UNBLOCK, &fullset, NULL); 00324 00325 if (option_highpriority) 00326 ast_set_priority(0); 00327 00328 dup2(child_stdin[0], STDIN_FILENO); 00329 dup2(child_stdout[1], STDOUT_FILENO); 00330 dup2(child_stderr[1], STDERR_FILENO); 00331 for (i = STDERR_FILENO + 1; i < 1024; i++) 00332 close(i); 00333 execv(argv[0], argv); 00334 fprintf(stderr, "Failed to execute '%s': %s\n", argv[0], strerror(errno)); 00335 _exit(1); 00336 } else { 00337 /* parent process */ 00338 int child_events_fd = child_stdin[1]; 00339 int child_commands_fd = child_stdout[0]; 00340 int child_errors_fd = child_stderr[0]; 00341 struct ast_frame *f; 00342 int ms; 00343 int exception; 00344 int ready_fd; 00345 int waitfds[2] = { child_errors_fd, child_commands_fd }; 00346 struct ast_channel *rchan; 00347 00348 pthread_sigmask(SIG_SETMASK, &oldset, NULL); 00349 00350 close(child_stdin[0]); 00351 child_stdin[0] = 0; 00352 close(child_stdout[1]); 00353 child_stdout[1] = 0; 00354 close(child_stderr[1]); 00355 child_stderr[1] = 0; 00356 00357 if (!(child_events = fdopen(child_events_fd, "w"))) { 00358 ast_chan_log(LOG_WARNING, chan, "Could not open stream for child events\n"); 00359 goto exit; 00360 } 00361 00362 if (!(child_commands = fdopen(child_commands_fd, "r"))) { 00363 ast_chan_log(LOG_WARNING, chan, "Could not open stream for child commands\n"); 00364 goto exit; 00365 } 00366 00367 if (!(child_errors = fdopen(child_errors_fd, "r"))) { 00368 ast_chan_log(LOG_WARNING, chan, "Could not open stream for child errors\n"); 00369 goto exit; 00370 } 00371 00372 setvbuf(child_events, NULL, _IONBF, 0); 00373 setvbuf(child_commands, NULL, _IONBF, 0); 00374 setvbuf(child_errors, NULL, _IONBF, 0); 00375 00376 res = 0; 00377 00378 while (1) { 00379 if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) { 00380 ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n"); 00381 res = -1; 00382 break; 00383 } 00384 00385 if (ast_check_hangup(chan)) { 00386 ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n"); 00387 send_child_event(child_events, 'H', NULL, chan); 00388 res = -1; 00389 break; 00390 } 00391 00392 ready_fd = 0; 00393 ms = 100; 00394 errno = 0; 00395 exception = 0; 00396 00397 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, 2, &exception, &ready_fd, &ms); 00398 00399 if (!AST_LIST_EMPTY(&u->finishlist)) { 00400 AST_LIST_LOCK(&u->finishlist); 00401 while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) { 00402 send_child_event(child_events, 'F', entry->filename, chan); 00403 free(entry); 00404 } 00405 AST_LIST_UNLOCK(&u->finishlist); 00406 } 00407 00408 if (rchan) { 00409 /* the channel has something */ 00410 f = ast_read(chan); 00411 if (!f) { 00412 ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n"); 00413 send_child_event(child_events, 'H', NULL, chan); 00414 res = -1; 00415 break; 00416 } 00417 00418 if (f->frametype == AST_FRAME_DTMF) { 00419 send_child_event(child_events, f->subclass, NULL, chan); 00420 if (u->option_autoclear) { 00421 if (!u->abort_current_sound && !u->playing_silence) 00422 send_child_event(child_events, 'T', NULL, chan); 00423 AST_LIST_LOCK(&u->playlist); 00424 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00425 send_child_event(child_events, 'D', entry->filename, chan); 00426 free(entry); 00427 } 00428 if (!u->playing_silence) 00429 u->abort_current_sound = 1; 00430 AST_LIST_UNLOCK(&u->playlist); 00431 } 00432 } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) { 00433 ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n"); 00434 send_child_event(child_events, 'H', NULL, chan); 00435 ast_frfree(f); 00436 res = -1; 00437 break; 00438 } 00439 ast_frfree(f); 00440 } else if (ready_fd == child_commands_fd) { 00441 char input[1024]; 00442 00443 if (exception || feof(child_commands)) { 00444 ast_chan_log(LOG_WARNING, chan, "Child process went away\n"); 00445 res = -1; 00446 break; 00447 } 00448 00449 if (!fgets(input, sizeof(input), child_commands)) 00450 continue; 00451 00452 command = ast_strip(input); 00453 00454 ast_chan_log(LOG_DEBUG, chan, "got command '%s'\n", input); 00455 00456 if (strlen(input) < 4) 00457 continue; 00458 00459 if (input[0] == 'S') { 00460 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) { 00461 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); 00462 send_child_event(child_events, 'Z', NULL, chan); 00463 strcpy(&input[2], "exception"); 00464 } 00465 if (!u->abort_current_sound && !u->playing_silence) 00466 send_child_event(child_events, 'T', NULL, chan); 00467 AST_LIST_LOCK(&u->playlist); 00468 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00469 send_child_event(child_events, 'D', entry->filename, chan); 00470 free(entry); 00471 } 00472 if (!u->playing_silence) 00473 u->abort_current_sound = 1; 00474 entry = make_entry(&input[2]); 00475 if (entry) 00476 AST_LIST_INSERT_TAIL(&u->playlist, entry, list); 00477 AST_LIST_UNLOCK(&u->playlist); 00478 } else if (input[0] == 'A') { 00479 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) { 00480 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); 00481 send_child_event(child_events, 'Z', NULL, chan); 00482 strcpy(&input[2], "exception"); 00483 } 00484 entry = make_entry(&input[2]); 00485 if (entry) { 00486 AST_LIST_LOCK(&u->playlist); 00487 AST_LIST_INSERT_TAIL(&u->playlist, entry, list); 00488 AST_LIST_UNLOCK(&u->playlist); 00489 } 00490 } else if (input[0] == 'H') { 00491 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]); 00492 send_child_event(child_events, 'H', NULL, chan); 00493 break; 00494 } else if (input[0] == 'O') { 00495 if (!strcasecmp(&input[2], "autoclear")) 00496 u->option_autoclear = 1; 00497 else if (!strcasecmp(&input[2], "noautoclear")) 00498 u->option_autoclear = 0; 00499 else 00500 ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]); 00501 } 00502 } else if (ready_fd == child_errors_fd) { 00503 char input[1024]; 00504 00505 if (exception || feof(child_errors)) { 00506 ast_chan_log(LOG_WARNING, chan, "Child process went away\n"); 00507 res = -1; 00508 break; 00509 } 00510 00511 if (fgets(input, sizeof(input), child_errors)) { 00512 command = ast_strip(input); 00513 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command); 00514 } 00515 } else if ((ready_fd < 0) && ms) { 00516 if (errno == 0 || errno == EINTR) 00517 continue; 00518 00519 ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno)); 00520 break; 00521 } 00522 } 00523 } 00524 00525 exit: 00526 if (gen_active) 00527 ast_deactivate_generator(chan); 00528 00529 if (child_events) 00530 fclose(child_events); 00531 00532 if (child_commands) 00533 fclose(child_commands); 00534 00535 if (child_errors) 00536 fclose(child_errors); 00537 00538 if (child_stdin[0]) 00539 close(child_stdin[0]); 00540 00541 if (child_stdin[1]) 00542 close(child_stdin[1]); 00543 00544 if (child_stdout[0]) 00545 close(child_stdout[0]); 00546 00547 if (child_stdout[1]) 00548 close(child_stdout[1]); 00549 00550 if (child_stderr[0]) 00551 close(child_stderr[0]); 00552 00553 if (child_stderr[1]) 00554 close(child_stderr[1]); 00555 00556 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) 00557 free(entry); 00558 00559 LOCAL_USER_REMOVE(u); 00560 00561 return res; 00562 }
| char* description | ( | void | ) |
Provides a description of the module.
Definition at line 580 of file app_externalivr.c.
00581 { 00582 return (char *) tdesc; 00583 }
| static void* gen_alloc | ( | struct ast_channel * | chan, | |
| void * | params | |||
| ) | [static] |
Definition at line 108 of file app_externalivr.c.
References calloc, and gen_state::u.
00109 { 00110 struct localuser *u = params; 00111 struct gen_state *state; 00112 00113 state = calloc(1, sizeof(*state)); 00114 00115 if (!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(), 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(), localuser::chan, 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(), localuser::chan, gen_state::current, gen_closestream(), ast_channel::language, list, LOG_WARNING, gen_state::stream, and gen_state::u.
Referenced by gen_readframe().
00143 { 00144 struct 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, gen_closestream(), gen_nextfile(), list, gen_state::stream, and gen_state::u.
Referenced by gen_generate().
00174 { 00175 struct ast_frame *f = NULL; 00176 struct 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 }
| 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 594 of file app_externalivr.c.
References ASTERISK_GPL_KEY.
00595 { 00596 return ASTERISK_GPL_KEY; 00597 }
| int load_module | ( | void | ) |
Initialize the module.
TE STUFF END
Definition at line 575 of file app_externalivr.c.
References app_exec, and ast_register_application().
00576 { 00577 return ast_register_application(app, app_exec, synopsis, descrip); 00578 }
| static struct playlist_entry* make_entry | ( | const char * | filename | ) | [static] |
Definition at line 231 of file app_externalivr.c.
References calloc.
Referenced by app_exec().
00232 { 00233 struct playlist_entry *entry; 00234 00235 entry = calloc(1, sizeof(*entry) + strlen(filename) + 10); 00236 00237 if (!entry) 00238 return NULL; 00239 00240 strcpy(entry->filename, filename); 00241 00242 return entry; 00243 }
| static void send_child_event | ( | FILE * | handle, | |
| const char | event, | |||
| const char * | data, | |||
| const struct ast_channel * | chan | |||
| ) | [static] |
Definition at line 93 of file app_externalivr.c.
References ast_chan_log, localuser::chan, and LOG_DEBUG.
Referenced by app_exec().
00095 { 00096 char tmp[256]; 00097 00098 if (!data) { 00099 snprintf(tmp, sizeof(tmp), "%c,%10d", event, (int)time(NULL)); 00100 } else { 00101 snprintf(tmp, sizeof(tmp), "%c,%10d,%s", event, (int)time(NULL), data); 00102 } 00103 00104 fprintf(handle, "%s\n", tmp); 00105 ast_chan_log(LOG_DEBUG, chan, "sent '%s'\n", tmp); 00106 }
| 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 564 of file app_externalivr.c.
References ast_unregister_application(), and STANDARD_HANGUP_LOCALUSERS.
00565 { 00566 int res; 00567 00568 res = ast_unregister_application(app); 00569 00570 STANDARD_HANGUP_LOCALUSERS; 00571 00572 return res; 00573 }
| 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 585 of file app_externalivr.c.
References STANDARD_USECOUNT.
00586 { 00587 int res; 00588 00589 STANDARD_USECOUNT(res); 00590 00591 return res; 00592 }
const char* app = "ExternalIVR" [static] |
Definition at line 52 of file app_externalivr.c.
const char* descrip [static] |
Definition at line 56 of file app_externalivr.c.
struct ast_generator gen [static] |
Definition at line 224 of file app_externalivr.c.
Referenced by app_exec(), and ast_activate_generator().
Definition at line 84 of file app_externalivr.c.
const char* synopsis = "Interfaces with an external IVR application" [static] |
Definition at line 54 of file app_externalivr.c.
const char* tdesc = "External IVR Interface Application" [static] |
Definition at line 50 of file app_externalivr.c.
1.5.1