Caffeine. The gateway drug.
Eddie Vedder
The Asterisk dialplan has evolved into a simple yet powerful programming interface for call handling. Many people, however, especially those with a programming background, prefer to implement call handling in a traditional programming language. The Asterisk Gateway Interface (AGI) allows for the development of first-party call control in the programming language of your choice.
This section gives a quick example of using the AGI.
First, let’s create the script that we’re going to run. AGI scripts are usually placed in /var/lib/asterisk/agi-bin.
$ cd /var/lib/asterisk/agi-bin $ vim hello-world.sh #!/bin/bash # Consume all variables sent by Asterisk while read VAR && [ -n ${VAR} ] ; do : ; done # Answer the call. echo "ANSWER" read RESPONSE # Say the letters of "Hello World" echo 'SAY ALPHA "Hello World" ""' read RESPONSE exit 0 $ chown asterisk:asterisk hello-world.sh $ chmod 700 hello-world.sh
Now, add the following line to /etc/asterisk/extensions.conf, in your
[sets]
context:
exten => 237,1,AGI(hello-world.sh)
Save and reload your dialplan, and when you call extension 237 you should hear Allison spell out “Hello World.”
There are a few variants of AGI that differ primarily in the method used to communicate with Asterisk. It is good to be aware of all the options so you can make the best choice based on the needs of your application.
Process-based AGI is the simplest variant of AGI. The quick-start example
at the beginning of this chapter is an example of a process-based AGI
script. The script is invoked using the AGI()
application from the Asterisk dialplan. The application to run
is specified as the first argument to AGI()
. Unless a full path is specified, the
application is expected to be in the /var/lib/asterisk/agi-bin directory.
Arguments to be passed to your AGI application can be specified as
additional arguments to the AGI()
application in the Asterisk
dialplan. The syntax is:
AGI(command
[,arg1
[,arg2
[,...]]])
Ensure that your application has the proper
permissions set so that the Asterisk process user has permissions to
execute it. Otherwise, AGI()
will
fail.
Once Asterisk executes your AGI application,
communication between Asterisk and your application will take place over
stdin
and stdout
. More details
about this communication will be covered in “AGI Communication Overview”. For more details about invoking AGI()
from the dialplan, check the
documentation built into Asterisk:
*CLI>
core show application AGI
It is the simplest form of AGI to implement.
It is the least efficient form of AGI with regard to resource consumption. Systems with high load should consider FastAGI, discussed in “FastAGI—AGI over TCP”, instead.
EAGI (Enhanced AGI) is a slight variant on AGI()
. It is invoked in the Asterisk dialplan as EAGI()
. The difference is that in addition
to the communication on stdin
and
stdout
, Asterisk also provides a unidirectional
stream of audio coming from the channel on file descriptor 3. For more
details on how to invoke EAGI()
from the Asterisk dialplan,
check the documentation built into Asterisk:
*CLI>
core show application EAGI
It has the simplicity of process-based AGI, with the addition of a simple read-only stream of the channel’s audio. This is the only variant that offers this feature.
Since a new process must be spawned to run your application for every call, it has the same efficiency concerns as regular, process-based AGI.
For an alternative way of gaining
access to the audio outside Asterisk, consider using JACK. Asterisk has a module for JACK integration,
called app_jack
. It
provides the JACK()
dialplan application and the
JACK_HOOK()
dialplan
function.
FastAGI is the
term used for AGI call control over a TCP connection. With
process-based AGI, an instance of an AGI application is executed on the
system for every call, and communication with that application is done
over stdin
and stdout
. With
FastAGI, a TCP connection is made
to a FastAGI server. Call control is done using the same AGI protocol,
but the communication is over the TCP connection and does not require a
new process to be started for every call. The AGI protocol is discussed
in more detail in “AGI Communication Overview”. Using FastAGI is
much more scalable than process-based AGI, though it is also more
complex to implement.
To use FastAGI you invoke the AGI()
application in the Asterisk dialplan, but instead of
providing the name of the application to execute, you provide an
agi://
URL. For example:
exten => 238,1,AGI(agi://127.0.0.1)
The default port number for a FastAGI connection
is 4573
. A different port number can
be appended to the URL after a colon. For example:
exten => 238,1,AGI(agi://127.0.0.1:4574)
Just as with process-based AGI, arguments can be
passed to a FastAGI application. To do so, add them as additional
arguments to the AGI()
application,
delimited by commas:
exten => 238,1,AGI(agi://192.168.1.199,arg1
,arg2
,arg3
)
FastAGI also supports the usage of DNS SRV
records, if you provide a URL in the form of hagi://
. By using SRV records, your DNS
servers can return multiple hosts that Asterisk can attempt to connect
to. This can be used for high availability and load balancing. In the
following example, to find a FastAGI server to connect to, Asterisk will
perform a DNS lookup for _agi._tcp.shifteight.org
:
exten => 238,1,AGI(hagi://shifteight.org)
In this example, the DNS servers for the shifteight.org
domain would need at least one
SRV record configured for _agi._tcp.shifteight.org
.
It’s more efficient than process-based AGI. Rather than spawning a process per call, a FastAGI server can be built to handle many calls.
DNS can be used to achieve high availability and load balancing among FastAGI servers to further enhance scalability.
It is more complex to implement a FastAGI server than to implement a process-based AGI application.
Async AGI allows an application that uses the Asterisk Manager Interface (AMI) to asynchronously queue up AGI commands to be executed on a channel. This can be especially useful if you are already making extensive use of the AMI and would like to enhance your application to handle call control, rather than writing a detailed Asterisk dialplan or developing a separate FastAGI server.
More information on the Asterisk Manager Interface can be found in Chapter 17.
Async AGI is invoked by the AGI()
application in the Asterisk dialplan. The argument to
AGI()
should be agi:async
, as shown in the following
example:
exten => 239,AGI(agi:async)
Additional information on how to use async AGI over the AMI can be found in the next section.
An existing AMI application can be used to control calls using AGI commands.
It is the most complex way to implement AGI.
The preceding section discussed the variations of
AGI that can be used. This section goes into more detail about how your
custom AGI application communicates with Asterisk once AGI()
has been invoked.
Once AGI()
or EAGI()
has been invoked from the Asterisk
dialplan, some information is passed to the AGI application to set up
the AGI session. This section discusses what steps are taken at the
beginning of an AGI session for the different variants of AGI.
For a process-based AGI application or a connection to a FastAGI server, the variables listed in Table 18-1 will be the first pieces of information sent from Asterisk to your application. Each variable will be on its own line, in the form:
agi_variable
:value
For an example of the variables that might be sent to an AGI application, see the AGI communication debug output in “Quick Start”. The end of the list of variables will be indicated by a blank line. The code handles these variables by reading lines of input in a loop until a blank line is received. At that point, the application continues and begins executing AGI commands.
When you use async AGI, Asterisk will send out a manager event called
AsyncAGI
to initiate the async AGI
session. This event will allow applications listening to manager
events to take over control of the call via the AGI manager action.
Here is an example manager event sent out by Asterisk:
Event: AsyncAGI Privilege: agi,all SubEvent: Start Channel: SIP/0000FFFF0001-00000000 Env: agi_request%3A%20async%0Aagi_channel%3A%20SIP%2F0000FFFF0001-00000000%0A agi_language%3A%20en%0Aagi_type%3A%20SIP%0A agi_uniqueid%3A%201285219743.0%0A agi_version%3A%201.8.0-beta5%0Aagi_callerid%3A%2012565551111%0A agi_calleridname%3A%20Julie%20Bryant%0Aagi_callingpres%3A%200%0A agi_callingani2%3A%200%0Aagi_callington%3A%200%0Aagi_callingtns%3A%200%0A agi_dnid%3A%20111%0Aagi_rdnis%3A%20unknown%0Aagi_context%3A%20LocalSets%0A agi_extension%3A%20111%0Aagi_priority%3A%201%0Aagi_enhanced%3A%200.0%0A agi_accountcode%3A%20%0Aagi_threadid%3A%20-1339524208%0A%0A
The value of the Env
header in this AsyncAGI
manager event is all on one line.
The long value of the Env
header
has been URI encoded.
Once an AGI session has been set up, Asterisk begins performing call processing in response to commands sent from the AGI application. As soon as an AGI command has been issued to Asterisk, no further commands will be processed on that channel until the current command has been completed. When it finishes processing a command, Asterisk will respond with the result.
The AGI processes commands in a serial manner.
Once a command has been executed, no further commands can be executed
until Asterisk has returned a response. Some commands can take a very
long time to execute. For example, the EXEC
AGI command executes an Asterisk
application. If the command is EXEC
Dial
, AGI communication is blocked until the call is done.
If your AGI application needs to interact further with Asterisk at
this point, it can do so using the AMI, which is covered in Chapter 17.
You can retrieve a full list of available AGI
commands from the Asterisk console by running the command agi
show commands
. These commands are described in Table 18-2. To get more detailed information on a
specific AGI command, including syntax information for any arguments
that a command expects, use agi show commands topic
. For example, to see the
built-in documentation for the COMMAND
ANSWER
AGI command, you would use agi show commands topic
ANSWER
.
AGI command | Description |
---|---|
ANSWER | Answer the incoming call. |
ASYNCAGI BREAK | End an async AGI session and have the channel return to the Asterisk dialplan. |
CHANNEL STATUS | Retrieve the status of the channel. This is used to retrieve the current state of the channel, such as up (answered), down (hung up), or ringing. |
DATABASE DEL | Delete a key/value pair from the built-in AstDB. |
DATABASE DELTREE | Delete a tree of key/value pairs from the built-in AstDB. |
DATABASE GET | Retrieve the value for a key in the AstDB. |
DATABASE PUT | Set the value for a key in the AstDB. |
EXEC | Execute an Asterisk dialplan application on
the channel. This command is very powerful in that between
EXEC and GET FULL VARIABLE , you can do anything
with the call that you can do from the Asterisk
dialplan. |
GET DATA | Read digits from the caller. |
GET
FULL VARIABLE | Evaluate an Asterisk dialplan expression. You can send a
string that contains variables and/or dialplan functions, and
Asterisk will return the result after making the appropriate
substitutions. This command is very powerful in that between
EXEC and GET FULL VARIABLE , you can do anything
with the call that you can do from the Asterisk
dialplan. |
GET OPTION | Stream a sound file while waiting for a digit from the
caller. This is similar to the Background() dialplan
application. |
GET VARIABLE | Retrieve the value of a channel variable. |
HANGUP | Hang up the channel.a |
NOOP | Do nothing. You will get a result response from this command, just like any other. It can be used as a simple test of the communication path with Asterisk. |
RECEIVE CHAR | Receive a single character. This only works for channel
types that support it, such as IAX2 using TEXT frames or SIP using the MESSAGE method. |
RECEIVE TEXT | Receive a text message. This only works in the same cases
as RECEIVE CHAR . |
RECORD FILE | Record the audio from the caller to a file. This is a
blocking operation similar to the Record() dialplan application. To
record a call in the background while you perform other
operations, use EXEC Monitor or EXEC MixMonitor . |
SAY ALPHA | Say a string of characters. You can find an example of
this in “Quick Start”. To get localized
handling of this and the other SAY commands, set the channel language
either in the device configuration file (e.g., sip.conf) or in the dialplan, by
setting the CHANNEL(language)
dialplan function. |
SAY DIGITS | Say a string of digits. For example, 100 would be said as “one zero zero” if the channel’s language is set to English. |
SAY NUMBER | Say a number. For example, 100 would be said as “one hundred” if the channel’s language is set to English. |
SAY PHONETIC | Say a string of characters, but use a common word for each letter (Alpha, Bravo, Charlie…). |
SAY DATE | Say a given date. |
SAY TIME | Say a given time. |
SAY DATETIME | Say a given date and time using a specified format. |
SEND IMAGE | Send an image to a channel. IAX2 supports this, but there are no actively developed IAX2 clients that support it that we know of. |
SEND TEXT | Send text to a channel that supports it. This can be used with SIP and IAX2 channels, at least. |
SET AUTOHANGUP | Schedule the channel to be hung up at a specified point in time in the future. |
SET CALLERID | Set the caller ID name and number on the channel. |
SET CONTEXT | Set the current dialplan context on the channel. |
SET EXTENSION | Set the current dialplan extension on the channel. |
SET MUSIC | Start or stop music on hold on the channel. |
SET PRIORITY | Set the current dialplan priority on the channel. |
SET VARIABLE | Set a channel variable to a given value. |
STREAM FILE | Stream the contents of a file to a channel. |
CONTROL STREAM FILE | Stream the contents of a file to a channel, but also allow the channel to control the stream. For example, the channel can pause, rewind, or fast-forward the stream. |
TDD MODE | Toggle the TDD (Telecommunications Device for the Deaf) mode on the channel. |
VERBOSE | Send a message to the verbose logger channel. Verbose
messages show up on the Asterisk console if the verbose
setting is high enough. Verbose messages will also go to any
logfile that has been configured for the verbose logger channel in /etc/asterisk/logger.conf. |
WAIT FOR DIGIT | Wait for the caller to press a digit. |
SPEECH CREATE | Initialize speech recognition. This must be done before using other speech AGI commands.b |
SPEECH SET | Set a speech engine setting. The settings that are available are specific to the speech recognition engine in use. |
SPEECH DESTROY | Destroy resources that were allocated for doing speech recognition. This command should be the last speech command executed. |
SPEECH LOAD GRAMMAR | Load a grammar. |
SPEECH UNLOAD GRAMMAR | Unload a grammar. |
SPEECH ACTIVATE GRAMMAR | Activate a grammar that has been loaded. |
SPEECH DEACTIVATE GRAMMAR | Deactivate a grammar. |
SPEECH RECOGNIZE | Play a prompt and perform speech recognition, as well as wait for digits to be pressed. |
GOSUB | Execute a dialplan subroutine. This will perform in the
same way as the GoSub()
dialplan application. |
a When the b While Asterisk includes a core API for handling speech recognition, it does not come with a module that provides a speech recognition engine. Digium currently provides two commercial options for speech recognition: Lumenvox and Vestec. |
AGI commands are sent to Asterisk on a single line. The line must end with a single newline character. Once a command has been sent to Asterisk, no further commands will be processed until the last command has finished and a response has been sent back to the AGI application. Here is an example response to an AGI command:
200 result=0
The Asterisk console allows debugging the
communications with an AGI application. To enable AGI communication
debugging, run the agi set debug on
command. To
turn debugging off, use agi set debug off
. While
this debugging mode is on, all communication to and from an AGI
application will be printed out to the Asterisk console. An example
of this output can be found in “Quick Start”.
When you’re using async AGI, you issue commands by using the AGI manager
action. To see the built-in documentation for the AGI manager action,
run manager show command AGI
at the Asterisk CLI. A demonstration will help
clarify how AGI commands are executed using the async AGI method.
First, an extension is created in the dialplan that runs an async AGI
session on a channel:
exten => 240,AGI(agi:async)
When the AGI dialplan application is executed, a manager event
called AsyncAGI
will be sent out
with all the AGI environment variables. Details about this event are
in “Async AGI”. After this, AGI manager actions
can start to take place via AMI.
The following shows an example manager-action
execution and the manager events that are emitted during async AGI
processing. After the initial execution of the AGI
manager action, there is an immediate
response to indicate that the command has been queued up for
execution. Later, there is a manager event that indicates that the
queued command has been executed. The CommandID
header can be used to associate
the initial request with the event that indicates that the command has
been executed:
Action: AGI
Channel: SIP/0004F2060EB4-00000013
ActionID: my-action-id
CommandID: my-command-id
Command: VERBOSE "Puppies like cotton candy." 1
Response: Success ActionID: my-action-id Message: Added AGI command to queue Event: AsyncAGI Privilege: agi,all SubEvent: Exec Channel: SIP/0004F2060EB4-00000013 CommandID: my-command-id Result: 200%20result%3D1%0A
The following output is what was seen on the Asterisk console during this async AGI session:
-- Executing [7011@phones:1] AGI("SIP/0004F2060EB4-00000013", "agi:async") in new stack agi:async: Puppies like cotton candy. == Spawn extension (phones, 7011, 1) exited non-zero on 'SIP/0004F2060EB4-00000013'
An AGI session ends when your AGI application is ready for it to end. The details about how this happens depend on whether your application is using process-based AGI, FastAGI, or async AGI.
Your AGI application may exit or close its connection at any time. As long as the channel has not hung up before your application ends, dialplan execution will continue.
If channel hangup occurs while your AGI session is still active, Asterisk will provide notification that this has occurred so that your application can adjust its operation as appropriate.
If a channel hangs up while your AGI
application is still executing, a couple of things will happen. If an
AGI command is in the middle of executing, you may receive a result
code of -1
. You should not depend
on this, though, since not all AGI commands require channel interaction. If
the command being executed does not require channel interaction, the
result will not reflect the hangup.
The next thing that happens after a channel
hangs up is that an explicit notification of the hangup is sent to
your application. For process-based AGI, the signal SIGHUP
will be sent to the process to notify
it of the hangup. For a FastAGI connection, Asterisk will send a line
containing the word HANGUP
.
Once the hangup has happened, the only AGI commands that may be used are those that do not require channel interaction. The documentation for the AGI commands built into Asterisk includes an indication of whether or not each command can be used once the channel has been hung up.
When you’re using async AGI, the manager interface provides mechanisms to
notify you about channel hangups. When you would like to end an async
AGI session for a channel, you must execute the ASYNCAGI BREAK
command. When the async AGI
session ends, Asterisk will send an AsyncAGI
manager event with a SubEvent
of End
. The following is an example of ending
an async AGI session:
Action: AGI
Channel: SIP/0004F2060EB4-0000001b
ActionID: my-action-id
CommandID: my-command-id
Command: ASYNCAGI BREAK
Response: Success ActionID: my-action-id Message: Added AGI command to queue Event: AsyncAGI Privilege: agi,all SubEvent: End Channel: SIP/0004F2060EB4-0000001b
At this point, the channel returns to the next step in the Asterisk dialplan (assuming it has not yet been hung up).
Example 18-1 is an example of an AGI script. To run this script you would first place it in the /var/lib/asterisk/agi-bin directory. Then you would execute it from the Asterisk dialplan like this:
exten => 241,1,AGI(account-lookup.py) same => n,Hangup()
This example is written in Python and is very
sparsely documented for brevity. It demonstrates how an AGI script
interfaces with Asterisk using stdin
and stdout
.
The script prompts a user to enter an account number, and then plays back a value associated with that number. In the interest of brevity, we have hardcoded a few fake accounts into the script—this would obviously be something normally handled by a database connection.
The script is intentionally terse, since we are interested in briefly showing some AGI functions without filling this book with pages of code.
#!/usr/bin/env python
# An example for AGI (Asterisk Gateway Interface).
import
sys
def
agi_command
(
cmd
):
'''Write out the command and return the response'''
cmd
sys
.
stdout
.
flush
()
#clear the buffer
return
sys
.
stdin
.
readline
()
.
strip
()
# strip whitespace
asterisk_env
=
{}
# read AGI env vars from Asterisk
while
True
:
line
=
sys
.
stdin
.
readline
()
.
strip
()
if
not
len
(
line
):
break
var_name
,
var_value
=
line
.
split
(
':'
,
1
)
asterisk_env
[
var_name
]
=
var_value
# Fake "database" of accounts.
ACCOUNTS
=
{
'12345678'
:
{
'balance'
:
'50'
},
'11223344'
:
{
'balance'
:
'10'
},
'87654321'
:
{
'balance'
:
'100'
},
}
response
=
agi_command
(
'ANSWER'
)
# three arguments: prompt, timeout, maxlength
response
=
agi_command
(
'GET DATA enter_account 3000 8'
)
if
'timeout'
in
response
:
response
=
agi_command
(
'STREAM FILE goodbye ""'
)
sys
.
exit
(
0
)
# The response will look like: 200 result=<digits>
# Split on '=', we want index 1
account
=
response
.
split
(
'='
,
1
)[
1
]
if
account
==
'-1'
:
# digits if error
response
=
agi_command
(
'STREAM FILE astcc-account-number-invalid ""'
)
response
=
agi_command
(
'HANGUP'
)
sys
.
exit
(
0
)
if
account
not
in
ACCOUNTS
:
# invalid
response
=
agi_command
(
'STREAM FILE astcc-account-number-invalid ""'
)
sys
.
exit
(
0
)
balance
=
ACCOUNTS
[
account
][
'balance'
]
response
=
agi_command
(
'STREAM FILE account-balance-is ""'
)
response
=
agi_command
(
'SAY NUMBER
%s
""'
%
(
balance
))
sys
.
exit
(
0
)
There have been a number of efforts to create frameworks or libraries that make AGI programming easier. You will notice that several of these frameworks also appeared in Chapter 17. Just as with AMI, when evaluating a framework, we recommend you find one that meets the following criteria:
Has this project been around for a few years? A mature project is far less likely to have serious bugs in it.
Check the age of the latest update. If the project hasn’t been updated in a few years, there’s a strong possibility it has been abandoned. It might still be usable, but you’ll be on your own. Similarly, what does the bug tracker look like? Are there a lot of important bugs being ignored? (Be discerning here, since often the realities of maintaining a free project require disciplined triage—not everybody’s features are going to get added.)
Is this a well-written framework? If it was not engineered well, you should be aware of that when deciding whether to trust your project to it.
Is there an active community of developers using this project? It’s likely you’ll need help; will it be available when you need it?
The code should be well commented, but ideally, a wiki or other official documentation to support the library is essential.
The frameworks listed in Table 18-3 met all or most of the preceding criteria at this writing. If you do not see a library listed here for your preferred programming language, it might be out there somewhere, but simply didn’t make our list.
Framework | Language |
---|---|
Adhearsion | Ruby |
Asterisk-Java | Java |
AsterNET | .NET |
ding-dong | Node.js |
PAGI | PHP |
Panoramisk | Python |
StarPy | Python + Twisted |
AGI provides a powerful interface to Asterisk that allows you to implement first-party call control in the programming language of your choice. You can take multiple approaches to implementing an AGI application. Some approaches can provide better performance, but at the cost of more complexity. AGI provides a programming environment that may make it easier to integrate Asterisk with other systems, or just provide a more comfortable call-control programming environment for the experienced programmer. In many cases, the use of a prebuilt framework will be the best approach, especially when evaluating or prototyping a complex project. For the ultimate performance, we still recommend you consider writing as much of your application as you can using the Asterisk dialplan.