Asterisk - The Open Source Telephony Project
18.5.0
|
This section of the documentation includes an overview of the Asterisk architecture from a developer's point of view. For detailed API discussion, see the documentation associated with public API header files. This documentation assumes some knowledge of what Asterisk is and how to use it.
The intent behind this documentation is to start looking at Asterisk from a high level and progressively dig deeper into the details. It begins with talking about the different types of components that make up Asterisk and eventually will go through interactions between these components in different use cases.
Throughout this documentation, many links are also provided as references to more detailed information on related APIs, as well as the related source code to what is being discussed.
Feedback and contributions to this documentation are very welcome. Please send your comments to the asterisk-dev mailing list on http://lists.digium.com/.
Thank you, and enjoy Asterisk!
Asterisk is a highly modularized application. There is a core application that is built from the source in the main/
directory. However, it is not very useful by itself.
There are many modules that are loaded at runtime. Asterisk modules have names that give an indication as to what functionality they provide, but the name is not special in any technical sense. When Asterisk loads a module, the module registers the functionality that it provides with the Asterisk core.
There are many types of interfaces that modules can implement and register their implementations of with the Asterisk core. Any module is allowed to register as many of these different interfaces as they would like. Generally, related functionality is grouped into a single module.
In this section, the types of interfaces are discussed. Later, there will be discussions about how different components interact in various scenarios.
An implementation of the codec interpreter interface provides the ability to convert between two codecs. Asterisk currently only has the ability to translate between audio codecs.
These modules have no knowledge about phone calls or anything else about why they are being asked to convert audio. They just get audio samples as input in their specified input format, and are expected to provide audio in the specified output format.
It is possible to have multiple paths to get from codec A to codec B once many codec implementations are registered. After modules have been loaded, Asterisk builds a translation table with measurements of the performance of each codec translator so that it can always find the best path to get from A to B.
Codec modules typically live in the codecs/
directory in the source tree.
For a list of codec interpreter implementations, see Module: Codecs.
For additional information on the codec interpreter API, see the interface definition in include/asterisk/translate.h
.
For core implementation details related to the codec interpreter API, see main/translate.c
.
An implementation of the file format handler interface provides Asterisk the ability to read and optionally write files. File format handlers may provide access to audio, video, or image files.
The interface for a file format handler is rather primitive. A module simply tells the Asterisk core that it can handle files with a given extension, for example, ".wav". It also says that after reading the file, it will provide audio in the form of codec X. If a file format handler provides the ability to write out files, it also must specify what codec the audio should be in before provided to the file format handler.
File format modules typically live in the formats/
directory in the source tree.
For a list of file format handler implementations, see Module: Media File Formats.
For additional information on the file format handler API, see the interface definition in include/asterisk/file.h
.
For core implementation details related to the file format API, see main/file.c
.
There are some C APIs in Asterisk that are optional. Core APIs are built into the main application and are always available. Optional C APIs are provided by a module and are only available for use when the module is loaded. Some of these API providers also contain their own interfaces that other modules can implement and register.
Modules that provide a C API typically live in the res/
directory in the source tree.
Some examples of modules that provide C APIs (potentially among other things) are:
The Asterisk manager interface is a socket interface for monitoring and control of Asterisk. It is a core feature built in to the main application. However, modules can register actions that may be requested by clients.
Modules that register manager actions typically do so as auxiliary functionality to complement whatever main functionality it provides. For example, a module that provides call conferencing services may have a manager action that will return the list of participants in a conference.
The Asterisk CLI is a feature implemented in the main application. Modules may register additional CLI commands.
The Asterisk channel driver interface is the most complex and most important interface available. The Asterisk channel API provides the telephony protocol abstraction which allows all other Asterisk features to work independently of the telephony protocol in use.
The specific interface that channel drivers implement is the ast_channel_tech interface. A channel driver must implement functions that perform various call signaling tasks. For example, they must implement a method for initiating a call and hanging up a call. The ast_channel data structure is the abstract channel data structure. Each ast_channel instance has an associated ast_channel_tech which identifies the channel type. An ast_channel instance represents one leg of a call (a connection between Asterisk and an endpoint).
Channel drivers typically live in the channels/
directory in the source tree.
For a list of channel driver implementations, see Module: Asterisk Channel Drivers.
For additional information on the channel API, see include/asterisk/channel.h
.
For additional implementation details regarding the core ast_channel API, see main/channel.c
.
Bridging is the operation which connects two or more channels together. A simple two channel bridge is a normal A to B phone call, while a multi-party bridge would be something like a 3-way call or a full conference call.
The bridging API allows modules to register bridging technologies. An implementation of a bridging technology knows how to take two (or optionally more) channels and connect them together. Exactly how this happens is up to the implementation.
This interface is used such that the code that needs to pass audio between channels doesn't need to know how it is done. Underneath, the conferencing may be done in the kernel (via DAHDI), via software methods inside of Asterisk, or could be done in hardware in the future if someone implemented a module to do so.
At the time of this writing, the bridging API is still relatively new, so it is not used everywhere that bridging operations are performed. The ConfBridge dialplan application is a new conferencing application which has been implemented on top of this bridging API.
Bridging technology modules typically live in the bridges/
directory in the source tree.
For a list of bridge technology implementations, see bridges.
For additional information on the bridging API, see
include/asterisk/bridge.h
include/asterisk/bridge_technology.h
include/asterisk/bridge_channel.h
include/asterisk/bridge_features.h
include/asterisk/bridge_after.h
For additional implementation details regarding the core bridging API, see main/bridge.c
and main/bridge_channel.c
.
The Asterisk core implements functionality for keeping records of calls. These records are built while calls are processed and live in data structures. At the end of the call, these data structures are released. Before the records are thrown away, they are passed in to all of the registered CDR handlers. These handlers may write out the records to a file, post them to a database, etc.
CDR modules typically live in the cdr
directory in the source tree.
For a list of CDR handlers, see Module: CDR Drivers.
For additional information on the CDR API, see include/asterisk/cdr.h
.
For additional implementation details regarding CDR handling, see main/cdr.c
.
The Asterisk core includes a generic event system that allows Asterisk components to report events that can be subscribed to by other parts of the system. One of the things built on this event system is Call Event Logging (CEL).
CEL is similar to CDR in that they are both for tracking call history. While CDR records are typically have a one record to one call relationship, CEL events are many events to one call. The CEL modules look very similar to CDR modules.
CEL modules typically live in the cel/
directory in the source tree.
For a list of CEL handlers, see cel_drivers.
For additional information about the CEL API, see include/asterisk/cel.h
.
For additional implementation details for the CEL API, see main/cel.c
.
Dialplan applications implement features that interact with calls that can be executed from the Asterisk dialplan. For example, in extensions.conf
:
exten => 123,1,NoOp()
In this case, NoOp is the application. Of course, NoOp doesn't actually do anything.
These applications use a number of APIs available in Asterisk to interact with the channel. One of the most important tasks of an application is to continuously read audio from the channel, and also write audio back to the channel. The details of how this is done is usually hidden behind an API call used to play a file or wait for digits to be pressed by a caller.
In addition to interacting with the channel that originally executed the application, dialplan applications sometimes also create additional outbound channels. For example, the Dial() application creates an outbound channel and bridges it to the inbound channel. Further discussion about the functionality of applications will be discussed in detailed use cases.
Dialplan applications are typically found in the apps/
directory in the source tree.
For a list of dialplan applications, see Dial plan applications.
For details on the API used to register an application with the Asterisk core, see include/asterisk/pbx.h
.
As the name suggests, dialplan functions, like dialplan applications, are primarily used from the Asterisk dialplan. Functions are used mostly in the same way that variables are used in the dialplan. They provide a read and/or write interface, with optional arguments. While they behave similarly to variables, they storage and retrieval of a value is more complex than a simple variable with a text value.
For example, the CHANNEL()
dialplan function allows you to access data on the current channel.
exten => 123,1,NoOp(This channel has the name: ${CHANNEL(name)})
Dialplan functions are typically found in the funcs/
directory in the source tree.
For a list of dialplan function implementations, see Module: Dial plan functions.
For details on the API used to register a dialplan function with the Asterisk core, see include/asterisk/pbx.h
.
The Asterisk core provides an API for handling RTP streams. However, the actual handling of these streams is done by modules that implement the RTP engine interface. Implementations of an RTP engine typically live in the res/
directory of the source tree, and have a res_rtp_
prefix in their name.
The Asterisk core implements an API that can be used by components that need access to timing services. For example, a timer is used to send parts of an audio file at proper intervals when playing back a sound file to a caller. The API relies on timing interface implementations to provide a source for reliable timing.
Timing interface implementations are typically found in the res/
subdirectory of the source tree.
For a list of timing interface implementations, see timing_interfaces.
For additional information on the timing API, see include/asterisk/timing.h
.
For additional implementation details for the timing API, see main/timing.c
.
Asterisk is a very heavily multi threaded application. It uses the POSIX threads API to manage threads and related services such as locking. Almost all of the Asterisk code that interacts with pthreads does so by going through a set of wrappers used for debugging and code reduction.
Threads in Asterisk can be classified as one of the following types:
A channel is a fundamental concept in Asterisk. Channels are either inbound or outbound. An inbound channel is created when a call comes in to the Asterisk system. These channels are the ones that execute the Asterisk dialplan. A thread is created for every channel that executes the dialplan. These threads are referred to as a channel thread. They are sometimes also referred to as a PBX thread, since one of the primary tasks of the thread is to execute the Asterisk dialplan for an inbound call.
A channel thread starts out by only being responsible for a single Asterisk channel. However, there are cases where a second channel may also live in a channel thread. When an inbound channel executes an application such as Dial()
, an outbound channel is created and bridged to the inbound channel once it answers.
Dialplan applications always execute in the context of a channel thread. Dialplan functions almost always do, as well. However, it is possible to read and write dialplan functions from an asynchronous interface such as the Asterisk CLI or the manager interface (AMI). However, it is still always the channel thread that is the owner of the ast_channel data structure.
Network monitor threads exist in almost every major channel driver in Asterisk. They are responsible for monitoring whatever network they are connected to (whether that is an IP network, the PSTN, etc.) and monitor for incoming calls or other types of incoming requests. They handle the initial connection setup steps such as authentication and dialed number validation. Finally, once the call setup has been completed, the monitor threads will create an instance of an Asterisk channel (ast_channel), and start a channel thread to handle the call for the rest of its lifetime.
There are a number of TCP based services that use threads, as well. Some examples include SIP and the AMI. In these cases, threads are used to handle each TCP connection.
The Asterisk CLI also operates in a similar manner. However, instead of TCP, the Asterisk CLI operates using connections to a UNIX domain socket.
There are other miscellaneous threads throughout the system that perform a specific task. For example, the event API (include/asterisk/event.h) uses a thread internally (main/event.c) to handle asychronous event dispatching. The devicestate API (include/asterisk/devicestate.h) uses a thread internally (main/devicestate.c) to asynchronously process device state changes.
This section covers some other important Asterisk architecture concepts.
As previously mentioned when discussing the bridging technology interface (Bridging Technologies), bridging is the act of connecting one or more channel together so that they may pass audio between each other. However, it was also mentioned that most of the code in Asterisk that does bridging today does not use this new bridging infrastructure. So, this section discusses the legacy bridging functionality that is used by the Dial()
and Queue()
applications.
When one of these applications decides it would like to bridge two channels together, it does so by executing the ast_channel_bridge() API call. From there, there are two types of bridges that may occur.
Now that there has been discussion about the various components that make up Asterisk, this section goes through examples to demonstrate how these components work together to provide useful functionality.
This example consists of a call that comes in to Asterisk via the SIP protocol. Asterisk accepts this call, plays back a sound file to the caller, and then hangs up.
Example dialplan:
exten => 5551212,1,Answer()
exten => 5551212,n,Playback(demo-congrats)
exten => 5551212,n,Hangup()
Answer()
. This application is a built in application that is defined in main/pbx.c. The Answer()
application code simply executes the ast_answer() API call. This API call operates on an ast_channel. It handles generic ast_channel hangup processing, as well as executes the answer callback function defined in the associated ast_channel_tech for the active channel. In this case, the sip_answer() function in chan_sip.c will get executed to handle the SIP specific operations required to answer a call.Playback()
application will be executed. The code for this application is in apps/app_playback.c. The code in the application is pretty simple. It does argument handling and uses API calls to play back the file, ast_streamfile(), ast_waitstream(), and ast_stopstream(), which set up file playback, wait for the file to finish playing, and then free up resources. Some of the important operations of these API calls are described in steps here:Playback()
application has finished, the dialplan execution loop continues to the next step in the dialplan, which is Hangup()
. This operates in a very similar manner to Answer()
in that it handles channel type agnostic hangup handling, and then calls down into the SIP channel interface to handle SIP specific hangup processing. At this point, even if there were more steps in the dialplan, processing would stop since the channel has been hung up. The channel thread will exit the dialplan processing loop and destroy the ast_channel data structure.This example consists of a call that comes in to Asterisk via the SIP protocol. Asterisk then makes an outbound call via the IAX2 protocol. When the far end over IAX2 answers, the call is bridged.
Example dialplan:
exten => 5551212,n,Dial(IAX2/mypeer)
Dial()
application.Dial()
application needs to create an outbound ast_channel. It does this by first using the ast_request() API call to request a channel called IAX2/mypeer
. This API call is a part of the core channel API (include/asterisk/channel.h). It will find a channel driver of type IAX2
and then execute the request callback in the appropriate ast_channel_tech interface. In this case, it is iax2_request() in channels/chan_iax2.c. This asks the IAX2 channel driver to allocate an ast_channel of type IAX2 and initialize it. The Dial()
application will then execute the ast_call() API call for this new ast_channel. This will call into the call callback of the ast_channel_tech, iax2_call(), which requests that the IAX2 channel driver initiate the outbound call.Dial()
application will communicate this back to the inbound SIP channel. It does this by calling the ast_answer() core channel API call.Dial()
application. For example, if one side of the call hangs up, the bridge will stop.Dial()
application. The application owns the outbound channel since that is where it was created. So, the outbound IAX2 channel will be destroyed before Dial()
is complete. Destroying the channel is done by using the ast_hangup() API call. The application will return back to the dialplan processing loop. From there, the loop will see that there is nothing else to execute, so it will hangup on the inbound channel as well using the ast_hangup() function. ast_hangup() performs a number of channel type independent hangup tasks, but also executes the hangup callback of ast_channel_tech (sip_hangup()). Finally, the channel thread exits.Asterisk provides generic implementations of a number of data structures.
Astobj2 stands for the Asterisk Object model, version 2. The API is defined in include/asterisk/astobj2.h. Some internal implementation details for astobj2 can be found in main/astobj2.c. There is a version 1, and it still exists in the source tree. However, it is considered deprecated.
Astobj2 provides reference counted object handling. It also provides a container interface for astobj2 objects. The container provided is a hash table.
See the astobj2 API for more details about how to use it. Examples can be found all over the code base.
Asterisk provides a set of macros for handling linked lists. They are defined in include/asterisk/linkedlists.h.
Asterisk provides a set of macros for handling doubly linked lists, as well. They are defined in include/asterisk/dlinkedlists.h.
Asterisk provides an implementation of the max heap data structure. The API is defined in include/asterisk/heap.h. The internal implementation details can be found in main/heap.c.
Asterisk includes a number of built in debugging tools to help in diagnosing common types of problems.
Asterisk keeps track of a list of all active threads on the system. A list of threads can be viewed from the Asterisk CLI by running the command core show threads
.
Asterisk has a compile time option called DEBUG_THREADS
. When this is on, the pthread wrapper API in Asterisk keeps track of additional information related to threads and locks to aid in debugging. In addition to just keeping a list of threads, Asterisk also maintains information about every lock that is currently held by any thread on the system. It also knows when a thread is blocking while attempting to acquire a lock. All of this information is extremely useful when debugging a deadlock. This data can be acquired from the Asterisk CLI by running the core show locks
CLI command.
The definitions of these wrappers can be found in include/asterisk/lock.h
and include/asterisk/utils.h
. Most of the implementation details can be found in main/utils.c
.
Dynamic memory management in Asterisk is handled through a number of wrappers defined in include/asterisk/utils.h
. By default, all of these wrappers use the standard C library malloc(), free(), etc. functions. However, if Asterisk is compiled with the MALLOC_DEBUG option enabled, additional memory debugging is included.
The Asterisk memory debugging system provides the following features:
A number of CLI commands are provided to access data on the current set of memory allocations. Those are:
memory show summary
memory show allocations
The implementation of this memory debugging system can be found in main/astmm.c
.
Return to the Table of Contents