snmp_note_store.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 420 行

ERL
420
字号
%% ``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 via the world wide web 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.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %%     $Id$%%-module(snmp_note_store).-include_lib("snmp/src/app/snmp_internal.hrl").-include("snmp_debug.hrl").-include("snmp_verbosity.hrl").%% External exports-export([start_link/3, get_note/2, set_note/4, info/1, verbosity/2]).%% Internal exports-export([init/1, 	 handle_call/3, 	 handle_cast/2, 	 handle_info/2, 	 terminate/2, 	 code_change/3]).-export([timer/3]).-define(timeout, 30000).  % Perform gc twice in a minute.-ifndef(default_verbosity).-define(default_verbosity,silence).-endif.-ifdef(snmp_debug).-define(GS_START_LINK(Args),	gen_server:start_link(?MODULE, Args, [{debug,[trace]}])).-else.-define(GS_START_LINK(Args),	gen_server:start_link(?MODULE, Args, [])).-endif.-record(state, {mod, notes, timer, timeout, active = false}).%%%-----------------------------------------------------------------%%% Implements a database for notes with a lifetime. Once in a%%% while, the database will be gc:ed, to get rid of old notes.%%% This database will not contain much data.%%% Options is a list of Option, where Option is%%%   {verbosity, silence|log|debug|trace} % undocumented feature%%%-----------------------------------------------------------------start_link(Prio, Mod, Opts) ->    ?d("start_link -> entry with"	"~n   Prio: ~p"	"~n   Mod:  ~p"	"~n   Opts: ~p", [Prio, Mod, Opts]),    ?GS_START_LINK([Prio, Mod, Opts]).%%-----------------------------------------------------------------%% Interface functions.%%-----------------------------------------------------------------get_note(Pid, Key) ->    call(Pid, {get_note, Key}).%% Lifetime is in 1/10 sec.set_note(Pid, Lifetime, Key, Value) ->    call(Pid, {set_note, Lifetime, Key, Value}).info(Pid) ->    call(Pid, info).verbosity(Pid, Verbosity) ->     cast(Pid, {verbosity, Verbosity}).init([Prio, Mod, Opts]) ->    ?d("init -> entry with"	"~n   Prio: ~p"	"~n   Mod:  ~p"	"~n   Opts: ~p", [Prio, Mod, Opts]),    case (catch do_init(Prio, Mod, Opts)) of	{ok, State} ->	    {ok, State};	E ->	    error_msg("failed starting note-store: ~n~p", [E]),	    {stop, E}    end.do_init(Prio, Mod, Opts) ->    process_flag(trap_exit, true),    process_flag(priority, Prio),    put(sname, get_sname(Opts)),    put(verbosity, get_verbosity(Opts)),    put(snmp_component, get_component(Mod)),    ?vlog("starting",[]),    Notes   = ets:new(snmp_note_store, [set, protected]),     Timeout = get_timeout(Opts),    State   = #state{mod     = Mod, 		     notes   = Notes, 		     timeout = Timeout, 		     timer   = start_timer(Timeout)},    ?vdebug("started",[]),    {ok, State}.%%-----------------------------------------------------------------%% A note is any internal information that has to be%% stored for some time (the Lifetime).%% A note is stored in ets as {Key, {BestBefore, Value}},%% where BestBefore is currentTime + Lifetime. %% A GC-op can destroy any notes with CurTime > BestBore.%% Lifetime is in centiseconds or infinity, in which case%% the note is eternal.%%-----------------------------------------------------------------handle_call({set_note, Lifetime, Key, Value}, _From, 	    #state{mod = Mod, notes = Notes} = State)   when integer(Lifetime) ->    ?vlog("set note <~p,~p> with life time ~p",[Key,Value,Lifetime]),    RealUpTime = snmp_misc:now(cs) - Mod:system_start_time(),    ?vtrace("handle_call(set_note) -> RealUpTime: ~p",[RealUpTime]),    BestBefore = RealUpTime + Lifetime,    ?vtrace("handle_call(set_note) -> BestBefore: ~p",[BestBefore]),    Val = ets:insert(Notes, {Key, {BestBefore, Value}}),    NState = activate_timer(State),    {reply, Val, NState};handle_call({set_note, infinity, Key, Value}, _From, State) ->    ?vlog("set note <~p,~p>",[Key,Value]),    Val = ets:insert(State#state.notes, {Key, {infinity, Value}}),    ?vdebug("set note; old value: ~p",[Val]),    {reply, Val, State};handle_call({get_note, Key}, _From, 	    #state{mod = Mod, notes = Notes} = State) ->    ?vlog("get note ~p",[Key]),    Val = handle_get_note(Notes, Mod, Key),    ?vdebug("get note: ~p",[Val]),    {reply, Val, State};handle_call(info, _From, #state{timer = Pid, notes = Notes} = State) ->    ?vlog("info",[]),    Info = get_info(Pid, Notes),    {reply, Info, State};handle_call(stop, _From, State) ->    ?vlog("stop",[]),    {stop, normal, ok, State};handle_call(Req, From, State) ->    warning_msg("received unexpected request from ~p: ~n~p",[From, Req]),    {reply, {error, {unknown_request, Req}}, State}.handle_cast({verbosity,Verbosity}, State) ->    ?vlog("verbosity: ~p -> ~p",[get(verbosity),Verbosity]),    put(verbosity,snmp_verbosity:validate(Verbosity)),    {noreply, State};    handle_cast(Msg, State) ->    warning_msg("received unexpected message: ~n~p",[Msg]),    {noreply, State}.    %%-----------------------------------------------------------------%% If there are no possible garbage left, we don't%% have to wait for timeout, and perform another%% gc, because we won't do anything. So%% we switch the timeout off in that case.%% It will be switched on as soon as we get some%% other message.%%-----------------------------------------------------------------handle_info(timeout, State) ->    ?vdebug("timeout",[]),    case gc(State) of	nothing_left ->	    NState = deactivate_timer(State),	    {noreply, NState};	work_to_do ->	    NState = activate_timer(State),	    {noreply, NState}    end;handle_info({'EXIT', Pid, Reason}, 	    #state{timer = Pid, timeout = Timeout} = State) ->    ?vinfo("exit message from the timer process ~p for reason ~p",	   [Pid, Reason]),    set_state(State#state{timer = start_timer(Timeout)});handle_info({'EXIT',Pid,Reason}, State) ->    ?vlog("exit message from ~p for reason ~p",[Pid,Reason]),    {noreply, State};handle_info(Info, State) ->    warning_msg("received unexpected info: ~n~p",[Info]),    {noreply, State}.set_state(S) ->    case gc(S) of	nothing_left ->	    NState = deactivate_timer(S),	    {noreply, NState};	work_to_do ->	    NState = activate_timer(S),	    {noreply, NState}    end.terminate(Reason, _State) ->    ?vdebug("terminate: ~p",[Reason]),    ok.%%----------------------------------------------------------%% Code change%%----------------------------------------------------------% downgradecode_change({down, _Vsn}, State, _Extra) ->    NState = activate_timer(deactivate_timer(State)),    {ok, NState};% upgradecode_change(_Vsn, State, _Extra) ->    process_flag(trap_exit, true),    NState = restart_timer(State),    {ok, NState}.%%----------------------------------------------------------%% Timer%%----------------------------------------------------------activate_timer(#state{timer = Pid, active = false} = State) ->    Pid ! activate,    receive	activated -> ok    end,    State#state{active = true};activate_timer(State) ->    State.deactivate_timer(#state{timer = Pid, active = true} = State) ->    Pid ! deactivate,    receive	deactivated -> ok    end,    State#state{timeout = false};deactivate_timer(State) ->    State.start_timer(Timeout) ->    spawn_link(?MODULE, timer, [self(), passive, Timeout]).%% Kill, restart and activate timer.restart_timer(#state{timer = Pid, timeout = Timeout} = State) ->    ?d("restart_timer -> kill current timer process ~p",[Pid]),    exit(Pid, kill),    ?d("restart_timer -> await acknowledgement",[]),    receive	{'EXIT', Pid, _Reason} ->	    ok    end,    ?d("restart_timer -> start a new timer process",[]),    activate_timer(State#state{timer = start_timer(Timeout), active = false}).timer(Pid, passive, Timeout) ->    receive	deactivate ->	    ?d("timer(passive) -> deactivate request, just send ack",[]),	    Pid ! deactivated,	    ?MODULE:timer(Pid, passive, Timeout);	activate ->	    ?d("timer(deactive) -> activate request, send ack",[]),	    Pid ! activated,	    ?d("timer(deactive) -> activate",[]),	    ?MODULE:timer(Pid, active, Timeout)		% code replacement    after	Timeout ->	    ?d("timer(deactive) -> timeout",[]),	    ?MODULE:timer(Pid, passive, Timeout)    end;timer(Pid, active, Timeout) ->    receive	activate ->	    ?d("timer(active) -> activate request, just send ack",[]),	    Pid ! activated,	    ?MODULE:timer(Pid, active, Timeout);	deactivate ->	    ?d("timer(active) -> deactivate request, send ack",[]),	    Pid ! deactivated,	    ?d("timer(active) -> deactivate",[]),	    ?MODULE:timer(Pid, passive, Timeout)    after	Timeout ->	    ?d("timer(active) -> timeout",[]),	    Pid ! timeout,	    ?MODULE:timer(Pid, active, Timeout)    end.    handle_get_note(Notes, Mod, Key) ->    case ets:lookup(Notes, Key) of	[{Key, {infinity, Val}}] ->	    Val;	[{Key, {BestBefore, Val}}] ->	    ?vtrace("get note -> BestBefore: ~w", [BestBefore]),	    StartTime = Mod:system_start_time(), 	    ?vtrace("get note -> StartTime: ~w", [StartTime]),	    case (snmp_misc:now(cs) - StartTime) of		Now when BestBefore >= Now ->		    ?vtrace("get note -> Now: ~w", [Now]),		    Val;		_ ->		    ets:delete(Notes, Key),		    undefined	    end;	[] -> undefined    end.%%-----------------------------------------------------------------%% Clean up all old notes in the database.%%-----------------------------------------------------------------gc(#state{mod = Mod, notes = Notes}) ->    RealUpTime = snmp_misc:now(cs) - Mod:system_start_time(),    gc(nothing_left, ets:tab2list(Notes), Notes, RealUpTime).gc(Flag, [{_Key, {infinity, _}} | T], Tab, Now) -> gc(Flag, T, Tab, Now);gc(Flag, [{Key, {BestBefore, _}} | T], Tab, Now)   when integer(BestBefore), BestBefore < Now ->    ets:delete(Tab, Key),    gc(Flag, T, Tab, Now);gc(_Flag, [_ | T], Tab, Now) -> gc(work_to_do, T, Tab, Now);gc(Flag, [], _Tab, _Now) -> Flag.        %%-----------------------------------------------------------------get_info(Tmr, Notes) ->    ProcSize = proc_mem(self()),    TMRSz    = proc_mem(Tmr),    NotesSz  = tab_size(Notes),    [{process_memory, [{notes, ProcSize}, {timer, TMRSz}]},     {db_memory, [{notes, NotesSz}]}].proc_mem(P) when pid(P) ->    case (catch erlang:process_info(P, memory)) of	{memory, Sz} ->	    Sz;	_ ->	    undefined    end;proc_mem(_) ->    undefined.tab_size(T) ->    case (catch ets:info(T, memory)) of	Sz when integer(Sz) ->	    Sz;	_ ->	    undefined    end.%%-----------------------------------------------------------------call(Pid, Req) ->    call(Pid, Req, infinity).call(Pid, Req, Timeout) ->    gen_server:call(Pid, Req, Timeout).cast(Pid, Msg) ->    gen_server:cast(Pid, Msg).%%-----------------------------------------------------------------%% info_msg(F, A) ->%%     ?snmp_info(get(snmp_component), "Note store server " ++ F, A).warning_msg(F, A) ->    ?snmp_warning(get(snmp_component), "Note store server " ++ F, A).error_msg(F, A) ->    ?snmp_error(get(snmp_component), "Note store server " ++ F, A).%%-----------------------------------------------------------------get_verbosity(Opts) ->    snmp_misc:get_option(verbosity, Opts, ?default_verbosity).get_sname(Opts) ->    snmp_misc:get_option(sname, Opts, ns).get_timeout(Opts) ->    snmp_misc:get_option(timeout, Opts, ?timeout).get_component(snmpm) ->     "manager";get_component(snmpa) ->    "agent";get_component(_) ->    "".

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?