SystemProbeCommon.cs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. using System.Diagnostics;
  2. using System.Net;
  3. using System.Net.NetworkInformation;
  4. using System.Net.Sockets;
  5. namespace RackPeek.Domain.Discovery;
  6. /// <summary>Host reads that the BCL already does the same way on every platform.</summary>
  7. internal static class SystemProbeCommon {
  8. public static string Hostname() {
  9. try {
  10. return Dns.GetHostName();
  11. }
  12. catch {
  13. return Environment.MachineName;
  14. }
  15. }
  16. public static int Cores() => Environment.ProcessorCount;
  17. public static long FallbackMemoryBytes() => GC.GetGCMemoryInfo().TotalAvailableMemoryBytes;
  18. public static IReadOnlyList<NicFact> Nics() {
  19. try {
  20. return NetworkInterface.GetAllNetworkInterfaces()
  21. .Select(ToFact)
  22. .Where(n => n.Ipv4 != null)
  23. .ToList();
  24. }
  25. catch {
  26. return [];
  27. }
  28. }
  29. private static NicFact ToFact(NetworkInterface nic) {
  30. IPInterfaceProperties properties = nic.GetIPProperties();
  31. var ipv4 = properties.UnicastAddresses
  32. .FirstOrDefault(a => a.Address.AddressFamily == AddressFamily.InterNetwork)
  33. ?.Address.ToString();
  34. var hasGateway = properties.GatewayAddresses
  35. .Any(g => g.Address.AddressFamily == AddressFamily.InterNetwork
  36. && !g.Address.Equals(IPAddress.Any));
  37. return new NicFact(
  38. nic.Name,
  39. nic.OperationalStatus == OperationalStatus.Up,
  40. nic.NetworkInterfaceType == NetworkInterfaceType.Loopback,
  41. hasGateway,
  42. ipv4,
  43. FormatMac(nic));
  44. }
  45. /// <summary>Lowercase colon-separated, matching what ARP tables report — or null.</summary>
  46. private static string? FormatMac(NetworkInterface nic) {
  47. try {
  48. var bytes = nic.GetPhysicalAddress().GetAddressBytes();
  49. return bytes.Length == 6
  50. ? string.Join(':', bytes.Select(b => b.ToString("x2")))
  51. : null;
  52. }
  53. catch {
  54. return null;
  55. }
  56. }
  57. /// <summary>Reads a file, returning null for anything unreadable rather than throwing.</summary>
  58. public static async Task<string?> TryReadFileAsync(string path, CancellationToken cancellationToken) {
  59. try {
  60. return File.Exists(path)
  61. ? await File.ReadAllTextAsync(path, cancellationToken)
  62. : null;
  63. }
  64. catch {
  65. return null;
  66. }
  67. }
  68. /// <summary>
  69. /// Runs a command and returns stdout, or null if it cannot be run, fails, or
  70. /// takes longer than a few seconds — a hung probe must not hang a timer-driven
  71. /// <c>rpk discover</c> forever. Stderr is drained concurrently so a chatty child
  72. /// cannot deadlock on a full pipe.
  73. /// </summary>
  74. public static async Task<string?> TryRunAsync(
  75. string fileName,
  76. string arguments,
  77. CancellationToken cancellationToken) {
  78. try {
  79. using var timeout = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
  80. timeout.CancelAfter(TimeSpan.FromSeconds(10));
  81. using var process = new Process {
  82. StartInfo = new ProcessStartInfo {
  83. FileName = fileName,
  84. Arguments = arguments,
  85. RedirectStandardOutput = true,
  86. RedirectStandardError = true,
  87. UseShellExecute = false,
  88. CreateNoWindow = true
  89. }
  90. };
  91. if (!process.Start())
  92. return null;
  93. try {
  94. Task<string> stdout = process.StandardOutput.ReadToEndAsync(timeout.Token);
  95. Task<string> stderr = process.StandardError.ReadToEndAsync(timeout.Token);
  96. await process.WaitForExitAsync(timeout.Token);
  97. await stderr;
  98. return process.ExitCode == 0 ? (await stdout).Trim() : null;
  99. }
  100. catch (OperationCanceledException) {
  101. try {
  102. process.Kill(true);
  103. }
  104. catch {
  105. // It may have exited in the meantime; nothing left to do.
  106. }
  107. return null;
  108. }
  109. }
  110. catch {
  111. return null;
  112. }
  113. }
  114. }