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