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