aboutsummaryrefslogtreecommitdiffstats
path: root/lib/megaco/src/engine/megaco_config.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/megaco/src/engine/megaco_config.erl')
-rw-r--r--lib/megaco/src/engine/megaco_config.erl2113
1 files changed, 2113 insertions, 0 deletions
diff --git a/lib/megaco/src/engine/megaco_config.erl b/lib/megaco/src/engine/megaco_config.erl
new file mode 100644
index 0000000000..2058c53973
--- /dev/null
+++ b/lib/megaco/src/engine/megaco_config.erl
@@ -0,0 +1,2113 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Handle configuration of Megaco/H.248
+%%----------------------------------------------------------------------
+
+-module(megaco_config).
+
+-behaviour(gen_server).
+
+%% Application internal exports
+-export([
+ start_link/0,
+ stop/0,
+
+ start_user/2,
+ stop_user/1,
+
+ user_info/2,
+ update_user_info/3,
+ conn_info/2,
+ update_conn_info/3,
+ system_info/1,
+
+ %% incr_counter/2,
+ incr_trans_id_counter/1,
+ incr_trans_id_counter/2,
+
+ %% Verification functions
+ verify_val/2,
+ verify_strict_uint/1,
+ verify_strict_int/1, verify_strict_int/2,
+ verify_uint/1,
+ verify_int/1, verify_int/2,
+
+
+ %% Reply limit counter
+ cre_reply_counter/2,
+ get_reply_counter/2,
+ incr_reply_counter/2,
+ del_reply_counter/2,
+
+ %% Pending limit counter
+ cre_pending_counter/3,
+ get_pending_counter/2,
+ incr_pending_counter/2,
+ del_pending_counter/2,
+ %% Backward compatibillity functions (to be removed in later versions)
+ cre_pending_counter/1,
+ get_pending_counter/1,
+ incr_pending_counter/1,
+ del_pending_counter/1,
+
+ lookup_local_conn/1,
+ connect/4, finish_connect/4,
+ autoconnect/4,
+ disconnect/1,
+ connect_remote/3,
+ disconnect_remote/2,
+ init_conn_data/4,
+
+ trans_sender_exit/2
+
+ ]).
+
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(SERVER, ?MODULE).
+-record(state, {parent_pid}).
+
+-include_lib("megaco/include/megaco.hrl").
+-include_lib("megaco/src/app/megaco_internal.hrl").
+
+
+-ifdef(MEGACO_TEST_CODE).
+-define(megaco_test_init(),
+ (catch ets:new(megaco_test_data, [set, public, named_table]))).
+-else.
+-define(megaco_test_init(),
+ ok).
+-endif.
+
+-define(TID_CNT(LMID), {LMID, trans_id_counter}).
+
+
+%%%----------------------------------------------------------------------
+%%% API
+%%%----------------------------------------------------------------------
+
+start_link() ->
+ ?d("start_link -> entry", []),
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [self()], []).
+
+stop() ->
+ ?d("stop -> entry", []),
+ call({stop, self()}).
+
+start_user(UserMid, Config) ->
+ call({start_user, UserMid, Config}).
+
+stop_user(UserMid) ->
+ call({stop_user, UserMid}).
+
+user_info(UserMid, all) ->
+ All0 = ets:match_object(megaco_config, {{UserMid, '_'}, '_'}),
+ All1 = [{Item, Val} || {{_, Item}, Val} <- All0, Item /= trans_sender],
+ case lists:keysearch(trans_id_counter, 1, All1) of
+ {value, {_, Val}} ->
+ lists:keyreplace(trans_id_counter, 1, All1, {trans_id, Val});
+ false when UserMid /= default ->
+ [{trans_id, undefined_serial}|All1];
+ false ->
+ All1
+ end;
+user_info(UserMid, receive_handle) ->
+ case call({receive_handle, UserMid}) of
+ {ok, RH} ->
+ RH;
+ {error, Reason} ->
+ exit(Reason)
+ end;
+user_info(UserMid, conn_data) ->
+ HandlePat = #megaco_conn_handle{local_mid = UserMid, remote_mid = '_'},
+ Pat = #conn_data{conn_handle = HandlePat,
+ serial = '_',
+ max_serial = '_',
+ request_timer = '_',
+ long_request_timer = '_',
+
+ auto_ack = '_',
+
+ trans_ack = '_',
+ trans_ack_maxcount = '_',
+
+ trans_req = '_',
+ trans_req_maxcount = '_',
+ trans_req_maxsize = '_',
+
+ trans_timer = '_',
+ trans_sender = '_',
+
+ pending_timer = '_',
+ sent_pending_limit = '_',
+ recv_pending_limit = '_',
+ reply_timer = '_',
+ control_pid = '_',
+ monitor_ref = '_',
+ send_mod = '_',
+ send_handle = '_',
+ encoding_mod = '_',
+ encoding_config = '_',
+ protocol_version = '_',
+ auth_data = '_',
+ user_mod = '_',
+ user_args = '_',
+ reply_action = '_',
+ reply_data = '_',
+ threaded = '_',
+ strict_version = '_',
+ long_request_resend = '_',
+ call_proxy_gc_timeout = '_',
+ cancel = '_',
+ resend_indication = '_',
+ segment_reply_ind = '_',
+ segment_recv_acc = '_',
+ segment_recv_timer = '_',
+ segment_send = '_',
+ segment_send_timer = '_',
+ max_pdu_size = '_',
+ request_keep_alive_timeout = '_'
+ },
+ %% ok = io:format("PATTERN: ~p~n", [Pat]),
+ ets:match_object(megaco_local_conn, Pat);
+user_info(UserMid, connections) ->
+ [C#conn_data.conn_handle || C <- user_info(UserMid, conn_data)];
+user_info(UserMid, mid) ->
+ ets:lookup_element(megaco_config, {UserMid, mid}, 2);
+user_info(UserMid, orig_pending_limit) ->
+ user_info(UserMid, sent_pending_limit);
+user_info(UserMid, trans_id) ->
+ case (catch user_info(UserMid, trans_id_counter)) of
+ {'EXIT', _} ->
+ %% There is only two cases where this can occure:
+ %% 1) The user does not exist
+ %% 2) Called before the first message is sent, use
+ %% undefined_serial, since there is no
+ %% "current transaction id"
+ case (catch user_info(UserMid, mid)) of
+ {'EXIT', _} ->
+ %% case 1:
+ exit({no_such_user, UserMid});
+ _ ->
+ undefined_serial
+ end;
+ Else ->
+ Else
+ end;
+user_info(UserMid, Item) ->
+ ets:lookup_element(megaco_config, {UserMid, Item}, 2).
+
+update_user_info(UserMid, orig_pending_limit, Val) ->
+ update_user_info(UserMid, sent_pending_limit, Val);
+update_user_info(UserMid, Item, Val) ->
+ call({update_user_info, UserMid, Item, Val}).
+
+conn_info(CH, Item)
+ when is_record(CH, megaco_conn_handle) andalso (Item /= cancel) ->
+ case Item of
+ conn_handle ->
+ CH;
+ mid ->
+ CH#megaco_conn_handle.local_mid;
+ local_mid ->
+ CH#megaco_conn_handle.local_mid;
+ remote_mid ->
+ CH#megaco_conn_handle.remote_mid;
+ conn_data ->
+ case lookup_local_conn(CH) of
+ [] ->
+ exit({no_such_connection, CH});
+ [ConnData] ->
+ ConnData
+ end;
+ _ ->
+ case lookup_local_conn(CH) of
+ [] ->
+ exit({no_such_connection, CH});
+ [ConnData] ->
+ conn_info(ConnData, Item)
+ end
+ end;
+conn_info(#conn_data{conn_handle = CH}, cancel) ->
+ %% To minimise raise-condition propabillity,
+ %% we always look in the table instead of
+ %% in the record for this one
+ ets:lookup_element(megaco_local_conn, CH, #conn_data.cancel);
+
+conn_info(CD, Item) when is_record(CD, conn_data) ->
+ case Item of
+ all ->
+ Tags0 = record_info(fields, conn_data),
+ Tags1 = replace(serial, trans_id, Tags0),
+ Tags = [mid, local_mid, remote_mid] ++
+ replace(max_serial, max_trans_id, Tags1),
+ [{Tag, conn_info(CD,Tag)} || Tag <- Tags,
+ Tag /= conn_data,
+ Tag /= trans_sender,
+ Tag /= cancel];
+ conn_data -> CD;
+ conn_handle -> CD#conn_data.conn_handle;
+ mid -> (CD#conn_data.conn_handle)#megaco_conn_handle.local_mid;
+ local_mid -> (CD#conn_data.conn_handle)#megaco_conn_handle.local_mid;
+ remote_mid -> (CD#conn_data.conn_handle)#megaco_conn_handle.remote_mid;
+ trans_id -> CH = CD#conn_data.conn_handle,
+ LocalMid = CH#megaco_conn_handle.local_mid,
+ Item2 = {LocalMid, trans_id_counter},
+ case (catch ets:lookup(megaco_config, Item2)) of
+ {'EXIT', _} ->
+ undefined_serial;
+ [] ->
+ user_info(LocalMid, min_trans_id);
+ [{_, Serial}] ->
+ Max = CD#conn_data.max_serial,
+ if
+ ((Max =:= infinity) andalso
+ is_integer(Serial) andalso
+ (Serial < 4294967295)) ->
+ Serial + 1;
+ (Max =:= infinity) andalso
+ is_integer(Serial) andalso
+ (Serial =:= 4294967295) ->
+ user_info(LocalMid,
+ min_trans_id);
+ Serial < Max ->
+ Serial + 1;
+ Serial =:= Max ->
+ user_info(LocalMid,
+ min_trans_id);
+ Serial =:= 4294967295 ->
+ user_info(LocalMid,
+ min_trans_id);
+ true ->
+ undefined_serial
+ end
+ end;
+ max_trans_id -> CD#conn_data.max_serial;
+ request_timer -> CD#conn_data.request_timer;
+ long_request_timer -> CD#conn_data.long_request_timer;
+
+ auto_ack -> CD#conn_data.auto_ack;
+
+ trans_ack -> CD#conn_data.trans_ack;
+ trans_ack_maxcount -> CD#conn_data.trans_ack_maxcount;
+
+ trans_req -> CD#conn_data.trans_req;
+ trans_req_maxcount -> CD#conn_data.trans_req_maxcount;
+ trans_req_maxsize -> CD#conn_data.trans_req_maxsize;
+
+ trans_timer -> CD#conn_data.trans_timer;
+
+ pending_timer -> CD#conn_data.pending_timer;
+ orig_pending_limit -> CD#conn_data.sent_pending_limit;
+ sent_pending_limit -> CD#conn_data.sent_pending_limit;
+ recv_pending_limit -> CD#conn_data.recv_pending_limit;
+ reply_timer -> CD#conn_data.reply_timer;
+ control_pid -> CD#conn_data.control_pid;
+ monitor_ref -> CD#conn_data.monitor_ref;
+ send_mod -> CD#conn_data.send_mod;
+ send_handle -> CD#conn_data.send_handle;
+ encoding_mod -> CD#conn_data.encoding_mod;
+ encoding_config -> CD#conn_data.encoding_config;
+ protocol_version -> CD#conn_data.protocol_version;
+ auth_data -> CD#conn_data.auth_data;
+ user_mod -> CD#conn_data.user_mod;
+ user_args -> CD#conn_data.user_args;
+ reply_action -> CD#conn_data.reply_action;
+ reply_data -> CD#conn_data.reply_data;
+ threaded -> CD#conn_data.threaded;
+ strict_version -> CD#conn_data.strict_version;
+ long_request_resend -> CD#conn_data.long_request_resend;
+ call_proxy_gc_timeout -> CD#conn_data.call_proxy_gc_timeout;
+ cancel -> CD#conn_data.cancel;
+ resend_indication -> CD#conn_data.resend_indication;
+ segment_reply_ind -> CD#conn_data.segment_reply_ind;
+ segment_recv_acc -> CD#conn_data.segment_recv_acc;
+ segment_recv_timer -> CD#conn_data.segment_recv_timer;
+ segment_send -> CD#conn_data.segment_send;
+ segment_send_timer -> CD#conn_data.segment_send_timer;
+ max_pdu_size -> CD#conn_data.max_pdu_size;
+ request_keep_alive_timeout -> CD#conn_data.request_keep_alive_timeout;
+ receive_handle ->
+ LocalMid = (CD#conn_data.conn_handle)#megaco_conn_handle.local_mid,
+ #megaco_receive_handle{local_mid = LocalMid,
+ encoding_mod = CD#conn_data.encoding_mod,
+ encoding_config = CD#conn_data.encoding_config,
+ send_mod = CD#conn_data.send_mod};
+ _ ->
+ exit({no_such_item, Item})
+ end;
+conn_info(BadHandle, _Item) ->
+ {error, {no_such_connection, BadHandle}}.
+
+replace(_, _, []) ->
+ [];
+replace(Item, WithItem, [Item|List]) ->
+ [WithItem|List];
+replace(Item, WithItem, [OtherItem|List]) ->
+ [OtherItem | replace(Item, WithItem, List)].
+
+
+update_conn_info(#conn_data{conn_handle = CH}, Item, Val) ->
+ do_update_conn_info(CH, Item, Val);
+update_conn_info(CH, Item, Val)
+ when is_record(CH, megaco_conn_handle) andalso (Item /= cancel) ->
+ do_update_conn_info(CH, Item, Val);
+update_conn_info(BadHandle, _Item, _Val) ->
+ {error, {no_such_connection, BadHandle}}.
+
+do_update_conn_info(CH, orig_pending_limit, Val) ->
+ do_update_conn_info(CH, sent_pending_limit, Val);
+do_update_conn_info(CH, Item, Val) ->
+ call({update_conn_data, CH, Item, Val}).
+
+
+system_info(all) ->
+ AllItems = [n_active_requests,
+ n_active_replies,
+ n_active_connections,
+ users,
+ connections,
+ text_config,
+ reply_counters,
+ pending_counters],
+ [{Item, system_info(Item)} || Item <- AllItems];
+system_info(Item) ->
+ case Item of
+ n_active_requests ->
+ ets:info(megaco_requests, size);
+ n_active_replies ->
+ ets:info(megaco_replies, size);
+ n_active_connections ->
+ ets:info(megaco_local_conn, size);
+ users ->
+ Pat = {{'_', mid}, '_'},
+ [Mid || {_, Mid} <- ets:match_object(megaco_config, Pat)];
+ connections ->
+ [C#conn_data.conn_handle || C <- ets:tab2list(megaco_local_conn)];
+ text_config ->
+ case ets:lookup(megaco_config, text_config) of
+ [] ->
+ [];
+ [{text_config, Conf}] ->
+ [Conf]
+ end;
+
+ reply_counters ->
+ reply_counters();
+
+ pending_counters ->
+ pending_counters();
+
+ recv_pending_counters ->
+ pending_counters(recv);
+
+ sent_pending_counters ->
+ pending_counters(sent);
+
+ BadItem ->
+ exit({no_such_item, BadItem})
+
+ end.
+
+
+get_env(Env, Default) ->
+ case application:get_env(megaco, Env) of
+ {ok, Val} -> Val;
+ undefined -> Default
+ end.
+
+lookup_local_conn(Handle) ->
+ ets:lookup(megaco_local_conn, Handle).
+
+
+autoconnect(RH, RemoteMid, SendHandle, ControlPid) ->
+ ?d("autoconnect -> entry with "
+ "~n RH: ~p"
+ "~n RemoteMid: ~p"
+ "~n SendHandle: ~p"
+ "~n ControlPid: ~p", [RH, RemoteMid, SendHandle, ControlPid]),
+ case RemoteMid of
+ {MidType, _MidValue} when is_atom(MidType) ->
+ call({connect, RH, RemoteMid, SendHandle, ControlPid, auto});
+ preliminary_mid ->
+ call({connect, RH, RemoteMid, SendHandle, ControlPid, auto});
+ BadMid ->
+ {error, {bad_remote_mid, BadMid}}
+ end.
+
+connect(RH, RemoteMid, SendHandle, ControlPid) ->
+ ?d("connect -> entry with "
+ "~n RH: ~p"
+ "~n RemoteMid: ~p"
+ "~n SendHandle: ~p"
+ "~n ControlPid: ~p", [RH, RemoteMid, SendHandle, ControlPid]),
+ case RemoteMid of
+ {MidType, _MidValue} when is_atom(MidType) ->
+ call({connect, RH, RemoteMid, SendHandle, ControlPid,
+ {plain, self()}});
+ preliminary_mid ->
+ call({connect, RH, RemoteMid, SendHandle, ControlPid,
+ {plain, self()}});
+ BadMid ->
+ {error, {bad_remote_mid, BadMid}}
+ end.
+
+finish_connect(ConnHandle, SendHandle, ControlPid, MFA) ->
+ ?d("finish_connect -> entry with "
+ "~n ConnHandle: ~p"
+ "~n SendHandle: ~p"
+ "~n ControlPid: ~p"
+ "~n MFA: ~p", [ConnHandle, SendHandle, ControlPid, MFA]),
+ call({finish_connect, ConnHandle, SendHandle, ControlPid, MFA}).
+
+connect_remote(ConnHandle, UserNode, Ref) ->
+ call({connect_remote, ConnHandle, UserNode, Ref}).
+
+disconnect(ConnHandle) ->
+ call({disconnect, ConnHandle}).
+
+disconnect_remote(ConnHandle, UserNode) ->
+ call({disconnect_remote, ConnHandle, UserNode}).
+
+
+incr_counter(Item, Incr) ->
+ try
+ begin
+ ets:update_counter(megaco_config, Item, Incr)
+ end
+ catch
+ error:_ ->
+ try
+ begin
+ cre_counter(Item, Incr)
+ end
+ catch
+ exit:_ ->
+ %% Ok, some other process got there before us,
+ %% so try again
+ ets:update_counter(megaco_config, Item, Incr)
+ end
+ end.
+%% incr_counter(Item, Incr) ->
+%% case (catch ets:update_counter(megaco_config, Item, Incr)) of
+%% {'EXIT', _} ->
+%% case (catch cre_counter(Item, Incr)) of
+%% {'EXIT', _} ->
+%% %% Ok, some other process got there before us,
+%% %% so try again
+%% ets:update_counter(megaco_config, Item, Incr);
+%% NewVal ->
+%% NewVal
+%% end;
+%% NewVal ->
+%% NewVal
+%% end.
+
+cre_counter(Item, Initial) ->
+ case whereis(?SERVER) =:= self() of
+ false ->
+ case call({cre_counter, Item, Initial}) of
+ {ok, Value} ->
+ Value;
+ Error ->
+ exit(Error)
+ end;
+ true ->
+ %% Check that the counter does not already exists
+ %% so we don't overwrite an already existing counter
+ case ets:lookup(megaco_config, Item) of
+ [] ->
+ ets:insert(megaco_config, {Item, Initial}),
+ {ok, Initial};
+ [_] ->
+ %% Ouch, now what?
+ {error, already_exists}
+
+ end
+ end.
+
+
+cre_reply_counter(ConnHandle, TransId) ->
+ Counter = {reply_counter, ConnHandle, TransId},
+ Initial = 1,
+ cre_counter(Counter, Initial).
+
+incr_reply_counter(ConnHandle, TransId) ->
+ Counter = {reply_counter, ConnHandle, TransId},
+ incr_counter(Counter, 1).
+
+get_reply_counter(ConnHandle, TransId) ->
+ Counter = {reply_counter, ConnHandle, TransId},
+ [{Counter, Val}] = ets:lookup(megaco_config, Counter),
+ Val.
+
+del_reply_counter(ConnHandle, TransId) ->
+ Counter = {reply_counter, ConnHandle, TransId},
+ ets:delete(megaco_config, Counter).
+
+reply_counters() ->
+ Pattern = {{reply_counter, '_', '_'}, '_'},
+ Counters1 = ets:match_object(megaco_config, Pattern),
+ [{ConnHandle, TransId, CounterVal} ||
+ {{reply_counter, ConnHandle, TransId}, CounterVal} <- Counters1].
+
+
+cre_pending_counter(TransId) ->
+ cre_pending_counter(sent, TransId, 0).
+
+cre_pending_counter(Direction, TransId, Initial) ->
+ Counter = {pending_counter, Direction, TransId},
+ cre_counter(Counter, Initial).
+
+incr_pending_counter(TransId) ->
+ incr_pending_counter(sent, TransId).
+
+incr_pending_counter(Direction, TransId) ->
+ Counter = {pending_counter, Direction, TransId},
+ incr_counter(Counter, 1).
+
+get_pending_counter(TransId) ->
+ get_pending_counter(sent, TransId).
+
+get_pending_counter(Direction, TransId) ->
+ Counter = {pending_counter, Direction, TransId},
+ [{Counter, Val}] = ets:lookup(megaco_config, Counter),
+ Val.
+
+del_pending_counter(TransId) ->
+ del_pending_counter(sent, TransId).
+
+del_pending_counter(Direction, TransId) ->
+ Counter = {pending_counter, Direction, TransId},
+ ets:delete(megaco_config, Counter).
+
+
+pending_counters() ->
+ Pattern = {{pending_counter, '_', '_'}, '_'},
+ Counters1 = ets:match_object(megaco_config, Pattern),
+ Counters2 = [{Direction, TransId, CounterVal} ||
+ {{pending_counter, Direction, TransId}, CounterVal} <-
+ Counters1],
+ RecvCounters = [{TransId, CounterVal} ||
+ {recv, TransId, CounterVal} <- Counters2],
+ SentCounters = [{TransId, CounterVal} ||
+ {sent, TransId, CounterVal} <- Counters2],
+ [{recv, RecvCounters}, {sent, SentCounters}].
+
+
+pending_counters(Direction)
+ when ((Direction =:= sent) orelse (Direction =:= recv)) ->
+ Pattern = {{pending_counter, Direction, '_'}, '_'},
+ Counters = ets:match_object(megaco_config, Pattern),
+ [{TransId, CounterVal} ||
+ {{pending_counter, D, TransId}, CounterVal} <-
+ Counters, (Direction == D)].
+
+%% A wrapping transaction id counter
+incr_trans_id_counter(ConnHandle) ->
+ incr_trans_id_counter(ConnHandle, 1).
+incr_trans_id_counter(ConnHandle, Incr)
+ when is_integer(Incr) andalso (Incr > 0) ->
+ case megaco_config:lookup_local_conn(ConnHandle) of
+ [] ->
+ {error, {no_such_connection, ConnHandle}};
+ [ConnData] ->
+ LocalMid = ConnHandle#megaco_conn_handle.local_mid,
+ Min = user_info(LocalMid, min_trans_id),
+ Max =
+ case ConnData#conn_data.max_serial of
+ infinity ->
+ 4294967295;
+ MS ->
+ MS
+ end,
+ Item = ?TID_CNT(LocalMid),
+ do_incr_trans_id_counter(ConnData, Item, Min, Max, Incr, -1)
+ end.
+
+do_incr_trans_id_counter(ConnData, _Item, _Min, _Max, 0, Serial) ->
+ ConnData2 = ConnData#conn_data{serial = Serial},
+ {ok, ConnData2};
+do_incr_trans_id_counter(ConnData, Item, Min, Max, N, _) ->
+ case (catch ets:update_counter(megaco_config, Item, {2, 1, Max, Min})) of
+ {'EXIT', _} ->
+ %% This can only happen for the first ever increment,
+ %% in which case N is equal to (the initial) Incr
+ ConnHandle = ConnData#conn_data.conn_handle,
+ init_trans_id_counter(ConnHandle, Item, N);
+ Serial ->
+ do_incr_trans_id_counter(ConnData, Item, Min, Max, N-1, Serial)
+ end.
+
+init_trans_id_counter(ConnHandle, Item, Incr) ->
+ case whereis(?SERVER) == self() of
+ false ->
+ call({init_trans_id_counter, ConnHandle, Item, Incr});
+ true ->
+ do_init_trans_id_counter(ConnHandle, Item, Incr)
+ end.
+
+do_init_trans_id_counter(ConnHandle, Item, Incr) ->
+ case megaco_config:lookup_local_conn(ConnHandle) of
+ [] ->
+ {error, {no_such_connection, ConnHandle}};
+ [ConnData] ->
+ %% Make sure that the counter still does not exist
+ LocalMid = ConnHandle#megaco_conn_handle.local_mid,
+ Min = user_info(LocalMid, min_trans_id),
+ Max =
+ case ConnData#conn_data.max_serial of
+ infinity ->
+ 4294967295;
+ MS ->
+ MS
+ end,
+ Item = ?TID_CNT(LocalMid),
+ Incr2 = {2, Incr, Max, Min},
+ case (catch ets:update_counter(megaco_config, Item, Incr2)) of
+ {'EXIT', _} ->
+ %% Yep, we are the first one here
+ Serial1 = Min + (Incr-1),
+ ets:insert(megaco_config, {Item, Serial1}),
+ ConnData2 = ConnData#conn_data{serial = Serial1},
+ {ok, ConnData2};
+ Serial2 ->
+ %% No, someone got there before we did
+ ConnData2 = ConnData#conn_data{serial = Serial2},
+ {ok, ConnData2}
+ end
+ end.
+
+%% For backward compatibillity (during code upgrade)
+reset_trans_id_counter(ConnHandle, Item, Serial) ->
+ LocalMid = ConnHandle#megaco_conn_handle.local_mid,
+ case megaco_config:lookup_local_conn(ConnHandle) of
+ [] ->
+ {error, {no_such_connection, ConnHandle}};
+ [ConnData] ->
+ do_reset_trans_id_counter(ConnData, LocalMid,
+ Item, Serial)
+ end.
+
+do_reset_trans_id_counter(ConnData, LocalMid, Item, Serial)
+ when is_integer(Serial) ->
+ Max = ConnData#conn_data.max_serial,
+ Overflow =
+ if
+ (Max == infinity) ->
+ Serial - 4294967295;
+
+ is_integer(Max) ->
+ Serial - Max
+ end,
+ NewSerial = user_info(LocalMid, min_trans_id) + (Overflow-1),
+ ConnData2 = ConnData#conn_data{serial = NewSerial},
+ ets:insert(megaco_config, {Item, NewSerial}),
+ {ok, ConnData2}.
+
+
+trans_sender_exit(Reason, CH) ->
+ ?d("trans_sender_exit -> entry with"
+ "~n Reason: ~p"
+ "~n CH: ~p", [Reason, CH]),
+ cast({trans_sender_exit, Reason, CH}).
+
+
+call(Request) ->
+ case (catch gen_server:call(?SERVER, Request, infinity)) of
+ {'EXIT', _} ->
+ {error, megaco_not_started};
+ Res ->
+ Res
+ end.
+
+
+cast(Msg) ->
+ case (catch gen_server:cast(?SERVER, Msg)) of
+ {'EXIT', _} ->
+ {error, megaco_not_started};
+ Res ->
+ Res
+ end.
+
+
+%%%----------------------------------------------------------------------
+%%% Callback functions from gen_server
+%%%----------------------------------------------------------------------
+
+%%----------------------------------------------------------------------
+%% Func: init/1
+%% Returns: {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%%----------------------------------------------------------------------
+
+init([Parent]) ->
+ ?d("init -> entry with "
+ "~n Parent: ~p", [Parent]),
+ process_flag(trap_exit, true),
+ case (catch do_init()) of
+ ok ->
+ ?d("init -> init ok", []),
+ {ok, #state{parent_pid = Parent}};
+ Else ->
+ ?d("init -> init error: "
+ "~n ~p", [Else]),
+ {stop, Else}
+ end.
+
+do_init() ->
+ ?megaco_test_init(),
+ ets:new(megaco_config, [public, named_table, {keypos, 1}]),
+ ets:new(megaco_local_conn, [public, named_table, {keypos, 2}]),
+ ets:new(megaco_remote_conn, [public, named_table, {keypos, 2}, bag]),
+ megaco_stats:init(megaco_stats, global_snmp_counters()),
+ init_scanner(),
+ init_user_defaults(),
+ init_users().
+
+
+
+init_scanner() ->
+ case get_env(scanner, undefined) of
+ undefined ->
+ Key = text_config,
+ Data = [],
+ ets:insert(megaco_config, {Key, Data});
+ flex ->
+ start_scanner(megaco_flex_scanner_handler,
+ start_link, [], [gen_server]);
+ {flex, Opts} when is_list(Opts) -> % For future use
+ start_scanner(megaco_flex_scanner_handler,
+ start_link, [Opts], [gen_server]);
+ {M, F, A, Mods} when is_atom(M) andalso
+ is_atom(F) andalso
+ is_list(A) andalso
+ is_list(Mods) ->
+ start_scanner(M, F, A, Mods)
+ end.
+
+start_scanner(M, F, A, Mods) ->
+ case megaco_misc_sup:start_permanent_worker(M, F, A, Mods) of
+ {ok, Pid, Conf} when is_pid(Pid) ->
+ Key = text_config,
+ Data = [Conf],
+ ets:insert(megaco_config, {Key, Data});
+ Else ->
+ throw({scanner_start_failed, Else})
+ end.
+
+init_user_defaults() ->
+ init_user_default(min_trans_id, 1),
+ init_user_default(max_trans_id, infinity),
+ init_user_default(request_timer, #megaco_incr_timer{}),
+ init_user_default(long_request_timer, timer:seconds(60)),
+
+ init_user_default(auto_ack, false),
+
+ init_user_default(trans_ack, false),
+ init_user_default(trans_ack_maxcount, 10),
+
+ init_user_default(trans_req, false),
+ init_user_default(trans_req_maxcount, 10),
+ init_user_default(trans_req_maxsize, 2048),
+
+ init_user_default(trans_timer, 0),
+ init_user_default(trans_sender, undefined),
+
+ init_user_default(pending_timer, timer:seconds(30)),
+ init_user_default(sent_pending_limit, infinity),
+ init_user_default(recv_pending_limit, infinity),
+ init_user_default(reply_timer, timer:seconds(30)),
+ init_user_default(send_mod, megaco_tcp),
+ init_user_default(encoding_mod, megaco_pretty_text_encoder),
+ init_user_default(protocol_version, 1),
+ init_user_default(auth_data, asn1_NOVALUE),
+ init_user_default(encoding_config, []),
+ init_user_default(user_mod, megaco_user_default),
+ init_user_default(user_args, []),
+ init_user_default(reply_data, undefined),
+ init_user_default(threaded, false),
+ init_user_default(strict_version, true),
+ init_user_default(long_request_resend, false),
+ init_user_default(call_proxy_gc_timeout, timer:seconds(5)),
+ init_user_default(cancel, false),
+ init_user_default(resend_indication, false),
+ init_user_default(segment_reply_ind, false),
+ init_user_default(segment_recv_acc, false),
+ init_user_default(segment_recv_timer, timer:seconds(10)),
+ init_user_default(segment_send, none),
+ init_user_default(segment_send_timer, timer:seconds(5)),
+ init_user_default(max_pdu_size, infinity),
+ init_user_default(request_keep_alive_timeout, plain).
+
+init_user_default(Item, Default) when Item /= mid ->
+ Val = get_env(Item, Default),
+ case do_update_user(default, Item, Val) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ throw(Reason)
+ end.
+
+init_users() ->
+ Users = get_env(users, []),
+ init_users(Users).
+
+init_users([]) ->
+ ok;
+init_users([{UserMid, Config} | Rest]) ->
+ case handle_start_user(UserMid, Config) of
+ ok ->
+ init_users(Rest);
+ Else ->
+ throw({bad_user, UserMid, Else})
+ end;
+init_users(BadConfig) ->
+ throw({bad_config, users, BadConfig}).
+
+%%----------------------------------------------------------------------
+%% Func: handle_call/3
+%% Returns: {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} | (terminate/2 is called)
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+
+handle_call({cre_counter, Item, Incr}, _From, S) ->
+ Reply = cre_counter(Item, Incr),
+ {reply, Reply, S};
+
+handle_call({del_counter, Item, Incr}, _From, S) ->
+ Reply = cre_counter(Item, Incr),
+ {reply, Reply, S};
+
+%% For backward compatibillity (code upgrade)
+handle_call({incr_trans_id_counter, ConnHandle}, _From, S) ->
+ Reply = incr_trans_id_counter(ConnHandle),
+ {reply, Reply, S};
+
+handle_call({init_trans_id_counter, ConnHandle, Item, Incr}, _From, S) ->
+ Reply = do_init_trans_id_counter(ConnHandle, Item, Incr),
+ {reply, Reply, S};
+
+%% For backward compatibillity (code upgrade)
+handle_call({reset_trans_id_counter, ConnHandle, Item, Serial}, _From, S) ->
+ Reply = reset_trans_id_counter(ConnHandle, Item, Serial),
+ {reply, Reply, S};
+
+handle_call({receive_handle, UserMid}, _From, S) ->
+ case catch make_receive_handle(UserMid) of
+ {'EXIT', _} ->
+ {reply, {error, {no_receive_handle, UserMid}}, S};
+ RH ->
+ {reply, {ok, RH}, S}
+ end;
+handle_call({connect, RH, RemoteMid, SendHandle, ControlPid}, _From, S) ->
+ Reply = handle_connect(RH, RemoteMid, SendHandle, ControlPid, auto),
+ {reply, Reply, S};
+handle_call({connect, RH, RemoteMid, SendHandle, ControlPid, Auto}, _From, S) ->
+ Reply = handle_connect(RH, RemoteMid, SendHandle, ControlPid, Auto),
+ {reply, Reply, S};
+
+handle_call({finish_connect, ConnHandle, SendHandle, ControlPid, MFA},
+ _From, S) ->
+ Reply = handle_finish_connect(ConnHandle, SendHandle, ControlPid, MFA),
+ {reply, Reply, S};
+
+handle_call({connect_remote, CH, UserNode, Ref}, _From, S) ->
+ Reply = handle_connect_remote(CH, UserNode, Ref),
+ {reply, Reply, S};
+
+handle_call({disconnect, ConnHandle}, _From, S) ->
+ Reply = handle_disconnect(ConnHandle),
+ {reply, Reply, S};
+handle_call({disconnect_remote, CH, UserNode}, _From, S) ->
+ Reply = handle_disconnect_remote(CH, UserNode),
+ {reply, Reply, S};
+
+handle_call({start_user, UserMid, Config}, _From, S) ->
+ Reply = handle_start_user(UserMid, Config),
+ {reply, Reply, S};
+handle_call({stop_user, UserMid}, _From, S) ->
+ Reply = handle_stop_user(UserMid),
+ {reply, Reply, S};
+handle_call({update_conn_data, CH, Item, Val}, _From, S) ->
+ case lookup_local_conn(CH) of
+ [] ->
+ {reply, {error, {no_such_connection, CH}}, S};
+ [CD] ->
+ Reply = handle_update_conn_data(CD, Item, Val),
+ {reply, Reply, S}
+ end;
+handle_call({update_user_info, UserMid, Item, Val}, _From, S) ->
+ case catch user_info(UserMid, mid) of
+ {'EXIT', _} ->
+ {reply, {error, {no_such_user, UserMid}}, S};
+ _ ->
+ Reply = do_update_user(UserMid, Item, Val),
+ {reply, Reply, S}
+ end;
+
+handle_call({stop, ParentPid}, _From, #state{parent_pid = ParentPid} = S) ->
+ Reason = normal,
+ Reply = ok,
+ {stop, Reason, Reply, S};
+
+handle_call(Req, From, S) ->
+ warning_msg("received unexpected request from ~p: "
+ "~n~w", [From, Req]),
+ {reply, {error, {bad_request, Req}}, S}.
+
+
+%%----------------------------------------------------------------------
+%% Func: handle_cast/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+
+handle_cast({trans_sender_exit, Reason, CH}, S) ->
+ warning_msg("transaction sender [~p] restarting: "
+ "~n~p", [CH, Reason]),
+ case lookup_local_conn(CH) of
+ [] ->
+ error_msg("connection data not found for ~p~n"
+ "when restarting transaction sender", [CH]);
+ [CD] ->
+ CD2 = trans_sender_start(CD#conn_data{trans_sender = undefined}),
+ ets:insert(megaco_local_conn, CD2)
+ end,
+ {noreply, S};
+
+handle_cast(Msg, S) ->
+ warning_msg("received unexpected message: "
+ "~n~w", [Msg]),
+ {noreply, S}.
+
+
+
+%%----------------------------------------------------------------------
+%% Func: handle_info/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+
+handle_info({'EXIT', Pid, Reason}, S) when Pid =:= S#state.parent_pid ->
+ {stop, Reason, S};
+
+handle_info(Info, S) ->
+ warning_msg("received unknown info: "
+ "~n~w", [Info]),
+ {noreply, S}.
+
+
+
+%%----------------------------------------------------------------------
+%% Func: terminate/2
+%% Purpose: Shutdown the server
+%% Returns: any (ignored by gen_server)
+%%----------------------------------------------------------------------
+
+terminate(_Reason, _State) ->
+ ok.
+
+
+%%----------------------------------------------------------------------
+%% Func: code_change/3
+%% Purpose: Convert process state when code is changed
+%% Returns: {ok, NewState}
+%%----------------------------------------------------------------------
+
+code_change(_Vsn, S, upgrade_from_pre_3_12) ->
+ upgrade_user_info_from_pre_3_12(),
+ upgrade_conn_data_from_pre_3_12(),
+ {ok, S};
+
+code_change(_Vsn, S, downgrade_to_pre_3_12) ->
+ downgrade_user_info_to_pre_3_12(),
+ downgrade_conn_data_to_pre_3_12(),
+ {ok, S};
+
+code_change(_Vsn, S, _Extra) ->
+ {ok, S}.
+
+
+%% -- Upgrade user info --
+
+upgrade_user_info_from_pre_3_12() ->
+ NewValues = [{request_keep_alive_timeout, plain}],
+ upgrade_user_info(NewValues).
+
+%% upgrade_user_info_from_pre_3_7() ->
+%% NewValues = [{segment_reply_ind, false},
+%% {segment_recv_acc, false},
+%% {segment_recv_timer, #megaco_incr_timer{}},
+%% {segment_send, none},
+%% {segment_send_timer, infinity},
+%% {max_pdu_size, infinity}],
+%% upgrade_user_info(NewValues).
+
+upgrade_user_info(NewValues) ->
+ Users = [default|system_info(users)],
+ F = fun({Item, Val}) ->
+ upgrade_user_info(Users, Item, Val)
+ end,
+ lists:foreach(F, NewValues),
+ ok.
+
+upgrade_user_info(Users, Item, Val) ->
+ F = fun(User) -> do_update_user(User, Item, Val) end,
+ lists:foreach(F, Users),
+ ok.
+
+
+%% %% -- Downgrade user info --
+
+downgrade_user_info_to_pre_3_12() ->
+ NewItems = [
+ request_keep_alive_timeout
+ ],
+ downgrade_user_info(NewItems).
+
+%% downgrade_user_info_to_pre_3_7() ->
+%% NewItems = [
+%% segment_reply_ind,
+%% segment_recv_acc,
+%% segment_recv_timer,
+%% segment_send,
+%% segment_send_timer,
+%% max_pdu_size
+%% ],
+%% downgrade_user_info(NewItems).
+
+downgrade_user_info(NewItems) ->
+ Users = [default|system_info(users)],
+ F = fun(Item) ->
+ downgrade_user_info(Users, Item)
+ end,
+ lists:foreach(F, NewItems),
+ ok.
+
+downgrade_user_info(Users, Item) ->
+ F = fun(User) -> do_downgrade_user_info(User, Item) end,
+ lists:foreach(F, Users),
+ ok.
+
+do_downgrade_user_info(User, Item) ->
+ ets:delete(megaco_config, {User, Item}).
+
+
+%% %% -- Upgrade conn data --
+
+upgrade_conn_data_from_pre_3_12() ->
+ Conns = system_info(connections),
+ Defaults = [{request_keep_alive_timeout, plain}],
+ upgrade_conn_data(Conns, Defaults).
+
+%% upgrade_conn_data_from_pre_3_7() ->
+%% Conns = system_info(connections),
+%% Defaults = [{segment_reply_ind, false},
+%% {segment_recv_acc, false},
+%% {segment_recv_timer, #megaco_incr_timer{}},
+%% {segment_send, false},
+%% {segment_send_timer, #megaco_incr_timer{}},
+%% {max_pdu_size, infinity}],
+%% upgrade_conn_data(Conns, Defaults).
+
+upgrade_conn_data(Conns, Defaults) ->
+ F = fun(CH) ->
+ case lookup_local_conn(CH) of
+ [] ->
+ ok;
+ [CD] ->
+ do_upgrade_conn_data(CD, Defaults)
+ end
+ end,
+ lists:foreach(F, Conns),
+ ok.
+
+do_upgrade_conn_data(OldStyleCD, Defaults) ->
+ NewStyleCD = new_conn_data(OldStyleCD, Defaults),
+ ets:insert(megaco_local_conn, NewStyleCD).
+
+%% Pre 3.12
+new_conn_data({conn_data, CH, Serial, MaxSerial, ReqTmr, LongReqTmr,
+ AutoAck,
+ TransAck, TransAckMaxCnt,
+ TransReq, TransReqMaxCnt, TransReqMaxSz,
+ TransTmr, TransSndr,
+
+ PendingTmr,
+ SentPendingLimit,
+ RecvPendingLimit,
+ ReplyTmr, CtrPid, MonRef,
+ Sendmod, SendHandle,
+ EncodeMod, EncodeConf,
+ ProtV, AuthData,
+ UserMod, UserArgs, ReplyAction, ReplyData,
+ Threaded,
+ StrictVersion,
+ LongReqResend,
+ Cancel,
+ ResendIndication,
+ SegmentReplyInd,
+ SegmentRecvAcc,
+ SegmentRecvTimer,
+ SegmentSend,
+ SegmentSendTimer,
+ MaxPDUSize
+ %% RequestKeepAliveTimerDefault - New values
+ },
+ Defaults) ->
+ #conn_data{conn_handle = CH,
+ serial = Serial,
+ max_serial = MaxSerial,
+ request_timer = ReqTmr,
+ long_request_timer = LongReqTmr,
+
+ auto_ack = AutoAck,
+
+ trans_ack = TransAck,
+ trans_ack_maxcount = TransAckMaxCnt,
+
+ trans_req = TransReq,
+ trans_req_maxcount = TransReqMaxCnt,
+ trans_req_maxsize = TransReqMaxSz,
+
+ trans_timer = TransTmr,
+ trans_sender = TransSndr,
+
+ pending_timer = PendingTmr,
+ sent_pending_limit = SentPendingLimit,
+ recv_pending_limit = RecvPendingLimit,
+
+ reply_timer = ReplyTmr,
+ control_pid = CtrPid,
+ monitor_ref = MonRef,
+ send_mod = Sendmod,
+ send_handle = SendHandle,
+ encoding_mod = EncodeMod,
+ encoding_config = EncodeConf,
+ protocol_version = ProtV,
+ auth_data = AuthData,
+ user_mod = UserMod,
+ user_args = UserArgs,
+ reply_action = ReplyAction,
+ reply_data = ReplyData,
+ threaded = Threaded,
+ strict_version = StrictVersion,
+ long_request_resend = LongReqResend,
+ cancel = Cancel,
+ resend_indication = ResendIndication,
+ segment_reply_ind = SegmentReplyInd,
+ segment_recv_acc = SegmentRecvAcc,
+ segment_recv_timer = SegmentRecvTimer,
+ segment_send = SegmentSend,
+ segment_send_timer = SegmentSendTimer,
+ max_pdu_size = MaxPDUSize,
+ request_keep_alive_timeout = get_default(request_keep_alive_timeout, Defaults)
+ }.
+
+
+get_default(Key, Defaults) ->
+ {value, {Key, Default}} = lists:keysearch(Key, 1, Defaults),
+ Default.
+
+
+%% %% -- Downgrade conn data --
+
+downgrade_conn_data_to_pre_3_12() ->
+ Conns = system_info(connections),
+ Downgrade = fun(NewCD) -> old_conn_data_to_pre_3_12(NewCD) end,
+ downgrade_conn_data(Downgrade, Conns).
+
+downgrade_conn_data(Downgrade, Conns) ->
+ F = fun(CH) ->
+ case lookup_local_conn(CH) of
+ [] ->
+ ok;
+ [CD] ->
+ do_downgrade_conn_data(Downgrade, CD)
+ end
+ end,
+ lists:foreach(F, Conns).
+
+do_downgrade_conn_data(Downgrade, NewStyleCD) ->
+ OldStyleCD = Downgrade(NewStyleCD),
+ ets:insert(megaco_local_conn, OldStyleCD).
+
+old_conn_data_to_pre_3_12(
+ #conn_data{conn_handle = CH,
+ serial = Serial,
+ max_serial = MaxSerial,
+ request_timer = ReqTmr,
+ long_request_timer = LongReqTmr,
+
+ auto_ack = AutoAck,
+
+ trans_ack = TransAck,
+ trans_ack_maxcount = TransAckMaxCnt,
+
+ trans_req = TransReq,
+ trans_req_maxcount = TransReqMaxCnt,
+ trans_req_maxsize = TransReqMaxSz,
+
+ trans_timer = TransTmr,
+ trans_sender = TransSndr,
+
+ pending_timer = PendingTmr,
+ sent_pending_limit = SentPendingLimit,
+ recv_pending_limit = RecvPendingLimit,
+
+ reply_timer = ReplyTmr,
+ control_pid = CtrPid,
+ monitor_ref = MonRef,
+ send_mod = Sendmod,
+ send_handle = SendHandle,
+ encoding_mod = EncodeMod,
+ encoding_config = EncodeConf,
+ protocol_version = ProtV,
+ auth_data = AuthData,
+ user_mod = UserMod,
+ user_args = UserArgs,
+ reply_action = ReplyAction,
+ reply_data = ReplyData,
+ threaded = Threaded,
+ strict_version = StrictVersion,
+ long_request_resend = LongReqResend,
+ cancel = Cancel,
+ resend_indication = ResendIndication,
+ segment_reply_ind = SegmentRecvAcc,
+ segment_recv_acc = SegmentRecvAcc,
+ segment_recv_timer = SegmentRecvTimer,
+ segment_send = SegmentSend,
+ segment_send_timer = SegmentSendTimer,
+ max_pdu_size = MaxPDUSize
+ %% request_keep_alive_timeout = RequestKeepAliveTimeout
+ }) ->
+ {conn_data, CH, Serial, MaxSerial, ReqTmr, LongReqTmr,
+ AutoAck,
+ TransAck, TransAckMaxCnt,
+ TransReq, TransReqMaxCnt, TransReqMaxSz,
+ TransTmr, TransSndr,
+ PendingTmr,
+ SentPendingLimit,
+ RecvPendingLimit,
+ ReplyTmr, CtrPid, MonRef,
+ Sendmod, SendHandle,
+ EncodeMod, EncodeConf,
+ ProtV, AuthData,
+ UserMod, UserArgs, ReplyAction, ReplyData,
+ Threaded,
+ StrictVersion,
+ LongReqResend,
+ Cancel,
+ ResendIndication,
+ SegmentRecvAcc,
+ SegmentRecvAcc,
+ SegmentRecvTimer,
+ SegmentSend,
+ SegmentSendTimer,
+ MaxPDUSize}.
+
+
+
+%%%----------------------------------------------------------------------
+%%% Internal functions
+%%%----------------------------------------------------------------------
+
+handle_start_user(default, _Config) ->
+ {error, bad_user_mid};
+handle_start_user(Mid, Config) ->
+ case catch user_info(Mid, mid) of
+ {'EXIT', _} ->
+ DefaultConfig = user_info(default, all),
+ do_handle_start_user(Mid, DefaultConfig),
+ do_handle_start_user(Mid, Config);
+ _LocalMid ->
+ {error, {user_already_exists, Mid}}
+ end.
+
+do_handle_start_user(UserMid, [{Item, Val} | Rest]) ->
+ case do_update_user(UserMid, Item, Val) of
+ ok ->
+ do_handle_start_user(UserMid, Rest);
+ {error, Reason} ->
+ ets:match_delete(megaco_config, {{UserMid, '_'}, '_'}),
+ {error, Reason}
+ end;
+do_handle_start_user(UserMid, []) ->
+ do_update_user(UserMid, mid, UserMid),
+ ok;
+do_handle_start_user(UserMid, BadConfig) ->
+ ets:match_delete(megaco_config, {{UserMid, '_'}, '_'}),
+ {error, {bad_user_config, UserMid, BadConfig}}.
+
+do_update_user(UserMid, Item, Val) ->
+ case verify_val(Item, Val) of
+ true ->
+ ets:insert(megaco_config, {{UserMid, Item}, Val}),
+ ok;
+ false ->
+ {error, {bad_user_val, UserMid, Item, Val}}
+ end.
+
+verify_val(Item, Val) ->
+ case Item of
+ mid -> true;
+ local_mid -> true;
+ remote_mid -> true;
+ min_trans_id -> verify_strict_uint(Val, 4294967295); % uint32
+ max_trans_id -> verify_uint(Val, 4294967295); % uint32
+ request_timer -> verify_timer(Val);
+ long_request_timer -> verify_timer(Val);
+
+ auto_ack -> verify_bool(Val);
+
+ trans_ack -> verify_bool(Val);
+ trans_ack_maxcount -> verify_uint(Val);
+
+ trans_req -> verify_bool(Val);
+ trans_req_maxcount -> verify_uint(Val);
+ trans_req_maxsize -> verify_uint(Val);
+
+ trans_timer -> verify_timer(Val) and (Val >= 0);
+ trans_sender when Val == undefined -> true;
+
+ pending_timer -> verify_timer(Val);
+ sent_pending_limit -> verify_uint(Val) andalso
+ (Val > 0);
+ recv_pending_limit -> verify_uint(Val) andalso
+ (Val > 0);
+ reply_timer -> verify_timer(Val);
+ control_pid when is_pid(Val) -> true;
+ monitor_ref -> true; % Internal usage only
+ send_mod when is_atom(Val) -> true;
+ send_handle -> true;
+ encoding_mod when is_atom(Val) -> true;
+ encoding_config when is_list(Val) -> true;
+ protocol_version -> verify_strict_uint(Val);
+ auth_data -> true;
+ user_mod when is_atom(Val) -> true;
+ user_args when is_list(Val) -> true;
+ reply_data -> true;
+ threaded -> verify_bool(Val);
+ strict_version -> verify_bool(Val);
+ long_request_resend -> verify_bool(Val);
+ call_proxy_gc_timeout -> verify_strict_uint(Val);
+ cancel -> verify_bool(Val);
+ resend_indication -> verify_resend_indication(Val);
+
+ segment_reply_ind -> verify_bool(Val);
+ segment_recv_acc -> verify_bool(Val);
+ segment_recv_timer -> verify_timer(Val);
+ segment_send -> verify_segmentation_window(Val);
+ segment_send_timer -> verify_timer(Val);
+ max_pdu_size -> verify_int(Val) andalso (Val > 0);
+ request_keep_alive_timeout ->
+ (verify_int(Val) andalso (Val >= 0)) orelse (Val =:= plain);
+
+ _ -> false
+ end.
+
+
+
+verify_bool(true) -> true;
+verify_bool(false) -> true;
+verify_bool(_) -> false.
+
+verify_resend_indication(flag) -> true;
+verify_resend_indication(Val) -> verify_bool(Val).
+
+-spec verify_strict_int(Int :: integer()) -> boolean().
+verify_strict_int(Int) when is_integer(Int) -> true;
+verify_strict_int(_) -> false.
+
+-spec verify_strict_int(Int :: integer(),
+ Max :: integer() | 'infinity') -> boolean().
+verify_strict_int(Int, infinity) ->
+ verify_strict_int(Int);
+verify_strict_int(Int, Max) ->
+ verify_strict_int(Int) andalso verify_strict_int(Max) andalso (Int =< Max).
+
+-spec verify_strict_uint(Int :: non_neg_integer()) -> boolean().
+verify_strict_uint(Int) when is_integer(Int) andalso (Int >= 0) -> true;
+verify_strict_uint(_) -> false.
+
+-spec verify_strict_uint(Int :: non_neg_integer(),
+ Max :: non_neg_integer() | 'infinity') -> boolean().
+verify_strict_uint(Int, infinity) ->
+ verify_strict_uint(Int);
+verify_strict_uint(Int, Max) ->
+ verify_strict_int(Int, 0, Max).
+
+-spec verify_uint(Val :: non_neg_integer() | 'infinity') -> boolean().
+verify_uint(infinity) -> true;
+verify_uint(Val) -> verify_strict_uint(Val).
+
+-spec verify_int(Val :: integer() | 'infinity') -> boolean().
+verify_int(infinity) -> true;
+verify_int(Val) -> verify_strict_int(Val).
+
+-spec verify_int(Int :: integer() | 'infinity',
+ Max :: integer() | 'infinity') -> boolean().
+verify_int(Int, infinity) ->
+ verify_int(Int);
+verify_int(infinity, _Max) ->
+ true;
+verify_int(Int, Max) ->
+ verify_strict_int(Int) andalso verify_strict_int(Max) andalso (Int =< Max).
+
+-spec verify_uint(Int :: non_neg_integer() | 'infinity',
+ Max :: non_neg_integer() | 'infinity') -> boolean().
+verify_uint(Int, infinity) ->
+ verify_uint(Int);
+verify_uint(infinity, _Max) ->
+ true;
+verify_uint(Int, Max) ->
+ verify_strict_int(Int, 0, Max).
+
+-spec verify_strict_int(Int :: integer(),
+ Min :: integer(),
+ Max :: integer()) -> boolean().
+verify_strict_int(Val, Min, Max)
+ when (is_integer(Val) andalso
+ is_integer(Min) andalso
+ is_integer(Max) andalso
+ (Val >= Min) andalso
+ (Val =< Max)) ->
+ true;
+verify_strict_int(_Val, _Min, _Max) ->
+ false.
+
+-spec verify_int(Val :: integer() | 'infinity',
+ Min :: integer(),
+ Max :: integer() | 'infinity') -> boolean().
+verify_int(infinity, Min, infinity) ->
+ verify_strict_int(Min);
+verify_int(Val, Min, infinity) ->
+ verify_strict_int(Val) andalso
+ verify_strict_int(Min) andalso (Val >= Min);
+verify_int(Int, Min, Max) ->
+ verify_strict_int(Int, Min, Max).
+
+verify_timer(Timer) ->
+ megaco_timer:verify(Timer).
+
+verify_segmentation_window(none) ->
+ true;
+verify_segmentation_window(K) ->
+ verify_int(K, 1, infinity).
+
+handle_stop_user(UserMid) ->
+ case catch user_info(UserMid, mid) of
+ {'EXIT', _} ->
+ {error, {no_such_user, UserMid}};
+ _ ->
+ case catch user_info(UserMid, connections) of
+ [] ->
+ ets:match_delete(megaco_config, {{UserMid, '_'}, '_'}),
+ ok;
+ {'EXIT', _} ->
+ {error, {no_such_user, UserMid}};
+ _Else ->
+ {error, {active_connections, UserMid}}
+ end
+ end.
+
+handle_update_conn_data(CD, Item = receive_handle, RH) ->
+ UserMid = (CD#conn_data.conn_handle)#megaco_conn_handle.local_mid,
+ if
+ is_record(RH, megaco_receive_handle) andalso
+ is_atom(RH#megaco_receive_handle.encoding_mod) andalso
+ is_list(RH#megaco_receive_handle.encoding_config) andalso
+ is_atom(RH#megaco_receive_handle.send_mod) andalso
+ (RH#megaco_receive_handle.local_mid /= UserMid) ->
+ CD2 = CD#conn_data{
+ encoding_mod = RH#megaco_receive_handle.encoding_mod,
+ encoding_config = RH#megaco_receive_handle.encoding_config,
+ send_mod = RH#megaco_receive_handle.send_mod},
+ ets:insert(megaco_local_conn, CD2),
+ ok;
+ true ->
+ {error, {bad_user_val, UserMid, Item, RH}}
+ end;
+handle_update_conn_data(CD, Item, Val) ->
+ case verify_val(Item, Val) of
+ true ->
+ CD2 = replace_conn_data(CD, Item, Val),
+ ets:insert(megaco_local_conn, CD2),
+ ok;
+ false ->
+ UserMid = (CD#conn_data.conn_handle)#megaco_conn_handle.local_mid,
+ {error, {bad_user_val, UserMid, Item, Val}}
+ end.
+
+replace_conn_data(CD, Item, Val) ->
+ case Item of
+ trans_id -> CD#conn_data{serial = Val};
+ max_trans_id -> CD#conn_data{max_serial = Val};
+ request_timer -> CD#conn_data{request_timer = Val};
+ long_request_timer -> CD#conn_data{long_request_timer = Val};
+
+ auto_ack -> update_auto_ack(CD, Val);
+
+ %% Accumulate trans ack before sending
+ trans_ack -> update_trans_ack(CD, Val);
+ trans_ack_maxcount -> update_trans_ack_maxcount(CD, Val);
+
+ %% Accumulate trans req before sending
+ trans_req -> update_trans_req(CD, Val);
+ trans_req_maxcount -> update_trans_req_maxcount(CD, Val);
+ trans_req_maxsize -> update_trans_req_maxsize(CD, Val);
+
+ trans_timer -> update_trans_timer(CD, Val);
+ %% trans_sender - Automagically updated by
+ %% update_auto_ack & update_trans_timer &
+ %% update_trans_ack & update_trans_req
+
+ pending_timer -> CD#conn_data{pending_timer = Val};
+ sent_pending_limit -> CD#conn_data{sent_pending_limit = Val};
+ recv_pending_limit -> CD#conn_data{recv_pending_limit = Val};
+ reply_timer -> CD#conn_data{reply_timer = Val};
+ control_pid -> CD#conn_data{control_pid = Val};
+ monitor_ref -> CD#conn_data{monitor_ref = Val};
+ send_mod -> CD#conn_data{send_mod = Val};
+ send_handle -> CD#conn_data{send_handle = Val};
+ encoding_mod -> CD#conn_data{encoding_mod = Val};
+ encoding_config -> CD#conn_data{encoding_config = Val};
+ protocol_version -> CD#conn_data{protocol_version = Val};
+ auth_data -> CD#conn_data{auth_data = Val};
+ user_mod -> CD#conn_data{user_mod = Val};
+ user_args -> CD#conn_data{user_args = Val};
+ reply_action -> CD#conn_data{reply_action = Val};
+ reply_data -> CD#conn_data{reply_data = Val};
+ threaded -> CD#conn_data{threaded = Val};
+ strict_version -> CD#conn_data{strict_version = Val};
+ long_request_resend -> CD#conn_data{long_request_resend = Val};
+ call_proxy_gc_timeout -> CD#conn_data{call_proxy_gc_timeout = Val};
+ cancel -> CD#conn_data{cancel = Val};
+ resend_indication -> CD#conn_data{resend_indication = Val};
+ segment_reply_ind -> CD#conn_data{segment_reply_ind = Val};
+ segment_recv_acc -> CD#conn_data{segment_recv_acc = Val};
+ segment_recv_timer -> CD#conn_data{segment_recv_timer = Val};
+ segment_send -> CD#conn_data{segment_send = Val};
+ segment_send_timer -> CD#conn_data{segment_send_timer = Val};
+ max_pdu_size -> CD#conn_data{max_pdu_size = Val};
+ request_keep_alive_timeout -> CD#conn_data{request_keep_alive_timeout = Val}
+ end.
+
+%% update auto_ack
+update_auto_ack(#conn_data{trans_sender = Pid,
+ trans_req = false} = CD,
+ false) when is_pid(Pid) ->
+ megaco_trans_sender:stop(Pid),
+ CD#conn_data{auto_ack = false, trans_sender = undefined};
+
+update_auto_ack(#conn_data{trans_timer = To,
+ trans_ack = true,
+ trans_sender = undefined} = CD,
+ true) when To > 0 ->
+ #conn_data{conn_handle = CH,
+ trans_ack_maxcount = AcksMax,
+ trans_req_maxcount = ReqsMax,
+ trans_req_maxsize = ReqsMaxSz} = CD,
+ {ok, Pid} = megaco_trans_sup:start_trans_sender(CH, To, ReqsMaxSz,
+ ReqsMax, AcksMax),
+
+ %% Make sure we are notified when/if the transaction
+ %% sender goes down.
+ %% Do we need to store the ref? Will we ever need to
+ %% cancel this (apply_at_exit)?
+ megaco_monitor:apply_at_exit(?MODULE, trans_sender_exit, [CH], Pid),
+
+ CD#conn_data{auto_ack = true, trans_sender = Pid};
+
+update_auto_ack(CD, Val) ->
+ ?d("update_auto_ack -> entry with ~p", [Val]),
+ CD#conn_data{auto_ack = Val}.
+
+%% update trans_ack
+update_trans_ack(#conn_data{auto_ack = true,
+ trans_req = false,
+ trans_sender = Pid} = CD,
+ false) when is_pid(Pid) ->
+ megaco_trans_sender:stop(Pid),
+ CD#conn_data{trans_ack = false, trans_sender = undefined};
+
+update_trans_ack(#conn_data{trans_timer = To,
+ auto_ack = true,
+ trans_sender = undefined} = CD,
+ true) when To > 0 ->
+ #conn_data{conn_handle = CH,
+ trans_ack_maxcount = AcksMax,
+ trans_req_maxcount = ReqsMax,
+ trans_req_maxsize = ReqsMaxSz} = CD,
+ {ok, Pid} = megaco_trans_sup:start_trans_sender(CH, To, ReqsMaxSz,
+ ReqsMax, AcksMax),
+
+ %% Make sure we are notified when/if the transaction
+ %% sender goes down.
+ %% Do we need to store the ref? Will we ever need to
+ %% cancel this (apply_at_exit)?
+ megaco_monitor:apply_at_exit(?MODULE, trans_sender_exit, [CH], Pid),
+
+ CD#conn_data{trans_ack = true, trans_sender = Pid};
+
+update_trans_ack(CD, Val) ->
+ ?d("update_trans_ack -> entry with ~p", [Val]),
+ CD#conn_data{trans_ack = Val}.
+
+%% update trans_req
+update_trans_req(#conn_data{trans_ack = false,
+ trans_sender = Pid} = CD,
+ false) when is_pid(Pid) ->
+ megaco_trans_sender:stop(Pid),
+ CD#conn_data{trans_req = false, trans_sender = undefined};
+
+update_trans_req(#conn_data{trans_timer = To,
+ trans_sender = undefined} = CD,
+ true) when To > 0 ->
+ #conn_data{conn_handle = CH,
+ trans_ack_maxcount = AcksMax,
+ trans_req_maxcount = ReqsMax,
+ trans_req_maxsize = ReqsMaxSz} = CD,
+ {ok, Pid} = megaco_trans_sup:start_trans_sender(CH, To, ReqsMaxSz,
+ ReqsMax, AcksMax),
+
+ %% Make sure we are notified when/if the transaction
+ %% sender goes down.
+ %% Do we need to store the ref? Will we ever need to
+ %% cancel this (apply_at_exit)?
+ megaco_monitor:apply_at_exit(?MODULE, trans_sender_exit, [CH], Pid),
+
+ CD#conn_data{trans_req = true, trans_sender = Pid};
+
+update_trans_req(CD, Val) ->
+ ?d("update_trans_req -> entry with ~p", [Val]),
+ CD#conn_data{trans_req = Val}.
+
+%% update trans_timer
+update_trans_timer(#conn_data{auto_ack = true,
+ trans_ack = true,
+ trans_sender = undefined} = CD,
+ To) when To > 0 ->
+ #conn_data{conn_handle = CH,
+ trans_ack_maxcount = AcksMax,
+ trans_req_maxcount = ReqsMax,
+ trans_req_maxsize = ReqsMaxSz} = CD,
+ {ok, Pid} = megaco_trans_sup:start_trans_sender(CH, To, ReqsMaxSz,
+ ReqsMax, AcksMax),
+
+ %% Make sure we are notified when/if the transaction
+ %% sender goes down.
+ %% Do we need to store the ref? Will we ever need to
+ %% cancel this (apply_at_exit)?
+ megaco_monitor:apply_at_exit(?MODULE, trans_sender_exit, [CH], Pid),
+
+ CD#conn_data{trans_timer = To, trans_sender = Pid};
+
+update_trans_timer(#conn_data{trans_req = true,
+ trans_sender = undefined} = CD,
+ To) when To > 0 ->
+ #conn_data{conn_handle = CH,
+ trans_ack_maxcount = AcksMax,
+ trans_req_maxcount = ReqsMax,
+ trans_req_maxsize = ReqsMaxSz} = CD,
+ {ok, Pid} = megaco_trans_sup:start_trans_sender(CH, To, ReqsMaxSz,
+ ReqsMax, AcksMax),
+
+ %% Make sure we are notified when/if the transaction
+ %% sender goes down.
+ %% Do we need to store the ref? Will we ever need to
+ %% cancel this (apply_at_exit)?
+ megaco_monitor:apply_at_exit(?MODULE, trans_sender_exit, [CH], Pid),
+
+ CD#conn_data{trans_timer = To, trans_sender = Pid};
+
+update_trans_timer(#conn_data{trans_sender = Pid} = CD, 0) when is_pid(Pid) ->
+ megaco_trans_sender:stop(Pid),
+ CD#conn_data{trans_timer = 0, trans_sender = undefined};
+
+update_trans_timer(#conn_data{trans_sender = Pid} = CD, To)
+ when is_pid(Pid) and (To > 0) ->
+ megaco_trans_sender:timeout(Pid, To),
+ CD#conn_data{trans_timer = To};
+
+update_trans_timer(CD, To) when To > 0 ->
+ CD#conn_data{trans_timer = To}.
+
+%% update trans_ack_maxcount
+update_trans_ack_maxcount(#conn_data{trans_sender = Pid} = CD, Max)
+ when is_pid(Pid) and (Max > 0) ->
+ megaco_trans_sender:ack_maxcount(Pid, Max),
+ CD#conn_data{trans_ack_maxcount = Max};
+
+update_trans_ack_maxcount(CD, Max)
+ when Max > 0 ->
+ ?d("update_trans_ack_maxcount -> entry with ~p", [Max]),
+ CD#conn_data{trans_ack_maxcount = Max}.
+
+%% update trans_req_maxcount
+update_trans_req_maxcount(#conn_data{trans_sender = Pid} = CD, Max)
+ when is_pid(Pid) and (Max > 0) ->
+ megaco_trans_sender:req_maxcount(Pid, Max),
+ CD#conn_data{trans_req_maxcount = Max};
+
+update_trans_req_maxcount(CD, Max)
+ when Max > 0 ->
+ ?d("update_trans_req_maxcount -> entry with ~p", [Max]),
+ CD#conn_data{trans_req_maxcount = Max}.
+
+%% update trans_req_maxsize
+update_trans_req_maxsize(#conn_data{trans_sender = Pid} = CD, Max)
+ when is_pid(Pid) and (Max > 0) ->
+ megaco_trans_sender:req_maxsize(Pid, Max),
+ CD#conn_data{trans_req_maxsize = Max};
+
+update_trans_req_maxsize(CD, Max)
+ when Max > 0 ->
+ ?d("update_trans_req_maxsize -> entry with ~p", [Max]),
+ CD#conn_data{trans_req_maxsize = Max}.
+
+
+
+handle_connect(RH, RemoteMid, SendHandle, ControlPid, Auto) ->
+ LocalMid = RH#megaco_receive_handle.local_mid,
+ ConnHandle = #megaco_conn_handle{local_mid = LocalMid,
+ remote_mid = RemoteMid},
+ ?d("handle_connect -> entry with"
+ "~n ConnHandle: ~p", [ConnHandle]),
+ case ets:lookup(megaco_local_conn, ConnHandle) of
+ [] ->
+ PrelMid = preliminary_mid,
+ PrelHandle = ConnHandle#megaco_conn_handle{remote_mid = PrelMid},
+ case ets:lookup(megaco_local_conn, PrelHandle) of
+ [] ->
+ case (catch init_conn_data(RH,
+ RemoteMid, SendHandle,
+ ControlPid, Auto)) of
+ {'EXIT', _Reason} ->
+ ?d("handle_connect -> init conn data failed: "
+ "~n ~p",[_Reason]),
+ {error, {no_such_user, LocalMid}};
+ ConnData ->
+ ?d("handle_connect -> new connection"
+ "~n ConnData: ~p", [ConnData]),
+ %% Brand new connection, use
+ %% When is the preliminary_mid used?
+ create_snmp_counters(ConnHandle),
+ %% Maybe start transaction sender
+ ConnData2 = trans_sender_start(ConnData),
+ ets:insert(megaco_local_conn, ConnData2),
+ {ok, ConnData2}
+ end;
+ [PrelData] ->
+ ?d("handle_connect -> connection upgrade"
+ "~n PrelData: ~p", [PrelData]),
+ %% OK, we need to fix the snmp counters. Used
+ %% with the temporary (preliminary_mid) conn_handle.
+ create_snmp_counters(ConnHandle),
+ ConnData = PrelData#conn_data{conn_handle = ConnHandle},
+ trans_sender_upgrade(ConnData),
+ ets:insert(megaco_local_conn, ConnData),
+ ets:delete(megaco_local_conn, PrelHandle),
+ update_snmp_counters(ConnHandle, PrelHandle),
+ TH = ConnHandle#megaco_conn_handle{local_mid = PrelMid,
+ remote_mid = RemoteMid},
+ TD = ConnData#conn_data{conn_handle = TH},
+ ?report_debug(TD,
+ "Upgrade preliminary_mid to "
+ "actual remote_mid",
+ [{preliminary_mid, preliminary_mid},
+ {local_mid, LocalMid},
+ {remote_mid, RemoteMid}]),
+ {ok, ConnData}
+ end;
+ [_ConnData] ->
+ {error, {already_connected, ConnHandle}}
+ end.
+
+handle_finish_connect(ConnHandle, SendHandle, ControlPid, MFA) ->
+ case (catch ets:lookup(megaco_local_conn, ConnHandle)) of
+ [#conn_data{monitor_ref = connected} = CD] ->
+ {M, F, A} = MFA,
+ Ref = megaco_monitor:apply_at_exit(M, F, A, ControlPid),
+ ConnData2 = CD#conn_data{monitor_ref = Ref,
+ control_pid = ControlPid,
+ send_handle = SendHandle},
+ ets:insert(megaco_local_conn, ConnData2),
+ {ok, Ref};
+ [#conn_data{monitor_ref = Ref}] ->
+ {ok, Ref};
+ [] ->
+ {error, {no_such_connection, ConnHandle}}
+ end.
+
+
+%% also trans_req == true
+trans_sender_start(#conn_data{conn_handle = CH,
+ auto_ack = true,
+ trans_ack = true,
+ trans_ack_maxcount = AcksMax,
+ trans_req_maxcount = ReqsMax,
+ trans_req_maxsize = ReqsMaxSz,
+ trans_timer = To,
+ trans_sender = undefined} = CD)
+ when To > 0 ->
+
+ ?d("trans_sender_start(ack) -> entry when"
+ "~n CH: ~p"
+ "~n To: ~p"
+ "~n AcksMax: ~p"
+ "~n ReqsMax: ~p"
+ "~n ReqsMaxSz: ~p", [CH, To, ReqsMaxSz, ReqsMax, AcksMax]),
+
+ {ok, Pid} = megaco_trans_sup:start_trans_sender(CH, To, ReqsMaxSz,
+ ReqsMax, AcksMax),
+
+ ?d("trans_sender_start(ack) -> Pid: ~p", [Pid]),
+
+ %% Make sure we are notified when/if the transaction
+ %% sender goes down.
+ %% Do we need to store the ref? Will we ever need to
+ %% cancel this (apply_at_exit)?
+ megaco_monitor:apply_at_exit(?MODULE, trans_sender_exit, [CH], Pid),
+
+ CD#conn_data{trans_sender = Pid};
+
+trans_sender_start(#conn_data{conn_handle = CH,
+ trans_req = true,
+ trans_ack_maxcount = AcksMax,
+ trans_req_maxcount = ReqsMax,
+ trans_req_maxsize = ReqsMaxSz,
+ trans_timer = To,
+ trans_sender = undefined} = CD)
+ when To > 0 ->
+
+ ?d("trans_sender_start(req) -> entry when"
+ "~n CH: ~p"
+ "~n To: ~p"
+ "~n AcksMax: ~p"
+ "~n ReqsMax: ~p"
+ "~n ReqsMaxSz: ~p", [CH, To, ReqsMaxSz, ReqsMax, AcksMax]),
+
+ {ok, Pid} = megaco_trans_sup:start_trans_sender(CH, To, ReqsMaxSz,
+ ReqsMax, AcksMax),
+
+ ?d("trans_sender_start(req) -> Pid: ~p", [Pid]),
+
+ %% Make sure we are notified when/if the transaction
+ %% sender goes down.
+ %% Do we need to store the ref? Will we ever need to
+ %% cancel this (apply_at_exit)?
+ megaco_monitor:apply_at_exit(?MODULE, trans_sender_exit, [CH], Pid),
+
+ CD#conn_data{trans_sender = Pid};
+
+trans_sender_start(CD) ->
+ ?d("trans_sender_start -> undefined", []),
+ CD#conn_data{trans_sender = undefined}.
+
+trans_sender_upgrade(#conn_data{conn_handle = CH,
+ trans_sender = Pid})
+ when is_pid(Pid) ->
+ ?d("trans_sende_upgrade -> entry when"
+ "~n CH: ~p"
+ "~n Pid: ~p", [CH, Pid]),
+ megaco_trans_sender:upgrade(Pid, CH);
+trans_sender_upgrade(_CD) ->
+ ok.
+
+
+handle_connect_remote(ConnHandle, UserNode, Ref) ->
+ Pat = #remote_conn_data{conn_handle = ConnHandle,
+ user_node = UserNode,
+ monitor_ref = '_'},
+ case ets:match_object(megaco_remote_conn, Pat) of
+ [] ->
+ RCD = #remote_conn_data{conn_handle = ConnHandle,
+ user_node = UserNode,
+ monitor_ref = Ref},
+ ets:insert(megaco_remote_conn, RCD),
+ ok;
+ _ ->
+ {error, {already_connected, ConnHandle, UserNode}}
+ end.
+
+init_conn_data(RH, RemoteMid, SendHandle, ControlPid) ->
+ init_conn_data(RH, RemoteMid, SendHandle, ControlPid, auto).
+init_conn_data(RH, RemoteMid, SendHandle, ControlPid, Auto) ->
+ Mid = RH#megaco_receive_handle.local_mid,
+ ConnHandle = #megaco_conn_handle{local_mid = Mid,
+ remote_mid = RemoteMid},
+ EncodingMod = RH#megaco_receive_handle.encoding_mod,
+ EncodingConfig = RH#megaco_receive_handle.encoding_config,
+ SendMod = RH#megaco_receive_handle.send_mod,
+ MonitorRef =
+ case Auto of
+ auto ->
+ undefined_auto_monitor_ref;
+ {plain, ConnectorPid} ->
+ {connecting, ConnectorPid}
+ end,
+ #conn_data{conn_handle = ConnHandle,
+ serial = undefined_serial,
+ max_serial = user_info(Mid, max_trans_id),
+ request_timer = user_info(Mid, request_timer),
+ long_request_timer = user_info(Mid, long_request_timer),
+
+ auto_ack = user_info(Mid, auto_ack),
+ trans_ack = user_info(Mid, trans_req),
+ trans_req = user_info(Mid, trans_req),
+
+ trans_timer = user_info(Mid, trans_timer),
+ trans_req_maxsize = user_info(Mid, trans_req_maxsize),
+ trans_req_maxcount = user_info(Mid, trans_req_maxcount),
+ trans_ack_maxcount = user_info(Mid, trans_ack_maxcount),
+
+ pending_timer = user_info(Mid, pending_timer),
+ sent_pending_limit = user_info(Mid, sent_pending_limit),
+ recv_pending_limit = user_info(Mid, recv_pending_limit),
+ reply_timer = user_info(Mid, reply_timer),
+ control_pid = ControlPid,
+ monitor_ref = MonitorRef,
+ send_mod = SendMod,
+ send_handle = SendHandle,
+ encoding_mod = EncodingMod,
+ encoding_config = EncodingConfig,
+ protocol_version = user_info(Mid, protocol_version),
+ auth_data = user_info(Mid, auth_data),
+ user_mod = user_info(Mid, user_mod),
+ user_args = user_info(Mid, user_args),
+ reply_action = undefined,
+ reply_data = user_info(Mid, reply_data),
+ threaded = user_info(Mid, threaded),
+ strict_version = user_info(Mid, strict_version),
+ long_request_resend = user_info(Mid, long_request_resend),
+ call_proxy_gc_timeout = user_info(Mid, call_proxy_gc_timeout),
+ cancel = false,
+ resend_indication = user_info(Mid, resend_indication),
+ segment_reply_ind = user_info(Mid, segment_reply_ind),
+ segment_recv_acc = user_info(Mid, segment_recv_acc),
+ segment_recv_timer = user_info(Mid, segment_recv_timer),
+ segment_send = user_info(Mid, segment_send),
+ segment_send_timer = user_info(Mid, segment_send_timer),
+ max_pdu_size = user_info(Mid, max_pdu_size),
+ request_keep_alive_timeout = user_info(Mid, request_keep_alive_timeout)
+ }.
+
+handle_disconnect(ConnHandle) when is_record(ConnHandle, megaco_conn_handle) ->
+ case ets:lookup(megaco_local_conn, ConnHandle) of
+ [ConnData] ->
+ ets:delete(megaco_local_conn, ConnHandle),
+ RemoteConnData = handle_disconnect_remote(ConnHandle, '_'),
+ {ok, ConnData, RemoteConnData};
+ [] ->
+ {error, {already_disconnected, ConnHandle}}
+ end.
+
+handle_disconnect_remote(ConnHandle, UserNode) ->
+ Pat = #remote_conn_data{conn_handle = ConnHandle,
+ user_node = UserNode,
+ monitor_ref = '_'},
+ RemoteConnData = ets:match_object(megaco_remote_conn, Pat),
+ ets:match_delete(megaco_remote_conn, Pat),
+ RemoteConnData.
+
+make_receive_handle(UserMid) ->
+ #megaco_receive_handle{local_mid = UserMid,
+ encoding_mod = user_info(UserMid, encoding_mod),
+ encoding_config = user_info(UserMid, encoding_config),
+ send_mod = user_info(UserMid, send_mod)}.
+
+
+%%-----------------------------------------------------------------
+%% Func: create_snmp_counters/1, update_snmp_counters/2
+%% Description: create/update all the SNMP statistic counters
+%%-----------------------------------------------------------------
+
+create_snmp_counters(CH) ->
+ create_snmp_counters(CH, snmp_counters()).
+
+% create_snmp_counters(CH, []) ->
+% ok;
+% create_snmp_counters(CH, [Counter|Counters]) ->
+% Key = {CH, Counter},
+% ets:insert(megaco_stats, {Key, 0}),
+% create_snmp_counters(CH, Counters).
+
+create_snmp_counters(CH, Counters) ->
+ F = fun(Counter) ->
+ Key = {CH, Counter},
+ ets:insert(megaco_stats, {Key, 0})
+ end,
+ lists:foreach(F, Counters).
+
+
+update_snmp_counters(CH, PrelCH) ->
+ update_snmp_counters(CH, PrelCH, snmp_counters()).
+
+update_snmp_counters(_CH, _PrelCH, []) ->
+ ok;
+update_snmp_counters(CH, PrelCH, [Counter|Counters]) ->
+ PrelKey = {PrelCH, Counter},
+ Key = {CH, Counter},
+ [{PrelKey,PrelVal}] = ets:lookup(megaco_stats, PrelKey),
+ ets:update_counter(megaco_stats, Key, PrelVal),
+ ets:delete(megaco_stats, PrelKey),
+ update_snmp_counters(CH, PrelCH, Counters).
+
+
+global_snmp_counters() ->
+ [medGwyGatewayNumErrors].
+
+snmp_counters() ->
+ [medGwyGatewayNumTimerRecovery,
+ medGwyGatewayNumErrors].
+
+
+
+%%-----------------------------------------------------------------
+
+%% Time in milli seconds
+%% t() ->
+%% {A,B,C} = erlang:now(),
+%% A*1000000000+B*1000+(C div 1000).
+
+
+%%-----------------------------------------------------------------
+
+warning_msg(F, A) ->
+ ?megaco_warning("Config server: " ++ F, A).
+
+error_msg(F, A) ->
+ ?megaco_error("Config server: " ++ F, A).
+