aboutsummaryrefslogtreecommitdiffstats
path: root/lib/os_mon/src/disksup.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/os_mon/src/disksup.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/os_mon/src/disksup.erl')
-rw-r--r--lib/os_mon/src/disksup.erl369
1 files changed, 369 insertions, 0 deletions
diff --git a/lib/os_mon/src/disksup.erl b/lib/os_mon/src/disksup.erl
new file mode 100644
index 0000000000..3340f7ee72
--- /dev/null
+++ b/lib/os_mon/src/disksup.erl
@@ -0,0 +1,369 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-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%
+%%
+-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==dragonfly;
+ 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, dragonfly}, Port, Threshold) ->
+ Result = my_cmd("/bin/df -k -t ufs,hammer", 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 well
+check_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 div 1024, Cap} |
+ check_disks_win32(T, Threshold)]; % Return Total Capacity in Kbytes
+ {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 conversion
+minutes_to_ms(Minutes) ->
+ trunc(60000*Minutes).
+
+skip_to_eol([]) ->
+ [];
+skip_to_eol([$\n | T]) ->
+ T;
+skip_to_eol([_ | T]) ->
+ skip_to_eol(T).