-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHardwareId.cs
More file actions
150 lines (136 loc) · 4.48 KB
/
HardwareId.cs
File metadata and controls
150 lines (136 loc) · 4.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#nullable enable
using System;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
namespace InstDotNet;
/// <summary>
/// Provides unique hardware identifiers for the system.
/// </summary>
public static class HardwareId
{
private static string? _cachedId;
/// <summary>
/// Gets a unique hardware identifier for this system.
/// Tries multiple methods in order of preference:
/// 1. Systemd machine-id (/etc/machine-id)
/// 2. DMI system UUID (/sys/class/dmi/id/product_uuid)
/// 3. First MAC address
/// 4. Hostname (fallback)
/// </summary>
public static string GetUniqueId()
{
if (_cachedId != null)
{
return _cachedId;
}
// Try systemd machine-id (most reliable on modern Linux)
try
{
var machineIdPath = "/etc/machine-id";
if (File.Exists(machineIdPath))
{
var machineId = File.ReadAllText(machineIdPath).Trim();
if (!string.IsNullOrEmpty(machineId))
{
_cachedId = $"machine-{machineId}";
return _cachedId;
}
}
}
catch (Exception)
{
// Ignore errors
}
// Try DMI system UUID
try
{
var dmiUuidPath = "/sys/class/dmi/id/product_uuid";
if (File.Exists(dmiUuidPath))
{
var dmiUuid = File.ReadAllText(dmiUuidPath).Trim();
if (!string.IsNullOrEmpty(dmiUuid))
{
_cachedId = $"dmi-{dmiUuid}";
return _cachedId;
}
}
}
catch (Exception)
{
// Ignore errors
}
// Try first MAC address
try
{
var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces()
.Where(nic => nic.OperationalStatus == OperationalStatus.Up &&
nic.NetworkInterfaceType != NetworkInterfaceType.Loopback)
.OrderBy(nic => nic.NetworkInterfaceType)
.ToList();
foreach (var nic in networkInterfaces)
{
var macAddress = nic.GetPhysicalAddress().ToString();
if (!string.IsNullOrEmpty(macAddress) && macAddress != "000000000000")
{
_cachedId = $"mac-{macAddress}";
return _cachedId;
}
}
}
catch (Exception)
{
// Ignore errors
}
// Fallback to hostname
try
{
var hostname = Environment.MachineName;
if (!string.IsNullOrEmpty(hostname))
{
_cachedId = $"host-{hostname}";
return _cachedId;
}
}
catch (Exception)
{
// Ignore errors
}
// Last resort: generate a random ID (not ideal, but better than nothing)
_cachedId = $"random-{Guid.NewGuid():N}";
return _cachedId;
}
/// <summary>
/// Gets a sanitized version of the hardware ID suitable for use as an MQTT client ID.
/// MQTT client IDs must be alphanumeric and can contain hyphens and underscores.
/// </summary>
/// <param name="prefix">Optional prefix to prepend to the hardware ID (e.g., "UwbManager")</param>
/// <returns>A sanitized MQTT client ID string, truncated to 128 characters if necessary</returns>
public static string GetMqttClientId(string? prefix = null)
{
var hardwareId = GetUniqueId();
var clientId = string.IsNullOrEmpty(prefix) ? hardwareId : $"{prefix}-{hardwareId}";
// Sanitize for MQTT: only alphanumeric, hyphens, and underscores allowed
// Replace any invalid characters with hyphens
var sanitized = new System.Text.StringBuilder();
foreach (var c in clientId)
{
if (char.IsLetterOrDigit(c) || c == '-' || c == '_')
{
sanitized.Append(c);
}
else
{
sanitized.Append('-');
}
}
// Ensure it's not too long (MQTT spec recommends max 23 characters for client IDs)
// But we'll allow up to 128 characters as per MQTT 3.1.1 spec
var result = sanitized.ToString();
if (result.Length > 128)
{
result = result.Substring(0, 128);
}
return result;
}
}