1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2001-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21%%---------------------------------------------------------------------- 22%% Purpose : Scanner for text encoded Megaco/H.248 messages 23%%---------------------------------------------------------------------- 24 25-module(megaco_flex_scanner). 26 27-export([is_enabled/0, is_reentrant_enabled/0, is_scanner_port/2]). 28-export([start/0, start/1, stop/1, scan/2]). 29 30-define(NUM_SCHED(), erlang:system_info(schedulers)). 31-define(SCHED_ID(), erlang:system_info(scheduler_id)). 32-define(SMP_SUPPORT_DEFAULT(), erlang:system_info(smp_support)). 33 34-dialyzer({nowarn_function, is_enabled/0}). 35is_enabled() -> 36 (true =:= ?ENABLE_MEGACO_FLEX_SCANNER). 37 38-dialyzer({nowarn_function, is_reentrant_enabled/0}). 39is_reentrant_enabled() -> 40 (true =:= ?MEGACO_REENTRANT_FLEX_SCANNER). 41 42is_scanner_port(Port, Port) when is_port(Port) -> 43 true; 44is_scanner_port(Port, Ports) when is_tuple(Ports) -> 45 is_own_port(Port, Ports); 46is_scanner_port(_, _) -> 47 false. 48 49is_own_port(Port, Ports) -> 50 is_own_port(Port, size(Ports), Ports). 51 52is_own_port(_Port, 0, _Ports) -> 53 false; 54is_own_port(Port, N, Ports) when (N > 0) -> 55 case element(N, Ports) of 56 Port -> 57 true; 58 _ -> 59 is_own_port(Port, N-1, Ports) 60 end. 61 62 63%%---------------------------------------------------------------------- 64%% Start the flex scanner 65%%---------------------------------------------------------------------- 66 67start() -> 68 start(?SMP_SUPPORT_DEFAULT()). 69 70start(SMP) when ((SMP =:= true) orelse (SMP =:= false)) -> 71 (catch do_start(is_reentrant_enabled() andalso SMP)). 72 73do_start(SMP) -> 74 Path = lib_dir(), 75 erl_ddll:start(), 76 load_driver(Path), 77 PortOrPorts = open_drv_port(SMP), 78 {ok, PortOrPorts}. 79 80 81lib_dir() -> 82 case code:priv_dir(megaco) of 83 {error, Reason} -> 84 throw({error, {priv_dir, Reason}}); 85 P when is_list(P) -> 86 P ++ "/lib" 87 end. 88 89 90load_driver(Path) -> 91 case erl_ddll:load_driver(Path, drv_name()) of 92 ok -> 93 ok; 94 {error, Reason} -> 95 case (catch erl_ddll:format_error(Reason)) of 96 FormatReason when is_list(FormatReason) -> 97 throw({error, {load_driver, FormatReason}}); 98 _ -> 99 throw({error, {load_driver, Reason}}) 100 end 101 end. 102 103 104open_drv_port(true) -> 105 open_drv_ports(?NUM_SCHED(), []); 106open_drv_port(_) -> 107 open_drv_port(). 108 109open_drv_ports(0, Acc) -> 110 list_to_tuple(Acc); 111open_drv_ports(N, Acc) when is_integer(N) andalso (N > 0) -> 112 Port = open_drv_port(), 113 open_drv_ports(N-1, [Port | Acc]). 114 115open_drv_port() -> 116 case (catch erlang:open_port({spawn, drv_name()}, [binary])) of 117 Port when is_port(Port) -> 118 Port; 119 {'EXIT', Reason} -> 120 erl_ddll:unload_driver(drv_name()), 121 throw({error, {open_port, Reason}}) 122 end. 123 124drv_name() -> 125 case erlang:system_info(threads) of 126 true -> 127 "megaco_flex_scanner_drv_mt"; 128 false -> 129 "megaco_flex_scanner_drv" 130 end. 131 132 133%%---------------------------------------------------------------------- 134%% Stop the flex scanner 135%%---------------------------------------------------------------------- 136 137stop(Port) when is_port(Port) -> 138 erlang:port_close(Port), 139 erl_ddll:unload_driver(drv_name()), 140 stopped; 141stop(Ports) when is_tuple(Ports) -> 142 stop(tuple_to_list(Ports)); 143stop(Ports) when is_list(Ports) -> 144 lists:foreach(fun(Port) -> erlang:port_close(Port) end, Ports), 145 erl_ddll:unload_driver(drv_name()), 146 stopped. 147 148 149%%---------------------------------------------------------------------- 150%% Scan a message 151%%---------------------------------------------------------------------- 152 153scan(Binary, Port) when is_port(Port) -> 154 do_scan(Binary, Port); 155scan(Binary, Ports) when is_tuple(Ports) -> 156%% p("scan -> entry with" 157%% "~n Ports: ~p", [Ports]), 158 do_scan(Binary, select_port(Ports)). 159 160do_scan(Binary, Port) -> 161%% p("do_scan -> entry with" 162%% "~n Port: ~p", [Port]), 163 case erlang:port_control(Port, $s, Binary) of 164 [] -> 165 receive 166 {tokens, Tokens, LatestLine} -> 167%% p("do_scan -> OK with:" 168%% "~n length(Tokens): ~p" 169%% "~n LatestLine: ~p", [length(Tokens), LatestLine]), 170 Vsn = version(Tokens), 171 {ok, Tokens, Vsn, LatestLine} 172 after 5000 -> 173%% p("do_scan -> ERROR", []), 174 {error, "Driver term send failure", 1} 175 end; 176 Reason -> 177%% p("do_scan -> port control failed: " 178%% "~n Reason: ~p", [Reason]), 179 {error, Reason, 1} 180 end. 181 182select_port(Ports) -> 183 SchedId = ?SCHED_ID(), 184 %% lists:nth(SchedId, Ports). 185 element(SchedId, Ports). 186 187version([]) -> 188 99; % Let the parser deal with this 189version([{'SafeChars',_,"!/1"}|_]) -> 190 1; 191version([{'SafeChars',_,"megaco/1"}|_]) -> 192 1; 193version([{'SafeChars',_,"!/2"}|_]) -> 194 2; 195version([{'SafeChars',_,"megaco/2"}|_]) -> 196 2; 197version([{'SafeChars',_,"!/3"}|_]) -> 198 3; 199version([{'SafeChars',_,"megaco/3"}|_]) -> 200 3; 201version([{'SafeChars',_,[$!, $/ | Vstr]}|_]) -> 202 guess_version(Vstr); 203version([{'SafeChars',_,[$m, $e, $g, $a, $c, $o, $/ | Vstr]}|_]) -> 204 guess_version(Vstr); 205version([_|T]) -> 206 version(T). 207 208 209guess_version([C]) when (48 =< C) and (C =< 57) -> 210 C-48; 211guess_version(Str) when is_list(Str) -> 212 case (catch list_to_integer(Str)) of 213 I when is_integer(I) -> 214 I; 215 _ -> 216 99 % Let the parser deal with this 217 end; 218guess_version(_) -> 219 99. % Let the parser deal with this 220 221 222%% p(F, A) -> 223%% io:format("~w [~p,~p] " ++ F ++ "~n", [?MODULE, self(), ?SCHED_ID() | A]). 224