Call Us Today! 877.742.2583




Page tree
Skip to end of metadata
Go to start of metadata

 Table of Contents (click to expand)


0. Status

 Historical note (click to expand)

This note has been created by John Boteler on 2014.04.10, and haven't been changed since. Although there was no major update in the source in at least 7 years, so this may still be accurate.

Maturing, I'll try to provide backwards compatibility for 2 FreeSWITCH releases if I have to make an API change.

TODO

  • Ability to have multiple event handler pids per node, each with their own event subscriptions?
  • Figure out how to handle premature exits of spawned outbound processes
  • Allow log/event handler and handle call processes to be registered processes or pids
  • Check pids obtained via 'get_pid' to determine the node they're on in case they're not the node we requested a pid from (load balancing)
  • Investigate supporting starting a gen_server/gen_fsm using proc_lib:spawn/4 and enter_loop
  • Make datastructures more proplist friendly


1. About

mod_erlang_event is a derivative of mod_event_socket that sends events and receives commands in Erlang's binary term format. This allows FreeSWITCH™ to behave like just another Erlang node (albeit a hidden one). It uses the ei library to handle encoding/decoding the Erlang binary format, and the ei_connect functions to handle the network connections.

About Erlang

Erlang is a general-purpose, concurrent, functional programming language, and a garbage-collected runtime system. It supports hot swapping, so that code can be changed without stopping a system.

This module, just as mod_event_socket, operates in two modes, inbound and outbound.


2. Installation

2.1. Vanilla FreeSWITCH installation

If FreeSWITCH has been installed from pre-built binaries (see Installation), all documented modules come pre-compiled with it, along with their sample configuration files in the <conf_dir>/autoload_configs/ directory.

2.2. Compiling from source

You need Erlang (and its development headers) installed to compile this module. You must have Erlang development files installed before running the FreeSWITCH ./configure (or rerun ./configure after having Erlang installed). The FreeSWITCH™ configure script now checks for the Erlang requirements, and configures the Makefile appropriately.

To install Erlang, please go to https://www.erlang.org/downloads. All source files can be downloaded there, and each release has instructions for popular platforms and package managers.

 Historical notes (click to expand)

Contents below may be out of date

The sections below are probably outdated. At least, the "Windows" section talks about Erlang R13, which came out in 2009.

Windows

As of SVN r13766, the module is known to work on windows (but it's not terribly well tested). Unfortunately the ei.lib that ships with windows releases of erlang isn't suitable for being used inside a DLL due to issues with thread local storage. The work around for this is to build it yourself following the README.win32 instructions included with the erlang source distribution and after you've run configure edit the eidefs.mk and remove -DUSE_DECLSPEC_THREAD from the THR_DEFS variable. Then you can just run `make release` in the libs/erl_interface directory and then edit mod_erlang_event's project options to point at the erl_interface/obj/win32/ei.lib for its linker flags. You'll probably also have to fix the include path in the compiler flags, too.

To add the erlang module to the freeswitch solution, right click on the solution and choose Add->Existing Project and browse to its .vcproj file. Once it's added edit the solution's properties, go to dependencies and for the module, make it depend on the freeswitch core lib.

ei binaries for compiling the module against R13B01 can be found here. The release libs are in obj/win32 and the debug ones are in obj.debug/win32.

OSX

If after compiling the module, loading it in FreeSWITCH gives you an error like

Error Loading module /usr/local/freeswitch/mod/mod_erlang_event.so
**dlopen(/usr/local/freeswitch/mod/mod_erlang_event.so, 6): Symbol not found: ___erl_errno_place

make sure you've built both Erlang AND FreeSWITCH with the same architecture. Erlang defaults to building 32 bit (even on a 64 bit kernel for some reason) and FreeSWITCH seems to default to 64 bit. The easiest solution is to rebuild erlang with the --enable-darwin-64bit argument to configure. Note, however, that 'make clean' doesn't work right for Erlang and its best to have a fresh source tree if recompiling to a new architecture.

Configuration

Step 1. Load mod_erlang_event in FreeSWITCH

First, make sure that <conf_dir>/autoload_configs/modules.conf.xml has the line <load module="mod_erlang_event"/> present, and is not commented out:

<conf_dir>/autoload_configs/modules.conf.xml
<configuration name="modules.conf" description="Modules">
  <modules>
    <!-- ... other modules ... -->
    <load module="mod_erlang_event"/>
    <!-- ... other modules ... -->
  </modules>
</configuration>

Step 2. Configure


mod_erlang_event's configuration file (<conf_dir>/autoload_configs/erlang_event.conf.xml) is very similar to the one for mod_event_socket, but some parameters are different. See Configuration Parameters below.


 Default configuration for vanilla installations (click to expand)
<conf_dir>/autoload_configs/erlang_event.conf.xml
<configuration name="erlang_event.conf" description="Erlang Socket Client">
  <settings>
    <param name="listen-ip" value="0.0.0.0"/>
    <param name="listen-port" value="8031"/>
    <!-- Specify the first part of the node name
         (the host part after the @ will be autodetected)
         OR pass a complete nodename to avoid autodetection
         eg. freeswitch@example or freeswitch@example.com.
         If you pass a complete node name, the 'shortname' parameter has no effect. -->
    <param name="nodename" value="freeswitch"/>
    <!-- Specify this OR 'cookie-file' or $HOME/.erlang.cookie will be read -->
    <param name="cookie" value="ClueCon"/>
    <!-- Read a cookie from an arbitary erlang cookie file instead -->
    <!--<param name="cookie-file" value="/$${temp_dir}/erlang.cookie"/>-->
    <param name="shortname" value="true"/>
    <!-- in additon to cookie, optionally restrict by ACL -->
    <!--<param name="apply-inbound-acl" value="lan"/>-->
    <!-- alternative is "binary" -->
    <!--<param name="encoding" value="string"/>--> 
    <!-- provide compatability with previous OTP release (use with care) -->
    <!--<param name="compat-rel" value="12"/> -->
  </settings>
</configuration>

Step 3. Restart FreeSWITCH

Use sudo service freeswitch restart where systemd  is used (reloadxml won't be enough).

Configuration parameters

The configuration parameters from mod_erlang_event.c:1232:

<param name="listen-ip" value="0.0.0.0"/>

Specify the IP address of the server where the Erlang application is located.

<param name="listen-port" value="8031"/>

By default, mod_erlang_event listens on a different port (8031) than mod_event_socket (8021).

<param name="cookie" value="ClueCon"/>

Instead of a password in mod_event_socketmod_erlang_event uses a cookie.

See Erlang Reference ManualDistributed Erlang chapter,  13.7 Security section for more on cookies.

<param name="cookie-file" value="?"/>

Help Needed - previously undocumented

I assume that value is a path, but this theory needs testing.

<param name="nodename" value="freeswitch"/>

<param name="shortname" value="true"/>

From the Erlang Reference ManualDistributed Erlang chapter,  13.2 Nodes section (with minor edits):

A node is an executing Erlang runtime system that has been given a name, using the command-line flag -name (long names) or -sname (short names).

The format of the node name is an atom name@host .

  • name  is the name given by the user.
  • host is the full host name if long names are used (i.e., FQDN), or the first part of the host name if short names are used (i.e., FQDN cropped to the first period).

node() returns the name of the node.

Example:

% erl -name dilbert
(dilbert@uab.ericsson.se)1> node().
'dilbert@uab.ericsson.se'

% erl -sname dilbert
(dilbert@uab)1> node().
dilbert@uab

nodename  is the name  of the remote Erlang node across the network that would handle the FreeSWITCH events. If the shortname parameter is turned on (i.e., set to "true"), the local Erlang node will be named using the "short name" convention, described above.

So, for a module listening on the IP that resolves to "example.com" and the nodename set to "freeswitch",

  • with shortname enabled, the full nodename is 'freeswitch@example'
  • with shortname disabled, it will  be 'freeswitch@example.com' instead.

How to set a custom name?

To force the nodename, specify the whole name (e.g., 'freeswitch@example.com') in the 'nodename' tag; mod_erlang_event will not try to guess what to call the node.

Erlang nodes with a short name can only talk to other nodes with a short name, and the same thing applies for nodes with long names.

<param name="encoding" value="string"/>

The encoding  parameter indicates if you would prefer events to be encoded as Erlang binaries or as Erlang strings. Binaries tend to be faster and consume less memory, but strings can be easier to work with.

<param name="compat-rel" value="12"/>

From the vanilla erlang_event.conf.xml: "provide compatibility with previous OTP release (use with care)".

<param name="apply-inbound-acl" value="lan"/>

Just as with mod_event_socket, provide additional restrictions via ACLs besides the cookie .

Undocumented parameters

The parameters below haven't been documented, but one can guess their functions. See their implementation in mod_erlang_event.c:1232.

  • max-event-bulk
  • max-log-bulk
  • stop-on-bind-error

2. Modes of operation

See mod_event_socket for a more detailed description.

2.1 Inbound mode

The external Erlang application (running locally or remotely) controls the FreeSWITCH instance by sending messages to it (e.g., to check status, originate calls, register to receive events, execute applications).

2.1.2 Example Inbound Connection

Here's a brief example of using the Erlang shell (erl) to communicate with FreeSWITCH™ using mod_erlang_event.

We assume FreeSWITCH

  • is running locally
  • with cookie set to 'ClueCon', 
  • nodename set to 'freeswitch' and
  • shortname enabled.

The host name is 'example.com', so the short node name will be freeswitch@example.

TODO: What about remote Erlang nodes?

 Click to expand ...

Update this section with info on how to achieve the same with remote nodes. My initial assumption was connecting remote nodes will only work with nodes with long names, but this seems to be wrong.

  1. Read up on it
  2. Add an example

Some resources:

2.1.2.1 Start a local Erlang node
$ erl -sname test -setcookie ClueCon
Erlang (BEAM) emulator version 5.6.4 [source] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.6.4  (abort with ^G)
(test@example)1>{foo, freeswitch@example} ! {api, status, ""}.
{api,status,[]}

The example above shows how to start a node with a short name (which is also reflected in the erl  shell's prompt), but you can always start one with a long name using the -name switch instead.

Don't forget to configure mod_erlang_event accordingly! Erlang node with short names cannot communicate with ones set up using long names, and vice versa. See Configuration and Configuration Parameters sections above.

The -setcookie value is the same as the one used for the cookie parameter in <conf_dir>/autoload_configs/erlang_event.conf.xml.

2.1.2.2 FreeSWITCH API commands 

We've just sent the status FreeSWITCH API command to FreeSWITCH™. Note how the connection was negotiated automagically. FreeSWITCH™ will send the reply back to us asynchronously (all Erlang messages are asynchronous), so we have to explicitly receive it:

(test@example)2> receive X -> X after 1000 -> timeout end.
{ok,"Content-Type: text/html\n\nUP 0 years, 0 days, 0 hours, 0 minutes, 35 seconds, 692 milliseconds, 193 microseconds\n0 session(s) since startup\n0 session(s) 0/30\n"}

We tried to receive anything in the current process' mailbox with a 1000 millisecond timeout that will return the atom timeout as the result of the expression instead. However, there was indeed something waiting for us in the mailbox: the result of the API command we just made.

Now, lets send an invalid command, and listen for the reply:

(test@example)3> {foo, freeswitch@example} ! {api, wtf, ""}, receive Y -> Y after 1000 -> timeout end.
{error,"wtf: Command not found!\n"}

So, as we can see, the command wtf isn't valid. Note that in both cases, a 2 element tuple is returned. Depending on the result of the command, the first element is either the atom ok  or error .

Erlang variables are immutable

Note that we had to use a different variable name (i.e., Y ) this time to receive the results. If we used X  again, Erlang would search the process' mailbox for an event that matched what X was bound.

You could also do f(X). to forget the value of X in the shell. See the erl  manual, or type help(). in erl . (Note: The dots at the end of the commands are important.)

2.1.2.3 Issue FreeSWITCH API commands in the background

We'll now issue the status FreeSWITCH API command in the background (using bgapi) as a final example:

(test@example)4> {foo, freeswitch@example} ! {bgapi, status, ""}, receive Z -> Z after 1000 -> timeout end.
{ok,"191d2b07-58ac-dd11-829b-000f1f68e553"}

Note that all we got was the Job-UUID of the background command.

There are two ways to get the results:

  1. Use receive block one more time to read the direct reply from the process' mailbox (just as after we fired the status command the first time):

    (test@example)5> receive A -> A after 1000 -> timeout end.
    {bgok,"191d2b07-58ac-dd11-829b-000f1f68e553",
         "Content-Type: text/html\n\nUP 0 years, 0 days, 1 hour, 11 minutes, 8 seconds, 654 milliseconds, 138
            microseconds\n0 session(s) since startup\n0 session(s) 0/30\n"}

    Note that the command's final result is tagged with bgok /bgerror.

  2. In addition to a directed reply, a normal BACKGROUND_JOB event (see Event List) is also fired whenever a FreeSWITCH API command is executed in the background, which you could alternately choose to receive.

api vs bgapi

 FreeSWITCH API command called with bgapi 

  • will be executed in its own thread,
  • is non-blocking,
  • will only return a Job-UUID for future reference.

Once the command is finished executing, FreeSWITCH will fire an BACKGROUND_JOB event with the result, and the Job-UUID can be used to look up the result if multiple BACKGROUND_JOB events have been received. (Of course, one needs to subscribe to BACKGROUND_JOB events first.)

See Event Socket Library's "bgapi" section to see how to set custom Job-UUID .

2.2 Outbound mode

A dialplan extension can open a connection to an external application (running locally or on a remote host), and then forward all system events related to incoming calls to this extension. That is, if scenario can be handled in an extension, then it can be processed by, and the workload offloaded to an external application.

When an incoming call traverses the dialplan, and matches an extension that calls an outbound socket, the call gets parked first. System events relating to the call are then sent from FreeSWITCH to specified Erlang node.

2.2.1 Strategies to handle calls with Erlang processes

2.2.1.1 Handle all incoming calls in one registered Erlang process
<conf_dir>/dialplan/default/ext_123456_erlang.xml
<include>
  <extension name="to_erlang">
    <condition field="destination_number" expression="^123456$"> 
      <action application="erlang" data="myhandler mynode@myserver"/>
    </condition>
  </extension>
</include>

The above extension will send calls for destination number 123456 to a registered process called myhandler on the (possibly remote) Erlang node mynode@myserver.

The myhandler process will handle all incoming calls to this extension sequentially.

To test it,

2.2.1.2 Spawn a process dynamically for each incoming call

Every incoming call will be handled in a newly spawned Erlang process, and the instructions handling the calls will be the same for all of them.

2.2.1.3 Distribute calls among Erlang processes

For example, process calls with different area codes in their caller ID (see Caller-ANI in Channel Variables) in Erlang processes that handle the specific area code. 

This is the most general of the three strategies. It could be used to

  • recreate the two preceding sections (2.2.1.1 and 2.2.1.2)
  • utilize already running (and maybe registered) worker processes (i.e., juggle their PIDs around based on the task)
  • only start a specific worker when it is necessary, and then reuse its PID (if it isn't a one-off process that is)
  • ?

Example Outbound Connection

The gist is that an XML Dialplan extension will invoke the remote Erlang node to handle incoming traffic. Depending on the conditions set up with the extension, this traffic could be an incoming call to a range of destination numbers for example.

Step 1. Implement the Erlang code 

Running an Erlang module as a distributed Erlang node, locally or remotely, will handle all traffic that gets directed from an extension in the FreeSWITCH dialplan.

The below module demonstrates mod_erlang_event's Erlang API, and the parts will be explained in later steps. Save it as myhandler.erl for example.

TODO: cut this up into 3 pieces and include the relevant piece in each of the appropriate dialplan examples below. In the end show the full example, and dialplan. Or is there a better way?

myhandler.erl
-module(myhandler).

-export([start/0,run/0,launch/1]).

start()->
  %% Start our handler process, and
  Pid = spawn(?MODULE,run,[]),
  %% Register it with the same name as the module (i.e., myhandler)
  register(?MODULE,Pid).

run()->
  %% Wait for messages from FreeSWITCH.
  receive
    %% A  new  call  is  starting, so  handle  it  in  this
    %% registered  process (in  this  case,  print out  the
    %% calls UUID).
    {call, Data}->
      %% `_Rest` is  a list of  all the channel  variables in
      %% the form [{"<name>","<value"}].
      %% (TODO: this should be documented at the "Erlang API" section below, with `call`, `call_event`, `event`, `get_pid`, and so on.)
      {event, [UUID | _Rest]} = Data,
      error_logger:info_msg("myhandler ~p: new call received, UUID is ~p~n",[self(), UUID]),
      run();
    %% Handling call events for already received calls.
    {call_event, Data} ->
      {event, [UUID | Rest]} = Data,
      %% Find and print the name of the received event.
      Name = proplists:get_value("Event-Name", Rest),
      error_logger:info_msg("myhandler ~p: UUID ~p, event ~p~n",[self(), UUID,Name]),
      run();
    %% FreeSWITCH request to handle the call in a new process. See Erlang API and (probably) Step 3 below.
    {get_pid, UUID, Ref, Pid} ->
      NewPid = spawn(?MODULE, run, []),
      error_logger:info_msg("myhandler ~p: request to spawn new handler process, returning ~p~n", [self(), NewPid]),
      Pid ! {Ref, NewPid},
      run()
  end.

%% Sample function to demonstrate RPC
launch(Ref) ->
  NewPid = spawn(?MODULE, run, []),
  error_logger:info_msg("myhandler ~p: launch request, returning ~p~n", [self(), NewPid]),
  {Ref, NewPid}.

Now compile it using erlc :

$ erlc myhandler.erl

Step 2. Start a distributed Erlang node and start handler process

The example below a node with a short name (which is also reflected in the erl  shell's prompt), but you can always start one with a long name using the -name switch instead.

Don't forget to configure mod_erlang_event accordingly! Erlang node with short names cannot communicate with ones set up using long names, and vice versa. See Configuration and Configuration Parameters sections above.

$ erl -sname mynode -setcookie ClueCon
Erlang (BEAM) emulator version 5.5.5 [source] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.5.5  (abort with ^G)
(mynode@myserver)1>

start up the myhandler  process in the mynode@myserver Erlang node:


Eshell V5.5.5  (abort with ^G)
(mynode@myserver)1> myhandler:start().
true


The -setcookie value is the same as the one used for the cookie parameter in <conf_dir>/autoload_configs/erlang_event.conf.xml.

Step 3. Add entry in dialplan to call the remote Erlang node

Use an already running registered process

Add an entry in your dialplan to redirect an inbound call to the Erlang application, by creating a new XML file for example:

<conf_dir>/dialplan/default/123456_erlang.xml
<include>
  <extension name="to_erlang">
    <condition field="destination_number" expression="^123456$"> 
      <action application="erlang" data="myhandler mynode@myserver"/>
    </condition>
  </extension>
</include>

The example above refers to a node that is set up using a short name, but one can change this in the  <conf_dir>/autoload_configs/erlang_event.conf.xml configuration file (see Configuration and Configuration Parameters sections above).

Don't forget to start the Erlang node accordingly! Erlang node with short names cannot communicate with ones set up using long names, and vice versa.

This will send calls for destination number 123456 to a registered process called myhandler on the (possibly remote) Erlang node mynode@myserver.

To test it,


Now dial extension 123456, and see what happens at the Erlang console:

 (mynode@myserver)2>
=INFO REPORT==== 23-Jan-2009::11:59:38 ===
myhandler: new call received, UUID is "4f77b818-e945-11dd-a442-9fe384e7e5a2"

=INFO REPORT==== 23-Jan-2009::11:59:39 ===
myhandler: UUID "4f77b818-e945-11dd-a442-9fe384e7e5a2", event "CHANNEL_PROGRESS"

=INFO REPORT==== 23-Jan-2009::11:59:39 ===
myhandler: UUID "4f77b818-e945-11dd-a442-9fe384e7e5a2", event "CHANNEL_PROGRESS_MEDIA"

=INFO REPORT==== 23-Jan-2009::11:59:39 ===
myhandler: UUID "4f77b818-e945-11dd-a442-9fe384e7e5a2", event "CHANNEL_PARK"

We've received all the events up to CHANNEL_PARK (see Event List for more). Now hang up the call, since we didn't add any code to answer it or do anything more interesting:

=INFO REPORT==== 23-Jan-2009::11:59:43 ===
myhandler: UUID "4f77b818-e945-11dd-a442-9fe384e7e5a2", event "CHANNEL_HANGUP"

You can answer the call, execute applications, hang up, etc. just as you would in an XML Dialplan. You can also send commands to FreeSWITCH, just as with inbound mode.

Spawn a dynamic Erlang process to handle the call

If you wanted to send events to a dynamic process instead of a registered one, you can instead do:

<conf_dir>/dialplan/default/123456_erlang.xml
<include>
  <!-- ... --> 
    <action application="erlang" data="myhandler:launch mynode@myserver"/>
  <!-- ... -->
</include>

This will make a remote procedure call (RPC) to myhandler:launch with a single argument, a unique reference. The function is expected to return a tuple of the form {Ref, NewPid} where

  • NewPid should be a spawned process, a newly launched gen_server, etc.
  • Ref is the original reference passed in.
<conf_dir>/dialplan/default/123456_erlang.xml
<include>
  <!-- ... --> 
    <action application="erlang" data="myhandler:! mynode@server"/>
  <!-- ... -->
</include>

If the string after the : is a '!', then mod_erlang_event knows you want to send a new process request to the registered process 'myhandler' which will return a pid that all events for that call will be sent to. The message the registered process receives is of the form:

{get_pid, UUID, Ref, Pid}

Where Ref is a unique reference used to identify this request, UUID is the call's UUID and Pid is the process to send the response to. The expected response is of the form:

{Ref, NewPid}

Where Ref is the original reference passed with get_pid and NewPid is the pid you'd like FreeSWITCH™ to send the events to.14877


The old new_pid message, which didn't include the call's UUID is deprecated in favor of get_pid as of SVN revision 14877 (09/15/09)


The module also supports true spawn/4 behaviour, but it turned out not to be so useful, so the rpc:call functionality above replaced it.

The myhandler example above supports all 3 outbound methods.

Erlang API


API

The api is very similar to the event socket, just expressed in erlang terms. You can send any of these terms by using the ! operator as above.

api

Send an API command. Examples:

{api, strftime, "%Y"}
{api, status, ""}
{api, originate, "sofia/mydomain.com/ext@yourvsp.com 1000"}

Note that the 3rd element of the tuple is currently mandatory. I may make it optional.

The result of the API call is sent as a message to the process that made the api call. The format of the reply is a tuple of the form {ok|error, "some reply"} as seen above.

bgapi

Same as the api command, but is non blocking. The sending process gets 2 messages, the message indicating the event was accepted, and then sometime later the actual result of the api command. See the example above.

filter

Specify event types to listen for. Note, this is not a filter out but rather a "filter in"; that is, when a filter is applied only the filtered values are received. Multiple filters on a socket connection are allowed.

Usage:

  {filter, '[add|delete] <EventHeader1> <ValueToFilter>'[, '[add|delete] <EventHeader2> <ValueToFilter>']}

Where ValueToFilter has the following syntax:

  [+|-][character_expression]
or
  /regex_expression/

If the symbol "+" is used then events containing header will be sent. If the symbol "-" is used then events will be sent excluding events containing the header.

If the expression is "+value" then events with header = value will be sent. If the expression is "-value" then events will be sent excluding events containing header with that value.

If symbols "+" or "-" have been omitted, then symbol "+" is assumed.

Example:

The following example will subscribe to all events and then create two filters, one to listen for HEARTBEATS and one to listen for CHANNEL_EXECUTE events.

[user@localhost ~]# erl  -sname foo@localhost -setcookie ClueCon
Erlang/OTP 17 [erts-6.2.1] [source] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V6.2.1  (abort with ^G)
(foo@localhost)1> {foo, 'freeswitch@localhost'} ! register_event_handler.
register_event_handler
(foo@localhost)2> flush().
Shell got ok
ok
(foo@localhost)3> {foo, 'freeswitch@localhost'} ! {filter, 'CC-Action agent-offering', 'CC-Queue support@default'}.
{filter,'CC-Action agent-offering',
        'CC-Queue support@default'}
(foo@localhost)4> flush().
Shell got {filter_command_processing_log,
              [{"added","CC-Action","agent-offering"},
               {"added","CC-Queue","support@localhost"}]}
ok
(foo@localhost)5> {foo, 'freeswitch@localhost'} ! {event, 'CUSTOM', 'callcenter::info'}.
{event,'CUSTOM','callcenter::info'}
(foo@localhost)6> flush().
Shell got ok
ok
(foo@localhost)7>

register_event_handler

Send the atom register_event_handler to register the process that sent this as the process to receive all subscribed events. You need to use the events command to indicate what events to receive, none are subscribed by default. Events are received in the form:

{event, [(UniqueID|'undefined'),
         {EventHeaderKey, EventHeaderValue},
         {EventHeaderKey2, EventHeaderValue2},
         ...]} 

This being, a tuple with the first element being the atom event followed by a variable length list of {Key, Value} tuples, the first value of which is the call's unique-id, or if the event doesn't relate to a call, the atom 'undefined'. This first element of the list is to make it easy to determine which call, if any, an event relates to without iterating through the entire list.

If you send this command again, the new sending process becomes the one the events are sent to.

event

Subscribe to an event. Examples:

{foo, freeswitch@example} ! {event, 'ALL'}
{event, 'CUSTOM', 'conference::maintenance'}
{event, 'CHANNEL_CREATE', 'CHANNEL_DESTROY', 'CUSTOM', 'conference::maintenance', 'sofia::register', 'sofia::expire'} 
Note that atoms beginning with uppercase letters or containing colons need to be quoted in single quotes.

nixevent

Same syntax as event. Does the inverse.

noevents

Just send the atom noevents to disable all events.

register_log_handler

Send the atom register_log_handler to make the current process the one to send log messages to. Logs are received in the format:




{log, [{level, LogLevel},
       {text_channel, TextChannel},
       {file, FileName},
       {func, FunctionName},
       {line, LineNumber},
       {data, "Log message"}]}




Logs at DEBUG level by default.

Sending this command again changes the process log messages are sent to to the sending process.

set_log_level

Change the log level. Valid levels are defined switch_types.h. Example:

{set_log_level, info}
{set_log_level, error}

nolog

Send nolog to disable logging.

exit

Send exit to close the connection.

sendevent


{sendevent, 'NOTIFY', [{"profile", "internal"}, {"event-string","check-sync;reboot=false"}, {"user", "false"},
{"host", "192.168.10.4"}, {"content-type", "application/simple-message-summary"}]} 

sendmsg

{sendmsg, "d9189508-7caf-dd11-829b-000f1f68e553", [{"call-command", "hangup"}, {"hangup-cause", "16"}]}

UUID can be a binary or a string.

getpid

Send getpid to receive {ok,Pid} where Pid is the fake erlang process id on the FreeSWITCH™ side. This is helpful if you want to link to the process so that, for example, FreeSWITCH™ can notice that your log handler process exited.

handlecall

{handlecall, "129d1446-0063-122c-15aa-001a923f6a0f", mycallhandler}
{handlecall, "129d1446-0063-122c-15aa-001a923f6a0f"} 

Send handlecall to attach an outbound call handler to the specified UUID where mycallhandler is a registered process name. FreeSWITCH™ will then send events related to that call to the registered process on the node that sent the handlecall message. The event messages sent are the same as using outbound mode. Use this for example if you have originated a call from FreeSWITCH™ using inbound mode but want to handle it specifically. UUID can be a binary or a string. If you omit the registered process name and just send {handlecall, UUID} the call's events will be sent to the process that send the message.

XML search bindings

This module also supports mod_xml_curl style bindings to allow FreeSWITCH™ to fetch configuration/directory/dialplan/etc from Erlang. Unlike mod_xml_curl, and the other modules with this functionality, however, the bindings are dynamic, not statically configured. To register the current process send a {bind, <BindType>} message to mod_erlang_event, where BindType is an atom of one of the binding types supported (see the mod_xml_curl documentation for these). After you do this, you will receive messages of the type:

{fetch, <Section>, <Tag>, <Key>, <Value>, <FetchID>, <Params>} 
where Section is an atom describing the binding type, FetchID is a UUID associated with the fetch request and Params is a list of key/value tuples with the parameters for this request.

To tell the switch to take some action, send back a reply of the format:

{fetch_reply, <FetchID>, <XML>} 
where FetchID is the ID you received in the request and XMLString is the XML reply you want to send. FetchID and XML can be binaries or strings.

The binding is automatically removed when the process or the entire node exits or disconnects. Multiple bindings for one section type are supported as of SVN r16697, the first one to respond wins.

Console Commands

There are only 2 right now:

erlang listeners - list all nodes connected and how many outbound sessions each has
erlang sessions <nodename> - lists all the outbound sessions for the specified node 

Feel free to suggest any others that might be useful.

Debugging

If you wish to see all the erlang terms sent and received from the module, add #define EI_DEBUG to src/include/switch_am_config.h and do a make clean; make; make install in the mod_erlang_event directory. Now every erlang message will be printed at DEBUG level to the logfile and to the console if you enable debug messages at the console.

freeswitch.erl

In the mod_erlang_event source directory, there's an erlang file called freeswitch.erl. It's a a module to ease dealing with the above API. The module is fairly well documented and exposes most of the API documented here. It will do all the low level send/recv stuff for you, so you can do stuff like:

 (test@example)3> freeswitch:api(freeswitch@example,status).

And the return value of that function call will be the result of the api command. The module also makes it easy to do bgapi commands effectively, as well as set up XML search bindings, event listeners and log handlers.




3 Comments

  1. Probably it is worth to add a reference to Erlang library here: https://github.com/jamhed/fswitch