#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 | 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... | |
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 66 of file app_externalivr.c. Referenced by app_exec(), gen_generate(), gen_nextfile(), and send_child_event(). |
|
||||||||||||
|
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(), ast_frame::frametype, free, 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 }
|
|
|
Provides a description of the module.
Definition at line 570 of file app_externalivr.c. References tdesc. 00571 {
00572 return (char *) tdesc;
00573 }
|
|
||||||||||||
|
Definition at line 107 of file app_externalivr.c. References calloc. 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 }
|
|
|
Definition at line 122 of file app_externalivr.c. References ast_closestream(). 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 }
|
|
||||||||||||||||||||
|
Definition at line 199 of file app_externalivr.c. References ast_chan_log, ast_frfree(), ast_write(), 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 }
|
|
|
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 }
|
|
||||||||||||
|
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 }
|
|
|
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 }
|
|
|
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, app_exec, ast_register_application(), descrip, and synopsis. 00566 {
00567 return ast_register_application(app, app_exec, synopsis, descrip);
00568 }
|
|
|
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 }
|
|
||||||||||||||||||||
|
Definition at line 92 of file app_externalivr.c. References ast_chan_log, 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 }
|
|
|
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 app, 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 }
|
|
|
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 }
|
|
|
Definition at line 51 of file app_externalivr.c. |
|
|
Definition at line 55 of file app_externalivr.c. |
|
|
Definition at line 223 of file app_externalivr.c. |
|
|
Definition at line 83 of file app_externalivr.c. |
|
|
Definition at line 53 of file app_externalivr.c. |
|
|
Definition at line 49 of file app_externalivr.c. |
1.4.2