diff options
Diffstat (limited to 'lib/megaco/src/flex/megaco_flex_scanner.erl')
-rw-r--r-- | lib/megaco/src/flex/megaco_flex_scanner.erl | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/lib/megaco/src/flex/megaco_flex_scanner.erl b/lib/megaco/src/flex/megaco_flex_scanner.erl new file mode 100644 index 0000000000..e471412c13 --- /dev/null +++ b/lib/megaco/src/flex/megaco_flex_scanner.erl @@ -0,0 +1,230 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% 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. +%% +%% %CopyrightEnd% +%% + +%%---------------------------------------------------------------------- +%% Purpose : Scanner for text encoded Megaco/H.248 messages +%%---------------------------------------------------------------------- + +-module(megaco_flex_scanner). + +-export([is_enabled/0, is_reentrant_enabled/0, is_scanner_port/2]). +-export([start/0, start/1, stop/1, scan/2]). + +-define(NUM_SCHED(), erlang:system_info(schedulers)). +-define(SCHED_ID(), erlang:system_info(scheduler_id)). +-define(SMP_SUPPORT_DEFAULT(), erlang:system_info(smp_support)). + +is_enabled() -> + case ?ENABLE_MEGACO_FLEX_SCANNER of + true -> + true; + _ -> + false + end. + +is_reentrant_enabled() -> + case ?MEGACO_REENTRANT_FLEX_SCANNER of + true -> + true; + _ -> + false + end. + +is_scanner_port(Port, Port) when is_port(Port) -> + true; +is_scanner_port(Port, Ports) when is_tuple(Ports) -> + is_own_port(Port, Ports); +is_scanner_port(_, _) -> + false. + +is_own_port(Port, Ports) -> + is_own_port(Port, size(Ports), Ports). + +is_own_port(_Port, 0, _Ports) -> + false; +is_own_port(Port, N, Ports) when (N > 0) -> + case element(N, Ports) of + Port -> + true; + _ -> + is_own_port(Port, N-1, Ports) + end. + + +%%---------------------------------------------------------------------- +%% Start the flex scanner +%%---------------------------------------------------------------------- + +start() -> + start(?SMP_SUPPORT_DEFAULT()). + +start(SMP) when ((SMP =:= true) orelse (SMP =:= false)) -> + (catch do_start(is_reentrant_enabled() andalso SMP)). + +do_start(SMP) -> + Path = lib_dir(), + erl_ddll:start(), + load_driver(Path), + PortOrPorts = open_drv_port(SMP), + {ok, PortOrPorts}. + + +lib_dir() -> + case code:priv_dir(megaco) of + {error, Reason} -> + throw({error, {priv_dir, Reason}}); + P when is_list(P) -> + P ++ "/lib" + end. + + +load_driver(Path) -> + case erl_ddll:load_driver(Path, drv_name()) of + ok -> + ok; + {error, Reason} -> + case (catch erl_ddll:format_error(Reason)) of + FormatReason when is_list(FormatReason) -> + throw({error, {load_driver, FormatReason}}); + _ -> + throw({error, {load_driver, Reason}}) + end + end. + + +open_drv_port(true) -> + open_drv_ports(?NUM_SCHED(), []); +open_drv_port(_) -> + open_drv_port(). + +open_drv_ports(0, Acc) -> + list_to_tuple(Acc); +open_drv_ports(N, Acc) when is_integer(N) andalso (N > 0) -> + Port = open_drv_port(), + open_drv_ports(N-1, [Port | Acc]). + +open_drv_port() -> + case (catch erlang:open_port({spawn, drv_name()}, [binary])) of + Port when is_port(Port) -> + Port; + {'EXIT', Reason} -> + erl_ddll:unload_driver(drv_name()), + throw({error, {open_port, Reason}}) + end. + +drv_name() -> + case erlang:system_info(threads) of + true -> + "megaco_flex_scanner_drv_mt"; + false -> + "megaco_flex_scanner_drv" + end. + + +%%---------------------------------------------------------------------- +%% Stop the flex scanner +%%---------------------------------------------------------------------- + +stop(Port) when is_port(Port) -> + erlang:port_close(Port), + erl_ddll:unload_driver(drv_name()), + stopped; +stop(Ports) when is_tuple(Ports) -> + stop(tuple_to_list(Ports)); +stop(Ports) when is_list(Ports) -> + lists:foreach(fun(Port) -> erlang:port_close(Port) end, Ports), + erl_ddll:unload_driver(drv_name()), + stopped. + + +%%---------------------------------------------------------------------- +%% Scan a message +%%---------------------------------------------------------------------- + +scan(Binary, Port) when is_port(Port) -> + do_scan(Binary, Port); +scan(Binary, Ports) when is_tuple(Ports) -> +%% p("scan -> entry with" +%% "~n Ports: ~p", [Ports]), + do_scan(Binary, select_port(Ports)). + +do_scan(Binary, Port) -> +%% p("do_scan -> entry with" +%% "~n Port: ~p", [Port]), + case erlang:port_control(Port, $s, Binary) of + [] -> + receive + {tokens, Tokens, LatestLine} -> +%% p("do_scan -> OK with:" +%% "~n length(Tokens): ~p" +%% "~n LatestLine: ~p", [length(Tokens), LatestLine]), + Vsn = version(Tokens), + {ok, Tokens, Vsn, LatestLine} + after 5000 -> +%% p("do_scan -> ERROR", []), + {error, "Driver term send failure", 1} + end; + Reason -> +%% p("do_scan -> port control failed: " +%% "~n Reason: ~p", [Reason]), + {error, Reason, 1} + end. + +select_port(Ports) -> + SchedId = ?SCHED_ID(), + %% lists:nth(SchedId, Ports). + element(SchedId, Ports). + +version([]) -> + 99; % Let the parser deal with this +version([{'SafeChars',_,"!/1"}|_]) -> + 1; +version([{'SafeChars',_,"megaco/1"}|_]) -> + 1; +version([{'SafeChars',_,"!/2"}|_]) -> + 2; +version([{'SafeChars',_,"megaco/2"}|_]) -> + 2; +version([{'SafeChars',_,"!/3"}|_]) -> + 3; +version([{'SafeChars',_,"megaco/3"}|_]) -> + 3; +version([{'SafeChars',_,[$!, $/ | Vstr]}|_]) -> + guess_version(Vstr); +version([{'SafeChars',_,[$m, $e, $g, $a, $c, $o, $/ | Vstr]}|_]) -> + guess_version(Vstr); +version([_|T]) -> + version(T). + + +guess_version([C]) when (48 =< C) and (C =< 57) -> + C-48; +guess_version(Str) when is_list(Str) -> + case (catch list_to_integer(Str)) of + I when is_integer(I) -> + I; + _ -> + 99 % Let the parser deal with this + end; +guess_version(_) -> + 99. % Let the parser deal with this + + +%% p(F, A) -> +%% io:format("~w [~p,~p] " ++ F ++ "~n", [?MODULE, self(), ?SCHED_ID() | A]). |