diff --git a/bin/malpd b/bin/malpd index 1914929..c4cb9e3 100755 --- a/bin/malpd +++ b/bin/malpd @@ -3,6 +3,7 @@ require "socket" require "fileutils" require "json" +require "shellwords" SOCKET_PATH = "/run/malpd/malpd.sock" @@ -63,8 +64,16 @@ def read_smart(name) result end +def read_inodes(mount) + out = `stat -f -c '%c %d' #{Shellwords.escape(mount)} 2>/dev/null`.strip + return nil if out.empty? + total, free = out.split.map(&:to_i) + return nil unless total && total > 0 + [total, total - free] +end + def read_filesystems - out = `findmnt -J -b -o SOURCE,TARGET,FSTYPE,SIZE,USED 2>/dev/null` + out = `findmnt -J -b -o SOURCE,TARGET,FSTYPE,SIZE,USED,OPTIONS 2>/dev/null` return [] if out.empty? data = JSON.parse(out) rescue nil return [] unless data @@ -86,14 +95,22 @@ def read_filesystems key = [source, target] next if seen[key] seen[key] = true - result << { - "type" => "filesystem", - "source" => source, - "mount" => target, - "fstype" => fstype, - "size" => size, - "used" => node["used"].to_i + + opts = node["options"].to_s.split(",") + entry = { + "type" => "filesystem", + "source" => source, + "mount" => target, + "fstype" => fstype, + "size" => size, + "used" => node["used"].to_i, + "readonly" => opts.include?("ro") } + if (ino = read_inodes(target)) + entry["inode_total"] = ino[0] + entry["inode_used"] = ino[1] + end + result << entry end result end diff --git a/cgi-bin/malp.rb b/cgi-bin/malp.rb index e6e2aad..deb31e2 100755 --- a/cgi-bin/malp.rb +++ b/cgi-bin/malp.rb @@ -210,21 +210,42 @@ if cgi.params.key?("content") used_b = entry["used"].to_i pct_f = size_b > 0 ? (used_b * 100.0 / size_b) : 0.0 pct = pct_f.round - cls = pct_f >= 90 ? "bad" : pct_f >= 75 ? "warn" : "ok" + space_cls = pct_f >= 90 ? "bad" : pct_f >= 75 ? "warn" : "ok" used_h = CGI.escapeHTML(human_capacity(used_b)) size_h = CGI.escapeHTML(human_capacity(size_b)) + fs_stats = [] + inode_cls = "ok" + if (itot = entry["inode_total"].to_i) > 0 + iused = entry["inode_used"].to_i + ipct_f = iused * 100.0 / itot + inode_cls = ipct_f >= 90 ? "bad" : ipct_f >= 75 ? "warn" : "ok" + fs_stats << [inode_cls, "Inodes #{ipct_f.round}%"] + end + fs_stats << ["warn", "READ-ONLY"] if entry["readonly"] + + severities = [space_cls, inode_cls] + card_cls = severities.include?("bad") ? "bad" : + severities.include?("warn") ? "warn" : "ok" + + stats_html = fs_stats.map { |c, txt| + %(#{CGI.escapeHTML(txt)}) + }.join + stats_block = fs_stats.empty? ? "" : + %(
#{stats_html}
) + html << <<~HTML -
+
#{mount} #{fstype} - #{pct}% + #{pct}%
#{source} - #{used_h} / #{size_h} + #{used_h} / #{size_h}
-
+
+ #{stats_block}
HTML end