core/common/MachineInfoUtil.cpp (674 lines of code) (raw):
// Copyright 2022 iLogtail Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "MachineInfoUtil.h"
#include <cstring>
#include <thread>
#include "curl/curl.h"
#include "rapidjson/document.h"
#include "rapidjson/rapidjson.h"
#include "AppConfig.h"
#include "FileSystemUtil.h"
#include "StringTools.h"
#include "common/FileSystemUtil.h"
#include "common/JsonUtil.h"
#include "common/UUIDUtil.h"
#include "logger/Logger.h"
#if defined(__linux__)
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <netdb.h>
#include <pwd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <algorithm>
#include <list>
#include <map>
#elif defined(_MSC_VER)
#include <WinSock2.h>
#include <Windows.h>
#endif
DEFINE_FLAG_STRING(agent_host_id, "", "");
const std::string sInstanceIdKey = "instance-id";
const std::string sOwnerAccountIdKey = "owner-account-id";
const std::string sRegionIdKey = "region-id";
const std::string sRandomHostIdKey = "random-hostid";
const std::string sECSAssistMachineIdKey = "ecs-assist-machine-id";
const std::string sCustomHostIdKey = "custom-hostid";
#if defined(_MSC_VER)
typedef LONG NTSTATUS, *PNTSTATUS;
#define STATUS_SUCCESS (0x00000000)
typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(POSVERSIONINFO);
bool GetRealOSVersion(POSVERSIONINFO osvi) {
HMODULE hMod = ::GetModuleHandle("ntdll.dll");
if (!hMod) {
LOG_ERROR(sLogger, ("GetModuleHandle failed", GetLastError()));
return false;
}
RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
if (nullptr == fxPtr) {
LOG_ERROR(sLogger, ("Get RtlGetVersion failed", GetLastError()));
return false;
}
return STATUS_SUCCESS == fxPtr(osvi);
}
#endif
namespace logtail {
std::string GetOsDetail() {
#if defined(__linux__)
std::string osDetail;
utsname* buf = new utsname;
if (-1 == uname(buf))
LOG_ERROR(sLogger, ("uname failed, errno", errno));
else {
osDetail.append(buf->sysname);
osDetail.append("; ");
osDetail.append(buf->release);
osDetail.append("; ");
osDetail.append(buf->version);
osDetail.append("; ");
osDetail.append(buf->machine);
}
delete buf;
return osDetail;
#elif defined(_MSC_VER)
// Because Latest Windows implement GetVersionEx according to manifests rather than
// actual runtime OS, we should get OSVERSIONINFOEX from RtlGetVersion.
// And we need to call GetVersionEx to get extra fields such as wProductType.
OSVERSIONINFOEX osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if (GetRealOSVersion((POSVERSIONINFO)&osvi)) {
OSVERSIONINFOEX extra;
ZeroMemory(&extra, sizeof(OSVERSIONINFOEX));
extra.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if (GetVersionEx((POSVERSIONINFO)&extra)) {
osvi.wProductType = extra.wProductType;
}
} else if (!GetVersionEx((LPOSVERSIONINFO)&osvi)) {
LOG_ERROR(sLogger, ("GetVersionEx failed", GetLastError()));
return "";
}
// According to https://docs.microsoft.com/zh-cn/windows/desktop/api/winnt/ns-winnt-_osversioninfoexa.
std::map<std::tuple<DWORD, DWORD, bool>, std::string> OS_AFTER_XP = {
{{10, 0, true}, "Windows 10"},
{{10, 0, false}, "Windows Server 2016"},
{{6, 3, true}, "Windows 8.1"},
{{6, 3, false}, "Windows Server 2012 R2"},
{{6, 2, true}, "Windows 8"},
{{6, 2, false}, "Windows Server 2012"},
{{6, 1, true}, "Windows 7"},
{{6, 1, false}, "Windows Server 2008 R2"},
{{6, 0, true}, "Windows Vista"},
{{6, 0, false}, "Windows Server 2008"},
};
auto key = std::tuple<DWORD, DWORD, bool>{
osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wProductType == VER_NT_WORKSTATION};
std::string osDetail;
if (OS_AFTER_XP.find(key) != OS_AFTER_XP.end())
osDetail = OS_AFTER_XP[key];
// More information is included for OS before XP.
SYSTEM_INFO siSysInfo;
GetSystemInfo(&siSysInfo);
if (osDetail.empty()) {
if (5 == osvi.dwMajorVersion && 2 == osvi.dwMinorVersion) {
if (GetSystemMetrics(SM_SERVERR2) != 0)
osDetail = "Windows Server 2003 R2";
else if (osvi.wSuiteMask & VER_SUITE_WH_SERVER)
osDetail = "Windows Home Server";
else if (GetSystemMetrics(SM_SERVERR2) == 0)
osDetail = "Windows Server 2003";
else if ((osvi.wProductType == VER_NT_WORKSTATION)
&& (siSysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)) {
osDetail = "Windows XP Professional"; // x64 Edition.
}
} else if (5 == osvi.dwMajorVersion && 1 == osvi.dwMinorVersion) {
osDetail = "Windows XP";
}
}
if (osDetail.empty()) {
osDetail = "Windows " + std::to_string(osvi.dwMajorVersion) + "." + std::to_string(osvi.dwMinorVersion);
}
return osDetail;
#endif
}
std::string GetUsername() {
#if defined(__linux__)
passwd* pw = getpwuid(getuid());
if (pw)
return std::string(pw->pw_name);
else
return "";
#elif defined(_MSC_VER)
const int INFO_BUFFER_SIZE = 100;
TCHAR infoBuf[INFO_BUFFER_SIZE];
DWORD bufCharCount = INFO_BUFFER_SIZE;
if (!GetUserName(infoBuf, &bufCharCount)) {
LOG_ERROR(sLogger, ("GetUserName failed", GetLastError()));
return "";
}
return std::string(infoBuf);
#endif
}
std::string GetHostName() {
char hostname[1024];
gethostname(hostname, 1024);
return std::string(hostname);
}
std::unordered_set<std::string> GetNicIpv4IPSet() {
struct ifaddrs* ifAddrStruct = NULL;
void* tmpAddrPtr = NULL;
std::unordered_set<std::string> ipSet;
getifaddrs(&ifAddrStruct);
for (struct ifaddrs* ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL) {
continue;
}
if (ifa->ifa_addr->sa_family == AF_INET) {
tmpAddrPtr = &((struct sockaddr_in*)ifa->ifa_addr)->sin_addr;
char addressBuffer[INET_ADDRSTRLEN] = "";
inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
std::string ip(addressBuffer);
// The loopback on most Linux distributions is lo, however it is not portable. For example loopback in OSX
// is lo0.
if (0 == strcmp("lo", ifa->ifa_name) || ip.empty() || StartWith(ip, "127.")) {
continue;
}
ipSet.insert(std::move(ip));
}
}
freeifaddrs(ifAddrStruct);
return ipSet;
}
std::string GetHostIpByHostName() {
std::string hostname = GetHostName();
// if hostname is invalid, other methods should be used to get correct ip.
if (!IsDigitsDotsHostname(hostname.c_str())) {
LOG_INFO(sLogger, ("invalid hostname", "will use other methods to obtain ip")("hostname", hostname));
return "";
}
struct addrinfo hints, *res = nullptr;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
int status = getaddrinfo(hostname.c_str(), NULL, &hints, &res);
if (status != 0 || res == nullptr) {
LOG_WARNING(sLogger,
("failed get address info", "will use other methods to obtain ip")("errMsg", gai_strerror(status)));
return "";
}
std::vector<sockaddr_in> addrs;
for (auto p = res; p != nullptr; p = p->ai_next) {
if (p->ai_family == AF_INET) {
addrs.emplace_back(*(struct sockaddr_in*)p->ai_addr);
}
}
freeaddrinfo(res);
std::string firstIp;
char ipStr[INET_ADDRSTRLEN + 1] = "";
#if defined(__linux__)
auto ipSet = GetNicIpv4IPSet();
for (size_t i = 0; i < addrs.size(); ++i) {
auto p = inet_ntop(AF_INET, &addrs[i].sin_addr, ipStr, INET_ADDRSTRLEN);
if (p == nullptr) {
continue;
}
auto tmp = std::string(ipStr);
if (ipSet.find(tmp) != ipSet.end()) {
return tmp;
}
if (i == 0) {
firstIp = tmp;
if (ipSet.empty()) {
LOG_INFO(sLogger, ("no entry from getifaddrs", "use first entry from getaddrinfo"));
return firstIp;
}
}
}
#elif defined(_MSC_VER)
for (size_t i = 0; i < addrs.size(); ++i) {
auto p = inet_ntop(AF_INET, &addrs[i].sin_addr, ipStr, INET_ADDRSTRLEN);
if (p == nullptr) {
continue;
}
auto tmp = std::string(ipStr);
// According to RFC 1918 (http://www.faqs.org/rfcs/rfc1918.html), private IP ranges are as below:
// 10.0.0.0 - 10.255.255.255 (10/8 prefix)
// 172.16.0.0 - 172.31.255.255 (172.16/12 prefix)
// 192.168.0.0 - 192.168.255.255 (192.168/16 prefix)
// 100.*.*.* , 30.*.*.* - alibaba office network
if (tmp.find("10.") == 0 || tmp.find("100.") == 0 || tmp.find("30.") == 0 || tmp.find("192.168.") == 0
|| tmp.find("172.16.") == 0 || tmp.find("172.17.") == 0 || tmp.find("172.18.") == 0
|| tmp.find("172.19.") == 0 || tmp.find("172.20.") == 0 || tmp.find("172.21.") == 0
|| tmp.find("172.22.") == 0 || tmp.find("172.23.") == 0 || tmp.find("172.24.") == 0
|| tmp.find("172.25.") == 0 || tmp.find("172.26.") == 0 || tmp.find("172.27.") == 0
|| tmp.find("172.28.") == 0 || tmp.find("172.29.") == 0 || tmp.find("172.30.") == 0
|| tmp.find("172.31.") == 0) {
return tmp;
}
if (i == 0) {
firstIp = tmp;
}
}
#endif
LOG_INFO(sLogger, ("no entry from getaddrinfo matches entry from getifaddrs", "use first entry from getaddrinfo"));
return firstIp;
}
std::string GetHostIpByInterface(const std::string& intf) {
#if defined(__linux__)
int sock;
struct sockaddr_in sin;
struct ifreq ifr;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
return "";
}
// use eth0 as the default ETH name
strncpy(ifr.ifr_name, intf.size() > 0 ? intf.c_str() : "eth0", IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ - 1] = 0;
if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) {
close(sock);
return "";
}
memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
char* ipaddr = inet_ntoa(sin.sin_addr);
close(sock);
if (ipaddr == NULL) {
return "";
}
return std::string(ipaddr);
#elif defined(_MSC_VER)
// TODO: For Windows, interface should be replace to adaptor name.
// Delay implementation, assume that GetIpByHostName will succeed.
return "";
#endif
}
std::string GetHostIp(const std::string& intf) {
std::string ip = GetHostIpByHostName();
#if defined(__linux__)
if (ip.empty() || ip.find("127.") == 0) {
if (intf.empty()) {
ip = GetHostIpByInterface("eth0");
if (ip.empty() || ip.find("127.") == 0) {
ip = GetHostIpByInterface("bond0");
}
return ip;
}
return GetHostIpByInterface(intf);
}
#endif
return ip;
}
std::string GetAnyAvailableIP() {
#if defined(__linux__)
std::string retIP;
char host[NI_MAXHOST];
auto ipSet = GetNicIpv4IPSet();
if (!ipSet.empty()) {
for (auto& ip : ipSet) {
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = 0;
int result = inet_pton(AF_INET, ip.c_str(), &sa.sin_addr);
if (result != 1) {
continue;
}
int s = getnameinfo(
(struct sockaddr*)&sa, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
if (s != 0) {
continue;
}
retIP = ip;
break;
}
}
return retIP;
#elif defined(_MSC_VER)
// TODO:
return "192.168.1.1";
#endif
}
bool GetKernelInfo(std::string& kernelRelease, int64_t& kernelVersion) {
#if defined(__linux__)
struct utsname buf;
if (-1 == uname(&buf)) {
LOG_ERROR(sLogger, ("uname failed, errno", errno));
return false;
}
kernelRelease.assign(buf.release);
std::vector<int64_t> versions;
char* p = buf.release;
char* maxP = buf.release + sizeof(buf.release);
while (*p && p < maxP) {
if (isdigit(*p)) {
versions.push_back(strtol(p, &p, 10));
} else {
p++;
}
}
kernelVersion = 0;
for (size_t i = 0; i < versions.size() && i < (size_t)4; ++i) {
kernelVersion = kernelVersion * 1000 + versions[i];
}
return true;
#else
return false;
#endif
}
uint32_t GetHostIpValueByInterface(const std::string& intf) {
#if defined(__linux__)
int sock;
struct sockaddr_in sin;
struct ifreq ifr;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
return 0;
}
// use eth0 as the default ETH name
strncpy(ifr.ifr_name, intf.size() > 0 ? intf.c_str() : "eth0", IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ - 1] = 0;
if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) {
close(sock);
return 0;
}
memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
close(sock);
return sin.sin_addr.s_addr;
#elif defined(_MSC_VER)
// TODO: For Windows, interface should be replace to adaptor name.
// Delay implementation, assume that GetIpByHostName will succeed.
return 0;
#endif
}
void GetAllPids(std::unordered_set<int32_t>& pids) {
#if defined(__linux__)
pids.clear();
fsutil::Dir procDir("/proc/");
if (!procDir.Open()) {
return;
}
fsutil::Entry entry;
while (true) {
entry = procDir.ReadNext();
if (!entry) {
return;
}
if (!entry.IsDir()) {
continue;
}
std::string name = entry.Name();
int32_t pid = strtol(name.data(), NULL, 10);
if (pid <= 0) {
continue;
}
pids.insert(pid);
}
#endif
}
bool GetRedHatReleaseInfo(std::string& os, int64_t& osVersion, std::string bashPath) {
static const boost::regex sReg(R"(^(\S+)\s+\S+\s+\S+\s+(\d+)\.(\d+).*$)");
bashPath.append("/etc/redhat-release");
os.clear();
std::string content, exception;
if (FileReadResult::kOK != ReadFileContent(bashPath, content)) {
return false;
}
boost::match_results<const char*> what;
if (BoostRegexSearch(content.c_str(), sReg, exception, what) && what.size() > 1) {
os = what[1].str();
osVersion = strtol(what[2].begin(), nullptr, 10) * 1000;
osVersion += strtol(what[3].begin(), nullptr, 10);
}
LOG_DEBUG(sLogger, ("read /etc/redhat-release content", content));
return !os.empty() && osVersion != 0;
}
// For hostnames in the following format, gethostbyname will fake an IP (and thus return wrong IP):
// 1. a, where a is a number between 0 and 2^32-1;
// 2. a.b, where a & b are numbers, and a is between 0 and 2^8-1, b is between 0 and 2^24-1;
// 3. a.b.c, where a, b & c are numbers, and a & b are between 0 and 2^8-1, c is between 0 and 2^16-1;
// 4. a.b.c.d, where a, b, c & d are numbers between 0 and 2^8-1.
// All numbers mentioned here can be both in base 8 or 10.
//
// see https://codebrowser.dev/glibc/glibc/nss/digits_dots.c.html#__nss_hostname_digits_dots_context for more details.
bool IsDigitsDotsHostname(const char* hostname) {
if (hostname && *hostname != '\0') {
const char* cp = hostname;
int16_t digits = 32;
while (*cp != '\0' && digits > 0) {
char* endp;
uint64_t sum = strtoul(cp, &endp, 0);
if ((sum == ULONG_MAX && errno == ERANGE) || sum >= (1UL << digits)) {
break;
}
cp = endp;
if (*cp == '.' && sum <= 255) {
digits -= 8;
cp++;
} else {
break;
}
}
if (*cp == '\0' && *(cp - 1) != '.') {
return false;
}
return true;
}
return false;
}
size_t FetchECSMetaCallback(char* buffer, size_t size, size_t nmemb, std::string* res) {
if (NULL == buffer) {
return 0;
}
size_t sizes = size * nmemb;
res->append(buffer, sizes);
return sizes;
}
bool ParseECSMeta(const std::string& meta, ECSMeta& metaObj) {
Json::Value doc;
std::string errMsg;
if (!ParseJsonTable(meta, doc, errMsg)) {
LOG_WARNING(sLogger, ("parse ecs meta fail, errMsg", errMsg)("meta", meta));
return false;
}
if (doc.isMember(sInstanceIdKey) && doc[sInstanceIdKey].isString()) {
metaObj.SetInstanceID(doc[sInstanceIdKey].asString());
}
if (doc.isMember(sOwnerAccountIdKey) && doc[sOwnerAccountIdKey].isString()) {
metaObj.SetUserID(doc[sOwnerAccountIdKey].asString());
}
if (doc.isMember(sRegionIdKey) && doc[sRegionIdKey].isString()) {
metaObj.SetRegionID(doc[sRegionIdKey].asString());
}
return metaObj.IsValid();
}
InstanceIdentity::InstanceIdentity() {
mEntity.getWriteBuffer().SetHostID({STRING_FLAG(agent_host_id), Hostid::Type::CUSTOM});
mEntity.swap();
}
void InstanceIdentity::DumpInstanceIdentity() {
if (mEntity.getReadBuffer().GetHostIdType() == Hostid::Type::ECS) {
mInstanceIdentityJson.clear();
mInstanceIdentityJson[sInstanceIdKey] = mEntity.getReadBuffer().GetEcsInstanceID().to_string();
mInstanceIdentityJson[sOwnerAccountIdKey] = mEntity.getReadBuffer().GetEcsUserID().to_string();
mInstanceIdentityJson[sRegionIdKey] = mEntity.getReadBuffer().GetEcsRegionID().to_string();
dumpInstanceIdentityToFile();
} else if (mEntity.getReadBuffer().GetHostIdType() == Hostid::Type::LOCAL && mHasGeneratedLocalHostId) {
mInstanceIdentityJson.clear();
mInstanceIdentityJson[sRandomHostIdKey] = mLocalHostId;
dumpInstanceIdentityToFile();
} else if (mEntity.getReadBuffer().GetHostIdType() == Hostid::Type::ECS_ASSIST && !mSerialNumber.empty()) {
mInstanceIdentityJson.clear();
mInstanceIdentityJson[sECSAssistMachineIdKey] = mSerialNumber;
dumpInstanceIdentityToFile();
} else if (mEntity.getReadBuffer().GetHostIdType() == Hostid::Type::CUSTOM) {
mInstanceIdentityJson.clear();
mInstanceIdentityJson[sCustomHostIdKey] = STRING_FLAG(agent_host_id);
dumpInstanceIdentityToFile();
}
}
void InstanceIdentity::InitFromNetwork() {
ECSMeta ecsMeta;
if (FetchECSMeta(ecsMeta)) {
InstanceIdentity::Instance()->UpdateInstanceIdentity(ecsMeta);
}
}
bool InstanceIdentity::InitFromFile() {
mInstanceIdentityFile = GetAgentDataDir() + PATH_SEPARATOR + "instance_identity";
bool initSuccess = false;
ECSMeta meta;
if (CheckExistance(mInstanceIdentityFile)) {
std::string instanceIdentityStr;
if (FileReadResult::kOK != ReadFileContent(mInstanceIdentityFile, instanceIdentityStr)) {
Json::Value doc;
std::string errMsg;
if (!ParseJsonTable(instanceIdentityStr, doc, errMsg)) {
LOG_WARNING(sLogger,
("parse instanceIdentity from file fail",
errMsg)("instanceIdentity", instanceIdentityStr)("file", mInstanceIdentityFile));
} else {
mInstanceIdentityJson = std::move(doc);
if (ParseECSMeta(instanceIdentityStr, meta)) {
// 存在 ecs meta信息,则认为instanceIdentity是ready的
initSuccess = true;
} else if (mInstanceIdentityJson.isMember(sRandomHostIdKey)
&& mInstanceIdentityJson[sRandomHostIdKey].isString()) {
// 不存在ecs meta信息, 则尝试读取下 random-hostid
mLocalHostId = mInstanceIdentityJson[sRandomHostIdKey].asString();
// 存在 random-hostid,则认为instanceIdentity是ready的
initSuccess = true;
} else if (mInstanceIdentityJson.isMember(sECSAssistMachineIdKey)
&& mInstanceIdentityJson[sECSAssistMachineIdKey].isString()) {
// 存在 ecs-assist-machine-id,则认为instanceIdentity是ready的
initSuccess = true;
} else if (mInstanceIdentityJson.isMember(sCustomHostIdKey)
&& mInstanceIdentityJson[sCustomHostIdKey].isString()) {
// 存在 custom-hostid,则认为instanceIdentity是ready的
initSuccess = true;
} else {
LOG_ERROR(sLogger,
("instanceIdentity is ready, but no ecs meta, random-hostid, ecs-assist-machine-id or "
"custom-hostid found, file",
mInstanceIdentityFile)("instanceIdentity", instanceIdentityStr));
}
}
} else {
LOG_ERROR(sLogger,
("read instanceIdentity from file fail, file", mInstanceIdentityFile)("instanceIdentity",
instanceIdentityStr));
}
}
// 计算hostid
if (meta.IsValid()) {
mEntity.getWriteBuffer().SetECSMeta(meta);
}
updateHostId(meta);
mEntity.swap();
return initSuccess;
}
bool InstanceIdentity::UpdateInstanceIdentity(const ECSMeta& meta) {
// 如果 meta合法 且 mInstanceID 发生变化,则更新ecs元数据
if (meta.IsValid() && mEntity.getReadBuffer().GetEcsInstanceID() != meta.GetInstanceID()) {
LOG_INFO(sLogger,
("ecs mInstanceID changed, old mInstanceID",
mEntity.getReadBuffer().GetEcsInstanceID())("new mInstanceID", meta.GetInstanceID()));
mEntity.getWriteBuffer().SetECSMeta(meta);
updateHostId(meta);
mEntity.swap();
return true;
}
return false;
}
void InstanceIdentity::dumpInstanceIdentityToFile() {
std::string errMsg;
std::string tmpFile = mInstanceIdentityFile + ".tmp";
if (!WriteFile(tmpFile, mInstanceIdentityJson.toStyledString(), errMsg)) {
LOG_ERROR(sLogger, ("failed to write instanceIdentity to tmp file", tmpFile)("error", errMsg));
return;
}
#if defined(_MSC_VER)
// The rename on Windows will fail if the destination is existing.
remove(mInstanceIdentityFile.c_str());
std::this_thread::sleep_for(std::chrono::milliseconds(1));
#endif
if (rename(tmpFile.c_str(), mInstanceIdentityFile.c_str()) != 0) {
errMsg = std::strerror(errno);
LOG_ERROR(sLogger,
("failed to rename tmp file to target", tmpFile)("target", mInstanceIdentityFile)("error", errMsg));
return;
}
LOG_INFO(sLogger,
("write instanceIdentity to file success, fileName",
mInstanceIdentityFile)("instanceIdentity", mInstanceIdentityJson.toStyledString()));
}
void InstanceIdentity::updateHostId(const ECSMeta& meta) {
Hostid newId;
if (meta.IsValid()) {
newId = {meta.GetInstanceID().to_string(), Hostid::Type::ECS};
} else {
getSerialNumberFromEcsAssist();
if (!mSerialNumber.empty()) {
newId = {mSerialNumber, Hostid::Type::ECS_ASSIST};
} else if (!STRING_FLAG(agent_host_id).empty()) {
newId = {STRING_FLAG(agent_host_id), Hostid::Type::CUSTOM};
} else {
getLocalHostId();
newId = {mLocalHostId, Hostid::Type::LOCAL};
}
}
// 只在ID发生变化时更新并记录日志
if (mEntity.getReadBuffer().GetHostID() != newId.GetHostID()
|| mEntity.getReadBuffer().GetHostIdType() != newId.GetType()) {
LOG_INFO(sLogger,
("change hostId, id from", mEntity.getReadBuffer().GetHostID())("to", newId.GetHostID())(
"type from", Hostid::TypeToString(mEntity.getReadBuffer().GetHostIdType()))(
"to", Hostid::TypeToString(newId.GetType())));
mEntity.getWriteBuffer().SetHostID(newId);
} else {
// 没有变化时,WriteBuffer的host id维持原状
Hostid hostId(mEntity.getReadBuffer().GetHostID().to_string(), mEntity.getReadBuffer().GetHostIdType());
mEntity.getWriteBuffer().SetHostID(hostId);
}
}
bool FetchECSMeta(ECSMeta& metaObj) {
CURL* curl = nullptr;
for (size_t retryTimes = 1; retryTimes <= 5; retryTimes++) {
curl = curl_easy_init();
if (curl) {
break;
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if (curl) {
std::string token;
auto* tokenHeaders = curl_slist_append(nullptr, "X-aliyun-ecs-metadata-token-ttl-seconds:3600");
if (!tokenHeaders) {
curl_easy_cleanup(curl);
return false;
}
curl_easy_setopt(curl, CURLOPT_URL, "http://100.100.100.200/latest/api/token");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, tokenHeaders);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
// 超时1秒
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &token);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, FetchECSMetaCallback);
CURLcode res = curl_easy_perform(curl);
curl_slist_free_all(tokenHeaders);
if (res != CURLE_OK) {
LOG_INFO(sLogger, ("fetch ecs token fail", curl_easy_strerror(res)));
curl_easy_cleanup(curl);
return false;
}
// Get metadata with token
std::string meta;
auto* metaHeaders = curl_slist_append(nullptr, ("X-aliyun-ecs-metadata-token: " + token).c_str());
if (!metaHeaders) {
curl_easy_cleanup(curl);
return false;
}
curl_easy_reset(curl);
curl_easy_setopt(curl, CURLOPT_URL, "http://100.100.100.200/latest/dynamic/instance-identity/document");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, metaHeaders);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
// 超时1秒
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &meta);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, FetchECSMetaCallback);
res = curl_easy_perform(curl);
curl_slist_free_all(metaHeaders);
if (res != CURLE_OK) {
LOG_INFO(sLogger, ("fetch ecs meta fail", curl_easy_strerror(res)));
curl_easy_cleanup(curl);
return false;
}
if (!ParseECSMeta(meta, metaObj)) {
curl_easy_cleanup(curl);
return false;
}
curl_easy_cleanup(curl);
return metaObj.IsValid();
}
LOG_WARNING(
sLogger,
("curl handler cannot be initialized during user environment identification", "ecs meta may be mislabeled"));
return false;
}
// 从云助手获取序列号
void InstanceIdentity::getSerialNumberFromEcsAssist() {
if (mHasTriedToGetSerialNumber) {
return;
}
if (CheckExistance(mEcsAssistMachineIdFile)) {
if (FileReadResult::kOK != ReadFileContent(mEcsAssistMachineIdFile, mSerialNumber)) {
mSerialNumber = "";
}
}
mHasTriedToGetSerialNumber = true;
}
void InstanceIdentity::getLocalHostId() {
if (!mLocalHostId.empty()) {
return;
}
mHasGeneratedLocalHostId = true;
mLocalHostId = CalculateRandomUUID();
}
} // namespace logtail