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 "MG" used by the test suite 24%%---------------------------------------------------------------------- 25-module(megaco_test_mg). 26 27-export([start/5, start/6, stop/1, 28 get_stats/1, reset_stats/1, 29 user_info/1, user_info/2, 30 update_user_info/3, 31 conn_info/1, conn_info/2, 32 update_conn_info/3, 33 service_change/1, 34 ack_info/2, rep_info/2, 35 group_requests/2, 36 notify_request/1, 37 await_notify_reply/1, 38 notify_request_and_reply/1, 39 cancel_request/2, 40 apply_load/2, 41 apply_multi_load/3, 42 enable_test_code/4, 43 encode_ar_first/2, 44 verbosity/2]). 45 46-export([mg/3, notify_request_handler_main/5]). 47-export([loader_main/4]). 48 49%% Megaco callback api 50-export([ 51 handle_connect/4, 52 handle_disconnect/5, 53 handle_syntax_error/5, 54 handle_message_error/5, 55 handle_trans_request/5, 56 handle_trans_long_request/5, 57 handle_trans_reply/6, 58 handle_trans_ack/6 59 ]). 60 61-include("megaco_test_lib.hrl"). 62-include_lib("megaco/include/megaco.hrl"). 63-include_lib("megaco/include/megaco_message_v1.hrl"). 64 65-define(A4444, tid(255*256*256) ). 66-define(A4445, tid(255*256*256 + 255) ). 67-define(A5555, tid(255*256*256 + 255*256) ). 68-define(A5556, tid(255*256*256 + 255*256 + 255) ). 69 70-record(mg, {parent = undefined, 71 inet_backend = default, 72 mid = undefined, 73 state = initiated, 74 req_handler = undefined, 75 call_mode = async, 76 group_size = 1, 77 encode_ar_first = false, 78 ack_info = undefined, 79 rep_info = undefined, 80 load_counter = 0, 81 reply_counter = 0, 82 mload_info = undefined, 83 dsi_timer, 84 evs = []}). 85 86-define(EVS_MAX, 10). 87 88 89%%% -------------------------------------------------------------------- 90 91start(Node, Mid, Encoding, Transport, Verbosity) -> 92 %% Conf = [{megaco_trace, io}], 93 %% Conf = [{megaco_trace, "megaco-mg.trace"}], 94 Conf = [{megaco_trace, false}], 95 start(Node, Mid, Encoding, Transport, Conf, Verbosity). 96 97start(Node, Mid, Encoding, Transport, Conf, Verbosity) -> 98 d("start mg[~p]: ~p" 99 "~n Encoding: ~p" 100 "~n Transport: ~p" 101 "~n Conf: ~p", [Node, Mid, Encoding, Transport, Conf]), 102 RI1 = encoding_config(Encoding), 103 RI2 = transport_config(Transport), 104 {RI3, Conf1} = transport_opts(Conf), 105 RI = {receive_info, RI1 ++ RI2 ++ RI3}, 106 Config = [{local_mid, Mid}, RI] ++ Conf1, 107 Self = self(), 108 Fun = 109 fun() -> 110 i("LOADER(~p,~p) started", [self(),node()]), 111 case (catch mg(Self, Verbosity, Config)) of 112 {'EXIT', Reason} -> 113 e("LOADER(~p,~p) terminating with exit" 114 "~n ~p", [Self, node(), Reason]), 115 exit(Reason); 116 Else -> 117 i("LOADER(~p,~p) terminating with" 118 "~n ~p", [Self, node(), Else]), 119 Else 120 end 121 end, 122 true = erlang:monitor_node(Node, true), 123 {Pid, MRef} = spawn_monitor(Node, Fun), 124 NodePing = net_adm:ping(Node), 125 ProcInfo = (catch proc_info(Pid)), 126 i("start mg[~p] -> ~p" 127 "~n self(): ~p" 128 "~n node(): ~p" 129 "~n Node ping: ~p" 130 "~n Loader: ~p" 131 "~n Monitor ref: ~p" 132 "~n Process info: ~p", 133 [Node, Pid, 134 Self, node(), NodePing, Pid, MRef, ProcInfo]), 135 await_started(Node, MRef, Pid). 136 137proc_info(Pid) -> 138 rpc:call(node(Pid), erlang, process_info, [Pid]). 139 140encoding_config({Encoding, EC}) when is_atom(Encoding) andalso is_list(EC) -> 141 {Mod, Port} = select_encoding(Encoding), 142 [{encoding_module, Mod}, 143 {encoding_config, EC}, 144 {port, Port}]; 145encoding_config(Encoding) when is_atom(Encoding) -> 146 {Mod, Port} = select_encoding(Encoding), 147 [{encoding_module, Mod}, 148 {encoding_config, []}, 149 {port, Port}]; 150encoding_config(Encoding) -> 151 throw({error, {invalid_encoding, Encoding}}). 152 153select_encoding(text) -> 154 {megaco_pretty_text_encoder, 2944}; 155select_encoding(pretty_text) -> 156 {megaco_pretty_text_encoder, 2944}; 157select_encoding(compact_text) -> 158 {megaco_compact_text_encoder, 2944}; 159select_encoding(binary) -> 160 {megaco_ber_encoder, 2945}; 161select_encoding(erl_dist) -> 162 {megaco_erl_dist_encoder, 2946}; 163select_encoding(Encoding) -> 164 throw({error, {invalid_encoding, Encoding}}). 165 166transport_config(tcp) -> 167 [{transport_module, megaco_tcp}]; 168transport_config(udp) -> 169 [{transport_module, megaco_udp}]; 170transport_config(TransportConfig) when is_list(TransportConfig) -> 171 {value, {transport, Trans}} = 172 lists:keysearch(transport, 1, TransportConfig), 173 transport_config(Trans) ++ 174 case lists:keysearch(host, 1, TransportConfig) of 175 {value, Value} -> 176 [Value]; 177 false -> 178 [] 179 end. 180 181transport_opts(Config) -> 182 case lists:keysearch(transport_opts, 1, Config) of 183 {value, TO} -> 184 Config1 = lists:keydelete(transport_opts, 1, Config), 185 {[TO], Config1}; 186 false -> 187 {[], Config} 188 end. 189 190 191await_started(Node, MonRef, Pid) -> 192 i("await_started -> entry with" 193 "~n MonRef: ~p" 194 "~n Pid: ~p", [MonRef, Pid]), 195 receive 196 {started, Pid} -> 197 d("await_started ~p - started" 198 "~n Process Info: ~p", [Pid, (catch proc_info(Pid))]), 199 true = erlang:monitor_node(Node, false), 200 erlang:demonitor(MonRef), 201 {ok, Pid}; 202 203 {nodedown, Node} -> 204 i("await_started ~p - received node down", [Pid]), 205 exit({node_down, Node}); 206 207 {'DOWN', MonRef, process, Pid, Info} -> 208 e("await_started ~p - received down signal: ~p", 209 [Pid, Info]), 210 true = erlang:monitor_node(Node, false), 211 exit({failed_starting, Pid, Info}); 212 213 {'EXIT', Pid, Reason} -> 214 e("await_started ~p - received exit signal: ~p", [Pid, Reason]), 215 true = erlang:monitor_node(Node, false), 216 exit({failed_starting, Pid, Reason}) 217 218 %% This timeout was originally 10 secs, but on some debug compiled 219 %% platforms, it was simply not long enough 220 after 20000 -> 221 NodePing = net_adm:ping(Node), 222 ProcInfo = (catch proc_info(Pid)), 223 FlushQ = megaco_test_lib:flush(), 224 e("await_started ~p - timeout: " 225 "~n net_adm:ping(~p): ~p" 226 "~n Process info: ~p" 227 "~n Messages in my queue: ~p", 228 [Pid, Node, NodePing, ProcInfo, FlushQ]), 229 true = erlang:monitor_node(Node, false), 230 exit({error, timeout}) 231 end. 232 233 234verbosity(Pid, V) -> 235 Pid ! {verbosity, V, self()}. 236 237 238stop(Pid) -> 239 server_request(Pid, stop, stopped). 240 241 242get_stats(Pid) -> 243 server_request(Pid, statistics, statistics_reply). 244 245reset_stats(Pid) -> 246 server_request(Pid, reset_stats, reset_stats_ack). 247 248 249user_info(Pid) -> 250 server_request(Pid, {user_info, all}, user_info_ack). 251 252user_info(Pid, Tag) when is_atom(Tag) -> 253 server_request(Pid, {user_info, Tag}, user_info_ack). 254 255 256update_user_info(Pid, Tag, Val) -> 257 server_request(Pid, {update_user_info, Tag, Val}, update_user_info_ack). 258 259 260conn_info(Pid) -> 261 server_request(Pid, {conn_info, all}, conn_info_ack). 262 263conn_info(Pid, Tag) when is_atom(Tag) -> 264 server_request(Pid, {conn_info, Tag}, conn_info_ack). 265 266 267update_conn_info(Pid, Tag, Val) -> 268 server_request(Pid, {update_conn_info, Tag, Val}, update_conn_info_ack). 269 270 271enable_test_code(Pid, Module, Where, Fun) 272 when is_atom(Module) andalso is_atom(Where) andalso is_function(Fun) -> 273 Tag = {Module, Where}, 274 server_request(Pid, {enable_test_code, Tag, Fun}, enable_test_code_reply). 275 276encode_ar_first(Pid, New) when is_atom(New) -> 277 server_request(Pid, {encode_ar_first, New}, encode_ar_first_reply). 278 279service_change(Pid) -> 280 server_request(Pid, service_change, service_change_reply). 281 282 283group_requests(Pid, N) -> 284 server_request(Pid, {group_requests, N}, group_requests_reply). 285 286 287ack_info(Pid, InfoPid) -> 288 Pid ! {{ack_info, InfoPid}, self()}. 289 290rep_info(Pid, InfoPid) -> 291 Pid ! {{rep_info, InfoPid}, self()}. 292 293 294notify_request(Pid) -> 295 Pid ! {notify_request, self()}. 296 297 298await_notify_reply(Pid) -> 299 await_reply(Pid, notify_request_reply). 300 301 302notify_request_and_reply(Pid) -> 303 notify_request(Pid), 304 await_notify_reply(Pid). 305 306 307cancel_request(Pid, Reason) -> 308 server_request(Pid, cancel_request, Reason, cancel_request_reply). 309 310 311apply_load(Pid, CounterStart) -> 312 server_request(Pid, apply_load, CounterStart, apply_load_ack). 313 314 315apply_multi_load(Pid, NumLoaders, NumReq) -> 316 server_request(Pid, apply_multi_load, {NumLoaders, NumReq}, apply_multi_load_ack). 317 318 319server_request(Pid, Req, ReplyTag) -> 320 Pid ! {Req, self()}, 321 await_reply(Pid, ReplyTag). 322 323server_request(Pid, Req, ReqData, ReplyTag) -> 324 Pid ! {Req, ReqData, self()}, 325 await_reply(Pid, ReplyTag). 326 327await_reply(Pid, ReplyTag) -> 328 await_reply(Pid, ReplyTag, infinity). 329 330await_reply(Pid, ReplyTag, Timeout) -> 331 receive 332 {ReplyTag, Reply, Pid} -> 333 Reply; 334 {'EXIT', Pid, Reason} -> 335 exit({failed, ReplyTag, Pid, Reason}) 336 after Timeout -> 337 exit({timeout, ReplyTag, Pid}) 338 end. 339 340 341server_reply(Pid, ReplyTag, Reply) -> 342 Pid ! {ReplyTag, Reply, self()}. 343 344 345%%% -------------------------------------------------------------------- 346 347 348mg(Parent, Verbosity, Config) -> 349 process_flag(trap_exit, true), 350 put(sname, "MG"), 351 %% put(verbosity, Verbosity), 352 put(verbosity, debug), % Enable debug printouts during init 353 i("mg -> starting"), 354 %% megaco:enable_trace(max, io), 355 case (catch init(Config)) of 356 {error, _} = Error -> 357 exit(Error); 358 359 {'EXIT', Reason} -> 360 exit({init_failed, Reason}); 361 362 {ok, IB, Mid, DSITimer} -> 363 notify_started(Parent), 364 MG = #mg{parent = Parent, 365 inet_backend = IB, 366 mid = Mid, 367 dsi_timer = DSITimer}, 368 i("mg -> started"), 369 put(verbosity, Verbosity), 370 case (catch loop(evs(MG, started))) of 371 {'EXIT', normal} -> 372 exit(normal); 373 {'EXIT', Reason} -> 374 e("mg failed with reason:" 375 "~n ~p", [Reason]), 376 exit(Reason); 377 Else -> 378 e("mg terminated:" 379 "~n ~p", [Else]), 380 exit({unexpected, Else}) 381 end 382 end. 383 384init(Config) -> 385 d("init -> entry with" 386 "~n Config: ~p", [Config]), 387 random_init(), 388 d("init -> random initiated"), 389 390 IB = get_conf(inet_backend, Config, default), 391 Mid = get_conf(local_mid, Config), 392 RI = get_conf(receive_info, Config), 393 394 d("init -> " 395 "~n Inet Backend: ~p" 396 "~n Mid: ~p" 397 "~n RI: ~p", [IB, Mid, RI]), 398 399 DSITimer = 400 case get_conf(display_system_info, Config, undefined) of 401 Time when is_integer(Time) -> 402 d("init -> creating display system info timer"), 403 create_timer(Time, display_system_info); 404 _ -> 405 undefined 406 end, 407 Conf0 = lists:keydelete(display_system_info, 1, Config), 408 Conf1 = lists:keydelete(inet_backend, 1, Conf0), 409 410 d("init -> start megaco"), 411 application:start(megaco), 412 413 414 d("init -> possibly enable megaco trace"), 415 case lists:keysearch(megaco_trace, 1, Conf0) of 416 {value, {megaco_trace, true}} -> 417 megaco:enable_trace(max, io); 418 {value, {megaco_trace, io}} -> 419 megaco:enable_trace(max, io); 420 {value, {megaco_trace, File}} when is_list(File) -> 421 megaco:enable_trace(max, File); 422 _ -> 423 ok 424 end, 425 Conf2 = lists:keydelete(megaco_trace, 1, Conf1), 426 427 d("init -> start megaco user"), 428 Conf3 = lists:keydelete(local_mid, 1, Conf2), 429 Conf4 = lists:keydelete(receive_info, 1, Conf3), 430 ok = megaco:start_user(Mid, Conf4), 431 d("init -> update user info (user_mod)"), 432 ok = megaco:update_user_info(Mid, user_mod, ?MODULE), 433 d("init -> update user info (user_args)"), 434 ok = megaco:update_user_info(Mid, user_args, [self(), Mid]), 435 436 d("init -> get user info (receive_handle)"), 437 RH = megaco:user_info(Mid, receive_handle), 438 d("init -> parse receive info"), 439 {MgcPort, MgcHost, RH1, TO} = parse_receive_info(RI, RH), 440 d("init -> start transport (with ~p)", [TO]), 441 {ok, _CH} = start_transport(IB, MgcPort, MgcHost, RH1, TO), 442 {ok, IB, Mid, DSITimer}. 443 444 445loop(#mg{parent = Parent, mid = Mid} = S) -> 446 d("loop -> await request", []), 447 receive 448 {display_system_info, Time} -> 449 display_system_info(S#mg.mid), 450 NewTimer = create_timer(Time, display_system_info), 451 loop(evs(S#mg{dsi_timer = NewTimer}, {dsi, Time})); 452 453 {verbosity, V, Parent} -> 454 i("loop -> received new verbosity: ~p", [V]), 455 put(verbosity,V), 456 loop(evs(S, {verb, V})); 457 458 459 {stop, Parent} -> 460 i("loop -> stopping", []), 461 display_system_info(S#mg.mid, "at finish "), 462 cancel_timer(S#mg.dsi_timer), 463 Res = do_stop(Mid), 464 d("loop -> stop result: ~p", [Res]), 465 server_reply(Parent, stopped, {ok, Res}), 466 done(evs(S, stop), normal); 467 468 {{enable_test_code, Tag, Fun}, Parent} -> 469 i("loop -> enable_test_code: ~p, ~p", [Tag, Fun]), 470 Reply = (catch ets:insert(megaco_test_data, {Tag, Fun})), 471 d("loop -> enable_test_code -> " 472 "~n Reply: ~p" 473 "~n ets:tab2list(megaco_test_data): ~p", 474 [Reply,ets:tab2list(megaco_test_data)]), 475 server_reply(Parent, enable_test_code_reply, Reply), 476 loop(evs(S, {enable_test_code, Tag})); 477 478 {{encode_ar_first, EAF}, Parent} -> 479 i("loop -> encode_ar_first: ~p", [EAF]), 480 {Reply, S1} = handle_encode_ar_first(S, EAF), 481 server_reply(Parent, encode_ar_first_reply, Reply), 482 loop(evs(S1#mg{encode_ar_first = EAF}, {enc_arf, EAF})); 483 484 %% Give me statistics 485 {statistics, Parent} -> 486 i("loop -> got request for statistics"), 487 Stats = do_get_statistics(Mid), 488 server_reply(Parent, statistics_reply, {ok, Stats}), 489 loop(evs(S, stats)); 490 491 {reset_stats, Parent} -> 492 i("loop -> got request to reset stats counters"), 493 do_reset_stats(Mid), 494 server_reply(Parent, reset_stats_ack, ok), 495 loop(evs(S, rst_stats)); 496 497 {{user_info, Tag}, Parent} -> 498 i("loop -> got user_info request for ~w", [Tag]), 499 Res = do_get_user_info(Mid, Tag), 500 d("loop -> Res: ~p", [Res]), 501 server_reply(Parent, user_info_ack, Res), 502 loop(evs(S, {ui, Tag})); 503 504 {{update_user_info, Tag, Val}, Parent} -> 505 i("loop -> got update_user_info: ~w -> ~p", [Tag, Val]), 506 Res = do_update_user_info(Mid, Tag, Val), 507 d("loop -> Res: ~p", [Res]), 508 server_reply(Parent, update_user_info_ack, Res), 509 loop(evs(S, {uui, {Tag, Val}})); 510 511 {{conn_info, Tag}, Parent} -> 512 i("loop -> got conn_info request for ~w", [Tag]), 513 Res = do_get_conn_info(Mid, Tag), 514 server_reply(Parent, conn_info_ack, Res), 515 loop(evs(S, {ci, Tag})); 516 517 {{update_conn_info, Tag, Val}, Parent} -> 518 i("loop -> got update_conn_info: ~w -> ~p", [Tag, Val]), 519 Res = do_update_conn_info(Mid, Tag, Val), 520 server_reply(Parent, update_conn_info_ack, Res), 521 loop(evs(S, {uci, {Tag, Val}})); 522 523 524 %% Do a service change 525 %% No server-reply here. Since the service change is 526 %% async, the reply (from the MGC) will come later. 527 {service_change, Parent} -> 528 i("loop -> received request to perform service change"), 529 S1 = 530 case (catch do_service_change(S)) of 531 {ok, MG} -> 532 d("loop -> service change initiated"), 533 MG; 534 Error -> 535 d("loop -> service change failed: ~p", [Error]), 536 server_reply(Parent, service_change_reply, Error), 537 S 538 end, 539 loop(evs(S1, svc_ch)); 540 541 {{group_requests, N}, Parent} when N > 0 -> 542 i("loop -> received group_requests ~p", [N]), 543 Reply = {ok, S#mg.group_size}, 544 server_reply(Parent, group_requests_reply, Reply), 545 loop(evs(S#mg{group_size = N}, {grp_reqs, N})); 546 547 {{ack_info, To}, Parent} -> 548 i("loop -> received request to inform about received ack's"), 549 loop(evs(S#mg{ack_info = To}, {acki, To})); 550 551 {{rep_info, To}, Parent} -> 552 i("loop -> received request to inform about received rep's "), 553 loop(evs(S#mg{rep_info = To}, {repi, To})); 554 555 %% Make a sync-call 556 {notify_request, Parent} -> 557 i("loop -> received request to send notify request "), 558 {Res, S1} = do_handle_notify_request(S), 559 d("loop -> notify request result: ~p", [Res]), 560 loop(evs(S1, not_req)); 561 562 %% sync-call complete 563 {notify_request_complete, NotifyReply, Pid} -> 564 i("loop -> received notify request complete from " 565 "~n ~p with" 566 "~n NotifyReply: ~p", 567 [Pid, NotifyReply]), 568 server_reply(Parent, notify_request_reply, NotifyReply), 569 loop(evs(S#mg{req_handler = undefined}, {not_reqc, NotifyReply})); 570 571 572 %% cancel requests 573 {cancel_request, Reason, Parent} -> 574 i("loop -> received request to cancel (all) megaco requests"), 575 Res = do_cancel_requests(Mid, Reason), 576 server_reply(Parent, cancel_request_reply, Res), 577 loop(evs(S, {creq, Reason})); 578 579 580 %% Apply multi-load 581 {apply_multi_load, {NL, NR}, Parent} -> 582 i("loop -> received apply_multi_load request: ~w, ~w", [NL, NR]), 583 S1 = start_loaders(S, NL, NR), 584 loop(evs(S1, {apply_mload, {NL, NR}})); 585 586 587 %% Apply some load 588 {apply_load, Times, Parent} -> 589 i("loop -> received apply_load request: ~w", [Times]), 590 S1 = 591 case update_load_times(S, Times) of 592 {ok, MG} -> 593 apply_load_timer(), 594 server_reply(Parent, apply_load_ack, ok), 595 MG; 596 Error -> 597 server_reply(Parent, apply_load_ack, Error), 598 S 599 end, 600 loop(evs(S1, {apply_load, Times})); 601 602 {apply_load_timeout, _} -> 603 d("loop -> received apply_load timeout", []), 604 S1 = do_apply_load(S), 605 loop(evs(S1, apply_loadto)); 606 607 608 %% Megaco callback messages 609 {request, Request, Mid, From} -> 610 d("loop -> received megaco request: " 611 "~n ~p" 612 "~n Mid: ~p" 613 "~n From: ~p", 614 [Request, Mid, From]), 615 {Reply, S1} = handle_megaco_request(S, Request), 616 d("loop -> send (megaco callback) request reply:" 617 "~n ~p", [Reply]), 618 From ! {reply, Reply, self()}, 619 loop(evs(S1, {req, {Request, Mid, From}})); 620 621 622 {'EXIT', Pid, Reason} -> 623 i("loop -> received exit signal from ~p: " 624 "~n ~p", [Pid, Reason]), 625 S1 = handle_exit(S, Pid, Reason), 626 loop(evs(S1, {exit, {Pid, Reason}})); 627 628 629 Invalid -> 630 error_msg("received invalid request: " 631 "~n ~p", [Invalid]), 632 loop(evs(S, {invalid, Invalid})) 633 634 end. 635 636 637evs(#mg{evs = EVS} = S, Ev) when (length(EVS) < ?EVS_MAX) -> 638 S#mg{evs = [{?FTS(), Ev}|EVS]}; 639evs(#mg{evs = EVS} = S, Ev) -> 640 S#mg{evs = [{?FTS(), Ev}|lists:droplast(EVS)]}. 641 642done(#mg{evs = EVS}, Reason) -> 643 info_msg("Exiting with latest event(s): " 644 "~n ~p" 645 "~n", [EVS]), 646 exit(Reason). 647 648 649handle_encode_ar_first(#mg{encode_ar_first = Old} = MG, New) 650 when (New =:= true) orelse (New =:= false) -> 651 {{ok, Old}, MG#mg{encode_ar_first = New}}; 652handle_encode_ar_first(MG, New) -> 653 {{error, {invalid_value, New}}, MG}. 654 655 656%% 657%% Stop user 658%% 659do_stop(Mid) -> 660 d("do_stop -> stopping user ~p", [Mid]), 661 Disco = fun close_conn/1, 662 lists:map(Disco, megaco:user_info(Mid, connections)), 663 megaco:stop_user(Mid). 664 665close_conn(CH) -> 666 d("do_stop -> closing connection ~p", [CH]), 667 Reason = {stopped_by_user,self()}, 668 Pid = megaco:conn_info(CH, control_pid), 669 SendMod = megaco:conn_info(CH, send_mod), 670 SendHandle = megaco:conn_info(CH, send_handle), 671 megaco:disconnect(CH, Reason), 672 megaco:cancel(CH, Reason), 673 case SendMod of 674 megaco_tcp -> megaco_tcp:close(SendHandle); 675 megaco_udp -> megaco_udp:close(SendHandle); 676 SendMod -> exit(Pid, Reason) 677 end. 678 679 680 681%% 682%% Get statistics 683%% 684do_get_statistics(Mid) -> 685 case megaco:user_info(Mid, connections) of 686 [CH] -> 687 do_get_conn_statistics(CH); 688 [] -> 689 [] 690 end. 691 692do_get_conn_statistics(CH) -> 693 {ok, Gen} = megaco:get_stats(), 694 %% Pid = megaco:conn_info(CH, control_pid), 695 SendMod = megaco:conn_info(CH, send_mod), 696 SendHandle = megaco:conn_info(CH, send_handle), 697 {ok, Trans} = 698 case SendMod of 699 megaco_tcp -> megaco_tcp:get_stats(SendHandle); 700 megaco_udp -> megaco_udp:get_stats(SendHandle) 701 end, 702 [{gen, Gen}, {trans, Trans}]. 703 704 705%% 706%% reset user stats 707%% 708do_reset_stats(Mid) -> 709 %% We only have one connection 710 [CH] = megaco:user_info(Mid, connections), 711 do_reset_stats1(CH). 712 713do_reset_stats1(CH) -> 714 megaco:reset_stats(), 715 case (catch megaco:conn_info(CH, send_mod)) of 716 {error, Reason} -> 717 error_msg("unexpected result when retrieving send module for " 718 "own connection ~p: ~p. " 719 "~nexiting...", [CH, Reason]), 720 exit({invalid_connection, CH, Reason}); 721 {'EXIT', Reason} -> 722 error_msg("exit signal when retrieving send module for " 723 "own connection ~p: ~p. " 724 "~nexiting...", [CH, Reason]), 725 exit({invalid_connection, CH, Reason}); 726 SendMod when is_atom(SendMod) -> 727 SendMod:reset_stats() 728 end. 729 730 731%% 732%% Get user info for user 733%% 734do_get_user_info(Mid, all = Tag) -> 735 case (catch megaco:user_info(Mid, Tag)) of 736 L when is_list(L) -> 737 lists:sort(L); 738 Else -> 739 Else 740 end; 741do_get_user_info(Mid, Tag) -> 742 (catch megaco:user_info(Mid, Tag)). 743 744 745%% 746%% Update user info for user 747%% 748do_update_user_info(Mid, Tag, Val) -> 749 (catch megaco:update_user_info(Mid, Tag, Val)). 750 751 752%% 753%% Get conn info 754%% 755do_get_conn_info(CH, all = Tag) when is_record(CH, megaco_conn_handle) -> 756 case (catch megaco:conn_info(CH, Tag)) of 757 L when is_list(L) -> 758 lists:sort(L); 759 Else -> 760 Else 761 end; 762do_get_conn_info(CH, Tag) when is_record(CH, megaco_conn_handle) -> 763 (catch megaco:conn_info(CH, Tag)); 764do_get_conn_info(Mid, Tag) -> 765 case megaco:user_info(Mid, connections) of 766 [CH|_] -> 767 do_get_conn_info(CH, Tag); 768 [] -> 769 [] 770 end. 771 772%% 773%% Update conn info for user 774%% 775do_update_conn_info(Mid, Tag, Val) -> 776 %% We only have one connection 777 [CH] = megaco:user_info(Mid, connections), 778 (catch megaco:update_conn_info(CH, Tag, Val)). 779 780 781 782 783%% 784%% Perform service change 785%% 786do_service_change(#mg{mid = Mid, 787 state = initiated, 788 encode_ar_first = EAF} = MG) -> 789 %% We only have one connection 790 d("do service change for ~p", [Mid]), 791 [CH] = megaco:user_info(Mid, connections), 792 Method = restart, 793 Reason = ?megaco_cold_boot, 794 case do_service_change(CH, Method, EAF, Reason) of 795 ok -> 796 {ok, MG#mg{state = connecting}}; 797 Error -> 798 d("service change for ~p failed: ~n~p", [Mid, Error]), 799 Error 800 end; 801do_service_change(#mg{state = State} = MG) -> 802 {{error, {invalid_state, State}}, MG}. 803 804do_service_change(ConnHandle, Method, EAF, Reason) -> 805 d("send service change using:" 806 "~n ConnHandle: ~p" 807 "~n Method: ~p" 808 "~n EAF: ~p" 809 "~n Reason: ~p", [ConnHandle, Method, EAF, Reason]), 810 SCP = cre_serviceChangeParm(Method, [Reason]), 811 TermId = [?megaco_root_termination_id], 812 SCR = cre_serviceChangeReq(TermId, SCP), 813 CR = cre_commandReq({serviceChangeReq, SCR}), 814 AR = cre_actionReq(?megaco_null_context_id,[CR]), 815 send_async(EAF, ConnHandle, [AR], []). 816 817 818%% Make a sync call 819do_handle_notify_request(#mg{mid = Mid, 820 group_size = N, 821 encode_ar_first = EAF, 822 state = connected} = MG) -> 823 d("do_handle_notify_request -> entry"), 824 [CH] = megaco:user_info(Mid, connections), 825 Pid = start_notify_request_handler(EAF, CH, N), 826 {ok, MG#mg{req_handler = Pid}}; 827do_handle_notify_request(#mg{state = State} = MG) -> 828 d("do_handle_notify_request -> entry with" 829 "~n State: ~p", [State]), 830 {{error, {invalid_state, State}}, MG}. 831 832 833 834%% 835%% Cancel requests 836%% 837do_cancel_requests(Mid, Reason) -> 838 [CH] = megaco:user_info(Mid, connections), 839 megaco:cancel(CH, Reason). 840 841 842%% 843%% Apply multi load 844%% 845start_loaders(#mg{mid = Mid, encode_ar_first = EAF} = MG, NumLoaders, Times) -> 846 [CH] = megaco:user_info(Mid, connections), 847 Env = get(), 848 Loaders = start_loaders1(NumLoaders, [], [Env, EAF, Times, CH]), 849 d("start_loaders -> Loaders: ~n~w", [Loaders]), 850 MG#mg{mload_info = {Loaders, 0, 0}}. 851 852start_loaders1(0, Acc, _) -> 853 Acc; 854start_loaders1(N, Acc, Args) -> 855 Pid = spawn_link(?MODULE, loader_main, Args), 856 start_loaders1(N-1, [Pid|Acc], Args). 857 858loader_main(Env, EAF, N, CH) -> 859 lists:foreach(fun({Tag,Val}) -> put(Tag,Val) end, Env), 860 loader_main(EAF, N, CH). 861 862loader_main(_EAF, 0, _) -> 863 d("loader_main -> done"), 864 exit(loader_done); 865loader_main(EAF, N, CH) -> 866 d("loader_main -> entry with: ~w", [N]), 867 {Act, _} = make_notify_request(), 868 _Res = send_sync(EAF, CH, Act, []), 869 loader_main(EAF, N-1, CH). 870 871 872 873handle_exit(#mg{parent = Pid} = S, Pid, Reason) -> 874 error_msg("received exit from the parent:" 875 "~n ~p", [Reason]), 876 done(S, {parent_terminated, Reason}); 877 878handle_exit(#mg{parent = Parent, req_handler = Pid} = MG, Pid, Reason) -> 879 error_msg("received unexpected exit from the request handler:" 880 "~n ~p", [Reason]), 881 server_reply(Parent, notify_request_reply, 882 {error, {request_handler_exit, Reason}}), 883 MG#mg{req_handler = undefined}; 884 885handle_exit(#mg{parent = Parent, mload_info = {Loaders0, Ok, Err}} = MG, 886 Pid, loader_done) -> 887 d("handle_exit(loader_done) -> entry when" 888 "~n Loaders0: ~p" 889 "~n Ok: ~p" 890 "~n Err: ~p", [Loaders0, Ok, Err]), 891 Loaders = lists:delete(Pid, Loaders0), 892 LoadInfo = 893 case Loaders of 894 [] -> 895 d("handle_exit -> multi load done"), 896 server_reply(Parent, apply_multi_load_ack, {ok, Ok+1, Err}), 897 undefined; 898 _ -> 899 {Loaders, Ok+1, Err} 900 end, 901 MG#mg{mload_info = LoadInfo}; 902 903 904handle_exit(#mg{parent = Parent, mload_info = {Loaders, Ok, Err}} = MG, 905 Pid, Reason) 906 when length(Loaders) > 0 -> 907 d("handle_exit -> entry when" 908 "~n Reason: ~p" 909 "~n Loaders: ~p" 910 "~n Ok: ~p" 911 "~n Err: ~p", [Reason, Loaders, Ok, Err]), 912 case lists:delete(Pid, Loaders) of 913 [] -> 914 %% since we cannot be empty prior the delete, 915 %% the last one exited... 916 server_reply(Parent, apply_multi_load, {ok, Ok, Err+1}), 917 MG#mg{mload_info = undefined}; 918 Loaders -> 919 %% Could not be this MG, so go on to the next 920 error_msg("received unexpected exit signal from ~p:" 921 "~n ~p", [Pid, Reason]); 922 Loaders1 -> 923 %% Not empty, but we removed one 924 MG#mg{mload_info = {Loaders1,Ok,Err+1}} 925 end; 926handle_exit(_MG, Pid, Reason) -> 927 error_msg("received unexpected exit signal from ~p:" 928 "~n ~p", [Pid, Reason]). 929 930 931parse_receive_info(RI, RH) -> 932 d("parse_receive_info -> get encoding module"), 933 EM = get_encoding_module(RI), 934 d("parse_receive_info -> get encoding config"), 935 EC = get_encoding_config(RI, EM), 936 d("parse_receive_info -> get transport module"), 937 TM = get_transport_module(RI), 938 d("parse_receive_info -> get transport port"), 939 TP = get_transport_port(RI), 940 d("parse_receive_info -> get transport host"), 941 TH = get_transport_host(RI), 942 d("parse_receive_info -> get transport opts"), 943 TO = get_transport_opts(RI), 944 RH1 = RH#megaco_receive_handle{send_mod = TM, 945 encoding_mod = EM, 946 encoding_config = EC}, 947 {TP, TH, RH1, TO}. 948 949 950start_transport(IB, 951 MgcPort, MgcHost, 952 #megaco_receive_handle{send_mod = megaco_tcp} = RH, TO) -> 953 start_tcp(IB, RH, TO, MgcPort, MgcHost); 954start_transport(IB, 955 MgcPort, MgcHost, 956 #megaco_receive_handle{send_mod = megaco_udp} = RH, TO) -> 957 start_udp(IB, RH, TO, MgcPort, MgcHost); 958start_transport(_, _, _, #megaco_receive_handle{send_mod = Mod}, _TO) -> 959 throw({error, {bad_send_mod, Mod}}). 960 961 962start_tcp(IB, RH, TO, MgcPort, MgcHost) -> 963 d("start tcp transport: " 964 "~n Inet Backend: ~p" 965 "~n MGC Port: ~p" 966 "~n MGC Host: ~p" 967 "~n Receive handle: ~p" 968 "~n Transport options: ~p", [IB, MgcPort, MgcHost, RH, TO]), 969 case megaco_tcp:start_transport() of 970 {ok, Sup} -> 971 d("tcp transport started: ~p", [Sup]), 972 start_tcp_connect(IB, TO, RH, MgcPort, MgcHost, Sup); 973 {error, Reason} -> 974 throw({error, {megaco_tcp_start_transport, Reason}}) 975 end. 976 977start_tcp_connect(IB, TO, RH, Port, Host, Sup) -> 978 d("try tcp connecting to: ~p:~p", [Host, Port]), 979 Opts = [{inet_backend, IB}, 980 {host, Host}, 981 {port, Port}, 982 {receive_handle, RH}, 983 {tcp_options, [{nodelay, true}]}] ++ TO, 984 try_start_tcp_connect(RH, Opts, Sup, 250, noError). 985 986try_start_tcp_connect(RH, Opts, Sup, Timeout, Error0) when (Timeout < 5000) -> 987 Sleep = random(Timeout) + 100, 988 d("try tcp connect (~p,~p)", [Timeout, Sleep]), 989 case megaco_tcp:connect(Sup, Opts) of 990 {ok, SendHandle, ControlPid} -> 991 d("tcp connected: ~p, ~p", [SendHandle, ControlPid]), 992 megaco_tcp_connect(RH, SendHandle, ControlPid); 993 Error1 when Error0 =:= noError -> % Keep the first error 994 d("failed connecting [1]: ~p", [Error1]), 995 sleep(Sleep), 996 try_start_tcp_connect(RH, Opts, Sup, Timeout*2, Error1); 997 Error2 -> 998 d("failed connecting [2]: ~p", [Error2]), 999 sleep(Sleep), 1000 try_start_tcp_connect(RH, Opts, Sup, Timeout*2, Error0) 1001 end; 1002try_start_tcp_connect(_RH, _Opts, Sup, _Timeout, Error) -> 1003 megaco_tcp:stop_transport(Sup), 1004 throw({error, {megaco_tcp_connect, Error}}). 1005 1006megaco_tcp_connect(RH, SendHandle, ControlPid) -> 1007 PrelMgcMid = preliminary_mid, 1008 d("megaco connect", []), 1009 {ok, CH} = megaco:connect(RH, PrelMgcMid, SendHandle, ControlPid), 1010 d("megaco connected: ~p", [CH]), 1011 {ok, CH}. 1012 1013start_udp(IB, RH, TO, MgcPort, MgcHost) -> 1014 d("start udp transport (~p)", [MgcPort]), 1015 case megaco_udp:start_transport() of 1016 {ok, Sup} -> 1017 d("udp transport started: ~p", [Sup]), 1018 start_udp_open(IB, RH, TO, MgcPort, MgcHost, Sup); 1019 %% Opts = [{inet_backend, IB}, {port, 0}, {receive_handle, RH}] ++ TO, 1020 %% d("udp open", []), 1021 %% case megaco_udp:open(Sup, Opts) of 1022 %% {ok, Handle, ControlPid} -> 1023 %% d("udp opened: ~p, ~p", [Handle, ControlPid]), 1024 %% megaco_udp_connect(IB, 1025 %% MgcPort, MgcHost, 1026 %% RH, Handle, ControlPid); 1027 %% {error, Reason} -> 1028 %% throw({error, {megaco_udp_open, Reason}}) 1029 %% end; 1030 {error, Reason} -> 1031 throw({error, {megaco_udp_start_transport, Reason}}) 1032 end. 1033 1034start_udp_open(IB, RH, TO, MgcPort, MgcHost, Sup) -> 1035 Opts = [{inet_backend, IB}, {port, 0}, {receive_handle, RH}] ++ TO, 1036 try_start_udp_open(RH, Opts, MgcPort, MgcHost, Sup). 1037 1038try_start_udp_open(RH, Opts, MgcPort, MgcHost, Sup) -> 1039 d("udp open", []), 1040 case megaco_udp:open(Sup, Opts) of 1041 {ok, Handle, ControlPid} -> 1042 d("udp opened: ~p, ~p", [Handle, ControlPid]), 1043 megaco_udp_connect(MgcPort, MgcHost, 1044 RH, Handle, ControlPid); 1045 {error, Reason} -> 1046 megaco_udp:stop_transport(Sup), 1047 throw({error, {megaco_udp_open, Reason}}) 1048 end. 1049 1050 1051 1052megaco_udp_connect(MgcPort, MgcHost, RH, Handle, ControlPid) -> 1053 MgcMid = preliminary_mid, 1054 SendHandle = megaco_udp:create_send_handle(Handle, MgcHost, MgcPort), 1055 d("megaco connect", []), 1056 {ok, CH} = megaco:connect(RH, MgcMid, SendHandle, ControlPid), 1057 d("megaco connected: ~p", [CH]), 1058 {ok, CH}. 1059 1060 1061update_load_times(#mg{load_counter = 0} = MG, Times) -> 1062 d("update_load_times(0) -> entry with" 1063 "~n Times: ~p", [Times]), 1064 {ok, MG#mg{load_counter = Times}}; 1065update_load_times(#mg{load_counter = N}, Times) -> 1066 d("update_load_times(~p) -> entry with" 1067 "~n Times: ~p", [N, Times]), 1068 {error, {already_counting, N}}. 1069 1070 1071do_apply_load(#mg{mid = Mid} = MG) -> 1072 d("do_apply_load -> entry"), 1073 case megaco:user_info(Mid, connections) of 1074 [CH] -> 1075 do_apply_load(MG, CH); 1076 [] -> 1077 i("failed to apply load: no connections for ~p", [Mid]), 1078 MG 1079 end. 1080 1081do_apply_load(#mg{parent = Parent, 1082 encode_ar_first = EAF, 1083 call_mode = Mode, 1084 group_size = Sz, 1085 load_counter = N0} = MG, CH) -> 1086 d("do_apply_load -> entry with" 1087 "~n Mode: ~p" 1088 "~n Sz: ~p" 1089 "~n N0: ~p", [Mode, Sz, N0]), 1090 {NofSent, Actions, ReplyData} = make_notify_request(N0, Sz), 1091 d("do_apply_load -> notifications constructed:" 1092 "~n NofSent: ~p" 1093 "~n Actions: ~p" 1094 "~n ReplyData: ~p", [NofSent, Actions, ReplyData]), 1095 N = N0 - NofSent, 1096 case Mode of 1097 sync -> 1098 Result = send_sync(EAF, CH, Actions, []), 1099 d("do_apply_load -> call result when N = ~p: ~n~p", [N,Result]), 1100 case N of 1101 0 -> 1102 d("do_apply_load -> load complete"), 1103 Parent ! {load_complete, self()}, 1104 MG#mg{call_mode = async, load_counter = 0}; 1105 _ -> 1106 d("do_apply_load -> make another round"), 1107 apply_load_timer(), 1108 MG#mg{call_mode = async, load_counter = N} 1109 end; 1110 async -> 1111 Result = send_async(EAF, CH, Actions, [{reply_data, ReplyData}]), 1112 d("do_apply_load -> cast result:~n ~p", [Result]), 1113 MG#mg{call_mode = sync, 1114 load_counter = N, 1115 reply_counter = NofSent} % Outstanding replies 1116 end. 1117 1118 1119start_notify_request_handler(EAF, CH, N) -> 1120 d("start_notify_request_handler -> entry with" 1121 "~n EAF: ~p" 1122 "~n CH: ~p" 1123 "~n N: ~p", [EAF, CH, N]), 1124 Env = get(), 1125 spawn_link(?MODULE, notify_request_handler_main, [self(), Env, EAF, CH, N]). 1126 1127notify_request_handler_main(Parent, Env, EAF, CH, N) -> 1128 F = fun({Tag, Val}) -> put(Tag, Val) end, 1129 lists:foreach(F, Env), 1130 d("notify_request_handler_main -> entry with" 1131 "~n Parent: ~p" 1132 "~n EAF: ~p" 1133 "~n CH: ~p" 1134 "~n N: ~p", [Parent, EAF, CH, N]), 1135 Res = do_notify_request(EAF, CH, N), 1136 d("notify_request_handler_main -> notify complete:" 1137 "~n Res: ~p", [Res]), 1138 Parent ! {notify_request_complete, {ok, Res}, self()}, 1139 unlink(Parent), 1140 exit(normal). 1141 1142do_notify_request(_EAF, _CH, N) when N =< 0 -> 1143 d("do_notify_request(~p) -> ignoring", [N]), 1144 ignore; 1145do_notify_request(EAF, CH, 1) -> 1146 d("do_notify_request(1) -> entry with"), 1147 {Action, _} = make_notify_request(), 1148 send_sync(EAF, CH, Action, []); 1149do_notify_request(EAF, CH, N) -> 1150 d("do_notify_request(~p) -> entry with", [N]), 1151 {N, Actions, _} = make_notify_request(N,N), 1152 send_sync(EAF, CH, Actions, []). 1153 1154make_notify_request(N, Sz) when (N >= Sz) andalso (Sz > 0) -> 1155 {Req, ReplyData} = make_notify_request(N, Sz, [], []), 1156 {Sz, Req, ReplyData}; 1157make_notify_request(N, _Sz) when N > 0 -> 1158 {Req, ReplyData} = make_notify_request(N, N, [], []), 1159 {N, Req, ReplyData}. 1160 1161 1162 1163make_notify_request(_Offset, 0, Actions, ReplyDatas) -> 1164 {lists:reverse(Actions), lists:reverse(ReplyDatas)}; 1165make_notify_request(Offset, N, Actions, ReplyDatas) when N > 0 -> 1166 TimeStamp = cre_timeNotation(), 1167 Event = cre_observedEvent("al/of", TimeStamp), 1168 Desc = cre_observedEventsDesc(2000 + N, [Event]), 1169 TidNum = 2#10000000 + Offset - N, 1170 NotifyReq = cre_notifyReq([#megaco_term_id{id = tid(TidNum)}],Desc), 1171 CmdReq = cre_commandReq({notifyReq, NotifyReq}), 1172 ActReq = cre_actionReq(?megaco_null_context_id, [CmdReq]), 1173 make_notify_request(Offset, N-1, [[ActReq]|Actions], [Desc|ReplyDatas]). 1174 1175make_notify_request() -> 1176 TimeStamp = cre_timeNotation("19990729", "22000000"), 1177 Event = cre_observedEvent("al/of", TimeStamp), 1178 Desc1 = cre_observedEventsDesc(2221, [Event]), 1179 Desc2 = cre_observedEventsDesc(2222, [Event]), 1180 Desc3 = cre_observedEventsDesc(2223, [Event]), 1181 Desc4 = cre_observedEventsDesc(2224, [Event]), 1182 NotifyReq1 = cre_notifyReq([#megaco_term_id{id = ?A4444}], Desc1), 1183 NotifyReq2 = cre_notifyReq([#megaco_term_id{id = ?A4445}], Desc2), 1184 CmdReq1 = cre_commandReq({notifyReq, NotifyReq1}), 1185 CmdReq2 = cre_commandReq({notifyReq, NotifyReq2}), 1186 ActReq = cre_actionReq(?megaco_null_context_id, [CmdReq1,CmdReq2]), 1187 {[ActReq], [Desc3,Desc4]}. 1188 1189 1190cre_actionReq(Cid, Cmds) -> 1191 #'ActionRequest'{contextId = Cid, 1192 commandRequests = Cmds}. 1193 1194cre_commandReq(Cmd) -> 1195 #'CommandRequest'{command = Cmd}. 1196 1197cre_serviceChangeReq(TermId, Parms) -> 1198 #'ServiceChangeRequest'{terminationID = TermId, 1199 serviceChangeParms = Parms}. 1200 1201cre_serviceChangeParm(Method, Reason) -> 1202 #'ServiceChangeParm'{serviceChangeMethod = Method, 1203 serviceChangeReason = Reason}. 1204 1205cre_notifyReq(Tid, EvsDesc) -> 1206 #'NotifyRequest'{terminationID = Tid, observedEventsDescriptor = EvsDesc}. 1207 1208% cre_notifyRep(Tid) -> 1209% #'NotifyReply'{terminationID = [Tid]}. 1210 1211% cre_notifyRep(Tid,Err) -> 1212% #'NotifyReply'{terminationID = [Tid], errorDescriptor = Err}. 1213 1214cre_observedEventsDesc(Id, EvList) -> 1215 #'ObservedEventsDescriptor'{requestId = Id, observedEventLst = EvList}. 1216 1217cre_observedEvent(Name, Not) -> 1218 #'ObservedEvent'{eventName = Name, timeNotation = Not}. 1219 1220cre_timeNotation() -> 1221 {{Year,Month,Day},{Hour,Min,Sec}} = calendar:universal_time(), 1222 D = lists:flatten(io_lib:format("~4..0w~2..0w~2..0w", [Year,Month,Day])), 1223 T = lists:flatten(io_lib:format("~2..0w~2..0w~4..0w", [Hour,Min,Sec])), 1224 cre_timeNotation(D, T). 1225 1226cre_timeNotation(D,T) -> 1227 #'TimeNotation'{date = D, time = T}. 1228 1229cre_error_descr(Code,Text) -> 1230 #'ErrorDescriptor'{errorCode = Code, errorText = Text}. 1231 1232% cre_error_descr(Code,FormatString,Args) -> 1233% Text = lists:flatten(io_lib:format(FormatString,Args)), 1234% cre_error_descr(Code,Text). 1235 1236 1237%% ----------------------- 1238%% Handle megaco callbacks 1239%% 1240 1241handle_megaco_request(#mg{state = connecting} = MG, 1242 {handle_connect, _CH, _PV}) -> 1243 d("handle_megaco_request(handle_connect,connecting) -> entry"), 1244 {ok, MG}; 1245handle_megaco_request(#mg{state = S} = MG, 1246 {handle_connect, _CH, _PV}) -> 1247 d("handle_megaco_request(handle_connect) -> entry"), 1248 Desc = 1249 lists:flatten(io_lib:format("not ready for connect in state ~p", [S])), 1250 ED = cre_error_descr(?megaco_internal_gateway_error, Desc), 1251 {{discard_ack, ED}, MG}; 1252 1253handle_megaco_request(#mg{req_handler = Pid} = MG, 1254 {handle_disconnect, _CH, _PV, R}) 1255 when is_pid(Pid) -> 1256 d("handle_megaco_request(handle_disconnect) -> entry with" 1257 "~n Pid: ~p", [Pid]), 1258 Error = {error, {disconnected, R}}, 1259 self() ! {notify_request_complete, Error, Pid}, 1260 unlink(Pid), 1261 exit(Pid, kill), 1262 {ok, MG#mg{req_handler = undefined, state = initiated}}; 1263handle_megaco_request(MG, {handle_disconnect, _CH, _PV, _R}) -> 1264 d("handle_megaco_request(handle_disconnect) -> entry"), 1265 {ok, MG#mg{state = initiated}}; 1266 1267handle_megaco_request(MG, 1268 {handle_syntax_error, _RH, _PV, _ED}) -> 1269 {reply, MG}; 1270 1271handle_megaco_request(#mg{req_handler = Pid} = MG, 1272 {handle_message_error, CH, PV, ED}) 1273 when is_pid(Pid) -> 1274 d("handle_megaco_request(handle_message_error) -> entry with" 1275 "~n Pid: ~p" 1276 "~n CH: ~p" 1277 "~n PV: ~p" 1278 "~n ED: ~p", [Pid, CH, PV, ED]), 1279 self() ! {notify_request_complete, ED, Pid}, 1280 unlink(Pid), 1281 exit(Pid, kill), 1282 {no_reply, MG#mg{req_handler = undefined}}; 1283handle_megaco_request(MG, {handle_message_error, CH, PV, ED}) -> 1284 d("handle_megaco_request(handle_message_error) -> entry with" 1285 "~n CH: ~p" 1286 "~n PV: ~p" 1287 "~n ED: ~p", [CH, PV, ED]), 1288 {no_reply, MG}; 1289 1290handle_megaco_request(MG, {handle_trans_request, _CH, _PV, _AR}) -> 1291 ED = cre_error_descr(?megaco_not_implemented, 1292 "Transaction requests not handled"), 1293 {{discard_ack, ED}, MG}; 1294 1295handle_megaco_request(MG, 1296 {handle_trans_long_request, _CH, _PV, _RD}) -> 1297 ED = cre_error_descr(?megaco_not_implemented, 1298 "Long transaction requests not handled"), 1299 {{discard_ack, ED}, MG}; 1300 1301handle_megaco_request(#mg{rep_info = P} = MG, 1302 {handle_trans_reply, CH, PV, AR, RD}) when is_pid(P) -> 1303 P ! {rep_received, self(), AR}, 1304 do_handle_trans_reply(MG, CH, PV, AR, RD); 1305handle_megaco_request(MG, {handle_trans_reply, CH, PV, AR, RD}) -> 1306 do_handle_trans_reply(MG, CH, PV, AR, RD); 1307 1308handle_megaco_request(#mg{ack_info = P} = MG, 1309 {handle_trans_ack, _CH, _PV, AS, _AD}) when is_pid(P) -> 1310 d("handle_megaco_request(handle_trans_ack,~p) -> entry",[P]), 1311 P ! {ack_received, self(), AS}, 1312 {ok, MG}; 1313handle_megaco_request(MG, {handle_trans_ack, _CH, _PV, _AS, _AD}) -> 1314 d("handle_megaco_request(handle_trans_ack) -> entry"), 1315 {ok, MG}. 1316 1317do_handle_trans_reply(#mg{parent = Parent, state = connecting} = MG, 1318 CH, _PV, {ok, Rep}, _RD) -> 1319 d("do_handle_trans_reply(connecting) -> entry with" 1320 "~n CH: ~p" 1321 "~n Rep: ~p", [CH, Rep]), 1322 server_reply(Parent, service_change_reply, ok), 1323 {ok, MG#mg{state = connected}}; 1324do_handle_trans_reply(#mg{parent = Parent, load_counter = 0} = MG, 1325 CH, _PV, {ok, Rep}, _RD) -> 1326 d("do_handle_trans_reply(load_counter = 0) -> entry with" 1327 "~n CH: ~p" 1328 "~n Rep: ~p", [CH, Rep, Parent]), 1329 handle_trans_reply_verify_act(Rep), 1330 server_reply(Parent, load_complete, ok), 1331 {ok, MG#mg{reply_counter = 0}}; 1332do_handle_trans_reply(#mg{reply_counter = 0} = MG, 1333 CH, _PV, {ok, Rep}, _RD) -> 1334 d("do_handle_trans_reply(reply_counter = 0) -> entry with" 1335 "~n CH: ~p" 1336 "~n Rep: ~p", [CH, Rep]), 1337 handle_trans_reply_verify_act(Rep), 1338 apply_load_timer(), 1339 {ok, MG}; 1340do_handle_trans_reply(#mg{reply_counter = N} = MG, 1341 CH, _PV, {ok, Rep}, _RD) -> 1342 d("do_handle_trans_reply(reply_counter = ~p) -> entry with" 1343 "~n CH: ~p" 1344 "~n Rep: ~p", [N, CH, Rep]), 1345 handle_trans_reply_verify_act(Rep), 1346 apply_load_timer(), 1347 {ok, MG#mg{reply_counter = N-1}}; 1348do_handle_trans_reply(MG, _CH, _PV, {error, ED}, _RD) -> 1349 i("unexpected error transaction: ~p", [ED]), 1350 {ok, MG}. 1351 1352 1353handle_trans_reply_verify_act([]) -> 1354 ok; 1355handle_trans_reply_verify_act([#'ActionReply'{commandReply = Rep}|Reps]) -> 1356 handle_trans_reply_verify_cmd(Rep), 1357 handle_trans_reply_verify_act(Reps); 1358handle_trans_reply_verify_act([Rep|Reps]) -> 1359 i("received 'propably' unexpected reply: ~n~p", [Rep]), 1360 handle_trans_reply_verify_act(Reps). 1361 1362handle_trans_reply_verify_cmd([]) -> 1363 ok; 1364handle_trans_reply_verify_cmd([Cmd|Cmds]) -> 1365 case Cmd of 1366 {notifyReply, #'NotifyReply'{terminationID = [Tid]}} -> 1367 d("received expected notification reply from ~n ~p", [Tid]); 1368 Else -> 1369 i("received unexpected notification reply ~n~p", [Else]) 1370 end, 1371 handle_trans_reply_verify_cmd(Cmds). 1372 1373 1374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1375 1376notify_started(Parent) -> 1377 Parent ! {started, self()}. 1378 1379 1380%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1381 1382%% The megaco user callback interface 1383 1384handle_connect(CH, PV, Pid, Mid) -> 1385 case CH#megaco_conn_handle.remote_mid of 1386 preliminary_mid -> 1387 %% Avoids deadlock 1388 ok; 1389 _ -> 1390 Reply = request(Pid, {handle_connect, CH, PV}, Mid), 1391 Reply 1392 end. 1393 1394handle_disconnect(_CH, _PV, 1395 {user_disconnect, {stopped_by_user, Pid}}, 1396 Pid, _Mid) -> 1397 ok; 1398handle_disconnect(CH, PV, R, Pid, Mid) -> 1399 request(Pid, {handle_disconnect, CH, PV, R}, Mid). 1400 1401handle_syntax_error(ReceiveHandle, ProtocolVersion, ErrorDescriptor, Pid, Mid) -> 1402 Req = {handle_syntax_error, ReceiveHandle, ProtocolVersion, 1403 ErrorDescriptor}, 1404 request(Pid, Req, Mid). 1405 1406handle_message_error(ConnHandle, ProtocolVersion, ErrorDescriptor, Pid, Mid) -> 1407 Req = {handle_message_error, ConnHandle, ProtocolVersion, ErrorDescriptor}, 1408 request(Pid, Req, Mid). 1409 1410handle_trans_request(CH, PV, AR, Pid, Mid) -> 1411 Reply = request(Pid, {handle_trans_request, CH, PV, AR}, Mid), 1412 Reply. 1413 1414handle_trans_long_request(ConnHandle, ProtocolVersion, ReqData, Pid, Mid) -> 1415 Req = {handle_trans_long_request, ConnHandle, ProtocolVersion, ReqData}, 1416 request(Pid, Req, Mid). 1417 1418handle_trans_reply(ConnHandle, ProtocolVersion, ActualReply, ReplyData, Pid, Mid) -> 1419 Req = {handle_trans_reply, ConnHandle, ProtocolVersion, 1420 ActualReply, ReplyData}, 1421 request(Pid, Req, Mid). 1422 1423handle_trans_ack(ConnHandle, ProtocolVersion, AckStatus, AckData, Pid, Mid) -> 1424 Req = {handle_trans_ack, ConnHandle, ProtocolVersion, AckStatus, AckData}, 1425 request(Pid, Req, Mid). 1426 1427 1428request(Pid, Request, Mid) -> 1429 Pid ! {request, Request, Mid, self()}, 1430 receive 1431 {reply, {delay, To, ED}, Pid} -> 1432 sleep(To), 1433 {discard_ack, ED}; 1434 {reply, Reply, Pid} -> 1435 Reply 1436 end. 1437 1438 1439%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1440 1441send_async(true, CH, Actions, Opts) -> 1442 d("send_async(true) -> encode actions first"), 1443 case megaco:encode_actions(CH, Actions, Opts) of 1444 {ok, BinOrBins} -> 1445 d("send_async(true) -> send message"), 1446 megaco:cast(CH, BinOrBins, Opts); 1447 Error -> 1448 d("send_async(true) -> encode failed: ~n~p", [Error]), 1449 Error 1450 end; 1451send_async(_, CH, Actions, Opts) -> 1452 d("send_async(true) -> send message"), 1453 megaco:cast(CH, Actions, Opts). 1454 1455send_sync(true, CH, Actions, Opts) -> 1456 d("send_sync(true) -> encode actions first"), 1457 case megaco:encode_actions(CH, Actions, Opts) of 1458 {ok, BinOrBins} -> 1459 d("send_sync(true) -> send message"), 1460 megaco:call(CH, BinOrBins, Opts); 1461 Error -> 1462 d("send_sync(true) -> encode failed: ~n~p", [Error]), 1463 Error 1464 end; 1465send_sync(_, CH, Actions, Opts) -> 1466 d("send_sync(false) -> send message"), 1467 megaco:call(CH, Actions, Opts). 1468 1469 1470%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1471 1472sleep(X) -> 1473 receive after X -> ok end. 1474 1475 1476info_msg(F,A) -> error_logger:info_msg("MG: " ++ F ++ "~n",A). 1477error_msg(F,A) -> error_logger:error_msg("MG: " ++ F ++ "~n",A). 1478 1479 1480get_encoding_module(RI) -> 1481 case (catch get_conf(encoding_module, RI)) of 1482 {error, _} -> 1483 undefined; 1484 Val -> 1485 Val 1486 end. 1487 1488get_encoding_config(RI, EM) -> 1489 case text_codec(EM) of 1490 true -> 1491 case megaco:system_info(text_config) of 1492 [Conf] when is_list(Conf) -> 1493 Conf; 1494 _ -> 1495 [] 1496 end; 1497 1498 false -> 1499 get_conf(encoding_config, RI) 1500 end. 1501 1502text_codec(megaco_compact_text_encoder) -> 1503 true; 1504text_codec(megaco_pretty_text_encoder) -> 1505 true; 1506text_codec(_) -> 1507 false. 1508 1509 1510get_transport_module(RI) -> 1511 get_conf(transport_module, RI). 1512 1513get_transport_port(RI) -> 1514 get_conf(port, RI). 1515 1516get_transport_host(RI) -> 1517 {ok, LocalHost} = inet:gethostname(), 1518 get_conf(host, RI, LocalHost). 1519 1520get_transport_opts(RI) -> 1521 get_conf(transport_opts, RI, []). 1522 1523 1524get_conf(Key, Config) -> 1525 case lists:keysearch(Key, 1, Config) of 1526 {value, {Key, Val}} -> 1527 Val; 1528 _ -> 1529 exit({error, {not_found, Key, Config}}) 1530 end. 1531 1532get_conf(Key, Config, Default) -> 1533 case lists:keysearch(Key, 1, Config) of 1534 {value, {Key, Val}} -> 1535 Val; 1536 _ -> 1537 Default 1538 end. 1539 1540 1541%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1542 1543tid(N) when N >= 0 -> 1544 {Rem1, Val1} = num2str(N), 1545 {Rem2, Val2} = num2str(Rem1), 1546 {0, Val3} = num2str(Rem2), 1547 [Val3, Val2, Val1]. 1548 1549num2str(N) when N >= 0 -> 1550 num2str(N, []). 1551 1552num2str(Rem, Val) when length(Val) == 8 -> 1553 {Rem, Val}; 1554num2str(N, Val) -> 1555 D = N div 2, 1556 case N rem 2 of 1557 1 -> 1558 num2str(D, [$1|Val]); 1559 0 -> 1560 num2str(D, [$0|Val]) 1561 end. 1562 1563 1564%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1565 1566%% e(F) -> 1567%% i(F, []). 1568 1569e(F, A) -> 1570 print(error, get(verbosity), "ERROR", F, A). 1571 1572 1573i(F) -> 1574 i(F, []). 1575 1576i(F, A) -> 1577 print(info, get(verbosity), "INFO", F, A). 1578 1579 1580d(F) -> 1581 d(F, []). 1582 1583d(F, A) -> 1584 print(debug, get(verbosity), "DBG", F, A). 1585 1586 1587printable(error, _) -> true; 1588printable(_, debug) -> true; 1589printable(info, info) -> true; 1590printable(_,_) -> false. 1591 1592print(Severity, Verbosity, P, F, A) -> 1593 print(printable(Severity, Verbosity), P, F, A). 1594 1595print(true, P, F, A) -> 1596 io:format("*** [~s] [~s] ~p ~s ***" 1597 "~n " ++ F ++ "~n~n", 1598 [?FTS(), P, self(), get(sname) | A]); 1599print(_, _, _, _) -> 1600 ok. 1601 1602 1603 1604%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1605 1606random_init() -> 1607 ok. 1608 1609random() -> 1610 random(50). 1611random(N) -> 1612 rand:uniform(N). 1613 1614 1615display_system_info(Mid) -> 1616 display_system_info(Mid, ""). 1617 1618display_system_info(Mid, Pre) -> 1619 TimeStr = ?FTS(), 1620 MibStr = lists:flatten(io_lib:format("~p ", [Mid])), 1621 megaco_test_lib:display_system_info(MibStr ++ Pre ++ TimeStr). 1622 1623 1624create_timer(Time, Event) -> 1625 erlang:send_after(Time, self(), {Event, Time}). 1626 1627cancel_timer(undefined) -> 1628 ok; 1629cancel_timer(Ref) -> 1630 erlang:cancel_timer(Ref). 1631 1632 1633apply_load_timer() -> 1634 create_timer(random(), apply_load_timeout). 1635 1636 1637