Improve: client/server - stable client ID via MachineGuid+path (V2)
This commit is contained in:
@@ -690,6 +690,49 @@ std::string getUsername()
|
||||
return u ? u : "?";
|
||||
}
|
||||
|
||||
// 读取 systemd / dbus 维护的 machine-id(与 Windows MachineGuid 等价)
|
||||
// /etc/machine-id 在系统首次启动时生成的随机 32 字符 hex GUID。
|
||||
// 对应 Windows: HKLM\Software\Microsoft\Cryptography\MachineGuid。
|
||||
// 重装系统才会变;同一镜像 dd 出来的多机会撞——但规范的批量部署
|
||||
// 工具 (cloud-init / kickstart) 会重置它。
|
||||
static std::string getMachineId()
|
||||
{
|
||||
// 优先 /etc/machine-id;某些精简系统可能放在 /var/lib/dbus/machine-id
|
||||
const char* paths[] = { "/etc/machine-id", "/var/lib/dbus/machine-id" };
|
||||
for (const char* p : paths) {
|
||||
std::ifstream f(p);
|
||||
if (!f.is_open()) continue;
|
||||
std::string id;
|
||||
std::getline(f, id);
|
||||
// 去掉尾部空白和换行
|
||||
while (!id.empty() && (id.back() == '\n' || id.back() == '\r' ||
|
||||
id.back() == ' ' || id.back() == '\t')) {
|
||||
id.pop_back();
|
||||
}
|
||||
if (!id.empty()) return id;
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// 路径归一化(Linux 版):解析符号链接 + 转小写
|
||||
// realpath 等价于 Windows 的 GetLongPathName,把 /usr/local/bin/../foo 这种
|
||||
// 折回到规范形式;小写化避免大小写差异引起 ID 不同(Linux 文件系统本身大小写
|
||||
// 敏感,但保持与 Windows V2 算法一致的归一化策略,跨端一致性优先)。
|
||||
static std::string normalizeExePathLower(const std::string& path)
|
||||
{
|
||||
char resolved[PATH_MAX] = {};
|
||||
std::string out;
|
||||
if (realpath(path.c_str(), resolved) != nullptr) {
|
||||
out = resolved;
|
||||
} else {
|
||||
out = path; // 解析失败(罕见):用原值
|
||||
}
|
||||
for (auto& c : out) {
|
||||
if (c >= 'A' && c <= 'Z') c = c - 'A' + 'a';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// 获取屏幕分辨率字符串(格式 "显示器数:宽*高")
|
||||
std::string getScreenResolution()
|
||||
{
|
||||
@@ -1010,17 +1053,30 @@ int main(int argc, char* argv[])
|
||||
logInfo.AddReserved(getUsername().c_str()); // [13] RES_USERNAME
|
||||
logInfo.AddReserved(getuid() == 0 ? 1 : 0); // [14] RES_ISADMIN
|
||||
logInfo.AddReserved(getScreenResolution().c_str()); // [15] RES_RESOLUTION
|
||||
// 计算客户端 ID(与服务端 CONTEXT_OBJECT::CalculateID 相同算法)
|
||||
// 格式: pubIP|hostname|os|cpu|path
|
||||
char cpuStr[32];
|
||||
snprintf(cpuStr, sizeof(cpuStr), "%uMHz", logInfo.dwCPUMHz);
|
||||
std::string idInput = (pubIP.empty() ? "?" : pubIP) + "|" +
|
||||
hostname + "|" +
|
||||
distro + "|" +
|
||||
cpuStr + "|" +
|
||||
exePath;
|
||||
g_myClientID = XXH64(idInput.c_str(), idInput.length(), 0);
|
||||
Mprintf("Calculated clientID: %llu (from: %s)\n", g_myClientID, idInput.c_str());
|
||||
// V2 ID 算法:machine-id + 归一化路径
|
||||
// - 同机同程序路径永远同 ID(不依赖 IP/hostname/distro/CPU 漂移)
|
||||
// - 局域网多机即便同镜像,cloud-init/kickstart 会让 machine-id 各不同
|
||||
// - machine-id 读取失败时退化到老算法(pubIP|hostname|distro|cpu|path)保兼容
|
||||
std::string machineId = getMachineId();
|
||||
if (!machineId.empty()) {
|
||||
std::string normPath = normalizeExePathLower(exePath);
|
||||
std::string idInput = machineId + "|" + normPath;
|
||||
g_myClientID = XXH64(idInput.c_str(), idInput.length(), 0);
|
||||
Mprintf("Calculated clientID(v2): %llu (machineId=%s, path=%s)\n",
|
||||
g_myClientID, machineId.c_str(), normPath.c_str());
|
||||
} else {
|
||||
// 老算法兜底(与服务端 CONTEXT_OBJECT::CalculateID 相同算法)
|
||||
// 格式: pubIP|hostname|os|cpu|path
|
||||
char cpuStr[32];
|
||||
snprintf(cpuStr, sizeof(cpuStr), "%uMHz", logInfo.dwCPUMHz);
|
||||
std::string idInput = (pubIP.empty() ? "?" : pubIP) + "|" +
|
||||
hostname + "|" +
|
||||
distro + "|" +
|
||||
cpuStr + "|" +
|
||||
exePath;
|
||||
g_myClientID = XXH64(idInput.c_str(), idInput.length(), 0);
|
||||
Mprintf("Calculated clientID(v1 fallback): %llu (machine-id 读取失败)\n", g_myClientID);
|
||||
}
|
||||
|
||||
logInfo.AddReserved(std::to_string(g_myClientID).c_str()); // [16] RES_CLIENT_ID
|
||||
logInfo.AddReserved((int)getpid()); // [17] RES_PID
|
||||
|
||||
Reference in New Issue
Block a user