mirror of
https://github.com/asterisk/asterisk.git
synced 2025-11-21 01:01:02 +00:00
(closes issue #11827)
Reported by: ctooley
Patches:
eivr_tcp_generic.patch uploaded by jpeeler (license 325)
This change adds the ability to communicate over a TCP socket instead of forking a child process.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@108404 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||||||
#include "asterisk/linkedlists.h"
|
#include "asterisk/linkedlists.h"
|
||||||
#include "asterisk/app.h"
|
#include "asterisk/app.h"
|
||||||
#include "asterisk/utils.h"
|
#include "asterisk/utils.h"
|
||||||
|
#include "asterisk/tcptls.h"
|
||||||
|
|
||||||
static const char *app = "ExternalIVR";
|
static const char *app = "ExternalIVR";
|
||||||
|
|
||||||
@@ -89,6 +90,8 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
|||||||
int eivr_events_fd, int eivr_commands_fd, int eivr_errors_fd,
|
int eivr_events_fd, int eivr_commands_fd, int eivr_errors_fd,
|
||||||
const char *args);
|
const char *args);
|
||||||
|
|
||||||
|
int eivr_connect_socket(struct ast_channel *chan, const char *host, int port);
|
||||||
|
|
||||||
static void send_eivr_event(FILE *handle, const char event, const char *data,
|
static void send_eivr_event(FILE *handle, const char event, const char *data,
|
||||||
const struct ast_channel *chan)
|
const struct ast_channel *chan)
|
||||||
{
|
{
|
||||||
@@ -305,6 +308,12 @@ static int app_exec(struct ast_channel *chan, void *data)
|
|||||||
int gen_active = 0;
|
int gen_active = 0;
|
||||||
int pid;
|
int pid;
|
||||||
char *buf, *pipe_delim_argbuf, *pdargbuf_ptr;
|
char *buf, *pipe_delim_argbuf, *pdargbuf_ptr;
|
||||||
|
|
||||||
|
char hostname[1024];
|
||||||
|
char *port_str = NULL;
|
||||||
|
int port = 0;
|
||||||
|
struct ast_tcptls_session_instance *ser;
|
||||||
|
|
||||||
struct ivr_localuser foo = {
|
struct ivr_localuser foo = {
|
||||||
.playlist = AST_LIST_HEAD_INIT_VALUE,
|
.playlist = AST_LIST_HEAD_INIT_VALUE,
|
||||||
.finishlist = AST_LIST_HEAD_INIT_VALUE,
|
.finishlist = AST_LIST_HEAD_INIT_VALUE,
|
||||||
@@ -333,90 +342,137 @@ static int app_exec(struct ast_channel *chan, void *data)
|
|||||||
pipe_delim_argbuf = ast_strdupa(data);
|
pipe_delim_argbuf = ast_strdupa(data);
|
||||||
while((pdargbuf_ptr = strchr(pipe_delim_argbuf, ',')) != NULL)
|
while((pdargbuf_ptr = strchr(pipe_delim_argbuf, ',')) != NULL)
|
||||||
pdargbuf_ptr[0] = '|';
|
pdargbuf_ptr[0] = '|';
|
||||||
|
|
||||||
if (pipe(child_stdin)) {
|
|
||||||
ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno));
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (pipe(child_stdout)) {
|
|
||||||
ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno));
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (pipe(child_stderr)) {
|
|
||||||
ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (chan->_state != AST_STATE_UP) {
|
|
||||||
ast_answer(chan);
|
|
||||||
}
|
|
||||||
if (ast_activate_generator(chan, &gen, u) < 0) {
|
|
||||||
ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
|
|
||||||
goto exit;
|
|
||||||
} else
|
|
||||||
gen_active = 1;
|
|
||||||
|
|
||||||
pid = fork();
|
if(!strncmp(args.cmd[0], "ivr://", 6)) {
|
||||||
if (pid < 0) {
|
struct server_args ivr_desc = {
|
||||||
ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
|
.accept_fd = -1,
|
||||||
goto exit;
|
.name = "IVR",
|
||||||
}
|
};
|
||||||
|
struct ast_hostent hp;
|
||||||
|
|
||||||
if (!pid) {
|
/*communicate through socket to server*/
|
||||||
/* child process */
|
if (chan->_state != AST_STATE_UP) {
|
||||||
int i;
|
ast_answer(chan);
|
||||||
|
}
|
||||||
|
if (ast_activate_generator(chan, &gen, u) < 0) {
|
||||||
|
ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
|
||||||
|
goto exit;
|
||||||
|
} else {
|
||||||
|
gen_active = 1;
|
||||||
|
}
|
||||||
|
|
||||||
signal(SIGPIPE, SIG_DFL);
|
ast_chan_log(LOG_DEBUG, chan, "Parsing hostname:port for socket connect from \"%s\"\n", args.cmd[0]);
|
||||||
pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
|
strncpy(hostname, args.cmd[0] + 6, sizeof(hostname));
|
||||||
|
if((port_str = strchr(hostname, ':')) != NULL) {
|
||||||
|
port_str[0] = 0;
|
||||||
|
port_str += 1;
|
||||||
|
port = atoi(port_str);
|
||||||
|
}
|
||||||
|
if(!port)
|
||||||
|
port = 2949; /*default port, if one is not provided*/
|
||||||
|
|
||||||
if (ast_opt_high_priority)
|
ast_gethostbyname(hostname, &hp);
|
||||||
ast_set_priority(0);
|
ivr_desc.sin.sin_family = AF_INET;
|
||||||
|
ivr_desc.sin.sin_port = htons(port);
|
||||||
|
memmove(&ivr_desc.sin.sin_addr.s_addr, hp.hp.h_addr, hp.hp.h_length);
|
||||||
|
ser = ast_tcptls_client_start(&ivr_desc);
|
||||||
|
|
||||||
dup2(child_stdin[0], STDIN_FILENO);
|
if (!ser) {
|
||||||
dup2(child_stdout[1], STDOUT_FILENO);
|
goto exit;
|
||||||
dup2(child_stderr[1], STDERR_FILENO);
|
}
|
||||||
for (i = STDERR_FILENO + 1; i < 1024; i++)
|
res = eivr_comm(chan, u, ser->fd, ser->fd, 0, pipe_delim_argbuf);
|
||||||
close(i);
|
|
||||||
execv(args.cmd[0], args.cmd);
|
|
||||||
fprintf(stderr, "Failed to execute '%s': %s\n", args.cmd[0], strerror(errno));
|
|
||||||
_exit(1);
|
|
||||||
} else {
|
} else {
|
||||||
/* parent process */
|
|
||||||
|
if (pipe(child_stdin)) {
|
||||||
close(child_stdin[0]);
|
ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno));
|
||||||
child_stdin[0] = 0;
|
goto exit;
|
||||||
close(child_stdout[1]);
|
}
|
||||||
child_stdout[1] = 0;
|
if (pipe(child_stdout)) {
|
||||||
close(child_stderr[1]);
|
ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno));
|
||||||
child_stderr[1] = 0;
|
goto exit;
|
||||||
res = eivr_comm(chan, u, child_stdin[1], child_stdout[0], child_stderr[0], pipe_delim_argbuf);
|
}
|
||||||
|
if (pipe(child_stderr)) {
|
||||||
exit:
|
ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
|
||||||
if (gen_active)
|
goto exit;
|
||||||
ast_deactivate_generator(chan);
|
}
|
||||||
|
if (chan->_state != AST_STATE_UP) {
|
||||||
if (child_stdin[0])
|
ast_answer(chan);
|
||||||
|
}
|
||||||
|
if (ast_activate_generator(chan, &gen, u) < 0) {
|
||||||
|
ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
|
||||||
|
goto exit;
|
||||||
|
} else {
|
||||||
|
gen_active = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pid) {
|
||||||
|
/* child process */
|
||||||
|
int i;
|
||||||
|
|
||||||
|
signal(SIGPIPE, SIG_DFL);
|
||||||
|
pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
|
||||||
|
|
||||||
|
if (ast_opt_high_priority)
|
||||||
|
ast_set_priority(0);
|
||||||
|
|
||||||
|
dup2(child_stdin[0], STDIN_FILENO);
|
||||||
|
dup2(child_stdout[1], STDOUT_FILENO);
|
||||||
|
dup2(child_stderr[1], STDERR_FILENO);
|
||||||
|
for (i = STDERR_FILENO + 1; i < 1024; i++)
|
||||||
|
close(i);
|
||||||
|
execv(args.cmd[0], args.cmd);
|
||||||
|
fprintf(stderr, "Failed to execute '%s': %s\n", args.cmd[0], strerror(errno));
|
||||||
|
_exit(1);
|
||||||
|
} else {
|
||||||
|
/* parent process */
|
||||||
|
|
||||||
close(child_stdin[0]);
|
close(child_stdin[0]);
|
||||||
|
child_stdin[0] = 0;
|
||||||
if (child_stdin[1])
|
|
||||||
close(child_stdin[1]);
|
|
||||||
|
|
||||||
if (child_stdout[0])
|
|
||||||
close(child_stdout[0]);
|
|
||||||
|
|
||||||
if (child_stdout[1])
|
|
||||||
close(child_stdout[1]);
|
close(child_stdout[1]);
|
||||||
|
child_stdout[1] = 0;
|
||||||
if (child_stderr[0])
|
|
||||||
close(child_stderr[0]);
|
|
||||||
|
|
||||||
if (child_stderr[1])
|
|
||||||
close(child_stderr[1]);
|
close(child_stderr[1]);
|
||||||
|
child_stderr[1] = 0;
|
||||||
while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list)))
|
res = eivr_comm(chan, u, child_stdin[1], child_stdout[0], child_stderr[0], pipe_delim_argbuf);
|
||||||
ast_free(entry);
|
}
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (gen_active)
|
||||||
|
ast_deactivate_generator(chan);
|
||||||
|
|
||||||
|
if (child_stdin[0])
|
||||||
|
close(child_stdin[0]);
|
||||||
|
|
||||||
|
if (child_stdin[1])
|
||||||
|
close(child_stdin[1]);
|
||||||
|
|
||||||
|
if (child_stdout[0])
|
||||||
|
close(child_stdout[0]);
|
||||||
|
|
||||||
|
if (child_stdout[1])
|
||||||
|
close(child_stdout[1]);
|
||||||
|
|
||||||
|
if (child_stderr[0])
|
||||||
|
close(child_stderr[0]);
|
||||||
|
|
||||||
|
if (child_stderr[1])
|
||||||
|
close(child_stderr[1]);
|
||||||
|
|
||||||
|
if (ser) {
|
||||||
|
fclose(ser->f);
|
||||||
|
ast_tcptls_session_instance_destroy(ser);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list)))
|
||||||
|
ast_free(entry);
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
||||||
@@ -436,21 +492,21 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
|||||||
FILE *eivr_commands = NULL;
|
FILE *eivr_commands = NULL;
|
||||||
FILE *eivr_errors = NULL;
|
FILE *eivr_errors = NULL;
|
||||||
FILE *eivr_events = NULL;
|
FILE *eivr_events = NULL;
|
||||||
|
|
||||||
if (!(eivr_events = fdopen(eivr_events_fd, "w"))) {
|
if (!(eivr_events = fdopen(eivr_events_fd, "w"))) {
|
||||||
ast_chan_log(LOG_WARNING, chan, "Could not open stream to send events\n");
|
ast_chan_log(LOG_WARNING, chan, "Could not open stream to send events\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (!(eivr_commands = fdopen(eivr_commands_fd, "r"))) {
|
if (!(eivr_commands = fdopen(eivr_commands_fd, "r"))) {
|
||||||
ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive commands\n");
|
ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive commands\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if(eivr_errors_fd) { /*if opening a socket connection, error stream will not be used*/
|
if(eivr_errors_fd) { /* if opening a socket connection, error stream will not be used */
|
||||||
if (!(eivr_errors = fdopen(eivr_errors_fd, "r"))) {
|
if (!(eivr_errors = fdopen(eivr_errors_fd, "r"))) {
|
||||||
ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive errors\n");
|
ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive errors\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setvbuf(eivr_events, NULL, _IONBF, 0);
|
setvbuf(eivr_events, NULL, _IONBF, 0);
|
||||||
setvbuf(eivr_commands, NULL, _IONBF, 0);
|
setvbuf(eivr_commands, NULL, _IONBF, 0);
|
||||||
@@ -633,18 +689,18 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
|||||||
|
|
||||||
exit:
|
exit:
|
||||||
|
|
||||||
if (eivr_events)
|
if (eivr_events)
|
||||||
fclose(eivr_events);
|
fclose(eivr_events);
|
||||||
|
|
||||||
if (eivr_commands)
|
if (eivr_commands)
|
||||||
fclose(eivr_commands);
|
fclose(eivr_commands);
|
||||||
|
|
||||||
if (eivr_errors)
|
if (eivr_errors)
|
||||||
fclose(eivr_errors);
|
fclose(eivr_errors);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unload_module(void)
|
static int unload_module(void)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user