From 3ada95f82f02cf31f9b943303225b7868d431910 Mon Sep 17 00:00:00 2001 From: markster Date: Wed, 22 Sep 2004 15:21:36 +0000 Subject: Implement Fast AGI (agi://) over TCP socket (Astricon talk idea) git-svn-id: http://svn.digium.com/svn/asterisk/trunk@3820 f38db490-d61c-443f-a65b-d21fe96a405b --- agi/fastagi-test | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ res/res_agi.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 187 insertions(+), 3 deletions(-) create mode 100755 agi/fastagi-test diff --git a/agi/fastagi-test b/agi/fastagi-test new file mode 100755 index 000000000..d3f13cf6b --- /dev/null +++ b/agi/fastagi-test @@ -0,0 +1,94 @@ +#!/usr/bin/perl +use strict; +use Socket; +use Carp; +use IO::Handle; + +my $port = 4573; + +$|=1; + +# Setup some variables +my %AGI; my $tests = 0; my $fail = 0; my $pass = 0; + +sub checkresult { + my ($res) = @_; + my $retval; + $tests++; + chomp $res; + if ($res =~ /^200/) { + $res =~ /result=(-?\d+)/; + if (!length($1)) { + print STDERR "FAIL ($res)\n"; + $fail++; + } else { + print STDERR "PASS ($1)\n"; + $pass++; + } + } else { + print STDERR "FAIL (unexpected result '$res')\n"; + $fail++; + } +} + +socket(SERVER, PF_INET, SOCK_STREAM, 0); +setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)); +bind(SERVER, sockaddr_in($port, INADDR_ANY)) || die("can't bind\n"); +listen(SERVER, SOMAXCONN); + +for(;;) { + my $raddr = accept(CLIENT, SERVER); + my ($s, $p) = sockaddr_in($raddr); + CLIENT->autoflush(1); + while() { + chomp; + last unless length($_); + if (/^agi_(\w+)\:\s+(.*)$/) { + $AGI{$1} = $2; + } + } + print STDERR "AGI Environment Dump from $s:$p --\n"; + foreach my $i (sort keys %AGI) { + print STDERR " -- $i = $AGI{$i}\n"; + } + + print STDERR "1. Testing 'sendfile'..."; + print CLIENT "STREAM FILE beep \"\"\n"; + my $result = ; + &checkresult($result); + + print STDERR "2. Testing 'sendtext'..."; + print CLIENT "SEND TEXT \"hello world\"\n"; + my $result = ; + &checkresult($result); + + print STDERR "3. Testing 'sendimage'..."; + print CLIENT "SEND IMAGE asterisk-image\n"; + my $result = ; + &checkresult($result); + + print STDERR "4. Testing 'saynumber'..."; + print CLIENT "SAY NUMBER 192837465 \"\"\n"; + my $result = ; + &checkresult($result); + + print STDERR "5. Testing 'waitdtmf'..."; + print CLIENT "WAIT FOR DIGIT 1000\n"; + my $result = ; + &checkresult($result); + + print STDERR "6. Testing 'record'..."; + print CLIENT "RECORD FILE testagi gsm 1234 3000\n"; + my $result = ; + &checkresult($result); + + print STDERR "6a. Testing 'record' playback..."; + print CLIENT "STREAM FILE testagi \"\"\n"; + my $result = ; + &checkresult($result); + close(CLIENT); + print STDERR "================== Complete ======================\n"; + print STDERR "$tests tests completed, $pass passed, $fail failed\n"; + print STDERR "==================================================\n"; +} + diff --git a/res/res_agi.c b/res/res_agi.c index dce634455..eb97bcb1c 100755 --- a/res/res_agi.c +++ b/res/res_agi.c @@ -3,9 +3,9 @@ * * Asterisk Gateway Interface * - * Copyright (C) 1999, Mark Spencer + * Copyright (C) 1999-2004, Digium, Inc. * - * Mark Spencer + * Mark Spencer * * This program is free software, distributed under the terms of * the GNU General Public License @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -79,6 +80,90 @@ LOCAL_USER_DECL; #define TONE_BLOCK_SIZE 200 +/* Max time to connect to an AGI remote host */ +#define MAX_AGI_CONNECT 2000 + +#define AGI_PORT 4573 + +static int launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int *opid) +{ + int s; + int flags; + struct pollfd pfds[1]; + char *host; + char *c; int port = AGI_PORT; + char *script; + struct sockaddr_in sin; + struct hostent *hp; + struct ast_hostent ahp; + ast_log(LOG_DEBUG, "Blah\n"); + host = ast_strdupa(agiurl + 6); + if (!host) + return -1; + /* Strip off any script name */ + if ((c = strchr(host, '/'))) { + *c = '\0'; + c++; + script = c; + } + if ((c = strchr(host, ':'))) { + *c = '\0'; + c++; + port = atoi(c + 1); + } + if (efd) { + ast_log(LOG_WARNING, "AGI URI's don't support Enhanced AGI yet\n"); + return -1; + } + hp = ast_gethostbyname(host, &ahp); + if (!hp) { + ast_log(LOG_WARNING, "Unable to locate host '%s'\n", host); + return -1; + } + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno)); + return -1; + } + flags = fcntl(s, F_GETFL); + if (flags < 0) { + ast_log(LOG_WARNING, "Fcntl(F_GETFL) failed: %s\n", strerror(errno)); + close(s); + return -1; + } + if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) { + ast_log(LOG_WARNING, "Fnctl(F_SETFL) failed: %s\n", strerror(errno)); + close(s); + return -1; + } + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); + if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) && (errno != EINPROGRESS)) { + ast_log(LOG_WARNING, "Connect failed with unexpected error: %s\n", strerror(errno)); + close(s); + return -1; + } + pfds[0].fd = s; + pfds[0].events = POLLOUT; + if (poll(pfds, 1, MAX_AGI_CONNECT) != 1) { + ast_log(LOG_WARNING, "Connect to '%s' failed!\n", agiurl); + close(s); + return -1; + } + if (write(s, "agi_network: yes\n", strlen("agi_network: yes\n")) < 0) { + ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno)); + close(s); + return -1; + } + ast_log(LOG_DEBUG, "Wow, connected!\n"); + fds[0] = s; + fds[1] = s; + *opid = -1; + return 0; +} + static int launch_script(char *script, char *argv[], int *fds, int *efd, int *opid) { char tmp[256]; @@ -88,6 +173,10 @@ static int launch_script(char *script, char *argv[], int *fds, int *efd, int *op int audio[2]; int x; int res; + + if (!strncasecmp(script, "agi://", 6)) + return launch_netscript(script, argv, fds, efd, opid); + if (script[0] != '/') { snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_AGI_DIR, script); script = tmp; @@ -1338,7 +1427,8 @@ static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, i if (!(readf = fdopen(agi->ctrl, "r"))) { ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n"); - kill(pid, SIGHUP); + if (pid > -1) + kill(pid, SIGHUP); close(agi->ctrl); return -1; } -- cgit v1.2.3