1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2001-2016. 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%%---------------------------------------------------------------------- 23%% Purpose: Simple example of an MG 24%% 25%% Example usage: 26%% 27%% cd megaco/examples/simple 28%% erl -pa ../../../megaco/ebin -s megaco_filter -s megaco 29%% megaco_simple_mg:start(). 30%%---------------------------------------------------------------------- 31 32-module(megaco_simple_mg). 33 34-behaviour(megaco_user). 35 36-export([ 37 start_batch/0, start_batch/1, init_batch/4, 38 start/0, start/3, 39 start/4, %% ???????????????????????? 40 stop/0, stop/1, 41 start_tcp_text/2, start_tcp_binary/2, 42 start_udp_text/2, start_udp_binary/2 43 ]). 44 45-export([ 46 handle_connect/2, 47 handle_disconnect/3, 48 handle_syntax_error/3, 49 handle_message_error/3, 50 handle_trans_request/3, 51 handle_trans_long_request/3, 52 handle_trans_reply/4, 53 handle_trans_ack/4, 54 handle_unexpected_trans/3, 55 handle_trans_request_abort/4 56 ]). 57 58-include_lib("megaco/include/megaco.hrl"). 59-include_lib("megaco/include/megaco_message_v1.hrl"). 60 61 62 63%%---------------------------------------------------------------------- 64%% To be used at command line: erl -s ?MODULE start_batch 65%%---------------------------------------------------------------------- 66 67start_batch() -> 68 start_batch([]). 69 70start_batch(Args0) when is_list(Args0) -> 71 {ok, LocalHost} = inet:gethostname(), 72 Defs = [{mgc_host, LocalHost}, {trace,false}, {debug, false}], 73 Args = parse_args(Args0, Defs), 74 MgcHost = get_arg(mgc_host, Args), 75 Trace = get_arg(trace, Args), 76 Debug = get_arg(debug, Args), 77 Pid = spawn(?MODULE, init_batch, [self(), MgcHost, Trace, Debug]), 78 receive 79 {init_batch, Pid, Res} -> 80 io:format("~p(~p): ~p~n", [?MODULE, ?LINE, Res]), 81 Res 82 end. 83 84parse_args([], Acc) -> 85 Acc; 86parse_args([Arg|Args], Acc) when is_atom(Arg) -> 87 case string:tokens(atom_to_list(Arg),"{},") of 88 ["mgc_host", Host] when is_list(Host) -> 89 parse_args(Args, parse_args(mgc_host, Host, Acc)); 90 ["trace",Trace] -> 91 parse_args(Args, parse_args(trace, list_to_atom(Trace), Acc)); 92 ["debug",Debug] -> 93 parse_args(Args, parse_args(debug, list_to_atom(Debug), Acc)); 94 _Invalid -> 95 parse_args(Args, Acc) 96 end. 97 98parse_args(Key, Val, Args) -> 99 Entry = {Key, Val}, 100 case lists:keyreplace(Key, 1, Args, {Key, Val}) of 101 Args -> 102 [Entry|Args]; 103 Args2 -> 104 Args2 105 end. 106 107get_arg(Key, Args) -> 108 {value, {Key, Val}} = lists:keysearch(Key, 1, Args), 109 Val. 110 111init_batch(ReplyTo, MgcHost, Trace, Debug) -> 112 register(?MODULE, self()), 113 Res = start(MgcHost, Trace, Debug), 114 ReplyTo ! {init_batch, self(), Res}, 115 receive 116 after infinity -> Res 117 end. 118 119 120%%---------------------------------------------------------------------- 121%% Starting the MG 122%%---------------------------------------------------------------------- 123 124%% ----------------------------------------------------------------------- 125 126init_inline_trace(true) -> 127 megaco:enable_trace(max, io); 128init_inline_trace(_) -> 129 ok. 130 131%% ----------------------------------------------------------------------- 132 133 134start() -> 135 {ok, LocalHost} = inet:gethostname(), 136 start(LocalHost, false, false). 137 138%% Used when calling from the erlang shell: 139start(MgcHost, Trace, Debug) 140 when is_atom(MgcHost) andalso is_atom(Trace) andalso is_atom(Debug) -> 141 start(atom_to_list(MgcHost), Trace, Debug); 142 143start(MgcHost, Trace, Debug) 144 when is_list(MgcHost) andalso is_atom(Trace) andalso is_atom(Debug) -> 145 put(debug, Debug), 146 d("start -> entry with" 147 "~n MgcHost: ~s" 148 "~n Trace: ~p", [MgcHost, Trace]), 149 init_inline_trace(Trace), 150 Starters = [fun start_tcp_text/2, 151 fun start_tcp_binary/2, 152 fun start_udp_text/2, 153 fun start_udp_binary/2], 154 [Fun(MgcHost, []) || Fun <- Starters]. 155 156start_tcp_text(MgcHost, Default) -> 157 d("start_tcp_text -> entry with" 158 "~n MgcHost: ~p", [MgcHost]), 159 Config = [{encoding_mod, megaco_pretty_text_encoder}, 160 {encoding_config, []}, 161 {send_mod, megaco_tcp} | Default], 162 Mid = {deviceName, "gateway_tt"}, 163 {Mid, start(MgcHost, ?megaco_ip_port_text, Mid, Config)}. 164 165start_tcp_binary(MgcHost, Default) -> 166 d("start_tcp_binary -> entry with" 167 "~n MgcHost: ~p", [MgcHost]), 168 Config = [{encoding_mod, megaco_binary_encoder}, 169 {encoding_config, []}, 170 {send_mod, megaco_tcp} | Default], 171 Mid = {deviceName, "gateway_tb"}, 172 {Mid, start(MgcHost, ?megaco_ip_port_binary, Mid, Config)}. 173 174start_udp_text(MgcHost, Default) -> 175 d("start_udp_text -> entry with" 176 "~n MgcHost: ~p", [MgcHost]), 177 Config = [{encoding_mod, megaco_pretty_text_encoder}, 178 {encoding_config, []}, 179 {send_mod, megaco_udp} | Default], 180 Mid = {deviceName, "gateway_ut"}, 181 {Mid, start(MgcHost, ?megaco_ip_port_text, Mid, Config)}. 182 183start_udp_binary(MgcHost, Default) -> 184 d("start_udp_binary -> entry with" 185 "~n MgcHost: ~p", [MgcHost]), 186 Config = [{encoding_mod, megaco_binary_encoder}, 187 {encoding_config, []}, 188 {send_mod, megaco_udp} | Default], 189 Mid = {deviceName, "gateway_ub"}, 190 {Mid, start(MgcHost, ?megaco_ip_port_binary, Mid, Config)}. 191 192start(MgcHost, MgcPort, Mid, Config) -> 193 case megaco:start_user(Mid, [{user_mod, ?MODULE} | Config]) of 194 ok -> 195 case start_transport(MgcHost, MgcPort, Mid) of 196 {ok, ConnHandle} -> 197 service_change(ConnHandle); 198 {error, Reason} -> 199 {error, Reason} 200 end; 201 {error, Reason} -> 202 {error, {start_user, Reason}} 203 end. 204 205start_transport(MgcHost, MgcPort, Mid) -> 206 RecHandle = megaco:user_info(Mid, receive_handle), 207 case RecHandle#megaco_receive_handle.send_mod of 208 megaco_tcp -> start_tcp(MgcHost, MgcPort, RecHandle); 209 megaco_udp -> start_udp(MgcHost, MgcPort, RecHandle); 210 SendMod -> {error, {bad_send_mod, SendMod}} 211 end. 212 213start_tcp(MgcHost, MgcPort, RecHandle) -> 214 d("start_tcp -> start transport"), 215 case megaco_tcp:start_transport() of 216 {ok, Pid} -> 217 d("start_tcp -> transport started: ~p", [Pid]), 218 Options = [{host, MgcHost}, 219 {port, MgcPort}, 220 {receive_handle, RecHandle}], 221 case megaco_tcp:connect(Pid, Options) of 222 {ok, SendHandle, ControlPid} -> 223 d("start_tcp -> connected: ~p", [ControlPid]), 224 MgcMid = preliminary_mid, 225 megaco:connect(RecHandle, MgcMid, SendHandle, ControlPid); 226 {error, Reason} -> 227 d("start_tcp -> connection failed: ~p", [Reason]), 228 {error, {megaco_tcp_connect, Reason}} 229 end; 230 {error, Reason} -> 231 d("start_tcp -> failed starting transport: ~p", [Reason]), 232 {error, {megaco_tcp_start_transport, Reason}} 233 end. 234 235start_udp(MgcHost, MgcPort, RecHandle) -> 236 d("start_udp -> start transport"), 237 case megaco_udp:start_transport() of 238 {ok, SupPid} -> 239 d("start_udp -> transport started: ~p", [SupPid]), 240 Options = [{port, 0}, {receive_handle, RecHandle}], 241 case megaco_udp:open(SupPid, Options) of 242 {ok, Handle, ControlPid} -> 243 d("start_udp -> port opened: ~p", [ControlPid]), 244 %% Socket = megaco_udp:socket(Handle), 245 %% MgPort = inet:port(Socket), BUGBUG BUGBUG 246 MgcMid = preliminary_mid, 247 SendHandle = megaco_udp:create_send_handle(Handle, 248 MgcHost, % BUGBUG BUGBUG 249 MgcPort), 250 megaco:connect(RecHandle, MgcMid, SendHandle, ControlPid); 251 {error, Reason} -> 252 d("start_udp -> failed open port: ~p", [Reason]), 253 {error, {megaco_udp_open, Reason}} 254 end; 255 {error, Reason} -> 256 d("start_udp -> failed starting transport: ~p", [Reason]), 257 {error, {megaco_udp_start_transport, Reason}} 258 end. 259 260service_change(ConnHandle) -> 261 service_change(ConnHandle, restart, ?megaco_cold_boot). 262 263service_change(ConnHandle, Method, Reason) -> 264 SCP = #'ServiceChangeParm'{serviceChangeMethod = Method, 265 serviceChangeReason = [Reason]}, 266 TermId = [?megaco_root_termination_id], 267 SCR = #'ServiceChangeRequest'{terminationID = TermId, 268 serviceChangeParms = SCP}, 269 CR = #'CommandRequest'{command = {serviceChangeReq, SCR}}, 270 AR = #'ActionRequest'{contextId = ?megaco_null_context_id, 271 commandRequests = [CR]}, 272 megaco:call(ConnHandle, [AR], []). 273 274%%---------------------------------------------------------------------- 275%% Stopping the MG 276%%---------------------------------------------------------------------- 277 278stop() -> 279 [{Mid, stop(Mid)} || Mid <- megaco:system_info(users)]. 280 281stop(Mid) -> 282 Reason = stopped_by_user, 283 Disco = fun(CH) -> 284 Pid = megaco:conn_info(CH, control_pid), 285 megaco:disconnect(CH, Reason), 286 megaco:cancel(CH, Reason), 287 exit(Pid, Reason) 288 end, 289 lists:map(Disco, megaco:user_info(Mid, connections)), 290 megaco:stop_user(Mid). 291 292%%---------------------------------------------------------------------- 293%% Invoked when a new connection is established 294%%---------------------------------------------------------------------- 295 296handle_connect(ConnHandle, ProtocolVersion) -> 297 d("handle_connect -> entry with" 298 "~n ConnHandle: ~p" 299 "~n ProtocolVersion: ~p", [ConnHandle, ProtocolVersion]), 300 ok. 301 302%%---------------------------------------------------------------------- 303%% Invoked when a connection is teared down 304%%---------------------------------------------------------------------- 305 306handle_disconnect(ConnHandle, ProtocolVersion, Reason) -> 307 d("handle_disconnect -> entry with" 308 "~n ConnHandle: ~p" 309 "~n ProtocolVersion: ~p" 310 "~n Reason: ~p", [ConnHandle, ProtocolVersion, Reason]), 311 megaco:cancel(ConnHandle, Reason), % Cancel the outstanding messages 312 d("handle_disconnect -> done", []), 313 ok. 314 315%%---------------------------------------------------------------------- 316%% Invoked when a received message had syntax errors 317%%---------------------------------------------------------------------- 318 319handle_syntax_error(ReceiveHandle, ProtocolVersion, ErrorDescriptor) -> 320 d("handle_syntax_error -> entry with" 321 "~n ReceiveHandle: ~p" 322 "~n ProtocolVersion: ~p" 323 "~n ErrorDescriptor: ~p", 324 [ReceiveHandle, ProtocolVersion, ErrorDescriptor]), 325 reply. 326 327%%---------------------------------------------------------------------- 328%% Invoked when a received message contained no transactions 329%%---------------------------------------------------------------------- 330 331handle_message_error(ConnHandle, ProtocolVersion, ErrorDescriptor) -> 332 d("handle_message_error -> entry with" 333 "~n ConnHandle: ~p" 334 "~n ProtocolVersion: ~p" 335 "~n ErrorDescriptor: ~p", 336 [ConnHandle, ProtocolVersion, ErrorDescriptor]), 337 no_reply. 338 339%%---------------------------------------------------------------------- 340%% Invoked for each transaction request 341%%---------------------------------------------------------------------- 342 343handle_trans_request(ConnHandle, ProtocolVersion, ActionRequests) -> 344 d("handle_trans_request -> entry with" 345 "~n ConnHandle: ~p" 346 "~n ProtocolVersion: ~p" 347 "~n ActionRequests: ~p", 348 [ConnHandle, ProtocolVersion, ActionRequests]), 349 ED = #'ErrorDescriptor'{errorCode = ?megaco_not_implemented, 350 errorText = "Transaction requests not handled"}, 351 {discard_ack, ED}. 352 353%%---------------------------------------------------------------------- 354%% Optionally invoked for a time consuming transaction request 355%%---------------------------------------------------------------------- 356 357handle_trans_long_request(ConnHandle, ProtocolVersion, ReqData) -> 358 d("handle_trans_long_request -> entry with" 359 "~n ConnHandle: ~p" 360 "~n ProtocolVersion: ~p" 361 "~n ReqData: ~p", [ConnHandle, ProtocolVersion, ReqData]), 362 ED = #'ErrorDescriptor'{errorCode = ?megaco_not_implemented, 363 errorText = "Long transaction requests not handled"}, 364 {discard_ack, ED}. 365 366%%---------------------------------------------------------------------- 367%% Optionally invoked for a transaction reply 368%%---------------------------------------------------------------------- 369 370handle_trans_reply(ConnHandle, ProtocolVersion, ActualReply, ReplyData) -> 371 d("handle_trans_reply -> entry with" 372 "~n ConnHandle: ~p" 373 "~n ProtocolVersion: ~p" 374 "~n ActualReply: ~p" 375 "~n ReplyData: ~p", 376 [ConnHandle, ProtocolVersion, ActualReply, ReplyData]), 377 ok. 378 379%%---------------------------------------------------------------------- 380%% Optionally invoked for a transaction acknowledgement 381%%---------------------------------------------------------------------- 382 383handle_trans_ack(ConnHandle, ProtocolVersion, AckStatus, AckData) -> 384 d("handle_trans_ack -> entry with" 385 "~n ConnHandle: ~p" 386 "~n ProtocolVersion: ~p" 387 "~n AckStatus: ~p" 388 "~n AckData: ~p", 389 [ConnHandle, ProtocolVersion, AckStatus, AckData]), 390 ok. 391 392 393%%---------------------------------------------------------------------- 394%% Invoked when an unexpected message has been received 395%%---------------------------------------------------------------------- 396 397handle_unexpected_trans(ConnHandle, ProtocolVersion, Trans) -> 398 d("handle_unexpected_trans -> entry with" 399 "~n ConnHandle: ~p" 400 "~n ProtocolVersion: ~p" 401 "~n AckStatus: ~p" 402 "~n AckData: ~p", 403 [ConnHandle, ProtocolVersion, Trans]), 404 ok. 405 406 407%%---------------------------------------------------------------------- 408%% Invoked when an unexpected message has been received 409%%---------------------------------------------------------------------- 410 411handle_trans_request_abort(ConnHandle, ProtocolVersion, TransId, Pid) -> 412 d("handle_trans_request_abort -> entry with" 413 "~n ConnHandle: ~p" 414 "~n ProtocolVersion: ~p" 415 "~n TransId: ~p" 416 "~n Pid: ~p", 417 [ConnHandle, ProtocolVersion, TransId, Pid]), 418 ok. 419 420 421%%---------------------------------------------------------------------- 422%% DEBUGGING 423%%---------------------------------------------------------------------- 424 425d(F) -> 426 d(F, []). 427 428d(F,A) -> 429 d(get(debug),F,A). 430 431d(true,F,A) -> 432 io:format("SIMPLE_MG: " ++ F ++ "~n", A); 433d(_, _F, _A) -> 434 ok. 435 436 437 438 439