LinuxSystemProbe.cs 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. namespace RackPeek.Domain.Discovery;
  2. public sealed class LinuxSystemProbe : ISystemProbe {
  3. private const string _blockDeviceRoot = "/sys/block";
  4. private const long _sectorBytes = 512;
  5. public bool IsSupported => OperatingSystem.IsLinux();
  6. public async Task<RawSystemSnapshot> ReadAsync(CancellationToken cancellationToken = default) {
  7. return new RawSystemSnapshot {
  8. Hostname = SystemProbeCommon.Hostname(),
  9. Cores = SystemProbeCommon.Cores(),
  10. Nics = SystemProbeCommon.Nics(),
  11. FallbackMemoryBytes = SystemProbeCommon.FallbackMemoryBytes(),
  12. BlockDevices = ReadBlockDevices(),
  13. OsReleaseFile = await SystemProbeCommon.TryReadFileAsync("/etc/os-release", cancellationToken),
  14. MemInfoFile = await SystemProbeCommon.TryReadFileAsync("/proc/meminfo", cancellationToken),
  15. MachineIdFile = await ReadMachineIdAsync(cancellationToken),
  16. CgroupFile = await SystemProbeCommon.TryReadFileAsync("/proc/1/cgroup", cancellationToken),
  17. DockerEnvPresent = File.Exists("/.dockerenv"),
  18. DmiVendor = await SystemProbeCommon.TryReadFileAsync("/sys/class/dmi/id/sys_vendor", cancellationToken),
  19. DmiProduct = await SystemProbeCommon.TryReadFileAsync("/sys/class/dmi/id/product_name", cancellationToken)
  20. };
  21. }
  22. private static async Task<string?> ReadMachineIdAsync(CancellationToken cancellationToken) =>
  23. await SystemProbeCommon.TryReadFileAsync("/etc/machine-id", cancellationToken)
  24. ?? await SystemProbeCommon.TryReadFileAsync("/var/lib/dbus/machine-id", cancellationToken);
  25. /// <summary>
  26. /// Whole disks as the kernel sees them, which is closer to what goes on an
  27. /// inventory card than the mounted filesystems would be.
  28. /// </summary>
  29. private static List<BlockDeviceFact> ReadBlockDevices() {
  30. try {
  31. if (!Directory.Exists(_blockDeviceRoot))
  32. return [];
  33. return Directory.EnumerateDirectories(_blockDeviceRoot)
  34. .Select(ReadBlockDevice)
  35. .OfType<BlockDeviceFact>()
  36. .OrderBy(d => d.Name, StringComparer.Ordinal)
  37. .ToList();
  38. }
  39. catch {
  40. return [];
  41. }
  42. }
  43. private static BlockDeviceFact? ReadBlockDevice(string path) {
  44. try {
  45. var name = Path.GetFileName(path);
  46. if (!long.TryParse(File.ReadAllText(Path.Combine(path, "size")).Trim(), out var sectors))
  47. return null;
  48. var rotationalPath = Path.Combine(path, "queue", "rotational");
  49. var rotational = File.Exists(rotationalPath)
  50. && File.ReadAllText(rotationalPath).Trim() == "1";
  51. return new BlockDeviceFact(name, sectors * _sectorBytes, rotational);
  52. }
  53. catch {
  54. return null;
  55. }
  56. }
  57. }