I fixed the typo in the XML, but we're still getting a lockup. Also, my log files are indicating that the request for sofia.conf is being issued _twice_ now. I'm attaching all the source code from our freeswitch module.

I believe freeswitch.erl is just a copy of the shared source for integrating freeswitch.

freeswitch_bind.erl is the one that does the bindings for the configs; it invokes and binds freeswitch_callback.erl.

freeswitch_monitor.erl is the process binding for "attaching" the FS node to ERLang.

Andrew Thompson wrote:
Mark,

I just noticed your original version had a typo for the global_settings
closing tab. If I fix that I do indeed get a temporary hang because of
the STUN stuff, but once that times out the module loads successfully.
Have you tried the re-factored module I attached in my previous email
(you'll want to fix that typo)?

Also, please don't send HTML-only email (like your last reply) it's a
pain for me to read :)

Andrew

_______________________________________________
FreeSWITCH-dev mailing list
[email protected]
http://lists.freeswitch.org/mailman/listinfo/freeswitch-dev
UNSUBSCRIBE:http://lists.freeswitch.org/mailman/options/freeswitch-dev
http://www.freeswitch.org


%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License at
%% http://www.mozilla.org/MPL/
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
%% License for the specific language governing rights and limitations
%% under the License.
%% 
%% @author Andrew Thompson <andrew AT hijacked DOT us>
%% @copyright 2008-2009 Andrew Thompson
%% @doc A module for interfacing with FreeSWITCH using mod_erlang_event.

-module(freeswitch).

-export([send/2, api/3, api/2, bgapi/3, bgapi/4, event/2,
                nixevent/2, noevents/1, close/1,
                get_event_header/2, get_event_body/1,
                get_event_name/1, getpid/1, sendmsg/3,
                sendevent/3, handlecall/2, handlecall/3, start_fetch_handler/4,
                start_log_handler/3, start_event_handler/3]).
-define(TIMEOUT, 5000).

%% @doc Return the value for a specific header in an event or 
`{error,notfound}'.
get_event_header([], _Needle) ->
        {error, notfound};
get_event_header({event, Headers}, Needle) when is_list(Headers) ->
        get_event_header(Headers, Needle);
get_event_header([undefined | Headers], Needle) ->
        get_event_header(Headers, Needle);
get_event_header([UUID | Headers], Needle) when is_list(UUID) ->
        get_event_header(Headers, Needle);
get_event_header([{Key,Value} | Headers], Needle) ->
        case Key of
                Needle ->
                        Value;
                _ ->
                        get_event_header(Headers, Needle)
        end.

%% @doc Return the name of the event.
get_event_name(Event) ->
        get_event_header(Event, "Event-Name").

%% @doc Return the body of the event or `{error, notfound}' if no event body.
get_event_body(Event) ->
        get_event_header(Event, "body").

%% @doc Send a raw term to FreeSWITCH. Returns the reply or `timeout' on a
%% timeout.
send(Node, Term) ->
        {send, Node} ! Term,
        receive
                Response ->
                        Response
        after ?TIMEOUT ->
                timeout
        end.

%% @doc Make a blocking API call to FreeSWITCH. The result of the API call is
%% returned or `timeout' if FreeSWITCH fails to respond.
api(Node, Cmd, Args) ->
        {api, Node} ! {api, Cmd, Args},
        receive
                {ok, X} -> 
                        {ok, X};
                {error, X} ->
                        {error, X}
        after ?TIMEOUT ->
                timeout
        end.

%% @doc Same as @link{api/3} except there's no additional arguments.
api(Node, Cmd) ->
        api(Node, Cmd, "").

%% @doc Make a backgrounded API call to FreeSWITCH. The asynchronous reply is
%% sent to calling process after it is received. This function
%% returns the result of the initial bgapi call or `timeout' if FreeSWITCH fails
%% to respond.
bgapi(Node, Cmd, Args) ->
        Self = self(),
        % spawn a new process so that both responses go here instead of 
directly to
        % the calling process.
        spawn(fun() ->
                {bgapi, Node} ! {bgapi, Cmd, Args},
                receive
                        {error, Reason} ->
                                % send the error condition to the calling 
process
                                Self ! {api, {error, Reason}};
                        {ok, JobID} ->
                                % send the reply to the calling process
                                Self ! {api, ok},
                                receive % wait for the job's reply
                                        {bgok, JobID, Reply} ->
                                                % send the actual command 
output back to the calling process
                                                Self ! {bgok, Reply};
                                        {bgerror, JobID, Reply} ->
                                                Self ! {bgerror, Reply}
                                end
                after ?TIMEOUT ->
                        % send a timeout to the calling process
                        Self ! {api, timeout}
                end
        end),

        % get the initial result of the command, NOT the asynchronous response, 
and
        % return it
        receive
                {api, X} -> X
        end.

%% @doc Make a backgrounded API call to FreeSWITCH. The asynchronous reply is
%% passed as the argument to `Fun' after it is received. This function
%% returns the result of the initial bgapi call or `timeout' if FreeSWITCH fails
%% to respond.
bgapi(Node, Cmd, Args, Fun) ->
        Self = self(),
        % spawn a new process so that both responses go here instead of 
directly to
        % the calling process.
        spawn(fun() ->
                {bgapi, Node} ! {bgapi, Cmd, Args},
                receive
                        {error, Reason} ->
                                % send the error condition to the calling 
process
                                Self ! {api, {error, Reason}};
                        {ok, JobID} ->
                                % send the reply to the calling process
                                Self ! {api, ok},
                                receive % wait for the job's reply
                                        {bgok, JobID, Reply} ->
                                                % Call the function with the 
reply
                                                Fun(ok, Reply);
                                        {bgerror, JobID, Reply} ->
                                                Fun(error, Reply)
                                end
                after ?TIMEOUT ->
                        % send a timeout to the calling process
                        Self ! {api, timeout}
                end
        end),

        % get the initial result of the command, NOT the asynchronous response, 
and
        % return it
        receive
                {api, X} -> X
        end.

%% @doc Request to receive any events in the list `List'.
event(Node, Events) when is_list(Events) ->
        {event, Node} ! list_to_tuple(lists:append([event], Events)),
        receive
                ok -> ok;
                {error, Reason} -> {error, Reason}
        after ?TIMEOUT ->
                timeout
        end;
event(Node, Event) when is_atom(Event) ->
        event(Node, [Event]).

%% @doc Stop receiving any events in the list `Events' from `Node'.
nixevent(Node, Events) when is_list(Events) ->
        {nixevent, Node} ! list_to_tuple(lists:append([nixevent], Events)),
        receive
                X -> X
        after ?TIMEOUT ->
                timeout
        end;
nixevent(Node, Event) when is_atom(Event) ->
        nixevent(Node, [Event]).

%% @doc Stop receiving any events from `Node'.
noevents(Node) ->
        {noevents, Node} ! noevents,
        receive
                ok -> ok;
                {error, Reason} -> {error, Reason}
        after ?TIMEOUT ->
                timeout
        end.

%% @doc Close the connection to `Node'.
close(Node) ->
        {close, Node} ! exit,
        receive
                ok -> ok
        after ?TIMEOUT ->
                timeout
        end.

%% @doc Send an event to FreeSWITCH. `EventName' is the name of the event and
%% `Headers' is a list of `{Key, Value}' string tuples. See the mod_event_socket
%% documentation for more information.
sendevent(Node, EventName, Headers) ->
        {sendevent, Node} ! {sendevent, EventName, Headers},
        receive
                ok -> ok;
                {error, Reason} -> {error, Reason}
        after ?TIMEOUT ->
                timeout
        end.

%% @doc Send a message to the call identified by `UUID'. `Headers' is a list of
%% `{Key, Value}' string tuples.
sendmsg(Node, UUID, Headers) ->
        {sendmsg, Node} ! {sendmsg, UUID, Headers},
        receive
                ok -> ok;
                {error, Reason} -> {error, Reason}
        after ?TIMEOUT ->
                timeout
        end.


%% @doc Get the fake pid of the FreeSWITCH node at `Node'. This can be helpful
%% for linking to the process. Returns `{ok, Pid}' or `timeout'.
getpid(Node) ->
        {getpid, Node} ! getpid,
        receive
                {ok, Pid} when is_pid(Pid) -> {ok, Pid}
        after ?TIMEOUT ->
                timeout
        end.

%% @doc Request that FreeSWITCH send any events pertaining to call `UUID' to
%% `Process' where process is a registered process name.
handlecall(Node, UUID, Process) ->
        {handle_call, Node} ! {handle_call, UUID, Process},
    io:format("Freeswitch.erl received call from ~w.~n", Node),
        receive
                ok -> ok;
                {error, Reason} -> {error, Reason}
        after ?TIMEOUT ->
                timeout
        end.

%% @doc Request that FreeSWITCH send any events pertaining to call `UUID' to
%% the calling process.
handlecall(Node, UUID) ->
        {handle_call, Node} ! {handle_call, UUID},
        receive
                ok -> ok;
                {error, Reason} -> {error, Reason}
        after ?TIMEOUT ->
                timeout
        end.

%% @private
start_handler(Node, Type, Module, Function) ->
        Self = self(),
        spawn(fun() ->
                monitor_node(Node, true),
                {foo, Node} ! Type,
                receive
                        ok ->
                                Self ! {Type, {ok, self()}},
                                apply(Module, Function, [Node]);
                        {error,Reason} ->
                                Self ! {Type, {error, Reason}}
                after ?TIMEOUT ->
                                Self ! {Type, timeout}
                end
              end),
        
        receive
                {Type, X} -> X
        end.

%% @todo Notify the process if it gets replaced by a new log handler.

%% @doc Spawn `Module':`Function' as a log handler. The process will receive
%% messages of the form `{log, [{level, LogLevel}, {text_channel, TextChannel}, 
{file, FileName}, {func, FunctionName}, {line, LineNumber}, {data, 
LogMessage}]}'
%% or `{nodedown, Node}' if the FreesSWITCH node at `Node' exits.
%% 
%% The function specified by `Module':`Function' should be tail recursive and is
%% passed one argument; the name of the FreeSWITCH node.
%% 
%% Subsequent calls to this function for the same node replaces the
%% previous event handler with the newly spawned one.
%% 
%% This function returns either `{ok, Pid}' where `Pid' is the pid of the newly
%% spawned process, `{error, Reason}' or the atom `timeout' if FreeSWITCH did
%% not respond.
start_log_handler(Node, Module, Function) ->
        start_handler(Node, register_log_handler, Module, Function).

%% @todo Notify the process if it gets replaced with a new event handler.

%% @doc Spawn Module:Function as an event handler. The process will receive
%% messages of the form `{event, [UniqueID, {Key, Value}, {...}]}' where
%% `UniqueID' is either a FreeSWITCH call ID or `undefined' or
%% `{nodedown, Node}' if the FreeSWITCH node at `Node' exits. 
%% 
%% The function specified by `Module':`Function' should be tail recursive and is
%% passed one argument; the name of the FreeSWITCH node.
%% 
%% Subsequent calls to this function for the same node replaces the
%% previous event handler with the newly spawned one.
%% 
%% This function returns either `{ok, Pid}' where `Pid' is the pid of the newly
%% spawned process, `{error, Reason}' or the atom `timeout' if FreeSWITCH did
%% not respond.
start_event_handler(Node, Module, Function) ->
        start_handler(Node, register_event_handler, Module, Function).

%% @doc Spawn Module:Function as an XML config fetch handler for configs of type
%% `Section'. See the FreeSWITCH documentation for mod_xml_rpc for more
%% information on sections. The process will receive messages of the form 
%% `{fetch, Section, Tag, Key, Value, ID, Data}' or `{nodedown, Node}' if the
%% FreeSWITCH node at `Node' exits.
%% 
%% The function specified by `Module':`Function' should be tail recursive and is
%% passed one argument; the name of the FreeSWITCH node. The function should
%% send tuples back to FreeSWITCH of the form `{fetch_reply, ID, XML}' where
%%`ID' is the ID received in the request tuple and  `XML' is XML in string or
%% binary form of the form noted in the mod_xml_rpc documentation.
%%
%% Subsequent calls to this function for the same node and section will yield
%% undefined behaviour.
%% 
%% This function returns either `{ok, Pid}' where `Pid' is the pid of the newly
%% spawned process, `{error, Reason}' or the atom `timeout' if FreeSWITCH did
%% not respond.
start_fetch_handler(Node, Section, Module, Function) ->
        start_handler(Node, {bind, Section}, Module, Function).
-module(freeswitch_app).
-author('[email protected]').

-behaviour(supervisor).

-export([start/2, stop/1, init/1, go/0]).

-define(MAX_RESTART,         5).
-define(MAX_TIME,           60).

%%----------------------------------------------------------------------
%% Application behaviour callbacks
%%----------------------------------------------------------------------
start(_Type, _Args) ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, [freeswitch_monitor]).

stop(_S) ->
    ok.

go() ->
    Apps = [tcp_server, figure, storage],
    lists:foreach(fun (App) ->
                          application:stop(App)
                  end,
                  lists:reverse(Apps)),
    lists:foreach(fun (App) ->
                          application:start(App)
                  end,
                  Apps).

%%----------------------------------------------------------------------
%% Supervisor behaviour callbacks
%%----------------------------------------------------------------------
init([Module]) ->
    {ok,
        {_SupFlags = {one_for_one, ?MAX_RESTART, ?MAX_TIME},
            [
              % FreeSWITCH supervisor.
              {   Module,                                  % Id       = 
internal id
                  {Module,start_link,[]},       % StartFun = {M, F, A}
                  permanent,                               % Restart  = 
permanent | transient | temporary
                  15000,                                    % Shutdown = 
brutal_kill | int() >= 0 | infinity
                  worker,                                  % Type     = worker 
| supervisor
                  [Module]                                 % Modules  = 
[Module] | dynamic
              }
            ]
        }
    }.
-module(freeswitch_bind).

-behaviour(gen_server).

-record(st, {fsnode, pbxpid, configpid, dirpid, dialpid}).

-export([start/3, terminate/2, code_change/3, init/1,
         handle_call/3, handle_cast/2, handle_info/2]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% gen_server methods
start(Node, Section, Pid) ->
    gen_server:start(?MODULE, [Node, Section, Pid], []).

init([Node, Section, Pid]) ->
        io:format( "freeswitch_bind:init( [Node=~w, Section=~w, Pid=~w])~n", 
[Node, Section, Pid] ),
    {api, Node} ! {bind, Section},
    receive
        ok ->
                {ok, ConfigurationPid } = freeswitch:start_fetch_handler( Node, 
configuration, freeswitch_callback, fetch_handler ),
                {ok, DirectoryPid } = freeswitch:start_fetch_handler( Node, 
directory, freeswitch_callback, fetch_handler ),
                {ok, DialplanPid } = freeswitch:start_fetch_handler( Node, 
dialplan, freeswitch_callback, fetch_handler ),
            {ok, #st{fsnode=Node, pbxpid=Pid, configpid=ConfigurationPid, 
dirpid=DirectoryPid, dialpid=DialplanPid}};
        {error, Reason} ->
            {stop, {error, {freeswitch_error, Reason}}}
    after 5000 ->
            {stop, {error, freeswitch_timeout}}
    end.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%
%%      If the request isn't recognized, just log it and do nothing.
%%
handle_call(Request, _From, State) ->
    io:format("freeswitch_bind:handle_call( ~w, _From, State) unrecognized 
request~n",
                [Request]),
    {reply, {error, unrecognized_request}, State}.

handle_cast(Message, State) ->
    error_logger:error_msg("~p received unrecognized cast ~p~n",
                           [self(), Message]),
    {noreply, State}.


handle_info({fetch, Section, Tag, Key, Value, FetchID, Params}, 
#st{fsnode=Node, pbxpid=Pid}=State) ->
    {ok, Xml} = freeswitch_callback:xml_fetch( {fetch, Section, Tag, Key, 
Value, Params} ),
%%      {ok, XML} = gen_server:call(Pid, {fetch, Section, Tag, Key, Value, 
Params}),
    {api, Node} ! {fetch_reply, FetchID, Xml},
    receive
        ok ->
            {noreply, State};
        {error, Reason} ->
            {stop, {error, Reason}, State}
  end;
handle_info(Info, State) ->
        io:format( "freeswitch_bind:handle_info() Info=~w~n", [Info]),
    {noreply, State}.
%% Author: mark
%% Created: Sep 15, 2009
%% Description: TODO: Add description to freeswitch_callback
-module(freeswitch_callback).

-behaviour(gen_server).

-record(st, {fsnode, pbxpid}).

-export([start/3, terminate/2, code_change/3, init/1,
        handle_call/3, handle_cast/2, handle_info/2, fetch_handler/1,
        xml_fetch/1]).

start(Node, Section, Pid) ->
    gen_server:start(?MODULE, [Node, Section, Pid], []).

init([Node, Section, Pid]) ->
        io:format( "freeswitch_callback:init( [Node=~w, Section=~w, 
Pid=~w])~n", [Node, Section, Pid] ),
    {ok, #st{fsnode=Node, pbxpid=Pid}}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%
%% Callback for freeswitch:start_fetch_handler() called in 
freeswitch_bind:init()
%%
fetch_handler( FreeswitchNode ) ->
        receive
                { nodedown, Node } ->
                        io:format( "freeswitch_callback:fetch_handler() Node ~w 
is down~n", [Node] ),
                        ok;
                { fetch, Section, Tag, Key, Value, FetchId, Params } ->
                        io:format( "freeswitch_callback:fetch_handler() 
Invoking xml_fetch()~n" ),
                    {ok, Xml} = xml_fetch( {fetch, Section, Tag, Key, Value, 
Params} ),
                        io:format( "freeswitch_callback:fetch_handler() Sending 
reply to FreeswitchNode ~w: ~s~n", [FreeswitchNode, Xml] ),
                        {api, FreeswitchNode} ! {fetch_reply, FetchId, Xml},
                        %% FreeswitchNode ! { fetch_reply, FetchId, Xml },
                        io:format( "freeswitch_callback:fetch_handler() Reply 
sent~n" )
%%                      io:format( "freeswitch_callback:fetch_handler() Waiting 
for confirmation reply was received~n"),
%%                      receive
%%                              ok ->
%%                                      io:format( 
"freeswitch_callback:fetch_handler() Reply receipt confirmed~n" ),
%%                                      ok;
%%                              {error, Reason} ->
%%                                      io:format( 
"freeswitch_callback:fetch_handler() Reply receipt failed due to error ~s~n", 
[Reason] ),
%%                                      {error, Reason}
%%                      end
        end,
        { ok } = fetch_handler( FreeswitchNode ),
        { ok }.

dumpParams( Params ) ->
        case Params of
                [] ->
                        ok;
                [ H | T ] ->
                        case H of
                                undefined ->
                                        io:format( "    undefined~n" );
                                { Key, Value } ->
                                        io:format( "    ~s=\"~s\"~n", [Key, 
Value ] )
                        end,
                        dumpParams( T )
        end.

%%
%%      Configuration handler for mod_sofia.
%%
xml_fetch({fetch, configuration, "configuration", "name", "sofia.conf", 
Params}) ->
        
        io:format( "freeswitch_callback:handle_call( {fetch, configuration, 
Tag=\"configuration\", Key=\"name\", Value=\"sofia.conf\", Params=...} )~n"),
        dumpParams( Params ),
        Xml =
"<document type=\"freeswitch/xml\">
        <section name=\"configuration\">
                <configuration name=\"sofia.conf\" description=\"sofia 
Endpoint\">
                        <global_settings>
                                <param name=\"log-level\" value=\"0\" />
                                <param name=\"auto-restart\" value=\"false\" />
                                <param name=\"debug-presence\" value=\"0\" />
                        </global_settings>
                        <settings>
                                <!-- ADD parameters here -->
                        </settings>
                        <profiles>
                                <profile name=\"external\">
                                        <gateways>
                                                <!-- Add gateways here -->
                                                <gateway name=\"asterlink.com\">
                                                        <param name=\"\" 
value=\"\" />
                                                        <param name=\"\" 
value=\"\" />
                                                        <param name=\"\" 
value=\"\" />
                                                        <param name=\"\" 
value=\"\" />
                                                        <param name=\"\" 
value=\"\" />
                                                        <param name=\"\" 
value=\"\" />
                                                        <param name=\"\" 
value=\"\" />
                                                </gateway>
                                        </gateways>
                                        <aliases>
                                                <alias name=\"outbound\" />
                                                <alias name=\"nat\" />
                                        </aliases>
                                        <domains>
                                                <domain name=\"all\" 
alias=\"false\" parse=\"true\" />
                                        </domains>
                                        <settings>
                                                <param name=\"debug\" 
value=\"0\" />
                                                <param name=\"sip-trace\" 
value=\"no\" />
                                                <param name=\"rfc2833-pt\" 
value=\"101\" />
                                                <param name=\"sip-port\" 
value=\"6080\" />
                                                <param name=\"dialplan\" 
value=\"XML\" />
                                                <param name=\"context\" 
value=\"public\" />
                                                <param name=\"dtmf-duration\" 
value=\"100\" />
                                                <param name=\"codec-prefs\" 
value=\"PCMU,PCMA,GSM\" />
                                                <param name=\"hold-music\" 
value=\"local_stream://moh\" />
                                                <param name=\"rtp-timer-name\" 
value=\"soft\" />
                                                <!--param 
name=\"enable-100rel\" value=\"true\" / -->
                                                <param name=\"manage-presence\" 
value=\"false\" />
                                                <!-- param name=\"dbname\" 
value=\"share_presence\" / -->
                                                <!-- param 
name=\"presence-hosts\" value=\"10.77.0.254\" / -->
                                                <!-- param 
name=\"force-register-domain\" value=\"10.77.0.254\" / -->
                                                <!-- param 
name=\"force-register-db-domain\" value=\"10.77.0.254\" / -->
                                                <!-- param 
name=\"aggressive-nat-detection\" value=\"true\" / -->
                                                <param 
name=\"inbound-codec-negotiation\" value=\"generous\" />
                                                <param name=\"nonce-ttl\" 
value=\"60\" />
                                                <param name=\"auth-calls\" 
value=\"false\" />
                                                <param name=\"rtp-timeout-sec\" 
value=\"1800\" />
                                                <param name=\"rtp-ip\" 
value=\"10.77.0.254\" />
                                                <param name=\"sip-ip\" 
value=\"10.77.0.254\" />
                                                <param name=\"ext-rtp-ip\" 
value=\"stun:stun.freeswitch.org\" />
                                                <param name=\"ext-sip-ip\" 
value=\"stun:stun.freeswitch.org\" />
                                                <param name=\"rtp-timout-sec\" 
value=\"300\" />
                                                <param 
name=\"rtp-hold-timeout-sec\" value=\"1800\" />
                                                <!-- param name=\"enable-3pcc\" 
value=\"true\" / -->
                                                <param name=\"tls-bind-params\" 
value=\"transport=tls\" />
                                                <param name=\"tls-sip-port\" 
value=\"6081\" />
                                                <param name=\"tls-cert-dir\" 
value=\"/opt/freeswitch/conf/ssl\" />
                                                <param name=\"tls-version\" 
value=\"tlsv1\" />
                                                <!-- param 
name=\"rtp-autoflush-during-bridge\" value=\"false\" / -->
                                                <!-- param 
name=\"rtp-rewrite-timestamp\" value=\"true\" / -->
                                                <!-- param 
name=\"pass-rfc2833\" value=\"true\" / -->
                                                <!-- param name=\"odbc-dsn\" 
value=\"dsn:user:pass\" / -->
                                                <!-- param 
name=\"inbound-bypass-media\" value=\"true\" / -->
                                                <!-- param 
name=\"inbound-proxy-media\" value=\"true\" / -->
                                                <!-- param 
name=\"inbound-late-negotiation\" value=\"true\" / -->
                                                <!-- param 
name=\"accept-blind-reg\" value=\"true\" / -->
                                                <!-- param 
name=\"accept-blind-auth\" value=\"true\" / -->
                                                <!-- param 
name=\"suppress-cng\" value=\"true\" / -->
                                                <param name=\"nonce-ttl\" 
value=\"60\" />
                                                <!-- param 
name=\"disable-transcoding\" value=\"true\" / -->
                                                <!-- param 
name=\"disable-transfer\" value=\"true\" / -->
                                                <!-- param 
name=\"NDLB-broken-auth-hash\" value=\"true\" / -->
                                                <!-- param 
name=\"NDLB-received-in-nat-reg-contact\" value=\"true\" / -->
                                                <param name=\"auth-calls\" 
value=\"true\" />
                                                <param 
name=\"inbound-reg-force-matching-username\" value=\"true\" />
                                                <param 
name=\"auth-all-packets\" value=\"false\" />
                                                <!-- param name=\"ext-rtp-ip\" 
value=\"stun.freeswitch.org\" / -->
                                                <!-- param name=\"ext-sip-ip\" 
value=\"stun.freeswitch.org\" / -->
                                                <param name=\"rtp-timeout-sec\" 
value=\"300\" />
                                                <param 
name=\"rtp-hold-timeout-sec\" value=\"1800\" />
                                                <!-- param name=\"vad\" 
value=\"in\" / -->
                                                <!-- param name=\"vad\" 
value=\"out\" / -->
                                                <!-- param name=\"vad\" 
value=\"both\" / -->
                                                <!-- param name=\"alias\" 
value=\"sip:10.77.0.231:5555\" / -->
                                                <param 
name=\"force-register-domain\" value=\"10.77.0.231\" />
                                                <param 
name=\"force-register-db-domain\" value=\"10.77.0.231\" />
                                                <!-- param 
name=\"force-subscription-expires\" value=\"60\" / -->
                                                <!-- param 
name=\"disable-transfer\" value=\"true\" / -->
                                                <!-- param 
name=\"disable-register\" value=\"true\" / -->
                                                <!-- param name=\"enable-3pcc\" 
value=\"true\" / -->
                                                <!-- param 
name=\"NDLB-force-rport\" value=\"true\" / -->
                                                <param name=\"challenge-realm\" 
value=\"auto_from\" />
                                                <!-- param 
name=\"disable-rtp-auto-adjust\" value=\"true\" / -->
                                                <!-- param 
name=\"inbound-use-callid-as-uuid\" value=\"true\" / -->
                                                <!-- param 
name=\"outbound-use-uuid-as-callid\" value=\"true\" / -->
                                                <!-- param 
name=\"rtp-autofix-timing\" value=\"false\" / -->
                                                <!-- param 
name=\"auto-rtp-bugs\" data=\"clear\" / -->
                                                <!-- param name=\"disable-srv\" 
value=\"false\" / -->
                                                <!-- param 
name=\"disable-naptr\" value=\"false\" / -->
                                                <!-- param name=\"timer-T1\" 
value=\"800\" / -->
                                                <!-- param name=\"timer-T1X64\" 
value=\"32000\" / -->
                                                <!-- param name=\"timer-T2\" 
value=\"4000\" / -->
                                                <!-- param name=\"timer-T4\" 
value=\"4000\" / -->
                                        </settings>
                                </profile>
                        </profiles>
                </configuration>
        </section>
</document>",
        {ok, Xml };

%%
%%      Configuration handler replies that the requested document section, tag, 
and key are not
%%      found.
%%
xml_fetch({fetch, configuration, Tag, Key, Value, Params}) ->
        io:format( "freeswitch_callback:handle_call( {fetch, configuration, 
Tag=~s, Key=~s, Value=~s, Params=...} )~n",
                [Tag, Key, Value]),
        dumpParams( Params ),
        Xml =
"<document type=\"freeswitch/xml\">
        <section name=\"result\">
                <result status=\"not found\" />
        </section>
</document>",
        {ok, Xml };

%%
%%      Directory handler replies that the requested document section, tag, and 
key are not
%%      found.
%%
xml_fetch({fetch, directory, Tag, Key, Value, Params}) ->
        io:format( "freeswitch_callback:xml_fetch( {fetch, directory, Tag=~s, 
Key=~s, Value=~s, Params=...} )~n",
                [Tag, Key, Value]),
        dumpParams( Params ),
        Xml =
"<document type=\"freeswitch/xml\">
        <section name=\"result\">
                <result status=\"not found\" />
        </section>
</document>",
        {ok, Xml };

%%
%%      Dialplan handler replies that the requested document section, tag, and 
key are not
%%      found.
%%
xml_fetch({fetch, dialplan, Tag, Key, Value, Params}) ->
        io:format( "freeswitch_callback:xml_fetch( {fetch, dialplan, Tag=~s, 
Key=~s, Value=~s, Params=...} )~n",
                [Tag, Key, Value]),
        dumpParams( Params ),
        Xml =
"<document type=\"freeswitch/xml\">
        <section name=\"result\">
                <result status=\"not found\" />
        </section>
</document>",
        {ok, Xml };

%%
%%      Default handler replies that the requested document section, tag, and 
key are not
%%      found.
%%
xml_fetch({fetch, Section, Tag, Key, Value, Params}) ->
        io:format( "freeswitch_callback:xml_fetch( {fetch, Section=~w, Tag=~s, 
Key=~s, Value=~s, Params=...} )~n",
                [Section, Tag, Key, Value]),
        dumpParams( Params ),
        Xml =
"<document type=\"freeswitch/xml\">
        <section name=\"result\">
                <result status=\"not found\" />
        </section>
</document>",
        {ok, Xml };

%%
%%      If the request isn't recognized, just log it.
%%
xml_fetch( Request ) ->
        io:format( "freeswitch_callback:xml_fetch( Request=~w ) not 
recognized~n",
                [Request]),
        Xml =
"<document type=\"freeswitch/xml\">
        <section name=\"result\">
                <result status=\"not found\" />
        </section>
</document>",
        {ok, Xml }.


%%
%%      If the request isn't recognized, just log it and do nothing.
%%
handle_call(Request, _From, State) ->
    io:format("freeswitch_callback:handle_call( ~w, _From, State) unrecognized 
request~n",
                [Request]),
    {reply, {error, unrecognized_request}, State}.

handle_cast(Message, State) ->
    error_logger:error_msg("~p received unrecognized cast ~p~n",
                           [self(), Message]),
    {noreply, State}.

handle_info(Info, State) ->
        io:format( "freeswitch_callback:handle_info() Info=~w~n", [Info]),
    {noreply, State}.

%%handle_info({fetch, Section, Tag, Key, Value, FetchID, Params}, 
#st{fsnode=Node, pbxpid=Pid}=State) ->
%%    {ok, XML} = gen_server:call(Pid, {fetch, Section, Tag, Key, Value, 
Params}),
%%    {api, Node} ! {fetch_reply, FetchID, XML},
%%    receive
%%      ok ->
%%          {noreply, State};
%%      {error, Reason} ->
%%          {stop, {error, Reason}, State}
%%    end.
-module(freeswitch_monitor).
-author('[email protected]').

-behaviour(gen_server).

-define(PID_ANNOUNCE, "run_freeswitch.pid=").
-define(PREFIX, "freeswitch< ").
-define(PROG, "./run_freeswitch").

-record(state, {port, prefix=?PREFIX, pid, fspid, sync=[], trigger, node}).

-export([start_link/0, terminate/2, code_change/3, init/1,
         handle_call/3, handle_cast/2, handle_info/2]).
-export([send/1]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% gen_server methods
start_link() ->
    process_flag(trap_exit, true),
    SName = lists:takewhile(fun ($.) -> false;
                                (_C) -> true
                            end,
                            net_adm:localhost()),
    FsNode = list_to_atom("freeswitch@" ++ SName),
        io:format("FsNode = ~w calling freeswitch:start_link()~n", [FsNode]),
    gen_server:start_link({global, freeswitch}, ?MODULE, FsNode, []).

init(FsNode) ->
    %% Have our terminate/2 function run when our supervisor shuts us down.
        io:format("init(~w) called~n",[FsNode]),
    State = #state{port=starconf:start_ext(?MODULE, ?PROG), node=FsNode},
        io:format("State = ~w~n", [State]),
    {ok, try_getpid(1000, State)}.

terminate(Reason, State) ->
    case Reason of
        {process_exited, _} ->
            ok;
        _ ->
            Port = State#state.port,
            catch port_command(Port, "shutdown\n"),
            receive
                {Port, {exit_status, _}} ->
                    ok
            after 5000 ->
                    starconf:stop_ext(State#state.port, State#state.pid)
            end
    end,
    ok.

code_change(OldVsn, State, _Extra) ->
    starconf:stop_ext(State#state.port, State#state.pid),
    {ok, #state{port=starconf:start_ext(?MODULE, OldVsn, ?PROG)}}.

handle_call(sync, From, #state{fspid=undefined}=State) ->
        io:format("freeswitch_monitor reached to handle sync call 1.~n"),
    {foo, State#state.node} ! getpid,
    {noreply, State#state{sync=[From | State#state.sync]}};
handle_call(sync, _From, State) ->
    %% We are synced, so return our Pid to link to.
        io:format("freeswitch_monitor reached to handle sync call 2 and is 
successful.~n"),
    {reply, {ok, self()}, State};
handle_call(_, _From, #state{fspid=undefined}=State) ->
        io:format("Freeswitch_monitor is not ready and throw error msg.~n"),
        {reply, {error, not_ready}, State};
handle_call({pbx_api, Pid, Tag, Cmd, Args}, _From, State) ->
    %% We background the call so we can continue, but reply to
    %% the original caller from our child process.
    CPid = spawn(fun () ->
                         link(State#state.fspid),
                         {api, State#state.node} ! {api, Cmd, Args},
                         receive
                             {error, Reason} ->
                                 Pid ! {Tag, {error, Reason}};
                             {ok, Reply} ->
                                 Pid ! {Tag, {ok, Reply}}
                         end
                 end),
    {reply, {ok, CPid}, State};
handle_call({pbx_bind, Section, Pid}, _From, State) ->
    Status = freeswitch_bind:start(State#state.node, Section, Pid),
    {reply, Status, State};
handle_call(Request, _From, State) ->
    error_logger:error_msg("~p:~p received unrecognized request ~p~n",
                           [?MODULE, self(), Request]),
    {reply, {error, unrecognized_request}, State}.

handle_cast({pbx_api, _Pid, _Tag, _Cmd, _Args}=Request, State) ->
    handle_call(Request, undefined, State),
    {noreply, State};
handle_cast({send, Cmd}, State) ->
    catch port_command(State#state.port, [Cmd, "\n"]),
    {noreply, State};
handle_cast(Message, State) ->
    error_logger:error_msg("~p:~p received unrecognized cast ~p~n",
                           [?MODULE, self(), Message]),
    {noreply, State}.

handle_info({Port, {exit_status, Status}}, #state{port=Port}=State) ->
    case Status of
        0 ->
            {stop, normal, State};
        _N ->
            {stop, {process_exited, Status}, State}
    end;
handle_info({try_getpid, Timeout}, State) ->
    {noreply, try_getpid(Timeout, State)};
handle_info({ok, Pid}, #state{sync=Sync}=State) when is_pid(Pid) ->
    error_logger:info_msg("~p:~p got ~p pid ~p~n",
                           [?MODULE, self(), State#state.node, Pid]),
    case State#state.trigger of
        undefined -> ok;
        Trigger ->
            timer:cancel(Trigger),
            ok
    end,
    lists:foreach(fun (From) ->
                          gen_server:reply(From, {ok, self()})
                  end,
                  Sync),
    {noreply, State#state{trigger=undefined, fspid=Pid, sync=[]}};
handle_info({Port, {data, {noeol, Output}}}, #state{port=Port}=State) ->
    io:format("~s~s", [State#state.prefix, Output]),
    {noreply, State#state{prefix=""}};
handle_info({Port, {data, {eol, Output}}}, #state{port=Port}=State) ->
    io:format("~s~s~n", [State#state.prefix, Output]),
    Len = string:len(?PID_ANNOUNCE),
    Pid = case string:substr(Output, 1, Len) of
              ?PID_ANNOUNCE ->
                  string:substr(Output, Len + 1);
              _ ->
                  State#state.pid
          end,
    {noreply, State#state{prefix=?PREFIX, pid=Pid}};
handle_info(Message, State) ->
    error_logger:error_msg("~p:~p: Got info ~p~n", [?MODULE, self(), Message]),
    {noreply, State}.

try_getpid(Timeout, #state{fspid=undefined}=State) ->
    error_logger:info_msg("~p:~p trying to get ~p pid~n",
                           [?MODULE, self(), State#state.node]),
    {foo, State#state.node} ! getpid,
    {ok, Trigger} = timer:send_after(Timeout, {try_getpid, Timeout}),
    State#state{trigger=Trigger};
try_getpid(_Timeout, State) ->
    State.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% API

send(Cmd) ->
    gen_server:cast({global, freeswitch}, {send, Cmd}).
_______________________________________________
FreeSWITCH-dev mailing list
[email protected]
http://lists.freeswitch.org/mailman/listinfo/freeswitch-dev
UNSUBSCRIBE:http://lists.freeswitch.org/mailman/options/freeswitch-dev
http://www.freeswitch.org

Reply via email to