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