Asterisk is one of the oldest and most stable telecommunication products ever openly released along with Kamailio SIP Server and Freeswitch. One of the reasons which make asterisk so awesome is its power to execute the scripts written in PHP, Perl, Shell, or some other languages.
Asterisk does so with the help of its AGI interface. AGI stands for Asterisk Gateway Interface.
AGI provides an interface between the Asterisk dial plan and an external program that wants to manipulate a channel in the dial plan. In general, the interface is synchronous - actions are taken on a channel from an AGI block and do not return until the action is completed. AGI can be run on the local machine as well as remote machine.
The support for different protocols is listed here, just in case.
Basically, AGI in the asterisk console can be debugged using the command
agi set debug on
The debugging for the same can be disabled by
agi set debug off
1- First, we check whether the file we are trying to execute actually exists or not.
2- Then, we retrieve information about the script file pointed to by its pathname through the stat function.
struct stat st;
if (stat(script, &st)) { }
3- Then, we create a pipe <toast>
that sends data into Asterisk from the script using the following command -
if (pipe(<From_script_to_asterisk_FD_array>)) // sends data into asterisk
Pipe function as per the Linux man page creates a unidirectional data channel that can be used for interprocess communication. The array pipefd is used to return two file descriptors referring to the ends of the pipe. pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe. Data written to the write end of the pipe is buffered by the kernel until it is read from the read end of the pipe
4- Then, we create a pipe <fromast>
that sends data out of the script into Asterisk using the following command -
if (pipe(<From_asterisk_to_script_FD_array> )) // sends data out of asterisk
In case of failure, simply close the 2 FDs that are opened by the command above and return.
5- Then, we create a pipe <audio>
that sends data out linear PCMA audio.
6- If the audio pipe is successfully created, then we try the file access mode and the file status flags.
res = fcntl(<audio_pipe>[1], F_GETFL);
where F_GETFL
- Return the file access mode and the file status flags;
and <audio_pipe>[1]
- the write end of the pipe
7- If success is returned,
res = fcntl(<audio_pipe>[1], F_SETFL, res | O_NONBLOCK);
fcntl
is used to manipulate/operate on file descriptors. In case of failure of any step, just close the FDs opened in its previous step.
8- Once everything is set in place, we go for the execution of the script. The execution will be done by forking the process, and the child process will take the task of executing the script while the parent process will continue with its original task. To fork a process, we simply go for
pid = fork();
9- Once forked, we pass the paths to the AGI via the environmental variables.
setenv("AST_CONFIG_DIR", <PREDEFINED_PATH>, 1);
10- We duplicate the file descriptor to redirect stdin, stdout or to provide an enhanced audio channel.
dup2(fromast[0], STDIN_FILENO);
dup2(toast[1], STDOUT_FILENO);
11- We close the remaining FDs other than corresponding to that of stdin/out/error. So, it is basically closing all the FDs starting from 3 as
0 → STDIN
1 → STDOUT
2 → STD_ERROR
12- We then execute the script on the forked process.
execv(script, argv);
Where argv
consists of the arguments passed over to the script through AGI.
13- Once the process is over, we just exit the forked process and close all the remaining opened FDs.
exit(1);
Needless to say, the devil is in the details. The more you will get into the technical side, the more knowledge can be gained. However, this is enough for you to grasp how the AGI interface is implemented in the asterisk and how it works with respect to a technical side.
It’s all about small things, small implementations that make a big difference