Asterisk Gateway Interface (AGI)

Rate this post

Asterisk Gateway Interface (AGI)

The Asterisk dialplan has evolved into a simple yet powerful programming interface for call handling. However, many people, especially those with a prior programming background, still prefer implementing their custom call handling in a different programming language. Using another programming language may also allow you to utilize existing code for integration with other systems. The Asterisk Gateway Interface (AGI) allows the development of first-party call control in the programming language of your choice. If you are not interested in implementing call control outside the native Asterisk dialplan, you may safely skip this topic post.

Quick Start

This section gives a quick example of using the AGI. First, add the following line to /etc/ asterisk/extensions.conf:

exten => 500,1,AGI(hello-world.sh)

Next, create a hello-world.sh script in /var/lib/asterisk/agi-bin, as shown in Example -1.

Example -1. A sample AGI script, 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

Now, call extension 500 with AGI debugging turned on, and listen to Allison spell out
“Hello World”:

*CLI> agi set debug on
AGI Debugging Enabled

              — Executing [500@phones:1] AGI(“SIP/0004F2060EB4-00000009”,
“hello-world.sh”) in new stack
— Launched AGI Script /var/lib/asterisk/agi-bin/hello-world.sh
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_request: hello-world.sh
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_channel: SIP/0004F2060EB4-00000009
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_language: en
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_type: SIP
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_uniqueid: 1284382003.9
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_version: SVN-branch-11-r378376
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_callerid: 2563619899
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_calleridname: Russell Bryant
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_callingpres: 0
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_callingani2: 0
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_callington: 0
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_callingtns: 0
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_dnid: 7010
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_rdnis: unknown
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_context: phones
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_extension: 500
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_priority: 1
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_enhanced: 0.0
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_accountcode:
<SIP/0004F2060EB4-00000009>AGI Tx >> agi_threadid: 140071216785168
<SIP/0004F2060EB4-00000009>AGI Tx >>
<SIP/0004F2060EB4-00000009>AGI Rx << ANSWER
<SIP/0004F2060EB4-00000009>AGI Tx >> 200 result=0
<SIP/0004F2060EB4-00000009>AGI Rx << SAY ALPHA “Hello World” “”
— <SIP/0004F2060EB4-00000009> Playing ‘letters/h.gsm’ (language ‘en’)
— <SIP/0004F2060EB4-00000009> Playing ‘letters/e.gsm’ (language ‘en’)
— <SIP/0004F2060EB4-00000009> Playing ‘letters/l.gsm’ (language ‘en’)
— <SIP/0004F2060EB4-00000009> Playing ‘letters/l.gsm’ (language ‘en’)
— <SIP/0004F2060EB4-00000009> Playing ‘letters/o.gsm’ (language ‘en’)
— <SIP/0004F2060EB4-00000009> Playing ‘letters/space.gsm’ (language ‘en’)
— <SIP/0004F2060EB4-00000009> Playing ‘letters/w.gsm’ (language ‘en’)
— <SIP/0004F2060EB4-00000009> Playing ‘letters/o.gsm’ (language ‘en’)
— <SIP/0004F2060EB4-00000009> Playing ‘letters/r.gsm’ (language ‘en’)
— <SIP/0004F2060EB4-00000009> Playing ‘letters/l.gsm’ (language ‘en’)
— <SIP/0004F2060EB4-00000009> Playing ‘letters/d.gsm’ (language ‘en’)

<SIP/0004F2060EB4-00000009>AGI Tx >> 200 result=0
— <SIP/0004F2060EB4-00000009>AGI Script hello-world.sh completed, returning 0

AGI Variants

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

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 exist 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[,…]]])

Once Asterisk executes your AGI application, communication between Asterisk and your application will take place over stdin and stdout. For more details about invoking AGI() from the dialplan, check the documentation built into Asterisk:

          *CLI> core show application AGI
Pros of process-based AGI
It is the simplest form of AGI to implement.

Cons of process-based AGI

It is the least efficient form of AGI with regard to resource consumption. Systems
with high load should consider FastAGI.

EAGI

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

Pros of Enhanced AGI
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.

Cons of Enhanced AGI
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.

DeadAGI Is Dead

In versions of Asterisk prior to 1.8, there was a dialplan application called DeadAGI() . Its purpose was similar to that of AGI() , except you used it on a channel that had already been hung up. This would usually be done in the special h extension, when you wanted to use an AGI application to aid in some type of post-call processing. Invoking DeadAGI() from the dialplan will still work, but you will get a WARNING message in the Asterisk log. It has been deprecated in favor of using AGI() in all cases. The code for AGI() has been updated so it knows how to correctly adjust its operation after a channel has been hung up.

Pros of DeadAGI
None. It’s dead.

Cons of DeadAGI
It’s dead. Really, don’t use it. If you do, your configuration may break if DeadAGI() is completely removed from Asterisk in a future version.

FastAGI—AGI over TCP

FastAGI is the term used for AGI call control over a TCP connection. With processbased 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. Using FastAGI is much more scalable than process-based AGI, though it is also more complex to implement.

FastAGI is used by invoking 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 => 1234,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 => 1234,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 => 1234,1,AGI(agi://192.168.1.199,arg1,arg2,arg3)

FastAGI also supports the usage of Service records (SRV records) if you provide a URL in the form of hagi:// . By using SRV records, you can list multiple hosts that Asterisk can attempt to connect to for purposes of high availability and load balancing. In the following example, to find a FastAGI server to connect to, Asterisk will do a DNS lookup for _agi._tcp.shifteight.org :

exten => 1234,1,AGI(hagi://shifteight.org)

Pros of FastAGI
It’s more efficient than process-based AGI. Instead of spawning a process per call, a FastAGI server can handle many calls.

DNS can be used to achieve high availability and load balancing among FastAGI servers to further enhance scalability.

Cons of FastAGI
It is more complex to implement a FastAGI server than to implement a process-based AGI application. However, implementing a TCP server has been done countless times before, so there are many examples available for virtually any programming language.

Async AGI—AMI-Controlled AGI

Async AGI is a newer method of using AGI that was first introduced in Asterisk 1.6.0. The purpose of async AGI is to allow 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 take advantage of the same application to handle call control, as opposed to writing a detailed Asterisk dialplan or developing a separate FastAGI server.

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 => 1234,1,AGI(agi:async)

Additional information on how to use async AGI over the AMI can be found in the next section.

Pros of async AGI
An existing AMI application can be used to control calls using AGI commands.

Cons of async AGI
It is the most complex way to implement AGI.

AGI Communication Overview

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.

Setting Up an AGI Session

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.

Process-based AGI/FastAGI

For a process-based AGI application or a connection to a FastAGI server, the variables listed in Table -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

Table -1. AGI environment variables

1t1

1t2

1t4

For an example of the variables that might be sent to an AGI application. The end of the list of variables will be indicated by a blank line. Example -1 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.

Async AGI

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

Commands and Responses

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.

A full list of available AGI commands can be retrieved from the Asterisk console by running the command agi show commands. These commands are described in Table -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 < COMMAND >. For example, to see the built-in documentation for the ANSWER AGI command, you would use agi show commands topic ANSWER.

Table 21-2. AGI commands

1t3

1t5

1t6

1t7

Process-based AGI/FastAGI

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

Async AGI

When you’re using async AGI, commands are issued 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 => 7011,1,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. 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’

Ending an AGI Session

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.

Process-based AGI/FastAGI

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 . If you would like to disable having Asterisk send the SIGHUP signal to your process-based AGI application or the HANGUP string to your FastAGI server, you can do so by setting the AGISIGHUP channel variable, as demonstrated in the following short example:
;
; Don’t send SIGHUP to an AGI process
; or the “HANGUP” string to a FastAGI server.
;
exten => 500,1,Set(AGISIGHUP=no)
same => n,AGI(my-agi-application)

At this point, Asterisk automatically adjusts its operation to be in DeadAGI mode. This just means that an AGI application can run on a channel that has been hung up. The only AGI commands that may be used at this point 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.

Async AGI

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 Asterisk dialplan if it has not yet been hung up.

Example: Account Database Access

Example -2 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 => agiexample1,1,AGI(agiexample1.py)

This example is written in Python and is documented throughout. It shows how a script could prompt a caller to enter an account number and then play back some information from that account. More importantly, it shows generally how an AGI script interfaces
with Asterisk to run AGI commands and receive a response back from Asterisk.

Example -2. agiexample1.py

#!/usr/bin/env python
#
# An example for AGI (Asterisk Gateway Interface).
#
# Answer a call. Retrieve an account number from the caller and play back
# some account information to the caller.
#
# Developed for “Asterisk: The Definitive Guide”
#

import sys

#
# AGI commands are written to stdout. After a command is processed,
# the response from Asterisk comes in via stdin.
#
def agi_command(cmd):
”’Write out the command and return the response”’
# print sends the command out to stdout.

print cmd
# Make sure it doesn’t get buffered.
sys.stdout.flush()
# Read the response line from stdin. Use strip() to remove any whitespace
# that may be present (primarily the line ending).
return sys.stdin.readline().strip()

#
# Read the AGI environment variables sent from Asterisk to the script
# at startup. The input looks like:
#        agi_request: example1.py
#        agi_channel: SIP/000FF2266EE4-00000009
#

#
# After this bit of code you will be able to retrieve this information as:
#      asterisk_env[‘agi_request’]
#      asterisk_env[‘agi_channel’]
#
asterisk_env = {}
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
#
# Set up a fake “database” of accounts. In a more realistic AGI script
# that is doing account data lookup, you would connect out to an external
# database or some other API to get this information. For the purpose of
# this example, we’ll skip that and do the data lookup from this local
# example set.
#
ACCOUNTS = {
‘12345678’: {‘balance’: ’50’},
‘11223344’: {‘balance’: ’10’},
‘87654321’: {‘balance’: ‘100’},
}
#
# Answer the call using the ANSWER AGI command.
#
# See: *CLI> agi show commands topic ANSWER
#
response = agi_command(‘ANSWER’)

#
# Prompt the caller to enter an account number using the GET DATA command.
#      – Play a prompt called please-enter-account-number
#      – Set a timeout of 30 seconds
#     – Read a maximum of 8 digits (our account numbers are 8 digits long)
#

# See: *CLI> agi show commands topic GET DATA
#
response = agi_command(‘GET DATA please-enter-account-number 30000 8’)

#
# If a timeout occurred, just exit back to the dialplan.
#
# The response will look like:
#
200 result=<digits> (timeout)
#
if ‘timeout’ in response:
sys.exit(0)

#
# The response will look like:
#
200 result=<digits>
# digits will be -1 if an error occurred.
#
# Split the response on the ‘=’, a maximum of 1 time. The result is a two
# element array. The second element in the array (index 1) is the part that
# came after the ‘=’ and is the account number we want.
#
account = response.split(‘=’, 1)[1]

#
# If an error occurred, just hang up.
#
# See: *CLI> agi show commands topic HANGUP
#
if account == ‘-1’:
response = agi_command(‘HANGUP’)
sys.exit(0)

#
# If the account is invalid, tell the caller and then exit to the dialplan.
#
# See: *CLI> agi show commands topic STREAM FILE
#
if account not in ACCOUNTS:
response = agi_command(‘STREAM FILE invalid-account “”‘)
sys.exit(0)

balance = ACCOUNTS[account][‘balance’]

#
# Tell the caller the balance and then exit back to the dialplan.
#
# See: *CLI> agi show commands topic SAY NUMBER
#
response = agi_command(‘STREAM FILE your-balance-is “”‘)
response = agi_command(‘SAY NUMBER %s “”‘ % (balance))
sys.exit(0)

Development Frameworks

There have been a number of efforts to create frameworks or libraries that make AGI programming easier. Table -3 lists some of them. If you do not see a library listed here for your preferred programming language, do a quick search for it and you’re likely to find one, as others do exist.

Table 21-3. AGI development frameworks

1t8

For any query or issue, feel free to discuss on http://discuss.eduguru.in
%d bloggers like this: