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