diff options
Diffstat (limited to 'lib/megaco/src/engine/megaco_monitor.erl')
-rw-r--r-- | lib/megaco/src/engine/megaco_monitor.erl | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/lib/megaco/src/engine/megaco_monitor.erl b/lib/megaco/src/engine/megaco_monitor.erl new file mode 100644 index 0000000000..f95a20cf58 --- /dev/null +++ b/lib/megaco/src/engine/megaco_monitor.erl @@ -0,0 +1,365 @@ +%% +%% %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: Monitor connections and timers +%%---------------------------------------------------------------------- + +-module(megaco_monitor). + +-behaviour(gen_server). + + +%%----------------------------------------------------------------- +%% Include files +%%----------------------------------------------------------------- + +-include_lib("megaco/src/app/megaco_internal.hrl"). + + +%% Application internal exports +-export([ + start_link/0, + stop/0, + + apply_after/4, + apply_after/5, + cancel_apply_after/1, + + lookup_request/1, + lookup_request_field/2, + match_requests/1, + which_requests/1, + insert_request/1, + update_request_field/3, update_request_fields/2, + delete_request/1, + + lookup_reply/1, + lookup_reply_field/2, + match_replies/1, + which_replies/1, + insert_reply/1, insert_reply_new/1, + update_reply_field/3, update_reply_fields/2, + delete_reply/1, + + apply_at_exit/4, + cancel_apply_at_exit/1 + ]). + +%% 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}). +-record(apply_at_exit, {ref, pid, module, function, arguments}). + + + +%%%---------------------------------------------------------------------- +%%% API +%%%---------------------------------------------------------------------- +start_link() -> + ?d("start -> entry", []), + gen_server:start_link({local, ?SERVER}, ?MODULE, [self()], []). + +stop() -> + call(stop). + +lookup_request(Key) -> + ets:lookup(megaco_requests, Key). + +lookup_request_field(Key, Field) -> + try + begin + {ok, ets:lookup_element(megaco_requests, Key, Field)} + end + catch + error:badarg -> + {error, not_found} + end. + +match_requests(Pat) -> + ets:match_object(megaco_requests, Pat). + +which_requests(Pat) -> + Spec = [{Pat, [], ['$$']}], + ets:select(megaco_requests, Spec). + +insert_request(Rec) -> + ets:insert(megaco_requests, Rec). + +update_request_field(Key, Field, NewValue) -> + ets:update_element(megaco_requests, Key, {Field, NewValue}). + +update_request_fields(Key, NewFields) when is_list(NewFields) -> + ets:update_element(megaco_requests, Key, NewFields). + +delete_request(Key) -> + ets:delete(megaco_requests, Key). + +lookup_reply(Key) -> + ets:lookup(megaco_replies, Key). + +lookup_reply_field(Key, Field) -> + try + begin + {ok, ets:lookup_element(megaco_replies, Key, Field)} + end + catch + error:badarg -> + {error, not_found} + end. + +match_replies(Pat) -> + ets:match_object(megaco_replies, Pat). + +which_replies(Pat) -> + Spec = [{Pat, [], ['$$']}], + ets:select(megaco_replies, Spec). + +insert_reply(Rec) -> + ets:insert(megaco_replies, Rec). + +insert_reply_new(Rec) -> + ets:insert_new(megaco_replies, Rec). + +update_reply_field(Key, Field, NewValue) -> + ets:update_element(megaco_replies, Key, {Field, NewValue}). + +update_reply_fields(Key, NewFields) when is_list(NewFields) -> + ets:update_element(megaco_replies, Key, NewFields). + +delete_reply(Key) -> + ets:delete(megaco_replies, Key). + +apply_after(M, F, A, Time) -> + apply_after(spawn_method, M, F, A, Time). + +apply_after(Method, M, F, A, Time) + when is_atom(M) andalso is_atom(F) andalso is_list(A) -> + if + Time =:= infinity -> + apply_after_infinity; + is_integer(Time) -> + Msg = {apply_after, Method, M, F, A}, + Ref = erlang:send_after(Time, whereis(?SERVER), Msg), + {apply_after, Ref} + end. + +cancel_apply_after({apply_after, Ref}) -> + case erlang:cancel_timer(Ref) of + TimeLeft when is_integer(TimeLeft) -> + {ok, TimeLeft}; + _ -> + {ok, 0} + end; +cancel_apply_after(apply_after_infinity) -> + ok; +cancel_apply_after(BadRef) -> + {error, {bad_ref, BadRef}}. + +%% Performs apply(M, F, [Reason | A]) when process Pid dies +apply_at_exit(M, F, A, Pid) + when is_atom(M) andalso is_atom(F) andalso is_list(A) andalso is_pid(Pid) -> + Ref = call({apply_at_exit, M, F, A, Pid}), + {apply_at_exit, Ref}. + +cancel_apply_at_exit({apply_at_exit, Ref}) -> + cast({cancel_apply_at_exit, Ref}); +cancel_apply_at_exit(BadRef) -> + {error, {bad_ref, BadRef}}. + +call(Request) -> + gen_server:call(?SERVER, Request, infinity). + +cast(Msg) -> + ?SERVER ! Msg, ok. + +%%%---------------------------------------------------------------------- +%%% Callback functions from gen_server +%%%---------------------------------------------------------------------- + +%%---------------------------------------------------------------------- +%% Func: init/1 +%% Returns: {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%%---------------------------------------------------------------------- + +init([Parent]) -> + ?d("init -> entry", []), + process_flag(trap_exit, true), + ets:new(megaco_requests, [public, named_table, {keypos, 2}]), + ets:new(megaco_replies, [public, named_table, {keypos, 2}]), + ?d("init -> done", []), + {ok, #state{parent_pid = Parent}}. + + +%%---------------------------------------------------------------------- +%% 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({apply_at_exit, M, F, A, Pid}, _From, S) -> + Ref = erlang:monitor(process, Pid), + AAE = #apply_at_exit{ref = Ref, + pid = Pid, + module = M, + function = F, + arguments = A}, + put({?MODULE, Ref}, AAE), + Reply = Ref, + {reply, Reply, S}; + +handle_call(stop, {Parent, _} = _From, #state{parent_pid = Parent} = S) -> + {stop, normal, ok, 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(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({cancel_apply_at_exit, Ref}, S) -> + case erase({?MODULE, Ref}) of + undefined -> + %% Reply = {error, {already_cancelled, {apply_at_exit, Ref}}}, + {noreply, S}; + _AAE -> + erlang:demonitor(Ref), + {noreply, S} + end; + +handle_info({apply_after, Method, M, F, A}, S) -> + handle_apply(Method, M, F, A, apply_after), + {noreply, S}; + +%% Handle the old format also... +handle_info({apply_after, M, F, A}, S) -> + handle_apply(M, F, A, apply_after), + {noreply, S}; + +handle_info({'DOWN', Ref, process, _Pid, Reason}, S) -> + case erase({?MODULE, Ref}) of + undefined -> + {noreply, S}; + AAE -> + M = AAE#apply_at_exit.module, + F = AAE#apply_at_exit.function, + A = AAE#apply_at_exit.arguments, + handle_apply(M, F, [Reason | A], apply_at_exit), + {noreply, S} + end; + +handle_info({'EXIT', Pid, Reason}, S) when Pid == S#state.parent_pid -> + %% [megaco_messenger:disconnect(CH, {stopped, Reason}) + %% || CH <- megaco:lookup_system_info(connections)], + {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, _Extra) -> + {ok, S}. + + +%%%---------------------------------------------------------------------- +%%% Internal functions +%%%---------------------------------------------------------------------- + +handle_apply(M, F, A, _ErrorTag) -> + spawn(M, F, A). + +handle_apply(spawn_method, M, F, A, _ErrorTag) -> + spawn(M, F, A); +handle_apply(_Method, M, F, A, _ErrorTag) -> + (catch apply(M, F, A)). + + +warning_msg(F, A) -> + ?megaco_warning("Monitor server: " ++ F, A). + + +% d(F) -> +% d(F,[]). + +% d(F,A) -> +% %% d(true,F,A). +% d(get(dbg),F,A). + +% d(true,F,A) -> +% io:format("*** [~s] ~p:~p ***" +% "~n " ++ F ++ "~n", +% [format_timestamp(now()), self(),?MODULE|A]); +% d(_, _, _) -> +% ok. + +% format_timestamp(Now) -> +% {N1, N2, N3} = Now, +% {Date, Time} = calendar:now_to_datetime(Now), +% {YYYY,MM,DD} = Date, +% {Hour,Min,Sec} = Time, +% FormatDate = +% io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", +% [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), +% lists:flatten(FormatDate). + + |