1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2003-2021. 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: Implements an "MGC" used by the test suite 24%%---------------------------------------------------------------------- 25-module(megaco_test_mgc). 26 27-export([start/4, start/5, stop/1, 28 get_stats/2, reset_stats/1, 29 user_info/1, user_info/2, conn_info/1, conn_info/2, 30 update_user_info/3, update_conn_info/3, 31 request_ignore/1, 32 request_discard/1, request_discard/2, 33 request_pending/1, request_pending/2, request_pending_ignore/1, 34 request_handle/1, request_handle/2, 35 request_handle_pending/1, request_handle_pending/2, 36 request_handle_sloppy/1, request_handle_sloppy/2, 37 ack_info/2, abort_info/2, req_info/2, 38 disconnect/2, 39 verbosity/2]). 40-export([mgc/3]). 41 42%% Megaco callback api 43-export([ 44 handle_connect/3, 45 handle_disconnect/4, 46 handle_syntax_error/4, 47 handle_message_error/4, 48 handle_trans_request/4, 49 handle_trans_long_request/4, 50 handle_trans_reply/5, 51 handle_trans_ack/5, 52 handle_unexpected_trans/4, 53 handle_trans_request_abort/5 54 ]). 55 56-include("megaco_test_lib.hrl"). 57-include_lib("megaco/include/megaco.hrl"). 58-include_lib("megaco/include/megaco_message_v1.hrl"). 59 60-define(A4444, ["11111111", "00000000", "00000000"]). 61-define(A4445, ["11111111", "00000000", "11111111"]). 62-define(A5555, ["11111111", "11111111", "00000000"]). 63-define(A5556, ["11111111", "11111111", "11111111"]). 64 65-define(valid_actions, 66 [ignore, pending, pending_ignore, discard_ack, handle_ack, handle_pending_ack, handle_sloppy_ack]). 67 68-record(mgc, {parent = undefined, 69 tcp_sup = undefined, 70 udp_sup = undefined, 71 req_action = discard_ack, 72 req_timeout = 0, 73 mid = undefined, 74 ack_info = undefined, 75 abort_info = undefined, 76 req_info = undefined, 77 mg = [], 78 dsi_timer, 79 evs = []}). 80 81-define(EVS_MAX, 10). 82 83 84%%% ------------------------------------------------------------------ 85 86start(Node, Mid, ET, Verbosity) -> 87 %% Conf = [{megaco_trace, io}], 88 %% Conf = [{megaco_trace, "megaco-mgc.trace"}], 89 Conf = [{megaco_trace, false}], 90 start(Node, Mid, ET, Conf, Verbosity). 91 92start(Node, Mid, ET, Conf, Verbosity) -> 93 d("start mgc[~p]: ~p" 94 "~n ET: ~p" 95 "~n Conf: ~p", [Node, Mid, ET, Conf]), 96 RI = {receive_info, mk_recv_info(ET)}, 97 Config = [{local_mid, Mid}, RI] ++ Conf, 98 Pid = spawn_link(Node, ?MODULE, mgc, [self(), Verbosity, Config]), 99 await_started(Pid). 100 101mk_recv_info(ET) -> 102 mk_recv_info(ET, []). 103 104mk_recv_info([], Acc) -> 105 Acc; 106mk_recv_info([{Encoding, Transport}|ET], Acc) 107 when is_atom(Encoding) andalso is_atom(Transport) -> 108 {EMod, Port} = select_encoding(Encoding), 109 TMod = select_transport(Transport), 110 RI = [{encoding_module, EMod}, 111 {encoding_config, []}, 112 {transport_module, TMod}, 113 {port, Port}], 114 mk_recv_info(ET, [RI|Acc]); 115mk_recv_info([{Encoding, Transport, TO}|ET], Acc) 116 when is_atom(Encoding) andalso is_atom(Transport) andalso is_list(TO) -> 117 {EMod, Port} = select_encoding(Encoding), 118 TMod = select_transport(Transport), 119 RI = [{encoding_module, EMod}, 120 {encoding_config, []}, 121 {transport_module, TMod}, 122 {port, Port}, 123 {transport_opts, TO}], 124 mk_recv_info(ET, [RI|Acc]); 125mk_recv_info([{Encoding, EC, Transport}|ET], Acc) 126 when is_atom(Encoding) andalso is_list(EC) andalso is_atom(Transport) -> 127 {EMod, Port} = select_encoding(Encoding), 128 TMod = select_transport(Transport), 129 RI = [{encoding_module, EMod}, 130 {encoding_config, EC}, 131 {transport_module, TMod}, 132 {port, Port}], 133 mk_recv_info(ET, [RI|Acc]); 134mk_recv_info([ET|_], _) -> 135 throw({error, {invalid_encoding_transport, ET}}). 136 137select_encoding(text) -> 138 {megaco_pretty_text_encoder, 2944}; 139select_encoding(pretty_text) -> 140 {megaco_pretty_text_encoder, 2944}; 141select_encoding(compact_text) -> 142 {megaco_compact_text_encoder, 2944}; 143select_encoding(binary) -> 144 {megaco_ber_encoder, 2945}; 145select_encoding(erl_dist) -> 146 {megaco_erl_dist_encoder, 2946}; 147select_encoding(Encoding) -> 148 throw({error, {invalid_encoding, Encoding}}). 149 150select_transport(tcp) -> 151 megaco_tcp; 152select_transport(udp) -> 153 megaco_udp; 154select_transport(Transport) -> 155 throw({error, {invalid_transport, Transport}}). 156 157 158await_started(Pid) -> 159 receive 160 {started, Pid} -> 161 d("await_started ~p: ok", [Pid]), 162 {ok, Pid}; 163 {'EXIT', Pid, 164 {failed_starting_tcp_listen, {could_not_start_listener, {gen_tcp_listen, eaddrinuse}}}} -> 165 e("await_started ~p: address already in use", [Pid]), 166 ?SKIP(eaddrinuse); 167 {'EXIT', Pid, Reason} -> 168 e("await_started ~p: received exit signal: ~p", [Pid, Reason]), 169 exit({failed_starting, Pid, Reason}) 170 after 10000 -> 171 e("await_started ~p: timeout", [Pid]), 172 exit({error, timeout}) 173 end. 174 175 176stop(Pid) -> 177 server_request(Pid, stop, stopped). 178 179get_stats(Pid, No) -> 180 server_request(Pid, {statistics, No}, {statistics_reply, No}). 181 182reset_stats(Pid) -> 183 server_request(Pid, reset_stats, reset_stats_ack). 184 185user_info(Pid) -> 186 server_request(Pid, {user_info, all}, user_info_ack). 187 188user_info(Pid, Tag) -> 189 server_request(Pid, {user_info, Tag}, user_info_ack). 190 191conn_info(Pid) -> 192 server_request(Pid, {conn_info, all}, conn_info_ack). 193 194conn_info(Pid, Tag) -> 195 server_request(Pid, {conn_info, Tag}, conn_info_ack). 196 197update_user_info(Pid, Tag, Val) -> 198 server_request(Pid, {update_user_info, Tag, Val}, update_user_info_ack). 199 200update_conn_info(Pid, Tag, Val) -> 201 server_request(Pid, {update_conn_info, Tag, Val}, update_conn_info_ack). 202 203disconnect(Pid, Reason) -> 204 server_request(Pid, {disconnect, Reason}, disconnected). 205 206ack_info(Pid, InfoPid) -> 207 Pid ! {ack_info, InfoPid, self()}. 208 209abort_info(Pid, InfoPid) -> 210 Pid ! {abort_info, InfoPid, self()}. 211 212req_info(Pid, InfoPid) -> 213 Pid ! {req_info, InfoPid, self()}. 214 215verbosity(Pid, V) -> 216 Pid ! {verbosity, V, self()}. 217 218request_ignore(Pid) -> 219 request_action(Pid, {ignore, infinity}). 220 221request_pending_ignore(Pid) -> 222 request_action(Pid, {pending_ignore, infinity}). 223 224request_discard(Pid) -> 225 request_discard(Pid,0). 226 227request_discard(Pid, To) -> 228 request_action(Pid, {discard_ack, To}). 229 230request_pending(Pid) -> 231 request_pending(Pid, 5000). 232 233request_pending(Pid, To) -> 234 request_action(Pid, {pending, To}). 235 236request_handle(Pid) -> 237 request_handle(Pid, 0). 238 239request_handle(Pid, To) -> 240 request_action(Pid, {handle_ack, To}). 241 242request_handle_pending(Pid) -> 243 request_handle_pending(Pid, 0). 244 245request_handle_pending(Pid, To) -> 246 request_action(Pid, {handle_pending_ack, To}). 247 248request_handle_sloppy(Pid) -> 249 request_handle_sloppy(Pid, 0). 250 251request_handle_sloppy(Pid, To) -> 252 request_action(Pid, {handle_sloppy_ack, To}). 253 254request_action(Pid, Action) -> 255 server_request(Pid, request_action, Action, request_action_ack). 256 257 258server_request(Pid, Req, ReplyTag) -> 259 Pid ! {Req, self()}, 260 receive 261 {ReplyTag, Reply, Pid} -> 262 Reply; 263 {'EXIT', Pid, Reason} -> 264 exit({failed, Req, Pid, Reason}) 265 after 10000 -> 266 exit({timeout, Req, Pid}) 267 end. 268 269server_request(Pid, Req, ReqData, ReplyTag) -> 270 Pid ! {Req, ReqData, self()}, 271 receive 272 {ReplyTag, Reply, Pid} -> 273 Reply; 274 {'EXIT', Pid, Reason} -> 275 exit({failed, Req, Pid, Reason}) 276 after 10000 -> 277 exit({timeout, Req, Pid}) 278 end. 279 280 281server_reply(Pid, ReplyTag, Reply) -> 282 Pid ! {ReplyTag, Reply, self()}. 283 284 285%%% ------------------------------------------------------------------ 286 287 288mgc(Parent, Verbosity, Config) -> 289 process_flag(trap_exit, true), 290 put(verbosity, Verbosity), 291 put(sname, "MGC"), 292 i("mgc -> starting"), 293 case (catch init(Config)) of 294 {error, Reason} -> 295 exit(Reason); 296 {Mid, TcpSup, UdpSup, DSITimer} -> 297 notify_started(Parent), 298 S = #mgc{parent = Parent, 299 tcp_sup = TcpSup, 300 udp_sup = UdpSup, 301 mid = Mid, 302 dsi_timer = DSITimer}, 303 i("mgc -> started"), 304 display_system_info("at start "), 305 loop(evs(S, started)) 306 end. 307 308init(Config) -> 309 d("init -> entry"), 310 random_init(), 311 Mid = get_conf(local_mid, Config), 312 RI = get_conf(receive_info, Config), 313 314 d("init -> maybe start the display system info timer"), 315 DSITimer = 316 case get_conf(display_system_info, Config, undefined) of 317 Time when is_integer(Time) -> 318 d("init -> creating display system info timer"), 319 create_timer(Time, display_system_info); 320 _ -> 321 undefined 322 end, 323 Conf0 = lists:keydelete(display_system_info, 1, Config), 324 325 d("init -> start megaco"), 326 application:start(megaco), 327 328 d("init -> possibly enable megaco trace"), 329 case lists:keysearch(megaco_trace, 1, Config) of 330 {value, {megaco_trace, true}} -> 331 megaco:enable_trace(max, io); 332 {value, {megaco_trace, io}} -> 333 megaco:enable_trace(max, io); 334 {value, {megaco_trace, File}} when is_list(File) -> 335 megaco:enable_trace(max, File); 336 _ -> 337 ok 338 end, 339 Conf1 = lists:keydelete(megaco_trace, 1, Conf0), 340 341 d("init -> start megaco user"), 342 Conf2 = lists:keydelete(local_mid, 1, Conf1), 343 Conf3 = lists:keydelete(receive_info, 1, Conf2), 344 ok = megaco:start_user(Mid, Conf3), 345 346 d("init -> update user info (user_mod)"), 347 ok = megaco:update_user_info(Mid, user_mod, ?MODULE), 348 349 d("init -> update user info (user_args)"), 350 ok = megaco:update_user_info(Mid, user_args, [self()]), 351 352 d("init -> get user info (receive_handle)"), 353 RH = megaco:user_info(Mid,receive_handle), 354 d("init -> parse receive info"), 355 Transports = parse_receive_info(RI, RH), 356 357 d("init -> start transports"), 358 {Tcp, Udp} = start_transports(Transports), 359 {Mid, Tcp, Udp, DSITimer}. 360 361loop(S) -> 362 d("loop -> await request"), 363 receive 364 {display_system_info, Time} -> 365 display_system_info(S#mgc.mid), 366 NewTimer = create_timer(Time, display_system_info), 367 loop(evs(S#mgc{dsi_timer = NewTimer}, {dsi, Time})); 368 369 {stop, Parent} when S#mgc.parent =:= Parent -> 370 i("loop -> stopping"), 371 display_system_info(S#mgc.mid, "at finish "), 372 cancel_timer(S#mgc.dsi_timer), 373 Mid = S#mgc.mid, 374 (catch close_conns(Mid)), 375 megaco:stop_user(Mid), 376 application:stop(megaco), 377 i("loop -> stopped"), 378 server_reply(Parent, stopped, ok), 379 done(evs(S, stop), normal); 380 381 {{disconnect, Reason}, Parent} when S#mgc.parent == Parent -> 382 i("loop -> disconnecting"), 383 Mid = S#mgc.mid, 384 [Conn|_] = megaco:user_info(Mid, connections), 385 Res = megaco:disconnect(Conn, {self(), Reason}), 386 server_reply(Parent, disconnected, Res), 387 loop(evs(S, {disconnect, Reason})); 388 389 {{update_user_info, Tag, Val}, Parent} when S#mgc.parent == Parent -> 390 i("loop -> got update_user_info: ~w -> ~p", [Tag, Val]), 391 Res = (catch megaco:update_user_info(S#mgc.mid, Tag, Val)), 392 d("loop -> Res: ~p", [Res]), 393 server_reply(Parent, update_user_info_ack, Res), 394 loop(evs(S, {uui, {Tag, Val}})); 395 396 {{user_info, Tag}, Parent} when S#mgc.parent == Parent -> 397 i("loop -> got user_info request for ~w", [Tag]), 398 Res = (catch megaco:user_info(S#mgc.mid, Tag)), 399 d("loop -> Res: ~p", [Res]), 400 server_reply(Parent, user_info_ack, Res), 401 loop(evs(S, {ui, Tag})); 402 403 {{update_conn_info, Tag, Val}, Parent} when S#mgc.parent == Parent -> 404 i("loop -> got update_conn_info: ~w -> ~p", [Tag, Val]), 405 Conns = megaco:user_info(S#mgc.mid, connections), 406 Fun = fun(CH) -> 407 (catch megaco:update_conn_info(CH, Tag, Val)) 408 end, 409 Res = lists:map(Fun, Conns), 410 d("loop -> Res: ~p", [Res]), 411 server_reply(Parent, update_conn_info_ack, Res), 412 loop(evs(S, {uci, {Tag, Val}})); 413 414 {{conn_info, Tag}, Parent} when S#mgc.parent =:= Parent -> 415 i("loop -> got conn_info request for ~w", [Tag]), 416 Conns = megaco:user_info(S#mgc.mid, connections), 417 Fun = fun(CH) -> 418 {CH, (catch megaco:conn_info(CH, Tag))} 419 end, 420 Res = lists:map(Fun, Conns), 421 d("loop -> Res: ~p", [Res]), 422 server_reply(Parent, conn_info_ack, Res), 423 loop(evs(S, {ci, Tag})); 424 425 426 %% 427 {request_action, {Action, To}, Parent} when S#mgc.parent == Parent -> 428 i("loop -> got new request_action: ~p:~w", [Action,To]), 429 {Reply, S1} = 430 case lists:member(Action, ?valid_actions) of 431 true when To >= 0; To == infinity -> 432 {{ok, S#mgc.req_action}, 433 S#mgc{req_action = Action, req_timeout = To}}; 434 true -> 435 {{error, {invalid_action_timeout, To}}, S}; 436 false -> 437 {{error, {invalid_action, Action}}, S} 438 end, 439 server_reply(Parent, request_action_ack, Reply), 440 loop(evs(S1, {req_act, {Action, To}})); 441 442 443 %% Reset stats 444 {reset_stats, Parent} when S#mgc.parent == Parent -> 445 i("loop -> got request to reset stats counters"), 446 do_reset_stats(S#mgc.mid), 447 server_reply(Parent, reset_stats_ack, ok), 448 loop(evs(S, rst_stats)); 449 450 451 %% Give me statistics 452 {{statistics, 1}, Parent} when S#mgc.parent == Parent -> 453 i("loop(stats1) -> got request for statistics 1"), 454 {ok, Gen} = megaco:get_stats(), 455 i("loop(stats1) -> gen stats: " 456 "~n ~p", [Gen]), 457 GetTrans = 458 fun(CH) -> 459 i("loop(stats1):GetTrans -> " 460 "get stats for connection ~p", [CH]), 461 Reason = {statistics, CH}, 462 Pid = megaco:conn_info(CH, control_pid), 463 i("loop(stats1):GetTrans -> control pid: ~p", [Pid]), 464 SendMod = megaco:conn_info(CH, send_mod), 465 i("loop(stats1):GetTrans -> " 466 "send module: ~p", [SendMod]), 467 SendHandle = megaco:conn_info(CH, send_handle), 468 i("loop(stats1):GetTrans -> " 469 "send handle: ~p", [SendHandle]), 470 {ok, Stats} = 471 case SendMod of 472 megaco_tcp -> megaco_tcp:get_stats(SendHandle); 473 megaco_udp -> megaco_udp:get_stats(SendHandle); 474 SendMod -> exit(Pid, Reason) 475 end, 476 i("loop(stats1):GetTrans -> stats: " 477 "~n ~p", [Stats]), 478 {SendHandle, Stats} 479 end, 480 Mid = S#mgc.mid, 481 Trans = lists:map(GetTrans, megaco:user_info(Mid, connections)), 482 Reply = {ok, [{gen, Gen}, {trans, Trans}]}, 483 i("loop(stats1) -> send reply"), 484 server_reply(Parent, {statistics_reply, 1}, Reply), 485 i("loop(stats1) -> done"), 486 loop(evs(S, {stats, 1})); 487 488 489 {{statistics, 2}, Parent} when S#mgc.parent == Parent -> 490 i("loop(stats2) -> got request for statistics 2"), 491 {ok, Gen} = megaco:get_stats(), 492 #mgc{tcp_sup = TcpSup, udp_sup = UdpSup} = S, 493 TcpStats = get_trans_stats(TcpSup, megaco_tcp), 494 UdpStats = get_trans_stats(UdpSup, megaco_udp), 495 Reply = {ok, [{gen, Gen}, {trans, [TcpStats, UdpStats]}]}, 496 i("loop(stats2) -> send reply"), 497 server_reply(Parent, {statistics_reply, 2}, Reply), 498 i("loop(stats2) -> done"), 499 loop(evs(S, {stats, 2})); 500 501 502 %% Megaco callback messages 503 {request, Request, From} -> 504 d("loop(request) -> received megaco request from ~p:" 505 "~n ~p", [From, Request]), 506 {Reply, S1} = handle_megaco_request(Request, S), 507 d("loop(request) -> send reply: ~n~p", [Reply]), 508 reply(From, Reply), 509 d("loop(request) -> done"), 510 loop(evs(S1, {req, Request})); 511 512 513 {ack_info, To, Parent} when S#mgc.parent == Parent -> 514 i("loop -> received request to inform about received ack's "), 515 loop(evs(S#mgc{ack_info = To}, {acki, To})); 516 517 518 {abort_info, To, Parent} when S#mgc.parent == Parent -> 519 i("loop -> received request to inform about received aborts "), 520 loop(evs(S#mgc{abort_info = To}, {abi, To})); 521 522 523 {req_info, To, Parent} when S#mgc.parent == Parent -> 524 i("loop -> received request to inform about received req's "), 525 loop(evs(S#mgc{req_info = To}, {reqi, To})); 526 527 528 {verbosity, V, Parent} when S#mgc.parent == Parent -> 529 i("loop -> received new verbosity: ~p", [V]), 530 put(verbosity,V), 531 loop(evs(S, {verb, V})); 532 533 534 {'EXIT', Pid, Reason} when S#mgc.tcp_sup =:= Pid -> 535 error_msg("MGC received unexpected exit " 536 "from TCP transport supervisor (~p):" 537 "~n ~p", [Pid, Reason]), 538 i("loop -> [tcp] exiting"), 539 display_system_info(S#mgc.mid, "at bad finish (tcp) "), 540 cancel_timer(S#mgc.dsi_timer), 541 Mid = S#mgc.mid, 542 (catch close_conns(Mid)), 543 megaco:stop_user(Mid), 544 application:stop(megaco), 545 i("loop -> stopped"), 546 StopReason = {error, {tcp_terminated, Pid, Reason}}, 547 server_reply(S#mgc.parent, stopped, StopReason), 548 done(evs(S, {tcp_sup_exit, Reason}), StopReason); 549 550 551 {'EXIT', Pid, Reason} when S#mgc.udp_sup =:= Pid -> 552 error_msg("MGC received unexpected exit " 553 "from UDP transport supervisor (~p):" 554 "~n ~p", [Pid, Reason]), 555 i("loop -> [udp] exiting"), 556 display_system_info(S#mgc.mid, "at bad finish (udp) "), 557 cancel_timer(S#mgc.dsi_timer), 558 Mid = S#mgc.mid, 559 (catch close_conns(Mid)), 560 megaco:stop_user(Mid), 561 application:stop(megaco), 562 i("loop -> stopped"), 563 StopReason = {error, {udp_terminated, Pid, Reason}}, 564 server_reply(S#mgc.parent, stopped, StopReason), 565 done(evs(S, {udp_sup_exit, Reason}), StopReason); 566 567 568 Invalid -> 569 i("loop -> received invalid request: ~p", [Invalid]), 570 loop(evs(S, {invalid, Invalid})) 571 end. 572 573 574evs(#mgc{evs = EVS} = S, Ev) when (length(EVS) < ?EVS_MAX) -> 575 echo_evs(S#mgc{evs = [{?FTS(), Ev}|EVS]}); 576evs(#mgc{evs = EVS} = S, Ev) -> 577 echo_evs(S#mgc{evs = [{?FTS(), Ev}|lists:droplast(EVS)]}). 578 579echo_evs(#mgc{evs = EVS} = S) -> 580 i("Events: " 581 "~n ~p", [EVS]), 582 S. 583 584done(#mgc{evs = EVS}, Reason) -> 585 info_msg("Exiting with latest event(s): " 586 "~n ~p" 587 "~n", [EVS]), 588 exit(Reason). 589 590 591do_reset_stats(Mid) -> 592 megaco:reset_stats(), 593 do_reset_trans_stats(megaco:user_info(Mid, connections), []). 594 595do_reset_trans_stats([], _Reset) -> 596 ok; 597do_reset_trans_stats([CH|CHs], Reset) -> 598 SendMod = megaco:conn_info(CH, send_mod), 599 case lists:member(SendMod, Reset) of 600 true -> 601 do_reset_trans_stats(CHs, Reset); 602 false -> 603 SendMod:reset_stats(), 604 do_reset_trans_stats(CHs, [SendMod|Reset]) 605 end. 606 607 608close_conns(Mid) -> 609 Reason = {self(), ignore}, 610 Disco = fun(CH) -> 611 (catch do_close_conn(CH, Reason)) 612 end, 613 lists:map(Disco, megaco:user_info(Mid, connections)). 614 615do_close_conn(CH, Reason) -> 616 d("close connection to ~p", [CH#megaco_conn_handle.remote_mid]), 617 Pid = megaco:conn_info(CH, control_pid), 618 SendMod = megaco:conn_info(CH, send_mod), 619 SendHandle = megaco:conn_info(CH, send_handle), 620 megaco:disconnect(CH, Reason), 621 case SendMod of 622 megaco_tcp -> megaco_tcp:close(SendHandle); 623 megaco_udp -> megaco_udp:close(SendHandle); 624 SendMod -> exit(Pid, Reason) 625 end. 626 627get_trans_stats(P, SendMod) when is_pid(P) -> 628 case (catch SendMod:get_stats()) of 629 {ok, Stats} -> 630 {SendMod, Stats}; 631 Else -> 632 {SendMod, Else} 633 end; 634get_trans_stats(_P, SendMod) -> 635 {SendMod, undefined}. 636 637parse_receive_info([], _RH) -> 638 throw({error, no_receive_info}); 639parse_receive_info(RI, RH) -> 640 parse_receive_info(RI, RH, []). 641 642parse_receive_info([], _RH, Transports) -> 643 d("parse_receive_info -> done when" 644 "~n Transports: ~p", [Transports]), 645 Transports; 646parse_receive_info([RI|RIs], RH, Transports) -> 647 d("parse_receive_info -> parse receive info"), 648 case (catch parse_receive_info1(RI, RH)) of 649 {error, Reason} -> 650 e("failed parsing receive info: ~p~n~p", [RI, Reason]), 651 exit({failed_parsing_recv_info, RI, Reason}); 652 RH1 -> 653 parse_receive_info(RIs, RH, [RH1|Transports]) 654 end. 655 656parse_receive_info1(RI, RH) -> 657 d("parse_receive_info1 -> get encoding module"), 658 EM = get_encoding_module(RI), 659 d("parse_receive_info1 -> get encoding config"), 660 EC = get_encoding_config(RI, EM), 661 d("parse_receive_info1 -> get transport module"), 662 TM = get_transport_module(RI), 663 d("parse_receive_info1 -> get transport port"), 664 TP = get_transport_port(RI), 665 d("parse_receive_info1 -> get transport opts"), 666 TO = get_transport_opts(RI), 667 RH1 = RH#megaco_receive_handle{send_mod = TM, 668 encoding_mod = EM, 669 encoding_config = EC}, 670 d("parse_receive_info1 -> " 671 "~n Transport Opts: ~p" 672 "~n Port: ~p" 673 "~n Receive handle: ~p", [TO, TP, RH1]), 674 {TO, TP, RH1}. 675 676 677 678%% -------------------------------------------------------- 679%% On some platforms there seem to take some time before 680%% a port is released by the OS (after having been used, 681%% as is often the case in the test suites). 682%% So, starting the transports is done in two steps. 683%% First) Start the actual transport(s) 684%% Second) Create the listener (tcp) or open the 685%% send/receive port (udp). 686%% The second step *may* need to be repeated! 687%% -------------------------------------------------------- 688start_transports([]) -> 689 throw({error, no_transport}); 690start_transports(Transports) when is_list(Transports) -> 691 {Tcp, Udp} = start_transports1(Transports, undefined, undefined), 692 ok = start_transports2(Transports, Tcp, Udp), 693 {Tcp, Udp}. 694 695start_transports1([], Tcp, Udp) -> 696 {Tcp, Udp}; 697start_transports1([{_TO, _Port, RH}|Transports], Tcp, Udp) 698 when ((RH#megaco_receive_handle.send_mod =:= megaco_tcp) andalso 699 (not is_pid(Tcp))) -> 700 d("try start tcp transport service"), 701 case megaco_tcp:start_transport() of 702 {ok, Sup} -> 703 d("tcp transport service started: ~p", [Sup]), 704 start_transports1(Transports, Sup, Udp); 705 Else -> 706 e("Failed starting TCP transport service:" 707 "~n ~p", [Else]), 708 throw({error, {failed_starting_tcp_transport, Else}}) 709 end; 710start_transports1([{_TO, _Port, RH}|Transports], Tcp, Udp) 711 when ((RH#megaco_receive_handle.send_mod =:= megaco_udp) andalso 712 (not is_pid(Udp))) -> 713 d("try start udp transport servuice"), 714 case megaco_udp:start_transport() of 715 {ok, Sup} -> 716 d("udp transport started: ~p", [Sup]), 717 start_transports1(Transports, Tcp, Sup); 718 Else -> 719 e("Failed starting UDP transport service:" 720 "~n ~p", [Else]), 721 throw({error, {failed_starting_udp_transport, Else}}) 722 end; 723start_transports1([_|Transports], Tcp, Udp) -> 724 start_transports1(Transports, Tcp, Udp). 725 726start_transports2([], _, _) -> 727 ok; 728start_transports2([{TO, Port, RH}|Transports], Tcp, Udp) 729 when RH#megaco_receive_handle.send_mod =:= megaco_tcp -> 730 start_tcp(TO, RH, Port, Tcp), 731 start_transports2(Transports, Tcp, Udp); 732start_transports2([{TO, Port, RH}|Transports], Tcp, Udp) 733 when RH#megaco_receive_handle.send_mod =:= megaco_udp -> 734 start_udp(TO, RH, Port, Udp), 735 start_transports2(Transports, Tcp, Udp). 736 737start_tcp(TO, RH, Port, Sup) -> 738 d("start tcp transport"), 739 start_tcp(TO, RH, Port, Sup, 250). 740 741start_tcp(TO, RH, Port, Sup, Timeout) 742 when is_pid(Sup) andalso is_integer(Timeout) andalso (Timeout > 0) -> 743 d("tcp listen on ~p", [Port]), 744 Opts = [{port, Port}, 745 {receive_handle, RH}, 746 {tcp_options, [{nodelay, true}]}] ++ TO, 747 try_start_tcp(Sup, Opts, Timeout, noError). 748 749try_start_tcp(Sup, Opts, Timeout, Error0) when (Timeout < 5000) -> 750 Sleep = random(Timeout) + 100, 751 d("try create tcp listen socket (~p,~p)", [Timeout, Sleep]), 752 case megaco_tcp:listen(Sup, Opts) of 753 ok -> 754 d("listen socket created", []), 755 Sup; 756 Error1 when Error0 =:= noError -> % Keep the first error 757 d("failed creating listen socket [1]: ~p", [Error1]), 758 sleep(Sleep), 759 try_start_tcp(Sup, Opts, Timeout*2, Error1); 760 Error2 -> 761 d("failed creating listen socket [2]: ~p", [Error2]), 762 sleep(Sleep), 763 try_start_tcp(Sup, Opts, Timeout*2, Error0) 764 end; 765try_start_tcp(Sup, _Opts, _Timeout, Error) -> 766 megaco_tcp:stop_transport(Sup), 767 case Error of 768 {error, Reason} -> 769 throw({error, {failed_starting_tcp_listen, Reason}}); 770 _ -> 771 throw({error, {failed_starting_tcp_listen, Error}}) 772 end. 773 774 775start_udp(TO, RH, Port, Sup) -> 776 d("start udp transport"), 777 start_udp(TO, RH, Port, Sup, 250). 778 779start_udp(TO, RH, Port, Sup, Timeout) -> 780 d("udp open ~p", [Port]), 781 Opts = [{port, Port}, {receive_handle, RH}] ++ TO, 782 try_start_udp(Sup, Opts, Timeout, noError). 783 784try_start_udp(Sup, Opts, Timeout, Error0) when (Timeout < 5000) -> 785 d("try open udp socket (~p)", [Timeout]), 786 case megaco_udp:open(Sup, Opts) of 787 {ok, _SendHandle, _ControlPid} -> 788 d("port opened", []), 789 Sup; 790 Error1 when Error0 =:= noError -> % Keep the first error 791 d("failed open port [1]: ~p", [Error1]), 792 sleep(Timeout), 793 try_start_udp(Sup, Opts, Timeout*2, Error1); 794 Error2 -> 795 d("failed open port [2]: ~p", [Error2]), 796 sleep(Timeout), 797 try_start_udp(Sup, Opts, Timeout*2, Error0) 798 end; 799try_start_udp(Sup, _Opts, _Timeout, Error) -> 800 megaco_udp:stop_transport(Sup), 801 throw({error, {failed_starting_udp_open, Error}}). 802 803 804%% ----------------------- 805%% Handle megaco callbacks 806%% 807 808handle_megaco_request({handle_connect, CH, _PV}, #mgc{mg = MGs} = S) -> 809 case lists:member(CH, MGs) of 810 true -> 811 i("MG already connected: ~n ~p", [CH]), 812 {error, S}; 813 false -> 814 {ok, S#mgc{mg = [CH|MGs]}} 815 end; 816 817handle_megaco_request({handle_disconnect, CH, _PV, R}, S) -> 818 d("handle_megaco_request(handle_disconnect) -> entry with" 819 "~n CH: ~p" 820 "~n R: ~p", [CH, R]), 821 CancelRes = (catch megaco:cancel(CH, R)), % Cancel the outstanding messages 822 d("handle_megaco_request(handle_disconnect) -> megaco cancel result: ~p", [CancelRes]), 823 MGs = lists:delete(CH, S#mgc.mg), 824 d("handle_megaco_request(handle_disconnect) -> MGs: ~p", [MGs]), 825 {ok, S#mgc{mg = MGs}}; 826 827handle_megaco_request({handle_syntax_error, _RH, _PV, _ED}, S) -> 828 {reply, S}; 829 830handle_megaco_request({handle_message_error, _CH, _PV, _ED}, S) -> 831 {no_reply, S}; 832 833handle_megaco_request({handle_trans_request, CH, PV, ARs}, 834 #mgc{req_info = P} = S) when is_pid(P) -> 835 d("handle_megaco_request(handle_trans_request,~p) -> entry", [P]), 836 P ! {req_received, self(), ARs}, 837 do_handle_trans_request(CH, PV, ARs, S); 838handle_megaco_request({handle_trans_request, CH, PV, ARs}, S) -> 839 d("handle_megaco_request(handle_trans_request) -> entry"), 840 do_handle_trans_request(CH, PV, ARs, S); 841 842handle_megaco_request({handle_trans_long_request, CH, PV, RD}, S) -> 843 d("handle_megaco_request(handle_long_trans_request) -> entry"), 844 Reply0 = handle_act_requests(CH, PV, RD, discard_ack), 845 Reply = 846 case S of 847 #mgc{req_action = ignore, req_timeout = To} -> 848 d("handle_megaco_request(handle_long_trans_request) -> " 849 "~n To: ~p", [To]), 850 {delay_reply, To, Reply0}; 851 _ -> 852 d("handle_megaco_request(handle_long_trans_request) -> " 853 "~n S: ~p", [S]), 854 Reply0 855 end, 856 {Reply, S}; 857 858handle_megaco_request({handle_trans_reply, _CH, _PV, _AR, _RD}, S) -> 859 {ok, S}; 860 861handle_megaco_request({handle_trans_ack, CH, PV, AS, AD}, 862 #mgc{ack_info = P} = S) when is_pid(P) -> 863 d("handle_megaco_request(handle_trans_ack,~p) -> entry when" 864 "~n CH: ~p" 865 "~n PV: ~p" 866 "~n AS: ~p" 867 "~n AD: ~p", [P, CH, PV, AS, AD]), 868 P ! {ack_received, self(), AS}, 869 {ok, S}; 870 871handle_megaco_request({handle_trans_ack, CH, PV, AS, AD}, S) -> 872 d("handle_megaco_request(handle_trans_ack) -> entry with" 873 "~n Conn Handle: ~p" 874 "~n Prot Version: ~p" 875 "~n Ack Status: ~p" 876 "~n Ack Data: ~p", [CH, PV, AS, AD]), 877 {ok, S}; 878 879handle_megaco_request({handle_unexpected_trans, CH, PV, TR}, S) -> 880 d("handle_megaco_request(handle_unexpected_trans) -> entry with" 881 "~n CH: ~p" 882 "~n PV: ~p" 883 "~n TR: ~p", [CH, PV, TR]), 884 {ok, S}; 885 886handle_megaco_request({handle_trans_request_abort, CH, PV, TI, Handler}, S) -> 887 d("handle_megaco_request(handle_trans_request_abort) -> entry with" 888 "~n CH: ~p" 889 "~n PV: ~p" 890 "~n TI: ~p" 891 "~n Handler: ~p", [CH, PV, TI, Handler]), 892 Reply = 893 case S#mgc.abort_info of 894 P when is_pid(P) -> 895 P ! {abort_received, self(), TI}, 896 ok; 897 _ -> 898 ok 899 end, 900 {Reply, S}. 901 902 903do_handle_trans_request(CH, PV, ARs, 904 #mgc{req_action = Action, req_timeout = To} = S) -> 905 d("do_handle_megaco_request(handle_trans_request) -> entry with" 906 "~n Action: ~p" 907 "~n To: ~p", [Action, To]), 908 case handle_act_requests(CH, PV, ARs, Action) of 909 {pending_ignore, ActReqs} -> 910 {{pending, ActReqs}, S#mgc{req_action = ignore}}; 911 Reply -> 912 {{delay_reply, To, Reply}, S} 913 end. 914 915 916handle_act_requests(_CH, _PV, _ActReqs, ignore) -> 917 ignore; 918handle_act_requests(_CH, _PV, ActReqs, pending) -> 919 {pending, ActReqs}; 920handle_act_requests(_CH, _PV, ActReqs, pending_ignore) -> 921 {pending_ignore, ActReqs}; 922handle_act_requests(CH, PV, ActReqs, handle_ack) -> 923 Reply = (catch do_handle_act_requests(CH, PV, ActReqs, [])), 924 {{handle_ack, ActReqs}, Reply}; 925handle_act_requests(CH, PV, ActReqs, handle_sloppy_ack) -> 926 Reply = (catch do_handle_act_requests(CH, PV, ActReqs, [])), 927 {{handle_sloppy_ack, ActReqs}, Reply}; 928handle_act_requests(CH, PV, ActReqs, _) -> 929 Reply = (catch do_handle_act_requests(CH, PV, ActReqs, [])), 930 {discard_ack, Reply}. 931 932do_handle_act_requests(_CH, _PV, [], ActReplies) -> 933 lists:reverse(ActReplies); 934do_handle_act_requests(CH, PV, [ActReq|ActReqs], ActReplies) -> 935 ActReply = handle_act_request(CH, PV, ActReq), 936 do_handle_act_requests(CH, PV, ActReqs, [ActReply|ActReplies]). 937 938handle_act_request(CH, PV, ActReq) -> 939 #'ActionRequest'{contextId = CtxId, commandRequests = Cmds} = ActReq, 940 CmdReplies = handle_cmd_requests(CH, PV, CtxId, Cmds), 941 #'ActionReply'{contextId = CtxId, 942 commandReply = CmdReplies}. 943 944handle_cmd_requests(CH, PV, ?megaco_null_context_id, 945 [#'CommandRequest'{command={serviceChangeReq,Req}}]) -> 946 Rep = service_change(CH, PV, Req), 947 [{serviceChangeReply, Rep}]; 948handle_cmd_requests(CH, PV, CtxId, Cmds) -> 949 do_handle_cmd_requests(CH, PV, CtxId, Cmds, []). 950 951do_handle_cmd_requests(_CH, _PV, _CtxId, [], CmdReplies) -> 952 lists:reverse(CmdReplies); 953do_handle_cmd_requests(CH, PV, CtxId, [Cmd|Cmds], CmdReplies) -> 954 CmdReply = handle_cmd_request(CH, PV, CtxId, Cmd), 955 do_handle_cmd_requests(CH, PV, CtxId, Cmds, [CmdReply|CmdReplies]). 956 957handle_cmd_request(CH, PV, CtxId, 958 #'CommandRequest'{command = {Tag,Req}}) -> 959 case Tag of 960 notifyReq -> 961 (catch handle_notify_req(CH,PV,CtxId,Req)); 962 963 serviceChangeReq -> 964 ED = cre_error_descr(?megaco_not_implemented, 965 "Service change only allowed " 966 "on null context handled"), 967 throw(ED); 968 969 _ -> 970 Code = ?megaco_not_implemented, 971 ED = cre_error_descr(Code,"Unknown command requst received:" 972 "~n Tag: ~p~n Req: ~p",[Tag,Req]), 973 throw(ED) 974 end. 975 976handle_notify_req(CH, PV, CtxId, 977 #'NotifyRequest'{terminationID = [Tid], 978 observedEventsDescriptor = EvDesc}) -> 979 handle_event(CH, PV, CtxId, Tid, EvDesc). 980 981handle_event(_CH, _PV, _Cid, Tid, EvDesc) -> 982 d("handle_event -> received" 983 "~n EvDesc: ~p" 984 "~n Tid: ~p", [EvDesc, Tid]), 985 {notifyReply, cre_notifyRep(Tid)}. 986 987 988service_change(CH, _PV, SCR) -> 989 SCP = SCR#'ServiceChangeRequest'.serviceChangeParms, 990 #'ServiceChangeParm'{serviceChangeAddress = Address, 991 serviceChangeProfile = Profile, 992 serviceChangeReason = [_Reason]} = SCP, 993 TermId = SCR#'ServiceChangeRequest'.terminationID, 994 if 995 TermId == [?megaco_root_termination_id] -> 996 MyMid = CH#megaco_conn_handle.local_mid, 997 Res = {serviceChangeResParms, 998 cre_serviceChangeResParms(MyMid, Address, Profile)}, 999 cre_serviceChangeReply(TermId, Res); 1000 true -> 1001 Res = {errorDescriptor, 1002 cre_error_descr(?megaco_not_implemented, 1003 "Only handled for root")}, 1004 cre_serviceChangeReply(TermId, Res) 1005 end. 1006 1007 1008 1009%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1010 1011cre_serviceChangeReply(TermId, Result) -> 1012 #'ServiceChangeReply'{terminationID = TermId, 1013 serviceChangeResult = Result}. 1014 1015cre_serviceChangeResParms(Mid, Addr, Prof) -> 1016 #'ServiceChangeResParm'{serviceChangeMgcId = Mid, 1017 serviceChangeAddress = Addr, 1018 serviceChangeProfile = Prof}. 1019 1020 1021cre_notifyRep(Tid) -> 1022 #'NotifyReply'{terminationID = [Tid]}. 1023 1024% cre_notifyRep(Tid,Err) -> 1025% #'NotifyReply'{terminationID = [Tid], errorDescriptor = Err}. 1026 1027cre_error_descr(Code,Text) -> 1028 #'ErrorDescriptor'{errorCode = Code, errorText = Text}. 1029 1030cre_error_descr(Code,FormatString,Args) -> 1031 Text = lists:flatten(io_lib:format(FormatString,Args)), 1032 cre_error_descr(Code,Text). 1033 1034 1035%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1036 1037notify_started(Parent) -> 1038 Parent ! {started, self()}. 1039 1040 1041%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1042 1043%% The megaco user callback interface 1044 1045handle_connect(CH, PV, Pid) -> 1046 case CH#megaco_conn_handle.remote_mid of 1047 preliminary_mid -> 1048 %% Avoids deadlock 1049 ok; 1050 _ -> 1051 Reply = request(Pid, {handle_connect, CH, PV}), 1052 Reply 1053 end. 1054 1055handle_disconnect(_CH, _PV, 1056 {user_disconnect, {Pid, ignore}}, 1057 Pid) -> 1058 ok; 1059handle_disconnect(CH, _PV, 1060 {user_disconnect, {Pid, cancel}}, 1061 Pid) -> 1062 megaco:cancel(CH, disconnected), 1063 ok; 1064handle_disconnect(CH, PV, R, Pid) -> 1065 request(Pid, {handle_disconnect, CH, PV, R}). 1066 1067handle_syntax_error(ReceiveHandle, ProtocolVersion, ErrorDescriptor, Pid) -> 1068 Req = {handle_syntax_error, ReceiveHandle, ProtocolVersion, 1069 ErrorDescriptor}, 1070 request(Pid, Req). 1071 1072handle_message_error(ConnHandle, ProtocolVersion, ErrorDescriptor, Pid) -> 1073 Req = {handle_message_error, ConnHandle, ProtocolVersion, ErrorDescriptor}, 1074 request(Pid, Req). 1075 1076handle_trans_request(CH, PV, AR, Pid) -> 1077 Reply = request(Pid, {handle_trans_request, CH, PV, AR}), 1078 Reply. 1079 1080handle_trans_long_request(ConnHandle, ProtocolVersion, ReqData, Pid) -> 1081 Req = {handle_trans_long_request, ConnHandle, ProtocolVersion, ReqData}, 1082 request(Pid, Req). 1083 1084handle_trans_reply(ConnHandle, ProtocolVersion, ActualReply, ReplyData, Pid) -> 1085 Req = {handle_trans_reply, ConnHandle, ProtocolVersion, 1086 ActualReply, ReplyData}, 1087 request(Pid, Req). 1088 1089handle_trans_ack(ConnHandle, ProtocolVersion, AckStatus, AckData, Pid) -> 1090 Req = {handle_trans_ack, ConnHandle, ProtocolVersion, AckStatus, AckData}, 1091 request(Pid, Req). 1092 1093handle_unexpected_trans(ConnHandle, ProtocolVersion, Trans, Pid) -> 1094 Req = {handle_unexpected_trans, ConnHandle, ProtocolVersion, Trans}, 1095 request(Pid, Req). 1096 1097handle_trans_request_abort(ConnHandle, ProtocolVersion, TransId, 1098 Handler, Pid) -> 1099 Req = {handle_trans_request_abort, 1100 ConnHandle, ProtocolVersion, TransId, Handler}, 1101 request(Pid, Req). 1102 1103 1104request(Pid, Request) -> 1105 Pid ! {request, Request, self()}, 1106 receive 1107 {reply, {delay_reply, To, Reply}, Pid} -> 1108 megaco:report_event(ignore, self(), Pid, 1109 "reply: delay_reply", [To, Reply]), 1110 sleep(To), 1111 megaco:report_event(ignore, self(), Pid, 1112 "reply: delay done now return", []), 1113 Reply; 1114 {reply, {exit, To, Reason}, Pid} -> 1115 megaco:report_event(ignore, self(), Pid, 1116 "reply: exit", [To, Reason]), 1117 sleep(To), 1118 megaco:report_event(ignore, self(), Pid, 1119 "reply: sleep done now exit", []), 1120 exit(Reason); 1121 {reply, Reply, Pid} -> 1122 megaco:report_event(ignore, self(), Pid, "reply", [Reply]), 1123 Reply 1124 end. 1125 1126 1127reply(To, Reply) -> 1128 To ! {reply, Reply, self()}. 1129 1130 1131%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1132 1133sleep(X) -> 1134 d("sleep -> ~w", [X]), 1135 receive after X -> ok end. 1136 1137 1138info_msg(F,A) -> error_logger:info_msg("MGC: " ++ F ++ "~n",A). 1139error_msg(F,A) -> error_logger:error_msg("MGC: " ++ F ++ "~n",A). 1140 1141 1142get_encoding_module(RI) -> 1143 case (catch get_conf(encoding_module, RI)) of 1144 {error, _} -> 1145 undefined; 1146 Val -> 1147 Val 1148 end. 1149 1150get_encoding_config(RI, EM) -> 1151 case text_codec(EM) of 1152 true -> 1153 case megaco:system_info(text_config) of 1154 [Conf] when is_list(Conf) -> 1155 Conf; 1156 _ -> 1157 [] 1158 end; 1159 1160 false -> 1161 get_conf(encoding_config, RI) 1162 end. 1163 1164text_codec(megaco_compact_text_encoder) -> 1165 true; 1166text_codec(megaco_pretty_text_encoder) -> 1167 true; 1168text_codec(_) -> 1169 false. 1170 1171 1172get_transport_module(RI) -> 1173 get_conf(transport_module, RI). 1174 1175get_transport_port(RI) -> 1176 get_conf(port, RI). 1177 1178get_transport_opts(RI) -> 1179 get_conf(transport_opts, RI, []). 1180 1181 1182get_conf(Key, Config) -> 1183 case lists:keysearch(Key, 1, Config) of 1184 {value, {Key, Val}} -> 1185 Val; 1186 _ -> 1187 exit({error, {not_found, Key, Config}}) 1188 end. 1189 1190get_conf(Key, Config, Default) -> 1191 case lists:keysearch(Key, 1, Config) of 1192 {value, {Key, Val}} -> 1193 Val; 1194 _ -> 1195 Default 1196 end. 1197 1198 1199%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1200 1201random_init() -> 1202 ok. 1203 1204random(N) -> 1205 rand:uniform(N). 1206 1207 1208display_system_info(Mid) -> 1209 display_system_info(Mid, ""). 1210 1211display_system_info(Mid, Pre) -> 1212 TimeStr = ?FTS(), 1213 MibStr = lists:flatten(io_lib:format("~p ", [Mid])), 1214 megaco_test_lib:display_system_info(MibStr ++ Pre ++ TimeStr). 1215 1216 1217create_timer(Time, Event) -> 1218 erlang:send_after(Time, self(), {Event, Time}). 1219 1220cancel_timer(undefined) -> 1221 ok; 1222cancel_timer(Ref) -> 1223 erlang:cancel_timer(Ref). 1224 1225 1226%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1227 1228e(F, A) -> 1229 print(error, get(verbosity), "ERROR", F, A). 1230 1231i(F) -> 1232 i(F, []). 1233 1234i(F, A) -> 1235 print(info, get(verbosity), "INFO", F, A). 1236 1237 1238d(F) -> 1239 d(F, []). 1240 1241d(F, A) -> 1242 print(debug, get(verbosity), "DBG", F, A). 1243 1244 1245printable(error, _) -> true; 1246printable(_, debug) -> true; 1247printable(info, info) -> true; 1248printable(_,_) -> false. 1249 1250print(Severity, Verbosity, P, F, A) -> 1251 print(printable(Severity,Verbosity), P, F, A). 1252 1253print(true, P, F, A) -> 1254 print(P, F, A); 1255print(_, _, _, _) -> 1256 ok. 1257 1258print(P, F, A) -> 1259 io:format("*** [~s] [~s] ~p ~s ***" 1260 "~n " ++ F ++ "~n~n", 1261 [?FTS(), P, self(), get(sname) | A]). 1262 1263 1264