149 lines
4.6 KiB
C#
149 lines
4.6 KiB
C#
using System.ComponentModel;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace MoonlightServers.Daemon.Helpers;
|
|
|
|
public partial class SystemMetrics
|
|
{
|
|
public record DiskInfo(
|
|
string MountPoint,
|
|
string Device,
|
|
string FileSystem,
|
|
long TotalBytes,
|
|
long UsedBytes,
|
|
long FreeBytes,
|
|
double UsedPercent,
|
|
long InodesTotal,
|
|
long InodesUsed,
|
|
long InodesFree,
|
|
double InodesUsedPercent
|
|
);
|
|
|
|
private static readonly HashSet<string> IgnoredFileSystems = new(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
"overlay", "aufs", // Container image layers
|
|
"tmpfs", "devtmpfs", "ramfs", // RAM-backed virtual filesystems
|
|
"sysfs", "proc", "devpts", // Kernel virtual filesystems
|
|
"cgroup", "cgroup2",
|
|
"pstore", "securityfs", "debugfs", "tracefs",
|
|
"mqueue", "hugetlbfs", "fusectl", "configfs",
|
|
"binfmt_misc", "nsfs", "rpc_pipefs",
|
|
"squashfs", // Snap package loop mounts
|
|
};
|
|
|
|
private static readonly string[] IgnoredMountPrefixes =
|
|
{
|
|
"/sys",
|
|
"/proc",
|
|
"/dev",
|
|
"/run/docker",
|
|
"/var/lib/docker",
|
|
"/var/lib/containers",
|
|
"/boot", "/boot/efi"
|
|
};
|
|
|
|
private static async Task<IReadOnlyList<DiskInfo>> ReadDisksAsync()
|
|
{
|
|
var lines = await File.ReadAllLinesAsync("/proc/mounts");
|
|
var results = new List<DiskInfo>();
|
|
|
|
foreach (var line in lines)
|
|
{
|
|
var parts = line.Split(' ');
|
|
if (parts.Length < 4) continue;
|
|
|
|
var device = parts[0];
|
|
var mountPoint = UnescapeOctal(parts[1]);
|
|
var fsType = parts[2];
|
|
|
|
if (IgnoredFileSystems.Contains(fsType)) continue;
|
|
if (IgnoredMountPrefixes.Any(p => mountPoint.StartsWith(p, StringComparison.Ordinal))) continue;
|
|
if (device == "none" || device.StartsWith("//", StringComparison.Ordinal)) continue;
|
|
|
|
var disk = ReadSingleDisk(device, mountPoint, fsType);
|
|
|
|
if (disk is not null)
|
|
results.Add(disk);
|
|
}
|
|
|
|
return results
|
|
.GroupBy(d => d.Device)
|
|
.Select(g => g.OrderBy(d => d.MountPoint.Length).First())
|
|
.OrderBy(d => d.MountPoint)
|
|
.ToList();
|
|
}
|
|
|
|
private static DiskInfo? ReadSingleDisk(string device, string mountPoint, string fsType)
|
|
{
|
|
if (NativeMethods.StatVfs(mountPoint, out var st) != 0)
|
|
{
|
|
var errno = Marshal.GetLastPInvokeError();
|
|
Console.WriteLine($"statvfs({mountPoint}) failed: errno {errno} ({new Win32Exception(errno).Message})");
|
|
return null;
|
|
}
|
|
|
|
var blockSize = (long)st.frsize;
|
|
var totalBytes = (long)st.blocks * blockSize;
|
|
var freeBytes = (long)st.bavail * blockSize;
|
|
|
|
if (totalBytes <= 0) return null;
|
|
|
|
var usedBytes = totalBytes - ((long)st.bfree * blockSize);
|
|
var usedPct = Math.Round((double)usedBytes / totalBytes * 100.0, 2);
|
|
|
|
long inodesTotal, inodesUsed, inodesFree;
|
|
double inodePct;
|
|
|
|
if (st.files == 0)
|
|
{
|
|
// Filesystem doesn't expose inode counts (FAT, exFAT, NTFS via ntfs-3g, etc.)
|
|
inodesTotal = inodesUsed = inodesFree = -1;
|
|
inodePct = -1.0;
|
|
}
|
|
else
|
|
{
|
|
inodesTotal = (long)st.files;
|
|
inodesFree = (long)st.ffree;
|
|
inodesUsed = inodesTotal - inodesFree;
|
|
inodePct = Math.Round((double)inodesUsed / inodesTotal * 100.0, 2);
|
|
}
|
|
|
|
return new DiskInfo(
|
|
MountPoint: mountPoint,
|
|
Device: device,
|
|
FileSystem: fsType,
|
|
TotalBytes: totalBytes,
|
|
UsedBytes: usedBytes,
|
|
FreeBytes: freeBytes,
|
|
UsedPercent: usedPct,
|
|
InodesTotal: inodesTotal,
|
|
InodesUsed: inodesUsed,
|
|
InodesFree: inodesFree,
|
|
InodesUsedPercent: inodePct
|
|
);
|
|
}
|
|
|
|
private static string UnescapeOctal(string s)
|
|
{
|
|
if (!s.Contains('\\')) return s;
|
|
|
|
var sb = new System.Text.StringBuilder(s.Length);
|
|
for (var i = 0; i < s.Length; i++)
|
|
{
|
|
if (s[i] == '\\' && i + 3 < s.Length
|
|
&& s[i + 1] is >= '0' and <= '7'
|
|
&& s[i + 2] is >= '0' and <= '7'
|
|
&& s[i + 3] is >= '0' and <= '7')
|
|
{
|
|
sb.Append((char)((s[i + 1] - '0') * 64 + (s[i + 2] - '0') * 8 + (s[i + 3] - '0')));
|
|
i += 3;
|
|
}
|
|
else
|
|
{
|
|
sb.Append(s[i]);
|
|
}
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
} |