disksup.erl

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

ERL
365
字号
%% ``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(disksup).-behaviour(gen_server).%% API-export([start_link/0]).-export([get_disk_data/0,	 get_check_interval/0, set_check_interval/1,	 get_almost_full_threshold/0, set_almost_full_threshold/1]).-export([dummy_reply/1, param_type/2, param_default/1]).%% gen_server callbacks-export([init/1, handle_call/3, handle_cast/2, handle_info/2,	 terminate/2, code_change/3]).%% Other exports-export([format_status/2]).-record(state, {threshold, timeout, os, diskdata = [],port}).%%----------------------------------------------------------------------%% API%%----------------------------------------------------------------------start_link() ->    gen_server:start_link({local, disksup}, disksup, [], []).get_disk_data() ->    os_mon:call(disksup, get_disk_data).get_check_interval() ->    os_mon:call(disksup, get_check_interval).set_check_interval(Minutes) ->    case param_type(disk_space_check_interval, Minutes) of	true ->	    os_mon:call(disksup, {set_check_interval, Minutes});	false ->	    erlang:error(badarg)    end.get_almost_full_threshold() ->    os_mon:call(disksup, get_almost_full_threshold).set_almost_full_threshold(Float) ->    case param_type(disk_almost_full_threshold, Float) of	true ->	    os_mon:call(disksup, {set_almost_full_threshold, Float});	false ->	    erlang:error(badarg)    end.dummy_reply(get_disk_data) ->    [{"none", 0, 0}];dummy_reply(get_check_interval) ->    minutes_to_ms(os_mon:get_env(disksup, disk_space_check_interval));dummy_reply({set_check_interval, _}) ->    ok;dummy_reply(get_almost_full_threshold) ->    round(os_mon:get_env(disksup, disk_almost_full_threshold) * 100);dummy_reply({set_almost_full_threshold, _}) ->    ok.param_type(disk_space_check_interval, Val) when is_integer(Val),						Val>=1 -> true;param_type(disk_almost_full_threshold, Val) when is_number(Val),						 0=<Val,						 Val=<1 -> true;param_type(_Param, _Val) -> false.param_default(disk_space_check_interval) -> 30;param_default(disk_almost_full_threshold) -> 0.80.%%----------------------------------------------------------------------%% gen_server callbacks%%----------------------------------------------------------------------init([]) ->      process_flag(trap_exit, true),    process_flag(priority, low),    OS = get_os(),    Port = case OS of		{unix, Flavor} when Flavor==sunos4;				    Flavor==solaris;				    Flavor==freebsd;				    Flavor==darwin;				    Flavor==linux;				    Flavor==openbsd;				    Flavor==irix64;				    Flavor==irix ->		   start_portprogram();	       {win32, _OSname} ->		   not_used;	       _ ->		   exit({unsupported_os, OS})	   end,    %% Read the values of some configuration parameters    Threshold = os_mon:get_env(disksup, disk_almost_full_threshold),    Timeout = os_mon:get_env(disksup, disk_space_check_interval),    %% Initiation first disk check    self() ! timeout,    {ok, #state{port=Port, os=OS,		threshold=round(Threshold*100),		timeout=minutes_to_ms(Timeout)}}.handle_call(get_disk_data, _From, State) ->    {reply, State#state.diskdata, State};handle_call(get_check_interval, _From, State) ->    {reply, State#state.timeout, State};handle_call({set_check_interval, Minutes}, _From, State) ->    Timeout = minutes_to_ms(Minutes),    {reply, ok, State#state{timeout=Timeout}};handle_call(get_almost_full_threshold, _From, State) ->    {reply, State#state.threshold, State};handle_call({set_almost_full_threshold, Float}, _From, State) ->    Threshold = round(Float * 100),    {reply, ok, State#state{threshold=Threshold}};handle_call({set_threshold, Threshold}, _From, State) -> % test only    {reply, ok, State#state{threshold=Threshold}}.handle_cast(_Msg, State) ->    {noreply, State}.handle_info(timeout, State) ->    NewDiskData = check_disk_space(State#state.os, State#state.port,				   State#state.threshold),    timer:send_after(State#state.timeout, timeout),    {noreply, State#state{diskdata = NewDiskData}};handle_info({'EXIT', _Port, Reason}, State) ->    {stop, {port_died, Reason}, State#state{port=not_used}};handle_info(_Info, State) ->    {noreply, State}.terminate(_Reason, State) ->    clear_alarms(),    case State#state.port of	not_used ->	    ok;	Port ->	    port_close(Port)    end,    ok.%% os_mon-2.0.1%% For live downgrade to/upgrade from os_mon-1.8[.1]code_change(Vsn, PrevState, "1.8") ->    case Vsn of	%% Downgrade from this version	{down, _Vsn} ->	    State = case PrevState#state.port of			not_used -> PrevState#state{port=noport};			_ -> PrevState		    end,	    {ok, State};	%% Upgrade to this version	_Vsn ->	    State = case PrevState#state.port of			noport -> PrevState#state{port=not_used};			_ -> PrevState		    end,	    {ok, State}    end;code_change(_OldVsn, State, _Extra) ->    {ok, State}.%%----------------------------------------------------------------------%% Other exports%%----------------------------------------------------------------------format_status(_Opt, [_PDict, #state{os = OS, threshold = Threshold,				    timeout = Timeout,				    diskdata = DiskData}]) ->    [{data, [{"OS", OS},	     {"Timeout", Timeout},	     {"Threshold", Threshold},	     {"DiskData", DiskData}]}].%%----------------------------------------------------------------------%% Internal functions%%----------------------------------------------------------------------get_os() ->    case os:type() of	{unix, sunos} ->	    case os:version() of		{5,_,_} -> {unix, solaris};		{4,_,_} -> {unix, sunos4};		V -> exit({unknown_os_version, V})	    end;        {unix, irix64} -> {unix, irix};	OS ->	    OS    end.%%--Port handling functions---------------------------------------------start_portprogram() ->     open_port({spawn, "sh -s disksup 2>&1"}, [stream]).my_cmd(Cmd0, Port) ->    %% Insert a new line after the command, in case the command    %% contains a comment character    Cmd = io_lib:format("(~s\n) </dev/null; echo  \"\^M\"\n", [Cmd0]),    Port ! {self(), {command, [Cmd, 10]}},    get_reply(Port, []).get_reply(Port, O) ->    receive         {Port, {data, N}} ->             case newline(N, O) of                {ok, Str} -> Str;                {more, Acc} -> get_reply(Port, Acc)            end;        {'EXIT', Port, Reason} ->	    exit({port_died, Reason})    end.newline([13|_], B) -> {ok, lists:reverse(B)};newline([H|T], B) -> newline(T, [H|B]);newline([], B) -> {more, B}.%%--Check disk space----------------------------------------------------check_disk_space({win32,_}, not_used, Threshold) ->    Result = os_mon_sysinfo:get_disk_info(),    check_disks_win32(Result, Threshold);check_disk_space({unix, solaris}, Port, Threshold) ->    Result = my_cmd("/usr/bin/df -lk", Port),    check_disks_solaris(skip_to_eol(Result), Threshold);check_disk_space({unix, irix}, Port, Threshold) ->    Result = my_cmd("/usr/sbin/df -lk",Port),    check_disks_irix(skip_to_eol(Result), Threshold);check_disk_space({unix, linux}, Port, Threshold) ->    Result = my_cmd("/bin/df -lk", Port),    check_disks_solaris(skip_to_eol(Result), Threshold);check_disk_space({unix, freebsd}, Port, Threshold) ->    Result = my_cmd("/bin/df -k -t ufs", Port),    check_disks_solaris(skip_to_eol(Result), Threshold);check_disk_space({unix, openbsd}, Port, Threshold) ->    Result = my_cmd("/bin/df -k -t ffs", Port),    check_disks_solaris(skip_to_eol(Result), Threshold);check_disk_space({unix, sunos4}, Port, Threshold) ->    Result = my_cmd("df", Port),    check_disks_solaris(skip_to_eol(Result), Threshold);check_disk_space({unix, darwin}, Port, Threshold) ->    Result = my_cmd("/bin/df -k -t ufs,hfs", Port),    check_disks_solaris(skip_to_eol(Result), Threshold).% This code works for Linux and FreeBSD as wellcheck_disks_solaris("", _Threshold) ->    [];check_disks_solaris("\n", _Threshold) ->    [];check_disks_solaris(Str, Threshold) ->    case io_lib:fread("~s~d~d~d~d%~s", Str) of	{ok, [_FS, KB, _Used, _Avail, Cap, MntOn], RestStr} ->	    if		Cap >= Threshold ->		    set_alarm({disk_almost_full, MntOn}, []);		true ->		    clear_alarm({disk_almost_full, MntOn})	    end,	    [{MntOn, KB, Cap} |	     check_disks_solaris(RestStr, Threshold)];	_Other ->	    check_disks_solaris(skip_to_eol(Str),Threshold)    end.%% Irix: like Linux with an extra FS type column and no '%'.check_disks_irix("", _Threshold) -> [];check_disks_irix("\n", _Threshold) -> [];check_disks_irix(Str, Threshold) ->    case io_lib:fread("~s~s~d~d~d~d~s", Str) of	{ok, [_FS, _FSType, KB, _Used, _Avail, Cap, MntOn], RestStr} ->	    if Cap >= Threshold -> set_alarm({disk_almost_full, MntOn}, []);	       true             -> clear_alarm({disk_almost_full, MntOn}) end,	    [{MntOn, KB, Cap} | check_disks_irix(RestStr, Threshold)];	_Other ->	    check_disks_irix(skip_to_eol(Str),Threshold)    end.check_disks_win32([], _Threshold) ->    [];check_disks_win32([H|T], Threshold) ->    case io_lib:fread("~s~s~d~d~d", H) of	{ok, [Drive,"DRIVE_FIXED",BAvail,BTot,_TotFree], _RestStr} ->	    Cap = trunc((BTot-BAvail) / BTot * 100),	    if		 Cap >= Threshold ->		    set_alarm({disk_almost_full, Drive}, []);		true ->		    clear_alarm({disk_almost_full, Drive})	    end,	    [{Drive, BTot, Cap} |	     check_disks_win32(T, Threshold)];	{ok,_,_RestStr} ->	    check_disks_win32(T,Threshold);	_Other ->	    []    end.%%--Alarm handling------------------------------------------------------set_alarm(AlarmId, AlarmDescr) ->    case get(AlarmId) of	set ->	    ok;	undefined ->	    alarm_handler:set_alarm({AlarmId, AlarmDescr}),	    put(AlarmId, set)    end.clear_alarm(AlarmId) ->    case get(AlarmId) of	set ->	    alarm_handler:clear_alarm(AlarmId),	    erase(AlarmId);	undefined ->	    ok    end.clear_alarms() ->    lists:foreach(fun({{disk_almost_full, _MntOn} = AlarmId, set}) ->			  alarm_handler:clear_alarm(AlarmId);		     (_Other) ->			  ignore		  end,		  get()).%%--Auxiliary-----------------------------------------------------------%% Type conversionminutes_to_ms(Minutes) ->    trunc(60000*Minutes).skip_to_eol([]) ->    [];skip_to_eol([$\n | T]) ->    T;skip_to_eol([_ | T]) ->    skip_to_eol(T).

⌨️ 快捷键说明

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