diff options
Diffstat (limited to 'lib/megaco/src')
-rw-r--r-- | lib/megaco/src/app/megaco.appup.src | 69 | ||||
-rw-r--r-- | lib/megaco/src/app/megaco_internal.hrl | 26 | ||||
-rw-r--r-- | lib/megaco/src/engine/megaco_config.erl | 439 | ||||
-rw-r--r-- | lib/megaco/src/engine/megaco_filter.erl | 286 | ||||
-rw-r--r-- | lib/megaco/src/engine/megaco_messenger.erl | 542 | ||||
-rw-r--r-- | lib/megaco/src/engine/megaco_monitor.erl | 33 | ||||
-rw-r--r-- | lib/megaco/src/flex/Makefile.in | 82 | ||||
-rw-r--r-- | lib/megaco/src/flex/megaco_flex_scanner_drv.flex.src | 7 | ||||
-rw-r--r-- | lib/megaco/src/flex/megaco_flex_scanner_handler.erl | 45 |
9 files changed, 1024 insertions, 505 deletions
diff --git a/lib/megaco/src/app/megaco.appup.src b/lib/megaco/src/app/megaco.appup.src index 163ff06651..f939f5e6cf 100644 --- a/lib/megaco/src/app/megaco.appup.src +++ b/lib/megaco/src/app/megaco.appup.src @@ -1,19 +1,20 @@ +%% This is an -*- erlang -*- file. %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2001-2010. 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% %% @@ -120,43 +121,97 @@ %% | %% v %% 3.13 +%% | +%% v +%% 3.14 +%% | +%% v +%% 3.14.1 %% %% {"%VSN%", [ + {"3.14", + [ + {load_module, megaco_messenger, soft_purge, soft_purge, [megaco_monitor]}, + {update, megaco_monitor, soft, soft_purge, soft_purge, []}, + {update, megaco_config, soft, soft_purge, soft_purge, []} + ] + }, + {"3.13", + [ + {load_module, megaco_messenger, soft_purge, soft_purge, [megaco_monitor]}, + {load_module, megaco_filter, soft_purge, soft_purge, []}, + {update, megaco_monitor, soft, soft_purge, soft_purge, []}, + {update, megaco_config, soft, soft_purge, soft_purge, []}, + {update, megaco_flex_scanner_handler, {advanced, downgrade_to_pre_3_13_1}, + soft_purge, soft_purge, []} + ] + }, {"3.12", [ + {load_module, megaco_filter, soft_purge, soft_purge, []}, {load_module, megaco_udp, soft_purge, soft_purge, []}, {load_module, megaco_messenger, soft_purge, soft_purge, [megaco_monitor]}, - {update, megaco_monitor, soft, soft_purge, soft_purge, []} + {update, megaco_config, soft, soft_purge, soft_purge, []}, + {update, megaco_monitor, soft, soft_purge, soft_purge, []}, + {update, megaco_flex_scanner_handler, {advanced, downgrade_to_pre_3_13_1}, + soft_purge, soft_purge, []} ] }, {"3.11.3", [ + {load_module, megaco_filter, soft_purge, soft_purge, []}, {load_module, megaco_udp, soft_purge, soft_purge, []}, {load_module, megaco_messenger, soft_purge, soft_purge, [megaco_config, megaco_monitor]}, {update, megaco_monitor, soft, soft_purge, soft_purge, []}, {update, megaco_config, {advanced, upgrade_from_pre_3_12}, + soft_purge, soft_purge, []}, + {update, megaco_flex_scanner_handler, {advanced, downgrade_to_pre_3_13_1}, soft_purge, soft_purge, []} ] } ], [ + {"3.14", + [ + {load_module, megaco_messenger, soft_purge, soft_purge, [megaco_monitor]}, + {update, megaco_monitor, soft, soft_purge, soft_purge, []}, + {update, megaco_config, soft, soft_purge, soft_purge, []} + ] + }, + {"3.13", + [ + {load_module, megaco_messenger, soft_purge, soft_purge, [megaco_monitor]}, + {load_module, megaco_filter, soft_purge, soft_purge, []}, + {update, megaco_monitor, soft, soft_purge, soft_purge, []}, + {update, megaco_config, soft, soft_purge, soft_purge, []}, + {update, megaco_flex_scanner_handler, {advanced, upgrade_from_pre_3_13_1}, + soft_purge, soft_purge, []} + ] + }, {"3.12", [ + {load_module, megaco_filter, soft_purge, soft_purge, []}, {load_module, megaco_udp, soft_purge, soft_purge, []}, {load_module, megaco_messenger, soft_purge, soft_purge, [megaco_monitor]}, - {update, megaco_monitor, soft, soft_purge, soft_purge, []} + {update, megaco_config, soft, soft_purge, soft_purge, []}, + {update, megaco_monitor, soft, soft_purge, soft_purge, []}, + {update, megaco_flex_scanner_handler, {advanced, upgrade_from_pre_3_13_1}, + soft_purge, soft_purge, []} ] }, {"3.11.3", [ + {load_module, megaco_filter, soft_purge, soft_purge, []}, {load_module, megaco_udp, soft_purge, soft_purge, []}, {load_module, megaco_messenger, soft_purge, soft_purge, [megaco_config, megaco_monitor]}, {update, megaco_monitor, soft, soft_purge, soft_purge, []}, {update, megaco_config, {advanced, downgrade_to_pre_3_12}, + soft_purge, soft_purge, []}, + {update, megaco_flex_scanner_handler, {advanced, upgrade_from_pre_3_13_1}, soft_purge, soft_purge, []} ] } diff --git a/lib/megaco/src/app/megaco_internal.hrl b/lib/megaco/src/app/megaco_internal.hrl index adbaacacef..2c124e9060 100644 --- a/lib/megaco/src/app/megaco_internal.hrl +++ b/lib/megaco/src/app/megaco_internal.hrl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1999-2010. 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% %% @@ -139,6 +139,22 @@ [?APPLICATION, ?MODULE, self()|A]))). +-define(megaco_ereport(Label, Report), + ?megaco_report(error_report, Label, Report)). + +-define(megaco_wreport(Label, Report), + ?megaco_report(warning_report, Label, Report)). + +-define(megaco_ireport(Label, Report), + ?megaco_report(info_report, Label, Report)). + +-define(megaco_report(Func, Label, Report), + (catch error_logger:Func([{label, Label}, + {application, ?APPLICATION}, + {module, ?MODULE}, + {process, self()} | Report]))). + + %%%---------------------------------------------------------------------- %%% Default (ignore) value of the Extra argument to the %%% megaco:receive_message/5 and process_received_message functions/5. diff --git a/lib/megaco/src/engine/megaco_config.erl b/lib/megaco/src/engine/megaco_config.erl index 2058c53973..6805db790d 100644 --- a/lib/megaco/src/engine/megaco_config.erl +++ b/lib/megaco/src/engine/megaco_config.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2000-2010. 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% %% @@ -224,149 +224,278 @@ update_user_info(UserMid, orig_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 + +conn_info(Data, Item) -> + %% The purpose of this is a compiler optimization... + %% Args are processed from left to right. + do_conn_info(Item, Data). + +do_conn_info(mid = _Item, #megaco_conn_handle{local_mid = Mid}) -> + Mid; +do_conn_info(local_mid = _Item, #megaco_conn_handle{local_mid = LMid}) -> + LMid; +do_conn_info(remote_mid = _Item, #megaco_conn_handle{remote_mid = RMid}) -> + RMid; +do_conn_info(conn_handle = _Item, CH) when is_record(CH, megaco_conn_handle) -> + CH; +do_conn_info(conn_data = _Item, CH) when is_record(CH, megaco_conn_handle) -> + case lookup_local_conn(CH) of + [] -> + exit({no_such_connection, CH}); + [ConnData] -> + ConnData + end; +do_conn_info(Item, CH) when is_record(CH, megaco_conn_handle) -> + case lookup_local_conn(CH) of + [] -> + exit({no_such_connection, CH}); + [ConnData] -> + do_conn_info(Item, ConnData) end; -conn_info(#conn_data{conn_handle = CH}, cancel) -> + +do_conn_info(cancel = _Item, #conn_data{conn_handle = CH}) -> + %% 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); +do_conn_info(cancel = _Item, CH) when is_record(CH, megaco_conn_handle) -> %% 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}) +do_conn_info(all = _Item, + #conn_data{conn_handle = CH, + serial = TransId, + max_serial = MaxTransId, + request_timer = ReqTmr, + long_request_timer = LongReqTmr, + auto_ack = AutoAck, + trans_ack = TransAck, + trans_ack_maxcount = TransAckMaxCount, + trans_req = TransReq, + trans_req_maxcount = TransReqMaxCount, + trans_req_maxsize = TransReqMaxSz, + trans_timer = TransTmr, + %% trans_sender, + pending_timer = PendingTmr, + sent_pending_limit = SentPendingLimit, + recv_pending_limit = RecvPendingLimit, + reply_timer = ReplyTmr, + control_pid = CtrlPid, + monitor_ref = MonRef, + send_mod = SendMod, + send_handle = SendHandle, + encoding_mod = EncodingMod, + encoding_config = EncodingConf, + protocol_version = ProtoVersion, + auth_data = AuthData, + user_mod = UserMod, + user_args = UserArgs, + reply_action = ReplyAction, + reply_data = ReplyData, + threaded = Threaded, + strict_version = StrictVersion, + long_request_resend = LongReqResend, + call_proxy_gc_timeout = CallProxyGCTimeout, + %% cancel, + resend_indication = ResendInd, + segment_reply_ind = SegReplyInd, + segment_recv_acc = SegRecvAcc, + segment_recv_timer = SegRecvTmr, + segment_send = SegSend, + segment_send_timer = SegSendTmr, + max_pdu_size = MaxPduSz, + request_keep_alive_timeout = RequestKeepAliveTmr}) -> + [{conn_handle, CH}, + {trans_id, TransId}, + {max_trans_id, MaxTransId}, + {request_timer, ReqTmr}, + {long_request_timer, LongReqTmr}, + {mid, CH#megaco_conn_handle.local_mid}, + {local_mid, CH#megaco_conn_handle.local_mid}, + {remote_mid, CH#megaco_conn_handle.remote_mid}, + {auto_ack, AutoAck}, + {trans_ack, TransAck}, + {trans_ack_maxcount, TransAckMaxCount}, + {trans_req, TransReq}, + {trans_req_maxcount, TransReqMaxCount}, + {trans_req_maxsize, TransReqMaxSz}, + {trans_timer, TransTmr}, + {pending_timer, PendingTmr}, + {sent_pending_limit, SentPendingLimit}, + {recv_pending_limit, RecvPendingLimit}, + {reply_timer, ReplyTmr}, + {control_pid, CtrlPid}, + {monitor_ref, MonRef}, + {send_mod, SendMod}, + {send_handle, SendHandle}, + {encoding_mod, EncodingMod}, + {encoding_config, EncodingConf}, + {protocol_version, ProtoVersion}, + {auth_data, AuthData}, + {user_mod, UserMod}, + {user_args, UserArgs}, + {reply_action, ReplyAction}, + {reply_data, ReplyData}, + {threaded, Threaded}, + {strict_version, StrictVersion}, + {long_request_resend, LongReqResend}, + {call_proxy_gc_timeout, CallProxyGCTimeout}, + {resend_indication, ResendInd}, + {segment_reply_ind, SegReplyInd}, + {segment_recv_acc, SegRecvAcc}, + {segment_recv_timer, SegRecvTmr}, + {segment_send, SegSend}, + {segment_send_timer, SegSendTmr}, + {max_pdu_size, MaxPduSz}, + {request_keep_alive_timeout, RequestKeepAliveTmr}]; + +do_conn_info(conn_data = _Item, CD) -> + CD; +do_conn_info(conn_handle = _Item, #conn_data{conn_handle = Val}) -> + Val; +do_conn_info(mid = _Item, + #conn_data{conn_handle = #megaco_conn_handle{local_mid = Val}}) -> + Val; +do_conn_info(local_mid = _Item, + #conn_data{conn_handle = #megaco_conn_handle{local_mid = Val}}) -> + Val; +do_conn_info(remote_mid = _Item, + #conn_data{conn_handle = #megaco_conn_handle{remote_mid = Val}}) -> + Val; +do_conn_info(trans_id = _Item, + #conn_data{conn_handle = #megaco_conn_handle{local_mid = LMid}, + max_serial = Max}) -> + Item2 = {LMid, trans_id_counter}, + case (catch ets:lookup(megaco_config, Item2)) of + {'EXIT', _} -> + undefined_serial; + [] -> + user_info(LMid, min_trans_id); + [{_, Serial}] -> + if + ((Max =:= infinity) andalso + is_integer(Serial) andalso + (Serial < 4294967295)) -> + Serial + 1; + ((Max =:= infinity) andalso + is_integer(Serial) andalso + (Serial =:= 4294967295)) -> + user_info(LMid, min_trans_id); + Serial < Max -> + Serial + 1; + Serial =:= Max -> + user_info(LMid, min_trans_id); + Serial =:= 4294967295 -> + user_info(LMid, min_trans_id); + true -> + undefined_serial + end 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)]. +do_conn_info(max_trans_id = _Item, #conn_data{max_serial = Val}) -> + Val; +do_conn_info(request_timer = _Item, #conn_data{request_timer = Val}) -> + Val; +do_conn_info(long_request_timer = _Item, #conn_data{long_request_timer = Val}) -> + Val; +do_conn_info(auto_ack = _Item, #conn_data{auto_ack = Val}) -> + Val; +do_conn_info(trans_ack = _Item, #conn_data{trans_ack = Val}) -> + Val; +do_conn_info(trans_ack_maxcount = _Item, #conn_data{trans_ack_maxcount = Val}) -> + Val; +do_conn_info(trans_req = _Item, #conn_data{trans_req = Val}) -> + Val; +do_conn_info(trans_req_maxcount = _Item, #conn_data{trans_req_maxcount = Val}) -> + Val; +do_conn_info(trans_req_maxsize = _Item, #conn_data{trans_req_maxsize = Val}) -> + Val; +do_conn_info(trans_timer = _Item, #conn_data{trans_timer = Val}) -> + Val; +do_conn_info(pending_timer = _Item, #conn_data{pending_timer = Val}) -> + Val; +do_conn_info(orig_pending_limit = _Item, #conn_data{sent_pending_limit = Val}) -> + Val; +do_conn_info(sent_pending_limit = _Item, #conn_data{sent_pending_limit = Val}) -> + Val; +do_conn_info(recv_pending_limit = _Item, #conn_data{recv_pending_limit = Val}) -> + Val; +do_conn_info(reply_timer = _Item, #conn_data{reply_timer = Val}) -> + Val; +do_conn_info(control_pid = _Item, #conn_data{control_pid = Val}) -> + Val; +do_conn_info(send_mod = _Item, #conn_data{send_mod = Val}) -> + Val; +do_conn_info(send_handle = _Item, #conn_data{send_handle = Val}) -> + Val; +do_conn_info(encoding_mod = _Item, #conn_data{encoding_mod = Val}) -> + Val; +do_conn_info(encoding_config = _Item, #conn_data{encoding_config = Val}) -> + Val; +do_conn_info(protocol_version = _Item, #conn_data{protocol_version = Val}) -> + Val; +do_conn_info(auth_data = _Item, #conn_data{auth_data = Val}) -> + Val; +do_conn_info(user_mod = _Item, #conn_data{user_mod = Val}) -> + Val; +do_conn_info(user_args = _Item, #conn_data{user_args = Val}) -> + Val; +do_conn_info(reply_action = _Item, #conn_data{reply_action = Val}) -> + Val; +do_conn_info(reply_data = _Item, #conn_data{reply_data = Val}) -> + Val; +do_conn_info(threaded = _Item, #conn_data{threaded = Val}) -> + Val; +do_conn_info(strict_version = _Item, #conn_data{strict_version = Val}) -> + Val; +do_conn_info(long_request_resend = _Item, + #conn_data{long_request_resend = Val}) -> + Val; +do_conn_info(call_proxy_gc_timeout = _Item, + #conn_data{call_proxy_gc_timeout = Val}) -> + Val; +do_conn_info(resend_indication = _Item, #conn_data{resend_indication = Val}) -> + Val; +do_conn_info(segment_reply_ind = _Item, #conn_data{segment_reply_ind = Val}) -> + Val; +do_conn_info(segment_recv_acc = _Item, #conn_data{segment_recv_acc = Val}) -> + Val; +do_conn_info(segment_recv_timer = _Item, + #conn_data{segment_recv_timer = Val}) -> + Val; +do_conn_info(segment_send = _Item, #conn_data{segment_send = Val}) -> + Val; +do_conn_info(segment_send_timer = _Item, + #conn_data{segment_send_timer = Val}) -> + Val; +do_conn_info(max_pdu_size = _Item, #conn_data{max_pdu_size = Val}) -> + Val; +do_conn_info(request_keep_alive_timeout = _Item, + #conn_data{request_keep_alive_timeout = Val}) -> + Val; +do_conn_info(receive_handle = _Item, + #conn_data{conn_handle = #megaco_conn_handle{local_mid = LMid}, + encoding_mod = EM, + encoding_config = EC, + send_mod = SM}) -> + #megaco_receive_handle{local_mid = LMid, + encoding_mod = EM, + encoding_config = EC, + send_mod = SM}; +do_conn_info(Item, Data) + when is_record(Data, conn_data) orelse is_record(Data, megaco_conn_handle) -> + exit({no_such_item, Item}); +do_conn_info(_Item, BadData) -> + {error, {no_such_connection, BadData}}. + + +%% 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) -> @@ -499,31 +628,19 @@ incr_counter(Item, Incr) -> end catch error:_ -> + %% Counter does not exist, so try creat it try begin cre_counter(Item, Incr) end catch exit:_ -> - %% Ok, some other process got there before us, - %% so try again + %% This is a raise condition. + %% When we tried to update the counter above, it + %% did not exist, but now it does... 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 @@ -531,8 +648,8 @@ cre_counter(Item, Initial) -> case call({cre_counter, Item, Initial}) of {ok, Value} -> Value; - Error -> - exit(Error) + {error, Reason} -> + exit({failed_creating_counter, Item, Initial, Reason}) end; true -> %% Check that the counter does not already exists @@ -542,7 +659,7 @@ cre_counter(Item, Initial) -> ets:insert(megaco_config, {Item, Initial}), {ok, Initial}; [_] -> - %% Ouch, now what? + %% Possibly a raise condition {error, already_exists} end @@ -1432,7 +1549,7 @@ verify_val(Item, 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); + (verify_uint(Val) orelse (Val =:= plain)); _ -> false end. diff --git a/lib/megaco/src/engine/megaco_filter.erl b/lib/megaco/src/engine/megaco_filter.erl index 23a91b1f1d..9df752789c 100644 --- a/lib/megaco/src/engine/megaco_filter.erl +++ b/lib/megaco/src/engine/megaco_filter.erl @@ -1,37 +1,83 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2000-2010. 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 : megaco/H.248 customization of generic event tracer +%% Purpose : Megaco/H.248 customization of the Event Tracer tool %%---------------------------------------------------------------------- -module(megaco_filter). --export([start/0, start/1, filter/1, +-export([start/0, start/1, filter/1, raw_filter/1, pretty_error/1, string_to_term/1]). + -include_lib("megaco/include/megaco.hrl"). -include_lib("megaco/include/megaco_message_v1.hrl"). -include_lib("megaco/src/app/megaco_internal.hrl"). -include_lib("et/include/et.hrl"). +%%---------------------------------------------------------------------- +%% BUGBUG: There are some opportunities for improvements: +%% +%% * This version of the module does only handle version 1 of the messages. +%% +%% * The record definition of megaco_transaction_reply is copied from +%% megaco_message_internal.hrl as that header file contains some +%% records that already are defined in megaco_message_{v1,v2,v3}.hrl. +%% * The records megaco_udp and megaco_tcp are copied from the files +%% megaco_udp.hrl and megaco_tcp.hrl respectively, as we cannot include +%% both header files. They both defines the macros HEAP_SIZE and GC_MSG_LIMIT. + +%%-include("megaco_message_internal.hrl"). +-record('megaco_transaction_reply', + { + transactionId, + immAckRequired = asn1_NOVALUE, + transactionResult, + segmentNumber = asn1_NOVALUE, + segmentationComplete = asn1_NOVALUE + }). + +%% -include_lib("megaco/src/udp/megaco_udp.hrl"). +-record(megaco_udp, + {port, + options = [], + socket, + receive_handle, + module = megaco, + serialize = false % false: Spawn a new process for each message + }). + +%% -include_lib("megaco/src/tcp/megaco_tcp.hrl"). +-record(megaco_tcp, + {host, + port, + options = [], + socket, + proxy_pid, + receive_handle, + module = megaco, + serialize = false % false: Spawn a new process for each message + }). +%%---------------------------------------------------------------------- + start() -> start([]). @@ -47,11 +93,20 @@ start(ExtraOptions) -> {title, "Megaco tracer - Erlang/OTP"} | ExtraOptions], et_viewer:start(Options). -filter(E) when is_record(E, event) -> +filter(E) -> + case catch raw_filter(E) of + {'EXIT', Reason} = Error-> + io:format("~p: ~p\n", [?MODULE, Error]), + exit(Reason); + E2 -> + E2 + end. + +raw_filter(E) when is_record(E, event) -> From = filter_actor(E#event.from), To = filter_actor(E#event.to), E2 = E#event{from = From, to = To}, - E3 = filter_contents(E#event.contents, E2, []), + E3 = filter_contents(E#event.contents, E2), {true, E3}. filter_actors(From, To, E) @@ -101,6 +156,9 @@ filter_user_actor(Actor) -> do_filter_actor(CH) when is_record(CH, megaco_conn_handle) -> Mid = CH#megaco_conn_handle.local_mid, do_filter_actor(Mid); +do_filter_actor(RH) when is_record(RH, megaco_receive_handle) -> + Mid = RH#megaco_receive_handle.local_mid, + do_filter_actor(Mid); do_filter_actor(Actor) -> case Actor of {ip4Address, {'IP4Address', [A1,A2,A3,A4], asn1_NOVALUE}} -> @@ -130,86 +188,108 @@ do_filter_actor(Actor) -> "UNKNOWN" end. -filter_contents([], E, Contents) -> - E#event{contents = lists:flatten(lists:reverse(Contents))}; -filter_contents([H | T], E, Contents) -> + +filter_contents(Contents, E) -> + do_filter_contents(Contents, E, missing_conn_data, []). + +do_filter_contents([H | T], E, ConnData, Contents) -> case H of - {line, _Mod, _Line} -> - filter_contents(T, E, Contents); + Udp when is_record(Udp, megaco_udp) -> + RH = Udp#megaco_udp.receive_handle, + Actor = filter_actor(RH), + E2 = E#event{from = Actor, to = Actor}, + Pretty = + ["Port: ", integer_to_list(Udp#megaco_udp.port), "\n", + "Encoder: ", atom_to_list(RH#megaco_receive_handle.encoding_mod)], + do_filter_contents(T, E2, ConnData, [[Pretty, "\n"], Contents]); + Tcp when is_record(Tcp, megaco_tcp) -> + RH = Tcp#megaco_tcp.receive_handle, + Actor = filter_actor(RH), + E2 = E#event{from = Actor, to = Actor}, + Pretty = + ["Port: ", integer_to_list(Tcp#megaco_tcp.port), "\n", + "Encoder: ", atom_to_list(RH#megaco_receive_handle.encoding_mod)], + do_filter_contents(T, E2, ConnData, [[Pretty, "\n"], Contents]); CD when is_record(CD, conn_data) -> CH = CD#conn_data.conn_handle, From = CH#megaco_conn_handle.local_mid, - To = CH#megaco_conn_handle.remote_mid, + To = CH#megaco_conn_handle.remote_mid, E2 = filter_actors(From, To, E), Serial = CD#conn_data.serial, E3 = append_serial(Serial, E2), - filter_contents(T, E3, Contents); + do_filter_contents(T, E3, CD, Contents); CH when is_record(CH, megaco_conn_handle) -> From = CH#megaco_conn_handle.local_mid, - To = CH#megaco_conn_handle.remote_mid, + To = CH#megaco_conn_handle.remote_mid, E2 = filter_actors(From, To, E), - filter_contents(T, E2, Contents); - {orig_conn_handle, _CH} -> - filter_contents(T, E, Contents); + do_filter_contents(T, E2, ConnData, Contents); RH when is_record(RH, megaco_receive_handle) -> Actor = RH#megaco_receive_handle.local_mid, E2 = filter_actors(Actor, Actor, E), - filter_contents(T, E2, Contents); - {pid, Pid} when is_pid(Pid) -> - filter_contents(T, E, Contents); - pending -> - filter_contents(T, E, Contents); - reply -> - filter_contents(T, E, Contents); + do_filter_contents(T, E2, ConnData, Contents); {error, Reason} -> Pretty = pretty_error({error, Reason}), E2 = prepend_error(E), - filter_contents(T, E2, [[Pretty, "\n"], Contents]); + do_filter_contents(T, E2, ConnData, [[Pretty, "\n"], Contents]); {'EXIT', Reason} -> Pretty = pretty_error({'EXIT', Reason}), E2 = prepend_error(E), - filter_contents(T, E2, [[Pretty, "\n"], Contents]); + do_filter_contents(T, E2, ConnData, [[Pretty, "\n"], Contents]); ED when is_record(ED, 'ErrorDescriptor') -> Pretty = pretty_error(ED), E2 = prepend_error(E), - filter_contents(T, E2, [[Pretty, "\n"], Contents]); + do_filter_contents(T, E2, ConnData, [[Pretty, "\n"], Contents]); Trans when is_record(Trans, 'TransactionRequest') -> - Pretty = pretty({trans, {transactionRequest, Trans}}), - filter_contents([], E, [[Pretty, "\n"], Contents]); + Pretty = pretty(ConnData, {trans, {transactionRequest, Trans}}), + do_filter_contents([], E, ConnData, [[Pretty, "\n"], Contents]); + {transactionRequest, Trans} when is_record(Trans, 'TransactionRequest') -> + Pretty = pretty(ConnData, {trans, {transactionRequest, Trans}}), + do_filter_contents([], E, ConnData, [[Pretty, "\n"], Contents]); Trans when is_record(Trans, 'TransactionReply') -> - Pretty = pretty({trans, {transactionReply, Trans}}), - filter_contents([], E, [[Pretty, "\n"], Contents]); + Pretty = pretty(ConnData, {trans, {transactionReply, Trans}}), + do_filter_contents([], E, ConnData, [[Pretty, "\n"], Contents]); + Trans when is_record(Trans, megaco_transaction_reply) -> + %% BUGBUG: Version 1 special + TransV1 = + #'TransactionReply'{transactionId = Trans#megaco_transaction_reply.transactionId, + immAckRequired = Trans#megaco_transaction_reply.immAckRequired, + transactionResult = Trans#megaco_transaction_reply.transactionResult}, + Pretty = pretty(ConnData, {trans, {transactionReply, TransV1}}), + do_filter_contents([], E, ConnData, [[Pretty, "\n"], Contents]); Trans when is_record(Trans, 'TransactionPending') -> - Pretty = pretty({trans, {transactionPending, Trans}}), - filter_contents([], E, [[Pretty, "\n"], Contents]); + Pretty = pretty(ConnData, {trans, {transactionPending, Trans}}), + do_filter_contents([], E, ConnData, [[Pretty, "\n"], Contents]); Trans when is_record(Trans, 'TransactionAck') -> - Pretty = pretty({trans, {transactionResponseAck, [Trans]}}), + Pretty = pretty(ConnData, {trans, {transactionResponseAck, [Trans]}}), case Trans#'TransactionAck'.lastAck of asn1_NOVALUE -> - filter_contents([], E, [[Pretty, "\n"], Contents]); + do_filter_contents([], E, ConnData, [[Pretty, "\n"], Contents]); Last -> Label = term_to_string(E#event.label), E2 = E#event{label = Label ++ ".." ++ integer_to_list(Last)}, - filter_contents([], E2, [[Pretty, "\n"], Contents]) + do_filter_contents([], E2, ConnData, [[Pretty, "\n"], Contents]) end; {context_id, _ContextId} -> - Pretty = pretty(H), - filter_contents(T, E, [[Pretty, "\n"], Contents]); + Pretty = pretty(ConnData, H), + do_filter_contents(T, E, ConnData, [[Pretty, "\n"], Contents]); {command_request, CmdReq} -> - Pretty = pretty(CmdReq), - filter_contents(T, E, [[Pretty, "\n"], Contents]); + Pretty = pretty(ConnData, CmdReq), + do_filter_contents(T, E, ConnData, [[Pretty, "\n"], Contents]); {user_reply, {ok, ARS}} -> - Pretty = [[pretty(AR), "\n"] || AR <- ARS], - filter_contents(T, E, [["REPLY: \n", Pretty, "\n"], Contents]); + Pretty = [[pretty(ConnData, AR), "\n"] || AR <- ARS], + do_filter_contents(T, E, ConnData, [["USER REPLY OK: \n", Pretty, "\n"], Contents]); {user_reply, Error} -> Pretty = pretty_error(Error), - filter_contents(T, E, [["REPLY: \n", Pretty, "\n"], Contents]); + do_filter_contents(T, E, ConnData, [["USER REPLY ERROR: \n", Pretty, "\n"], Contents]); {actionReplies, ARS} -> - Pretty = [[pretty(AR), "\n"] || AR <- ARS], - filter_contents(T, E, [["REPLY: \n", Pretty, "\n"], Contents]); + Pretty = [[pretty(ConnData, AR), "\n"] || AR <- ARS], + do_filter_contents(T, E, ConnData, [["ACTION REPLIES: \n", Pretty, "\n"], Contents]); MegaMsg when is_record(MegaMsg, 'MegacoMessage') -> - Pretty = pretty(MegaMsg), - filter_contents(T, E, [["MESSAGE: \n", Pretty, "\n"], Contents]); + Pretty = pretty(ConnData, MegaMsg), + do_filter_contents(T, E, ConnData, [Pretty, "\n", Contents]); + {message, MegaMsg} when is_record(MegaMsg, 'MegacoMessage') -> + Pretty = pretty(ConnData, MegaMsg), + do_filter_contents(T, E, ConnData, [Pretty, "\n", Contents]); {bytes, Bin} when is_binary(Bin) -> E2 = case E#event.label of @@ -223,15 +303,37 @@ filter_contents([H | T], E, Contents) -> E end, CharList = erlang:binary_to_list(Bin), - filter_contents(T, E2, [[CharList , "\n"], Contents]); - [] -> - filter_contents(T, E, Contents); + do_filter_contents(T, E2, ConnData, [[CharList , "\n"], Contents]); + List when is_list(List) -> + %% BUGBUG: Workaround as megaco_messenger puts nested lists in its traces + do_filter_contents(List ++ T, E, ConnData, Contents); + Int when is_integer(Int) -> + %% BUGBUG: Workaround as megaco_messenger puts nested lists in its traces + do_filter_contents(T, E, ConnData, Contents); + {line, _Mod, _Line} -> + do_filter_contents(T, E, ConnData, Contents); + {orig_conn_handle, _CH} -> + do_filter_contents(T, E, ConnData, Contents); + {pid, Pid} when is_pid(Pid) -> + do_filter_contents(T, E, ConnData, Contents); + pending -> + do_filter_contents(T, E, ConnData, Contents); + reply -> + do_filter_contents(T, E, ConnData, Contents); {test_lib, _Mod, _Fun} -> - filter_contents(T, E, Contents); + do_filter_contents(T, E, ConnData, Contents); + {trans_id, _TransId} -> + do_filter_contents(T, E, ConnData, Contents); + {send_func, _FunName} -> + do_filter_contents(T, E, ConnData, Contents); + Pid when is_pid(Pid) -> + do_filter_contents(T, E, ConnData, Contents); Other -> - Pretty = pretty(Other), - filter_contents(T, E, [[Pretty, "\n"], Contents]) - end. + Pretty = pretty(ConnData, Other), + do_filter_contents(T, E, ConnData, [[Pretty, "\n"], Contents]) + end; +do_filter_contents([], E, _ConnData, Contents) -> + E#event{contents = lists:flatten(lists:reverse(Contents))}. append_serial(Serial, E) when is_integer(Serial) -> Label = term_to_string(E#event.label), @@ -243,7 +345,7 @@ prepend_error(E) -> Label = term_to_string(E#event.label), E#event{label = "<ERROR> " ++ Label}. -pretty({context_id, ContextId}) -> +pretty(_ConnData, {context_id, ContextId}) -> if ContextId =:= ?megaco_null_context_id -> ["CONTEXT ID: -\n"]; @@ -254,61 +356,33 @@ pretty({context_id, ContextId}) -> is_integer(ContextId) -> ["CONTEXT ID: ",integer_to_list(ContextId), "\n"] end; -pretty(MegaMsg) when is_record(MegaMsg, 'MegacoMessage') -> - case catch megaco_pretty_text_encoder:encode_message([], MegaMsg) of - {ok, Bin} -> - term_to_string(Bin); - _Bad -> - term_to_string(MegaMsg) - end; -pretty(CmdReq) when is_record(CmdReq, 'CommandRequest') -> - case catch megaco_pretty_text_encoder:encode_command_request(CmdReq) of - {ok, IoList} -> - IoList2 = lists:flatten(IoList), - term_to_string(IoList2); - _Bad -> - term_to_string(CmdReq) - end; -pretty({complete_success, ContextId, RepList} = Res) -> +pretty(_ConnData, MegaMsg) when is_record(MegaMsg, 'MegacoMessage') -> + {ok, Bin} = megaco_pretty_text_encoder:encode_message([], MegaMsg), + term_to_string(Bin); +pretty(_ConnData, CmdReq) when is_record(CmdReq, 'CommandRequest') -> + {ok, IoList} = megaco_pretty_text_encoder:encode_command_request(CmdReq), + term_to_string(lists:flatten(IoList)); +pretty(_ConnData, {complete_success, ContextId, RepList}) -> ActRep = #'ActionReply'{contextId = ContextId, commandReply = RepList}, - case catch megaco_pretty_text_encoder:encode_action_reply(ActRep) of - {ok, IoList} -> - IoList2 = lists:flatten(IoList), - term_to_string(IoList2); - _Bad -> - term_to_string(Res) - end; -pretty(AR) when is_record(AR, 'ActionReply') -> - case catch megaco_pretty_text_encoder:encode_action_reply(AR) of - {ok, IoList} -> - IoList2 = lists:flatten(IoList), - term_to_string(IoList2); - _Bad -> - term_to_string(AR) - end; -pretty({partial_failure, ContextId, RepList} = Res) -> + {ok, IoList} = megaco_pretty_text_encoder:encode_action_reply(ActRep), + term_to_string(lists:flatten(IoList)); +pretty(_ConnData, AR) when is_record(AR, 'ActionReply') -> + {ok, IoList} = megaco_pretty_text_encoder:encode_action_reply(AR), + term_to_string(lists:flatten(IoList)); +pretty(_ConnData, {partial_failure, ContextId, RepList}) -> ActRep = #'ActionReply'{contextId = ContextId, commandReply = RepList}, - case catch megaco_pretty_text_encoder:encode_action_reply(ActRep) of - {ok, IoList} -> - IoList2 = lists:flatten(IoList), - term_to_string(IoList2); - _Bad -> - term_to_string(Res) - end; -pretty({trans, Trans}) -> - case catch megaco_pretty_text_encoder:encode_transaction(Trans) of + {ok, IoList} = megaco_pretty_text_encoder:encode_action_reply(ActRep), + term_to_string(lists:flatten(IoList)); +pretty(_ConnData, {trans, Trans}) -> + case megaco_pretty_text_encoder:encode_transaction(Trans) of {ok, Bin} when is_binary(Bin) -> - IoList2 = lists:flatten(binary_to_list(Bin)), - term_to_string(IoList2); + term_to_string(binary_to_list(Bin)); {ok, IoList} -> - IoList2 = lists:flatten(IoList), - term_to_string(IoList2); - _Bad -> - term_to_string(Trans) + term_to_string(lists:flatten(IoList)) end; -pretty(Other) -> +pretty(__ConnData, Other) -> term_to_string(Other). pretty_error({error, Reason}) -> diff --git a/lib/megaco/src/engine/megaco_messenger.erl b/lib/megaco/src/engine/megaco_messenger.erl index a9e4fd67b2..5fad29931b 100644 --- a/lib/megaco/src/engine/megaco_messenger.erl +++ b/lib/megaco/src/engine/megaco_messenger.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1999-2010. 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% %% @@ -99,20 +99,22 @@ %% N.B. Update cancel/1 with '_' when a new field is added -record(reply, { - trans_id, - local_mid, - state = prepare, % prepare | eval_request | waiting_for_ack | aborted - pending_timer_ref, - handler = undefined, % pid of the proc executing the callback func - timer_ref, - version, - %% bytes: Sent reply data: not acknowledged - bytes, % binary() | [{integer(), binary(), timer_ref()}] - ack_action, % discard_ack | {handle_ack, Data} - send_handle, - %% segments: Not sent reply data (segments) - segments = [] % [{integer(), binary()}] - }). + trans_id, + local_mid, + state = prepare, % prepare | eval_request | waiting_for_ack | aborted + pending_timer_ref, + handler = undefined, % pid of the proc executing the callback func + timer_ref, + version, + %% bytes: Sent reply data: not acknowledged + bytes, % binary() | [{integer(), binary(), timer_ref()}] + ack_action, % discard_ack | {handle_ack, Data} + send_handle, + %% segments: Not sent reply data (segments) + segments = [], % [{integer(), binary()}] + user_mod, + user_args + }). -record(trans_id, { @@ -1203,7 +1205,7 @@ prepare_request(ConnData, T, Rest, AckList, ReqList, Extra) -> LocalMid = ConnHandle#megaco_conn_handle.local_mid, TransId = to_remote_trans_id(ConnData), ?rt2("prepare request", [LocalMid, TransId]), - case megaco_monitor:lookup_reply(TransId) of + case lookup_reply(ConnData, TransId) of [] -> ?rt3("brand new request"), @@ -1222,18 +1224,22 @@ prepare_request(ConnData, T, Rest, AckList, ReqList, Extra) -> #conn_data{send_handle = SendHandle, pending_timer = InitTimer, - protocol_version = Version} = ConnData, + protocol_version = Version, + user_mod = UserMod, + user_args = UserArgs} = ConnData, {WaitFor, CurrTimer} = megaco_timer:init(InitTimer), M = ?MODULE, F = pending_timeout, - A = [ConnHandle, TransId, CurrTimer], + A = [ConnHandle, TransId, CurrTimer], PendingRef = megaco_monitor:apply_after(M, F, A, WaitFor), Rep = #reply{send_handle = SendHandle, trans_id = TransId, local_mid = LocalMid, pending_timer_ref = PendingRef, handler = self(), - version = Version}, + version = Version, + user_mod = UserMod, + user_args = UserArgs}, case megaco_monitor:insert_reply_new(Rep) of true -> prepare_normal_trans(ConnData, Rest, AckList, @@ -1250,9 +1256,14 @@ prepare_request(ConnData, T, Rest, AckList, ReqList, Extra) -> Extra) end; - [#reply{state = State, + %% We can ignore the Converted value here as we *know* + %% conn-data to be correct (not faked), so even if + %% the record was converted, it will now have correct + %% values for user_mod and user_args. + {_Converted, + #reply{state = State, handler = Pid, - pending_timer_ref = Ref} = Rep] + pending_timer_ref = Ref} = Rep} when (State =:= prepare) orelse (State =:= eval_request) -> ?rt2("request resend", [State, Pid, Ref]), @@ -1364,9 +1375,14 @@ prepare_request(ConnData, T, Rest, AckList, ReqList, Extra) -> end; - [#reply{state = waiting_for_ack, + %% We can ignore the Converted value here as we *know* + %% conn-data to be correct (not faked), so even if + %% the record was converted, it will now have correct + %% values for user_mod and user_args. + {_Converted, + #reply{state = waiting_for_ack, bytes = Bin, - version = Version} = Rep] -> + version = Version} = Rep} -> ?rt3("request resend when waiting for ack"), %% We have already sent a reply, but the receiver @@ -1384,7 +1400,13 @@ prepare_request(ConnData, T, Rest, AckList, ReqList, Extra) -> end, prepare_normal_trans(ConnData2, Rest, AckList, ReqList, Extra); - [#reply{state = aborted} = Rep] -> + + %% We can ignore the Converted value here as we *know* + %% conn-data to be correct (not faked), so even if + %% the record was converted, it will now have correct + %% values for user_mod and user_args. + {_Converted, + #reply{state = aborted} = Rep} -> ?rt3("request resend when already in aborted state"), %% OTP-4956: @@ -1424,15 +1446,15 @@ prepare_ack(ConnData, [], Rest, AckList, ReqList, Extra) -> do_prepare_ack(ConnData, T, AckList) -> TransId = to_remote_trans_id(ConnData), - case megaco_monitor:lookup_reply(TransId) of + case lookup_reply(ConnData, TransId) of [] -> %% The reply has already been garbage collected. Ignore. ?report_trace(ConnData, "discard ack (no receiver)", [T]), AckList; - [Rep] when Rep#reply.state =:= waiting_for_ack -> + {_Converted, Rep} when Rep#reply.state =:= waiting_for_ack -> %% Don't care about Msg and Rep version diff [{ConnData, Rep, T} | AckList]; - [_Rep] -> + {_Converted, _Rep} -> %% Protocol violation from the sender of this ack ?report_important(ConnData, "<ERROR> discard trans", [T, {error, "got ack before reply was sent"}]), @@ -1519,30 +1541,6 @@ check_pending_limit(Limit, Direction, TransId) -> aborted end. -%% check_pending_limit(infinity, _, _) -> -%% {ok, 0}; -%% check_pending_limit(Limit, Direction, TransId) -> -%% ?rt2("check pending limit", [Direction, Limit, TransId]), -%% case (catch megaco_config:get_pending_counter(Direction, TransId)) of -%% {'EXIT', _} -> -%% %% This function is only called when we "know" the -%% %% counter to exist. So, the only reason that this -%% %% would happen is of the counter has been removed. -%% %% This only happen if the pending limit has been -%% %% reached. In any case, this is basically the same -%% %% as aborted! -%% ?rt2("check pending limit - exit", []), -%% aborted; -%% Val when Val =< Limit -> -%% %% Since we have no intention to increment here, it -%% %% is ok to be _at_ the limit -%% ?rt2("check pending limit - ok", [Val]), -%% {ok, Val}; -%% _Val -> -%% ?rt2("check pending limit - aborted", [_Val]), -%% aborted -%% end. - check_and_maybe_incr_pending_limit(infinity, _, _) -> ok; @@ -1550,59 +1548,42 @@ check_and_maybe_incr_pending_limit(Limit, Direction, TransId) -> %% %% We need this kind of test to detect when we _pass_ the limit %% - ?rt2("check and maybe incr pending limit", [Direction, Limit, TransId]), + ?rt2("check and maybe incr pending limit", [{direction, Direction}, + {transaction_id, TransId}, + {counter_limit, Limit}]), try megaco_config:get_pending_counter(Direction, TransId) of Val when Val > Limit -> - ?rt2("check and maybe incr - aborted", [Direction, Val, Limit]), + ?rt2("check and maybe incr - aborted", [{counter_value, Val}]), aborted; % Already passed the limit Val -> - ?rt2("check and maybe incr - incr", [Direction, Val, Limit]), + ?rt2("check and maybe incr - incr", [{counter_value, Val}]), megaco_config:incr_pending_counter(Direction, TransId), if Val < Limit -> ok; % Still within the limit true -> ?rt2("check and maybe incr - error", - [Direction, Val, Limit]), + [{counter_value, Val}]), error % Passed the limit end catch _:_ -> %% Has not been created yet (connect). - megaco_config:cre_pending_counter(Direction, TransId, 1), - ok + %% Try create it, but bevare of possible raise condition + try + begin + megaco_config:cre_pending_counter(Direction, TransId, 1), + ok + end + catch + _:_ -> + %% Ouch, raise condition, increment instead... + megaco_config:incr_pending_counter(Direction, TransId), + ok + end end. -%% check_and_maybe_incr_pending_limit(infinity, _, _) -> -%% ok; -%% check_and_maybe_incr_pending_limit(Limit, Direction, TransId) -> -%% %% -%% %% We need this kind of test to detect when we _pass_ the limit -%% %% -%% ?rt2("check and maybe incr pending limit", [Direction, Limit, TransId]), -%% case (catch megaco_config:get_pending_counter(Direction, TransId)) of -%% {'EXIT', _} -> -%% %% Has not been created yet (connect). -%% megaco_config:cre_pending_counter(Direction, TransId, 1), -%% ok; -%% Val when Val > Limit -> -%% ?rt2("check and maybe incr - aborted", [Direction, Val, Limit]), -%% aborted; % Already passed the limit -%% Val -> -%% ?rt2("check and maybe incr - incr", [Direction, Val, Limit]), -%% megaco_config:incr_pending_counter(Direction, TransId), -%% if -%% Val < Limit -> -%% ok; % Still within the limit -%% true -> -%% ?rt2("check and maybe incr - error", -%% [Direction, Val, Limit]), -%% error % Passed the limit -%% end -%% end. - - %% BUGBUG BUGBUG BUGBUG %% %% Do we know that the Rep is still valid? A previous transaction @@ -1657,8 +1638,8 @@ handle_request(ConnData, TransId, T, Extra) -> %% Furthermore, the reply timer has not been started, %% so do the cleanup now ?rt1(ConnData, "pending limit already passed", [TransId]), - case megaco_monitor:lookup_reply(TransId) of - [Rep] -> + case lookup_reply(ConnData, TransId) of + {_Converted, Rep} -> cancel_reply(ConnData, Rep, aborted); _ -> ok @@ -1671,8 +1652,8 @@ do_handle_request(_, ignore, _ConnData, _TransId) -> ignore; do_handle_request(_, ignore_trans_request, ConnData, TransId) -> ?rt1(ConnData, "ignore trans request: don't reply", [TransId]), - case megaco_monitor:lookup_reply(TransId) of - [#reply{} = Rep] -> + case lookup_reply(ConnData, TransId) of + {_Converted, #reply{} = Rep} -> cancel_reply(ConnData, Rep, ignore); _ -> ignore @@ -1689,8 +1670,8 @@ do_handle_request({pending, RequestData}, _SendReply, _ConnData, _) -> do_handle_request(AckAction, {ok, Bin}, ConnData, TransId) when is_binary(Bin) -> ?rt1(ConnData, "handle request - ok", [AckAction, TransId]), - case megaco_monitor:lookup_reply(TransId) of - [#reply{pending_timer_ref = PendingRef} = Rep] -> + case lookup_reply(ConnData, TransId) of + {_Converted, #reply{pending_timer_ref = PendingRef} = Rep} -> #conn_data{reply_timer = InitTimer, conn_handle = ConnHandle} = ConnData, @@ -1729,8 +1710,8 @@ do_handle_request(AckAction, {ok, {Sent, NotSent}}, ConnData, TransId) ?rt1(ConnData, "handle request - ok [segmented reply]", [AckAction, TransId]), - case megaco_monitor:lookup_reply(TransId) of - [#reply{pending_timer_ref = PendingRef} = Rep] -> + case lookup_reply(ConnData, TransId) of + {_Converted, #reply{pending_timer_ref = PendingRef} = Rep} -> %% d("do_handle_request -> found reply record:" %% "~n Rep: ~p", [Rep]), @@ -1772,8 +1753,8 @@ do_handle_request(AckAction, {ok, {Sent, NotSent}}, ConnData, TransId) end; do_handle_request(_, {error, aborted}, ConnData, TransId) -> ?report_trace(ConnData, "aborted during our absence", [TransId]), - case megaco_monitor:lookup_reply(TransId) of - [Rep] -> + case lookup_reply(ConnData, TransId) of + {_Converted, Rep} -> cancel_reply(ConnData, Rep, aborted); _ -> ok @@ -1781,8 +1762,8 @@ do_handle_request(_, {error, aborted}, ConnData, TransId) -> ignore; do_handle_request(AckAction, {error, Reason}, ConnData, TransId) -> ?report_trace(ConnData, "error", [TransId, Reason]), - case megaco_monitor:lookup_reply(TransId) of - [Rep] -> + case lookup_reply(ConnData, TransId) of + {_Converted, Rep} -> Rep2 = Rep#reply{state = waiting_for_ack, ack_action = AckAction}, cancel_reply(ConnData, Rep2, Reason); @@ -2626,33 +2607,84 @@ handle_reply( handle_reply(#conn_data{conn_handle = CH} = CD, T, Extra) -> TransId = to_local_trans_id(CD), ?rt2("handle reply", [T, TransId]), - case megaco_monitor:lookup_request(TransId) of - [Req] when (is_record(Req, request) andalso - (CD#conn_data.cancel =:= true)) -> + case {megaco_monitor:request_lockcnt_inc(TransId), + megaco_monitor:lookup_request(TransId)} of + {_Cnt, [Req]} when (is_record(Req, request) andalso + (CD#conn_data.cancel =:= true)) -> ?TC_AWAIT_REPLY_EVENT(true), + ?report_trace(CD, "trans reply - cancel(1)", [T]), do_handle_reply_cancel(CD, Req, T); - [#request{remote_mid = RMid} = Req] when ((RMid =:= preliminary_mid) orelse - (RMid =:= CH#megaco_conn_handle.remote_mid)) -> + {Cnt, [#request{remote_mid = RMid} = Req]} when + ((Cnt =:= 1) andalso + ((RMid =:= preliminary_mid) orelse + (RMid =:= CH#megaco_conn_handle.remote_mid))) -> ?TC_AWAIT_REPLY_EVENT(false), %% Just in case conn_data got update after our lookup %% but before we looked up the request record, we %% check the cancel field again. case megaco_config:conn_info(CD, cancel) of true -> + ?report_trace(CD, "trans reply - cancel(2)", [T]), + megaco_monitor:request_lockcnt_del(TransId), do_handle_reply_cancel(CD, Req, T); false -> + ?report_trace(CD, "trans reply", [T]), do_handle_reply(CD, Req, TransId, T, Extra) end; - [#request{user_mod = UserMod, - user_args = UserArgs, - reply_action = Action, - reply_data = UserData, - remote_mid = RMid}] -> + {Cnt, [#request{remote_mid = RMid} = _Req]} when + (is_integer(Cnt) andalso + ((RMid =:= preliminary_mid) orelse + (RMid =:= CH#megaco_conn_handle.remote_mid))) -> + ?TC_AWAIT_REPLY_EVENT(false), + %% Ok, someone got there before me, now what? + %% This is a plain old raise condition + ?report_important(CD, "trans reply - raise condition", + [T, {request_lockcnt, Cnt}]), + megaco_monitor:request_lockcnt_dec(TransId); + + %% no counter + {_Cnt, [#request{remote_mid = RMid} = Req]} when + ((RMid =:= preliminary_mid) orelse + (RMid =:= CH#megaco_conn_handle.remote_mid)) -> + ?TC_AWAIT_REPLY_EVENT(false), + %% The counter does not exist. + %% This can only mean a code upgrade raise condition. + %% That is, this request record was created before + %% this feature (the counters) was instroduced. + %% The simples solution is this is to behave exactly as + %% before, that is pass it along, and leave it to the + %% user to figure out. + + %% Just in case conn_data got update after our lookup + %% but before we looked up the request record, we + %% check the cancel field again. + ?report_verbose(CD, "trans reply - old style", [T]), + case megaco_config:conn_info(CD, cancel) of + true -> + megaco_monitor:request_lockcnt_del(TransId), + do_handle_reply_cancel(CD, Req, T); + false -> + do_handle_reply(CD, Req, TransId, T, Extra) + end; + + {Cnt, [#request{user_mod = UserMod, + user_args = UserArgs, + reply_action = Action, + reply_data = UserData, + remote_mid = RMid}]} -> ?report_trace(CD, "received trans reply with invalid remote mid", - [T, RMid]), + [{transaction, T}, + {remote_mid, RMid}, + {request_lockcnt, Cnt}]), + if + is_integer(Cnt) -> + megaco_monitor:request_lockcnt_dec(TransId); + true -> + ok + end, WrongMid = CH#megaco_conn_handle.remote_mid, T2 = transform_transaction_reply_enc(CD#conn_data.protocol_version, T), @@ -2663,7 +2695,15 @@ handle_reply(#conn_data{conn_handle = CH} = CD, T, Extra) -> reply_data = UserData}, return_reply(CD2, TransId, UserReply, Extra); - [] -> + {Cnt, []} when is_integer(Cnt) -> + ?TC_AWAIT_REPLY_EVENT(undefined), + ?report_trace(CD, "trans reply (no receiver)", + [T, {request_lockcnt, Cnt}]), + megaco_monitor:request_lockcnt_dec(TransId), + return_unexpected_trans(CD, T, Extra); + + %% No counter + {_Cnt, []} -> ?TC_AWAIT_REPLY_EVENT(undefined), ?report_trace(CD, "trans reply (no receiver)", [T]), return_unexpected_trans(CD, T, Extra) @@ -2694,6 +2734,7 @@ do_handle_reply(CD, %% This is the first reply (maybe of many) megaco_monitor:delete_request(TransId), + megaco_monitor:request_lockcnt_del(TransId), megaco_monitor:cancel_apply_after(Ref), % OTP-4843 megaco_config:del_pending_counter(recv, TransId), % OTP-7189 @@ -2819,9 +2860,10 @@ handle_segment_reply(CD, {segment_no, SN}, {segmentation_complete, SC}]), TransId2 = to_remote_trans_id(CD#conn_data{serial = TransId}), - case megaco_monitor:lookup_reply(TransId2) of - [#reply{bytes = Sent, - segments = []} = Rep] when is_list(Sent) -> + case lookup_reply(CD, TransId2) of + {_Converted, + #reply{bytes = Sent, + segments = []} = Rep} when is_list(Sent) -> ?rt2("no unsent segments", [Sent]), handle_segment_reply_callback(CD, TransId, SN, SC, Extra), case lists:keysearch(SN, 1, Sent) of @@ -2845,8 +2887,9 @@ handle_segment_reply(CD, ok end; - [#reply{bytes = Sent, - segments = NotSent}] when is_list(Sent) andalso + {_Converted, + #reply{bytes = Sent, + segments = NotSent}} when is_list(Sent) andalso is_list(NotSent) -> ?rt2("unsent segments", [Sent, NotSent]), handle_segment_reply_callback(CD, TransId, SN, SC, Extra), @@ -2883,7 +2926,8 @@ handle_segment_reply(CD, ok end; - [#reply{state = State}] -> + {_Converted, + #reply{state = State}} -> %% We received a segment reply for a segmented reply we have %% not yet sent? This is either some sort of race condition %% or the "the other side" is really confused. @@ -3012,7 +3056,7 @@ handle_ack_callback(ConnData, AckStatus, {handle_ack, AckData}, T, Extra) -> _ -> [ConnHandle, Version, AckStatus, AckData, Extra | UserArgs] end, - Res = (catch apply(UserMod, handle_trans_ack, Args)), + Res = (catch handle_callback(ConnData, UserMod, handle_trans_ack, Args)), ?report_debug(ConnData, "return: trans ack", [T, AckData, {return, Res}]), case Res of ok -> @@ -3024,6 +3068,14 @@ handle_ack_callback(ConnData, AckStatus, {handle_ack, AckData}, T, Extra) -> Res. +handle_callback(ConnData, undefined = _UserMod, Func, Args) -> + ?report_important(ConnData, "callback: unknown callback module", + [{func, Func}, {args, Args}]), + ok; +handle_callback(_ConnData, UserMod, Func, Args) -> + (catch apply(UserMod, Func, Args)). + + handle_message_error(ConnData, _Error, _Extra) when ConnData#conn_data.monitor_ref == undefined_monitor_ref -> %% May occur if another process already has setup a @@ -3706,6 +3758,11 @@ insert_requests(ConnData, ConnHandle, insert_request(ConnData, ConnHandle, TransId, Action, Data, InitTimer, LongTimer) -> + %% We dont check the result of the lock-counter creation because + %% the only way it could already exist is if the transaction-id + %% range has wrapped and an old counter was not deleted. + megaco_monitor:request_lockcnt_cre(TransId), + #megaco_conn_handle{remote_mid = RemoteMid} = ConnHandle, #conn_data{protocol_version = Version, user_mod = UserMod, @@ -4290,6 +4347,7 @@ cancel_request(ConnData, Req, Reason) -> cancel_request2(ConnData, TransId, UserReply) -> megaco_monitor:delete_request(TransId), + megaco_monitor:request_lockcnt_del(TransId), megaco_config:del_pending_counter(recv, TransId), % OTP-7189 Serial = TransId#trans_id.serial, ConnData2 = ConnData#conn_data{serial = Serial}, @@ -4347,34 +4405,76 @@ receive_reply_remote(ConnData, UserReply) -> receive_reply_remote(ConnData, UserReply, Extra) -> TransId = to_local_trans_id(ConnData), - case (catch megaco_monitor:lookup_request(TransId)) of - [#request{timer_ref = {_Type, Ref}} = Req] -> %% OTP-4843 + case {megaco_monitor:request_lockcnt_inc(TransId), + (catch megaco_monitor:lookup_request(TransId))} of + {Cnt, [Req]} when (Cnt =:= 1) andalso is_record(Req, request) -> %% Don't care about Req and Rep version diff - megaco_monitor:delete_request(TransId), - megaco_monitor:cancel_apply_after(Ref), % OTP-4843 - megaco_config:del_pending_counter(recv, TransId), % OTP-7189 - - UserMod = Req#request.user_mod, - UserArgs = Req#request.user_args, - Action = Req#request.reply_action, - UserData = Req#request.reply_data, - ConnData2 = ConnData#conn_data{user_mod = UserMod, - user_args = UserArgs, - reply_action = Action, - reply_data = UserData}, - return_reply(ConnData2, TransId, UserReply, Extra); - + do_receive_reply_remote(ConnData, TransId, Req, UserReply, Extra); + + {Cnt, [Req]} when is_integer(Cnt) andalso is_record(Req, request) -> + %% Another process is accessing, handle as unexpected + %% (so it has a possibillity to get logged). + ?report_important(ConnData, "trans reply (no receiver)", + [{user_reply, UserReply}, + {request_lockcnt, Cnt}]), + megaco_monitor:request_lockcnt_dec(TransId), + return_unexpected_trans_reply(ConnData, TransId, UserReply, Extra); + + %% no counter + {_Cnt, [Req]} when is_record(Req, request) -> + %% The counter does not exist. + %% This can only mean a code upgrade raise condition. + %% That is, this request record was created before + %% this feature (the counters) was instroduced. + %% The simples solution to this is to behave exactly as + %% before, that is, pass it along, and leave it to the + %% user to figure out. + ?report_trace(ConnData, + "remote reply - " + "code upgrade raise condition", + [{user_reply, UserReply}]), + do_receive_reply_remote(ConnData, TransId, Req, UserReply, Extra); + + {Cnt, _} when is_integer(Cnt) -> + ?report_trace(ConnData, "trans reply (no receiver)", + [{user_reply, UserReply}, {request_lockcnt, Cnt}]), + megaco_monitor:request_lockcnt_dec(TransId), + return_unexpected_trans_reply(ConnData, TransId, UserReply, Extra); + _ -> ?report_trace(ConnData, "remote reply (no receiver)", - [UserReply]), + [{user_reply, UserReply}]), return_unexpected_trans_reply(ConnData, TransId, UserReply, Extra) end. -cancel_reply(ConnData, #reply{state = waiting_for_ack} = Rep, Reason) -> +do_receive_reply_remote(ConnData, TransId, + #request{timer_ref = {_Type, Ref}, + user_mod = UserMod, + user_args = UserArgs, + reply_action = Action, + reply_data = UserData} = _Req, + UserReply, Extra) -> + megaco_monitor:delete_request(TransId), + megaco_monitor:request_lockcnt_del(TransId), + megaco_monitor:cancel_apply_after(Ref), % OTP-4843 + megaco_config:del_pending_counter(recv, TransId), % OTP-7189 + + ConnData2 = ConnData#conn_data{user_mod = UserMod, + user_args = UserArgs, + reply_action = Action, + reply_data = UserData}, + return_reply(ConnData2, TransId, UserReply, Extra). + + +cancel_reply(ConnData, #reply{state = waiting_for_ack, + user_mod = UserMod, + user_args = UserArgs} = Rep, Reason) -> ?report_trace(ignore, "cancel reply [waiting_for_ack]", [Rep]), megaco_monitor:cancel_apply_after(Rep#reply.pending_timer_ref), Serial = (Rep#reply.trans_id)#trans_id.serial, - ConnData2 = ConnData#conn_data{serial = Serial}, + ConnData2 = ConnData#conn_data{serial = Serial, + user_mod = UserMod, + user_args = UserArgs}, T = #'TransactionAck'{firstAck = Serial}, Extra = ?default_user_callback_extra, handle_ack(ConnData2, {error, Reason}, Rep, T, Extra); @@ -4558,16 +4658,30 @@ reply_timeout(ConnHandle, TransId, {_, timeout}) -> reply_timeout(ConnHandle, TransId, Timer) -> ?report_trace(ConnHandle, "reply timeout", [Timer, TransId]), - case megaco_monitor:lookup_reply(TransId) of + case lookup_reply(undefined, TransId) of [] -> reply_not_found_ignore; - [#reply{state = waiting_for_ack, - ack_action = {handle_ack, _}} = Rep] -> + {Converted, + #reply{state = waiting_for_ack, + ack_action = {handle_ack, _}} = Rep} -> case megaco_config:lookup_local_conn(ConnHandle) of [CD] when (CD#conn_data.cancel =:= true) -> cancel_in_progress_ignore; - [CD] -> + [CD] when (Converted =:= true) -> + incNumTimerRecovery(ConnHandle), + %% When we did the reply record lookup, we had no + %% conn_data record, and the reply record was + %% converted. This means that the reply record + %% has no valid info about user_mod or user_args. + %% Therefor, the user_mod and user_args of the + %% conn_data record is better then nothing. + #conn_data{user_mod = UserMod, + user_args = UserArgs} = CD, + Rep2 = Rep#reply{user_mod = UserMod, + user_args = UserArgs}, + do_reply_timeout(ConnHandle, TransId, CD, Timer, Rep2); + [CD] when (Converted =:= false) -> incNumTimerRecovery(ConnHandle), do_reply_timeout(ConnHandle, TransId, CD, Timer, Rep); [] -> @@ -4576,10 +4690,25 @@ reply_timeout(ConnHandle, TransId, Timer) -> do_reply_timeout(ConnHandle, TransId, CD, Timer, Rep) end; - [#reply{state = waiting_for_ack, - bytes = Sent} = Rep] when is_list(Sent) -> + {Converted, + #reply{state = waiting_for_ack, + bytes = Sent} = Rep} when is_list(Sent) -> case megaco_config:lookup_local_conn(ConnHandle) of - [ConnData] -> + [ConnData] when (Converted =:= true) -> + incNumTimerRecovery(ConnHandle), + %% When we did the reply record lookup, we had no + %% conn_data record, and the reply record was + %% converted. This means that the reply record + %% has no valid info about user_mod or user_args. + %% Therefor, the user_mod and user_args of the + %% conn_data record is better then nothing. + #conn_data{user_mod = UserMod, + user_args = UserArgs} = ConnData, + Rep2 = Rep#reply{user_mod = UserMod, + user_args = UserArgs}, + do_reply_timeout(ConnHandle, TransId, ConnData, + Timer, Rep2); + [ConnData] when (Converted =:= false) -> incNumTimerRecovery(ConnHandle), do_reply_timeout(ConnHandle, TransId, ConnData, Timer, Rep); @@ -4590,10 +4719,12 @@ reply_timeout(ConnHandle, TransId, Timer) -> Timer, Rep) end; - [#reply{state = waiting_for_ack} = Rep] -> + {_Converted, + #reply{state = waiting_for_ack} = Rep} -> do_reply_timeout(ConnHandle, TransId, Timer, Rep); - [#reply{state = aborted} = Rep] -> + {_Converted, + #reply{state = aborted} = Rep} -> do_reply_timeout(ConnHandle, TransId, Timer, Rep); _ -> @@ -4686,22 +4817,41 @@ handle_reply_timer_timeout(ConnHandle, TransId) -> ?report_trace(ConnHandle, "handle reply timeout", [timeout, TransId]), incNumTimerRecovery(ConnHandle), %% OTP-4378 - case megaco_monitor:lookup_reply(TransId) of - [#reply{state = waiting_for_ack} = Rep] -> + case lookup_reply(undefined, TransId) of + {Converted, + #reply{state = waiting_for_ack} = Rep} -> Serial = (Rep#reply.trans_id)#trans_id.serial, - ConnData = + {Rep2, ConnData} = case megaco_config:lookup_local_conn(ConnHandle) of - [ConnData0] -> - ConnData0; - [] -> - fake_conn_data(ConnHandle) + [ConnData0] when (Converted =:= false) -> + #reply{user_mod = UserMod, + user_args = UserArgs} = Rep, + {Rep, + ConnData0#conn_data{user_mod = UserMod, + user_args = UserArgs}}; + [ConnData0] when (Converted =:= true) -> + {Rep#reply{user_mod = ConnData0#conn_data.user_mod, + user_args = ConnData0#conn_data.user_args}, + ConnData0}; + [] when (Converted =:= false) -> + ConnData0 = fake_conn_data(ConnHandle), + #reply{user_mod = UserMod, + user_args = UserArgs} = Rep, + {Rep, + ConnData0#conn_data{user_mod = UserMod, + user_args = UserArgs}}; + [] when (Converted =:= true) -> + %% We have no valid info about user_mod and user_args + {Rep, fake_conn_data(ConnHandle)} end, ConnData2 = ConnData#conn_data{serial = Serial}, T = #'TransactionAck'{firstAck = Serial}, Extra = ?default_user_callback_extra, - handle_ack(ConnData2, {error, timeout}, Rep, T, Extra); - [#reply{pending_timer_ref = Ref, % aborted? - bytes = SegSent}] -> % may be a binary + handle_ack(ConnData2, {error, timeout}, Rep2, T, Extra); + + {_Converted, + #reply{pending_timer_ref = Ref, % aborted? + bytes = SegSent}} -> % may be a binary megaco_monitor:cancel_apply_after(Ref), cancel_segment_timers(SegSent), megaco_monitor:delete_reply(TransId), @@ -4803,9 +4953,10 @@ pending_timeout(ConnHandle, TransId, Timer) -> handle_pending_timeout(CD, TransId, Timer) -> ?report_trace(CD, "handle pending timeout", []), - case megaco_monitor:lookup_reply(TransId) of - [#reply{state = State, - handler = Pid} = Rep] when (State =:= prepare) orelse + case lookup_reply(CD, TransId) of + {_Converted, + #reply{state = State, + handler = Pid} = Rep} when (State =:= prepare) orelse (State =:= eval_request) -> #conn_data{sent_pending_limit = Limit, @@ -4885,12 +5036,14 @@ handle_pending_timeout(CD, TransId, Timer) -> [] -> reply_not_found; % Trace ?? - [#reply{state = waiting_for_ack}] -> + {_Converted, + #reply{state = waiting_for_ack}} -> %% The reply has already been sent %% No need for any pending trans reply reply_has_been_sent; - [#reply{state = aborted} = Rep] -> + {_Converted, + #reply{state = aborted} = Rep} -> %% glitch, but cleanup just the same cancel_reply(CD, Rep, aborted), reply_aborted_state @@ -5096,7 +5249,65 @@ make_transaction_reply(_, TransId, IAR, TransRes) -> %%----------------------------------------------------------------- +%% This function is used as a wrapper for reply-record lookups. +%% The intention is that during upgrade, this function +%% can perform on-the-fly conversions of reply-records. +lookup_reply(CD, TransId) -> + case megaco_monitor:lookup_reply(TransId) of + [#reply{} = Rep] -> + {false, Rep}; + + %% Old (pre-3.13.1) version of the record => Convert to new version + [{reply, TransId, + LocalMid, State, PendingTmrRef, Handler, TimerRef, + Version, Bytes, AckAction, SendHandle, Segments}] + when is_record(CD, conn_data) -> + #conn_data{user_mod = UserMod, + user_args = UserArgs} = CD, + Rep = #reply{trans_id = TransId, + local_mid = LocalMid, + state = State, + pending_timer_ref = PendingTmrRef, + handler = Handler, + timer_ref = TimerRef, + version = Version, + bytes = Bytes, + ack_action = AckAction, + send_handle = SendHandle, + segments = Segments, + user_mod = UserMod, + user_args = UserArgs}, + {true, Rep}; + + %% Old (pre-3.13.1) version of the record => Convert to new version + [{reply, TransId, + LocalMid, State, PendingTmrRef, Handler, TimerRef, + Version, Bytes, AckAction, SendHandle, Segments}] -> + %% ConnData is not known here, so ignore for now + Rep = #reply{trans_id = TransId, + local_mid = LocalMid, + state = State, + pending_timer_ref = PendingTmrRef, + handler = Handler, + timer_ref = TimerRef, + version = Version, + bytes = Bytes, + ack_action = AckAction, + send_handle = SendHandle, + segments = Segments}, + {true, Rep}; + + Else -> + Else + end. + + %%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% info_msg(F, A) -> +%% ?megaco_info(F, A). + warning_msg(F, A) -> ?megaco_warning(F, A). @@ -5131,7 +5342,7 @@ error_msg(F, A) -> %% Time in milli seconds t() -> - {A,B,C} = erlang:now(), + {A,B,C} = os:timestamp(), A*1000000000+B*1000+(C div 1000). @@ -5156,3 +5367,4 @@ incNum(Cnt) -> Old end. + diff --git a/lib/megaco/src/engine/megaco_monitor.erl b/lib/megaco/src/engine/megaco_monitor.erl index f95a20cf58..29275371be 100644 --- a/lib/megaco/src/engine/megaco_monitor.erl +++ b/lib/megaco/src/engine/megaco_monitor.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2000-2010. 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% %% @@ -51,6 +51,11 @@ update_request_field/3, update_request_fields/2, delete_request/1, + request_lockcnt_cre/1, + request_lockcnt_del/1, + request_lockcnt_inc/1, + request_lockcnt_dec/1, + lookup_reply/1, lookup_reply_field/2, match_replies/1, @@ -115,6 +120,24 @@ update_request_fields(Key, NewFields) when is_list(NewFields) -> delete_request(Key) -> ets:delete(megaco_requests, Key). + +request_lockcnt_cre(TransId) -> + Key = {TransId, lockcnt}, + ets:insert_new(megaco_requests, {Key, 1}). + +request_lockcnt_del(TransId) -> + Key = {TransId, lockcnt}, + ets:delete(megaco_requests, Key). + +request_lockcnt_inc(TransId) -> + Key = {TransId, lockcnt}, + (catch ets:update_counter(megaco_requests, Key, 1)). + +request_lockcnt_dec(TransId) -> + Key = {TransId, lockcnt}, + (catch ets:update_counter(megaco_requests, Key, -1)). + + lookup_reply(Key) -> ets:lookup(megaco_replies, Key). diff --git a/lib/megaco/src/flex/Makefile.in b/lib/megaco/src/flex/Makefile.in index 782d6a4807..5af651d89b 100644 --- a/lib/megaco/src/flex/Makefile.in +++ b/lib/megaco/src/flex/Makefile.in @@ -1,19 +1,19 @@ # # %CopyrightBegin% -# -# Copyright Ericsson AB 2001-2009. All Rights Reserved. -# +# +# Copyright Ericsson AB 2001-2010. 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% include $(ERL_TOP)/make/target.mk @@ -23,13 +23,30 @@ MEGACO_INCLUDEDIR = ../../include include $(ERL_TOP)/make/$(TARGET)/otp.mk - # ---------------------------------------------------- # Application version # ---------------------------------------------------- include ../../vsn.mk VSN=$(MEGACO_VSN) +# ---------------------------------------------------- +# Dynamic Erlang Driver +# ---------------------------------------------------- +HAVE_USABLE_OTP_DED_MK = @HAVE_USABLE_OTP_DED_MK@ + +ifeq ($(HAVE_USABLE_OTP_DED_MK),yes) +# otp_ded.mk will be used on R13B04 and later +include $(ERL_TOP)/make/$(TARGET)/otp_ded.mk +else +# megacos configure provide the info instead +DED_CC = @CC@ +DED__NOWARN_NOTHR_CFLAGS = @DED_CFLAGS@ +DED_THR_DEFS = @DED_THR_DEFS@ +DED_LD = @DED_LD@ +DED_LDFLAGS = @DED_LDFLAGS@ +DED_INCLUDES = @DED_INCLUDES@ +DED_EXT = so +endif # ---------------------------------------------------- # The following variables differ on different systems, we set @@ -39,16 +56,16 @@ VSN=$(MEGACO_VSN) FLEX_VSN = $(shell flex --version) -TMP_CFLAGS = @DED_CFLAGS@ +TMP_CFLAGS = $(DED__NOWARN_NOTHR_CFLAGS) @OTP_EXTRA_FLAGS@ ifeq ($(TYPE),valgrind) CFLAGS = $(subst -O2, , $(TMP_CFLAGS)) -DVALGRIND else CFLAGS = $(TMP_CFLAGS) endif -CC = @CC@ -CFLAGS_MT = $(CFLAGS) -D_THREAD_SAFE -D_REENTRANT -LD = @DED_LD@ -LDFLAGS = @DED_LDFLAGS@ +CC = $(DED_CC) +CFLAGS_MT = $(CFLAGS) $(DED_THR_DEFS) +LD = $(DED_LD) +LDFLAGS = $(DED_LDFLAGS) LEX = @LEX@ LEXLIB = @LEXLIB@ PERL = @PERL@ @@ -87,18 +104,13 @@ ENABLE_MEGACO_FLEX_SCANNER_LINENO = @ENABLE_MEGACO_FLEX_SCANNER_LINENO@ endif endif - -SYSINCLUDE = -I$(ERL_TOP)/erts/emulator/beam \ - -I$(ERL_TOP)/erts/emulator/sys/$(ERLANG_OSTYPE) ifeq ($(findstring vxworks,$(TARGET)),vxworks) - SYSINCLUDE += -I$(ERL_TOP)/erts/etc/vxworks + DED_INCLUDES += -I$(ERL_TOP)/erts/etc/vxworks endif -DRIVER_INCLUDES = $(SYSINCLUDE) - PRIVDIR = ../../priv LIBDIR = $(PRIVDIR)/lib/$(TARGET) - +OBJDIR = $(PRIVDIR)/obj/$(TARGET) # ---------------------------------------------------- # Release directory specification @@ -138,8 +150,8 @@ ifeq ($(findstring vxworks,$(TARGET)),vxworks) FLEX_SCANNER_SO = SOLIBS = $(FLEX_SCANNER_SO) else -FLEX_SCANNER_SO = $(LIBDIR)/$(STD_DRV).so -FLEX_SCANNER_MT_SO = $(LIBDIR)/$(MT_DRV).so +FLEX_SCANNER_SO = $(LIBDIR)/$(STD_DRV).$(DED_EXT) +FLEX_SCANNER_MT_SO = $(LIBDIR)/$(MT_DRV).$(DED_EXT) SOLIBS = $(FLEX_SCANNER_SO) $(FLEX_SCANNER_MT_SO) endif endif @@ -175,7 +187,7 @@ else CFLAGS += -DMFS_FLEX_DEBUG=0 endif -CFLAGS += $(DRIVER_INCLUDES) $(DRV_FLAGS) -funroll-loops -Wall +CFLAGS += $(DED_INCLUDES) -I$(ERL_TOP)/erts/$(TARGET) $(DRV_FLAGS) -funroll-loops -Wall #ifneq ($(FLEX_VSN),) #CFLAGS += -DFLEX_VERSION="$(FLEX_VSN)" @@ -268,7 +280,7 @@ release_spec: opt $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin ifeq ($(ENABLE_MEGACO_FLEX_SCANNER),true) $(INSTALL_DATA) $(FLEX_FILES) $(C_TARGETS) $(RELSYSDIR)/src/flex - $(INSTALL_DATA) $(SOLIBS) $(RELSYSDIR)/priv/lib + $(INSTALL_PROGRAM) $(SOLIBS) $(RELSYSDIR)/priv/lib endif @@ -379,18 +391,30 @@ $(STD_DRV).c: $(STD_DRV).flex $(MT_DRV).c: $(MT_DRV).flex $(LEX) $(MT_LEX_FLAGS) -P$* -o$@ $< -solibs: $(LIBDIR) $(SOLIBS) +solibs: $(LIBDIR) $(OBJDIR) $(SOLIBS) + +$(OBJDIR)/$(STD_DRV).o: $(STD_DRV).c + @echo "compiling std driver:" + $(CC) -c $(STD_DRV_NAME) $(CFLAGS) -o $@ $< + +$(OBJDIR)/$(MT_DRV).o: $(MT_DRV).c + @echo "compiling multi-threaded driver:" + $(CC) -c $(MT_DRV_NAME) $(CFLAGS_MT) -o $@ $< + # No need to link with -lfl as we have also defined %option noyywrap - # and having -lfl doesn't work under Darwin for some reason. - Sean -$(LIBDIR)/$(STD_DRV).so: $(STD_DRV).c - @echo "std driver:" - $(CC) $(STD_DRV_NAME) $(CFLAGS) $(LDFLAGS) -o $(LIBDIR)/$(STD_DRV).so $< +$(LIBDIR)/$(STD_DRV).$(DED_EXT): $(OBJDIR)/$(STD_DRV).o + @echo "linking std driver:" + $(LD) $(LDFLAGS) -o $@ $< -$(LIBDIR)/$(MT_DRV).so: $(MT_DRV).c - @echo "multi-threaded driver:" - $(CC) $(MT_DRV_NAME) $(CFLAGS_MT) $(LDFLAGS) -o $(LIBDIR)/$(MT_DRV).so $< +$(LIBDIR)/$(MT_DRV).$(DED_EXT): $(OBJDIR)/$(MT_DRV).o + @echo "linking multi-threaded driver:" + $(LD) $(LDFLAGS) -o $@ $< $(LIBDIR): -mkdir -p $(LIBDIR) +$(OBJDIR): + -mkdir -p $(OBJDIR) + diff --git a/lib/megaco/src/flex/megaco_flex_scanner_drv.flex.src b/lib/megaco/src/flex/megaco_flex_scanner_drv.flex.src index 3520c34d50..96621193e8 100644 --- a/lib/megaco/src/flex/megaco_flex_scanner_drv.flex.src +++ b/lib/megaco/src/flex/megaco_flex_scanner_drv.flex.src @@ -8,12 +8,12 @@ * 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% * * ---------------------------------------------------------------------- @@ -361,9 +361,6 @@ static ErlDrvEntry mfs_entry = { MEGACO_DRIVER_FLAGS, /* driver_flags, used for port lock indication */ NULL, /* handle2, emulator internal use */ NULL /* process_exit, Called when a process monitor fires */ -#if defined(MEGACO_DRV_ENTRY_HAS_STOP_SELECT) - ,NULL /* stop_select, Called to close an event object */ -#endif }; diff --git a/lib/megaco/src/flex/megaco_flex_scanner_handler.erl b/lib/megaco/src/flex/megaco_flex_scanner_handler.erl index d09e0c6fff..420202134e 100644 --- a/lib/megaco/src/flex/megaco_flex_scanner_handler.erl +++ b/lib/megaco/src/flex/megaco_flex_scanner_handler.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2001-2010. 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% %% @@ -180,27 +180,28 @@ terminate(_Reason, _S) -> %% Purpose: Called to change the internal state %% Returns: {ok, NewState} %%---------------------------------------------------------------------- -%% code_change({down, _Vsn}, #state{conf = Conf} = State, downgrade_to_pre_3_8) -> -%% Port = downgrade_flex_scanner(Conf), -%% {ok, State#state{conf = {flex, Port}}}; + +code_change({down, _Vsn}, #state{conf = Conf} = State, downgrade_to_pre_3_13_1) -> + NewPorts = bump_flex_scanner(Conf), + {ok, State#state{conf = {flex, NewPorts}}}; + +code_change(_Vsn, #state{conf = Conf} = State, upgrade_from_pre_3_13_1) -> + NewPorts = bump_flex_scanner(Conf), + {ok, State#state{conf = {flex, NewPorts}}}; code_change(_Vsn, State, _Extra) -> {ok, State}. -%% downgrade_flex_scanner({flex, Port}) when is_port(Port) -> -%% Port; -%% downgrade_flex_scanner({flex, [Port]}) when is_port(Port) -> -%% Port; -%% downgrade_flex_scanner({flex, Ports}) when is_list(Ports) -> -%% megaco_flex_scanner:stop(Ports), -%% case megaco_flex_scanner:start() of -%% {ok, Port} -> -%% Port; -%% Error -> -%% exit(Error) -%% end; -%% downgrade_flex_scanner(BadConfig) -> -%% exit({invalid_config, BadConfig}). +bump_flex_scanner({flex, Ports}) -> + megaco_flex_scanner:stop(Ports), + case start_flex_scanners() of + {ok, NewPorts} -> + NewPorts; + Error -> + exit(Error) + end; +bump_flex_scanner(BadConfig) -> + exit({invalid_config, BadConfig}). %%%---------------------------------------------------------------------- |