1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2000-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21%% 22%%---------------------------------------------------------------------- 23%% Purpose: Monitor connections and timers 24%%---------------------------------------------------------------------- 25 26-module(megaco_monitor). 27 28-behaviour(gen_server). 29 30 31%%----------------------------------------------------------------- 32%% Include files 33%%----------------------------------------------------------------- 34 35-include_lib("megaco/src/app/megaco_internal.hrl"). 36 37 38%% Application internal exports 39-export([ 40 start_link/0, 41 stop/0, 42 43 apply_after/4, 44 apply_after/5, 45 cancel_apply_after/1, 46 47 lookup_request/1, 48 lookup_request_field/2, 49 match_requests/1, 50 which_requests/1, 51 insert_request/1, 52 update_request_field/3, update_request_fields/2, 53 delete_request/1, 54 55 request_lockcnt_cre/1, 56 request_lockcnt_del/1, 57 request_lockcnt_inc/1, 58 request_lockcnt_dec/1, 59 60 lookup_reply/1, 61 lookup_reply_field/2, 62 match_replies/1, 63 which_replies/1, 64 insert_reply/1, insert_reply_new/1, 65 update_reply_field/3, update_reply_fields/2, 66 delete_reply/1, 67 68 apply_at_exit/4, 69 cancel_apply_at_exit/1 70 ]). 71 72%% gen_server callbacks 73-export([init/1, handle_call/3, handle_cast/2, handle_info/2, 74 terminate/2, code_change/3]). 75 76-define(SERVER, ?MODULE). 77-record(state, {parent_pid}). 78-record(apply_at_exit, {ref, pid, module, function, arguments}). 79 80 81 82%%%---------------------------------------------------------------------- 83%%% API 84%%%---------------------------------------------------------------------- 85start_link() -> 86 ?d("start -> entry", []), 87 gen_server:start_link({local, ?SERVER}, ?MODULE, [self()], []). 88 89stop() -> 90 call(stop). 91 92lookup_request(Key) -> 93 ets:lookup(megaco_requests, Key). 94 95lookup_request_field(Key, Field) -> 96 try 97 begin 98 {ok, ets:lookup_element(megaco_requests, Key, Field)} 99 end 100 catch 101 error:badarg -> 102 {error, not_found} 103 end. 104 105match_requests(Pat) -> 106 ets:match_object(megaco_requests, Pat). 107 108which_requests(Pat) -> 109 Spec = [{Pat, [], ['$$']}], 110 ets:select(megaco_requests, Spec). 111 112insert_request(Rec) -> 113 ets:insert(megaco_requests, Rec). 114 115update_request_field(Key, Field, NewValue) -> 116 ets:update_element(megaco_requests, Key, {Field, NewValue}). 117 118update_request_fields(Key, NewFields) when is_list(NewFields) -> 119 ets:update_element(megaco_requests, Key, NewFields). 120 121delete_request(Key) -> 122 ets:delete(megaco_requests, Key). 123 124 125request_lockcnt_cre(TransId) -> 126 Key = {TransId, lockcnt}, 127 ets:insert_new(megaco_requests, {Key, 1}). 128 129request_lockcnt_del(TransId) -> 130 Key = {TransId, lockcnt}, 131 ets:delete(megaco_requests, Key). 132 133request_lockcnt_inc(TransId) -> 134 Key = {TransId, lockcnt}, 135 (catch ets:update_counter(megaco_requests, Key, 1)). 136 137request_lockcnt_dec(TransId) -> 138 Key = {TransId, lockcnt}, 139 (catch ets:update_counter(megaco_requests, Key, -1)). 140 141 142lookup_reply(Key) -> 143 ets:lookup(megaco_replies, Key). 144 145lookup_reply_field(Key, Field) -> 146 try 147 begin 148 {ok, ets:lookup_element(megaco_replies, Key, Field)} 149 end 150 catch 151 error:badarg -> 152 {error, not_found} 153 end. 154 155match_replies(Pat) -> 156 ets:match_object(megaco_replies, Pat). 157 158which_replies(Pat) -> 159 Spec = [{Pat, [], ['$$']}], 160 ets:select(megaco_replies, Spec). 161 162insert_reply(Rec) -> 163 ets:insert(megaco_replies, Rec). 164 165insert_reply_new(Rec) -> 166 ets:insert_new(megaco_replies, Rec). 167 168update_reply_field(Key, Field, NewValue) -> 169 ets:update_element(megaco_replies, Key, {Field, NewValue}). 170 171update_reply_fields(Key, NewFields) when is_list(NewFields) -> 172 ets:update_element(megaco_replies, Key, NewFields). 173 174delete_reply(Key) -> 175 ets:delete(megaco_replies, Key). 176 177apply_after(M, F, A, Time) -> 178 apply_after(spawn_method, M, F, A, Time). 179 180apply_after(Method, M, F, A, Time) 181 when is_atom(M) andalso is_atom(F) andalso is_list(A) -> 182 if 183 Time =:= infinity -> 184 apply_after_infinity; 185 is_integer(Time) -> 186 Msg = {apply_after, Method, M, F, A}, 187 Ref = erlang:send_after(Time, whereis(?SERVER), Msg), 188 {apply_after, Ref} 189 end. 190 191cancel_apply_after({apply_after, Ref}) -> 192 case erlang:cancel_timer(Ref) of 193 TimeLeft when is_integer(TimeLeft) -> 194 {ok, TimeLeft}; 195 _ -> 196 {error, {already_expired, Ref}} 197 end; 198cancel_apply_after(apply_after_infinity) -> 199 ok; 200cancel_apply_after(BadRef) -> 201 {error, {bad_ref, BadRef}}. 202 203%% Performs apply(M, F, [Reason | A]) when process Pid dies 204apply_at_exit(M, F, A, Pid) 205 when is_atom(M) andalso is_atom(F) andalso is_list(A) andalso is_pid(Pid) -> 206 Ref = call({apply_at_exit, M, F, A, Pid}), 207 {apply_at_exit, Ref}. 208 209cancel_apply_at_exit({apply_at_exit, Ref}) -> 210 cast({cancel_apply_at_exit, Ref}); 211cancel_apply_at_exit(BadRef) -> 212 {error, {bad_ref, BadRef}}. 213 214call(Request) -> 215 gen_server:call(?SERVER, Request, infinity). 216 217cast(Msg) -> 218 ?SERVER ! Msg, ok. 219 220%%%---------------------------------------------------------------------- 221%%% Callback functions from gen_server 222%%%---------------------------------------------------------------------- 223 224%%---------------------------------------------------------------------- 225%% Func: init/1 226%% Returns: {ok, State} | 227%% {ok, State, Timeout} | 228%% ignore | 229%% {stop, Reason} 230%%---------------------------------------------------------------------- 231 232init([Parent]) -> 233 ?d("init -> entry", []), 234 process_flag(trap_exit, true), 235 ets:new(megaco_requests, [public, named_table, {keypos, 2}]), 236 ets:new(megaco_replies, [public, named_table, {keypos, 2}]), 237 ?d("init -> done", []), 238 {ok, #state{parent_pid = Parent}}. 239 240 241%%---------------------------------------------------------------------- 242%% Func: handle_call/3 243%% Returns: {reply, Reply, State} | 244%% {reply, Reply, State, Timeout} | 245%% {noreply, State} | 246%% {noreply, State, Timeout} | 247%% {stop, Reason, Reply, State} | (terminate/2 is called) 248%% {stop, Reason, State} (terminate/2 is called) 249%%---------------------------------------------------------------------- 250 251handle_call({apply_at_exit, M, F, A, Pid}, _From, S) -> 252 Ref = erlang:monitor(process, Pid), 253 AAE = #apply_at_exit{ref = Ref, 254 pid = Pid, 255 module = M, 256 function = F, 257 arguments = A}, 258 put({?MODULE, Ref}, AAE), 259 Reply = Ref, 260 {reply, Reply, S}; 261 262handle_call(stop, {Parent, _} = _From, #state{parent_pid = Parent} = S) -> 263 {stop, normal, ok, S}; 264 265handle_call(Req, From, S) -> 266 warning_msg("received unexpected request from ~p: " 267 "~n~w",[From, Req]), 268 {reply, {error, {bad_request, Req}}, S}. 269 270 271%%---------------------------------------------------------------------- 272%% Func: handle_cast/2 273%% Returns: {noreply, State} | 274%% {noreply, State, Timeout} | 275%% {stop, Reason, State} (terminate/2 is called) 276%%---------------------------------------------------------------------- 277handle_cast(Msg, S) -> 278 warning_msg("received unexpected message: " 279 "~n~w", [Msg]), 280 {noreply, S}. 281 282%%---------------------------------------------------------------------- 283%% Func: handle_info/2 284%% Returns: {noreply, State} | 285%% {noreply, State, Timeout} | 286%% {stop, Reason, State} (terminate/2 is called) 287%%---------------------------------------------------------------------- 288 289handle_info({cancel_apply_at_exit, Ref}, S) -> 290 case erase({?MODULE, Ref}) of 291 undefined -> 292 %% Reply = {error, {already_cancelled, {apply_at_exit, Ref}}}, 293 {noreply, S}; 294 _AAE -> 295 erlang:demonitor(Ref), 296 {noreply, S} 297 end; 298 299handle_info({apply_after, Method, M, F, A}, S) -> 300 handle_apply(Method, M, F, A, apply_after), 301 {noreply, S}; 302 303%% Handle the old format also... 304handle_info({apply_after, M, F, A}, S) -> 305 handle_apply(M, F, A, apply_after), 306 {noreply, S}; 307 308handle_info({'DOWN', Ref, process, _Pid, Reason}, S) -> 309 case erase({?MODULE, Ref}) of 310 undefined -> 311 {noreply, S}; 312 AAE -> 313 M = AAE#apply_at_exit.module, 314 F = AAE#apply_at_exit.function, 315 A = AAE#apply_at_exit.arguments, 316 handle_apply(M, F, [Reason | A], apply_at_exit), 317 {noreply, S} 318 end; 319 320handle_info({'EXIT', Pid, Reason}, S) when Pid == S#state.parent_pid -> 321 %% [megaco_messenger:disconnect(CH, {stopped, Reason}) 322 %% || CH <- megaco:lookup_system_info(connections)], 323 {stop, Reason, S}; 324 325handle_info(Info, S) -> 326 warning_msg("received unknown info: " 327 "~n~w", [Info]), 328 {noreply, S}. 329 330 331%%---------------------------------------------------------------------- 332%% Func: terminate/2 333%% Purpose: Shutdown the server 334%% Returns: any (ignored by gen_server) 335%%---------------------------------------------------------------------- 336terminate(_Reason, _State) -> 337 ok. 338 339%%---------------------------------------------------------------------- 340%% Func: code_change/3 341%% Purpose: Convert process state when code is changed 342%% Returns: {ok, NewState} 343%%---------------------------------------------------------------------- 344code_change(_Vsn, S, _Extra) -> 345 {ok, S}. 346 347 348%%%---------------------------------------------------------------------- 349%%% Internal functions 350%%%---------------------------------------------------------------------- 351 352handle_apply(M, F, A, _ErrorTag) -> 353 spawn(M, F, A). 354 355handle_apply(spawn_method, M, F, A, _ErrorTag) -> 356 spawn(M, F, A); 357handle_apply(_Method, M, F, A, _ErrorTag) -> 358 (catch apply(M, F, A)). 359 360 361warning_msg(F, A) -> 362 ?megaco_warning("Monitor server: " ++ F, A). 363 364 365% d(F) -> 366% d(F,[]). 367 368% d(F,A) -> 369% %% d(true,F,A). 370% d(get(dbg),F,A). 371 372% d(true,F,A) -> 373% io:format("*** [~s] ~p:~p ***" 374% "~n " ++ F ++ "~n", 375% [format_timestamp(now()), self(),?MODULE|A]); 376% d(_, _, _) -> 377% ok. 378 379% format_timestamp(Now) -> 380% {N1, N2, N3} = Now, 381% {Date, Time} = calendar:now_to_datetime(Now), 382% {YYYY,MM,DD} = Date, 383% {Hour,Min,Sec} = Time, 384% FormatDate = 385% io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", 386% [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), 387% lists:flatten(FormatDate). 388 389 390