mirror of
https://github.com/netdata/netdata.git
synced 2025-04-21 20:23:00 +00:00
add unique machine id to status file (#19778)
* add unique machine id to status file * clear errno before listening for sockets * remove the pipe when exiting
This commit is contained in:
parent
bf340f9644
commit
edc82776ae
8 changed files with 236 additions and 8 deletions
|
@ -1068,6 +1068,8 @@ set(LIBNETDATA_FILES
|
||||||
src/libnetdata/os/mmap_limit.h
|
src/libnetdata/os/mmap_limit.h
|
||||||
src/libnetdata/signals/signals.c
|
src/libnetdata/signals/signals.c
|
||||||
src/libnetdata/signals/signals.h
|
src/libnetdata/signals/signals.h
|
||||||
|
src/libnetdata/os/machine_id.c
|
||||||
|
src/libnetdata/os/machine_id.h
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND LIBNETDATA_FILES ${INICFG_FILES})
|
list(APPEND LIBNETDATA_FILES ${INICFG_FILES})
|
||||||
|
|
|
@ -300,13 +300,16 @@ void netdata_cleanup_and_exit(EXIT_REASON reason, const char *action, const char
|
||||||
sqlite_close_databases();
|
sqlite_close_databases();
|
||||||
watcher_step_complete(WATCHER_STEP_ID_CLOSE_SQL_DATABASES);
|
watcher_step_complete(WATCHER_STEP_ID_CLOSE_SQL_DATABASES);
|
||||||
sqlite_library_shutdown();
|
sqlite_library_shutdown();
|
||||||
|
|
||||||
|
|
||||||
// unlink the pid
|
// unlink the pid
|
||||||
if(pidfile && *pidfile) {
|
if(pidfile && *pidfile && unlink(pidfile) != 0)
|
||||||
if(unlink(pidfile) != 0)
|
netdata_log_error("EXIT: cannot unlink pidfile '%s'.", pidfile);
|
||||||
netdata_log_error("EXIT: cannot unlink pidfile '%s'.", pidfile);
|
|
||||||
}
|
// unlink the pipe
|
||||||
|
const char *pipe = daemon_pipename();
|
||||||
|
if(pipe && *pipe && unlink(pipe) != 0)
|
||||||
|
netdata_log_error("EXIT: cannot unlink netdatacli socket file '%s'.", pipe);
|
||||||
|
|
||||||
watcher_step_complete(WATCHER_STEP_ID_REMOVE_PID_FILE);
|
watcher_step_complete(WATCHER_STEP_ID_REMOVE_PID_FILE);
|
||||||
|
|
||||||
netdata_ssl_cleanup();
|
netdata_ssl_cleanup();
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <openssl/pem.h>
|
#include <openssl/pem.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
|
||||||
#define STATUS_FILE_VERSION 9
|
#define STATUS_FILE_VERSION 10
|
||||||
|
|
||||||
#define STATUS_FILENAME "status-netdata.json"
|
#define STATUS_FILENAME "status-netdata.json"
|
||||||
|
|
||||||
|
@ -129,6 +129,7 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
|
||||||
|
|
||||||
buffer_json_member_add_object(wb, "host"); // ECS
|
buffer_json_member_add_object(wb, "host"); // ECS
|
||||||
{
|
{
|
||||||
|
buffer_json_member_add_uuid_compact(wb, "id", ds->machine_id.uuid);
|
||||||
buffer_json_member_add_string_or_empty(wb, "architecture", ds->architecture); // ECS
|
buffer_json_member_add_string_or_empty(wb, "architecture", ds->architecture); // ECS
|
||||||
buffer_json_member_add_string_or_empty(wb, "virtualization", ds->virtualization); // custom
|
buffer_json_member_add_string_or_empty(wb, "virtualization", ds->virtualization); // custom
|
||||||
buffer_json_member_add_string_or_empty(wb, "container", ds->container); // custom
|
buffer_json_member_add_string_or_empty(wb, "container", ds->container); // custom
|
||||||
|
@ -136,7 +137,7 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
|
||||||
|
|
||||||
buffer_json_member_add_object(wb, "boot"); // ECS
|
buffer_json_member_add_object(wb, "boot"); // ECS
|
||||||
{
|
{
|
||||||
buffer_json_member_add_uuid(wb, "id", ds->boot_id.uuid); // ECS
|
buffer_json_member_add_uuid_compact(wb, "id", ds->boot_id.uuid); // ECS
|
||||||
}
|
}
|
||||||
buffer_json_object_close(wb);
|
buffer_json_object_close(wb);
|
||||||
|
|
||||||
|
@ -224,6 +225,7 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
|
||||||
bool required_v3 = version >= 3 ? strict : false;
|
bool required_v3 = version >= 3 ? strict : false;
|
||||||
bool required_v4 = version >= 4 ? strict : false;
|
bool required_v4 = version >= 4 ? strict : false;
|
||||||
bool required_v5 = version >= 5 ? strict : false;
|
bool required_v5 = version >= 5 ? strict : false;
|
||||||
|
bool required_v10 = version >= 10 ? strict : false;
|
||||||
|
|
||||||
// Parse timestamp
|
// Parse timestamp
|
||||||
JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "@timestamp", datetime, error, required_v1);
|
JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "@timestamp", datetime, error, required_v1);
|
||||||
|
@ -254,6 +256,7 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
|
||||||
|
|
||||||
// Parse host object
|
// Parse host object
|
||||||
JSONC_PARSE_SUBOBJECT(jobj, path, "host", error, required_v1, {
|
JSONC_PARSE_SUBOBJECT(jobj, path, "host", error, required_v1, {
|
||||||
|
JSONC_PARSE_TXT2UUID_OR_ERROR_AND_RETURN(jobj, path, "id", ds->machine_id.uuid, error, required_v10);
|
||||||
JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "architecture", ds->architecture, error, required_v1);
|
JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "architecture", ds->architecture, error, required_v1);
|
||||||
JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "virtualization", ds->virtualization, error, required_v1);
|
JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "virtualization", ds->virtualization, error, required_v1);
|
||||||
JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "container", ds->container, error, required_v1);
|
JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "container", ds->container, error, required_v1);
|
||||||
|
@ -399,6 +402,9 @@ static void daemon_status_file_refresh(DAEMON_STATUS status) {
|
||||||
session_status.host_id = UUID_ZERO;
|
session_status.host_id = UUID_ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(UUIDiszero(session_status.machine_id))
|
||||||
|
session_status.machine_id = os_machine_id();
|
||||||
|
|
||||||
// copy items from the old status if they are not set
|
// copy items from the old status if they are not set
|
||||||
if(UUIDiszero(session_status.claim_id))
|
if(UUIDiszero(session_status.claim_id))
|
||||||
session_status.claim_id = last_session_status.claim_id;
|
session_status.claim_id = last_session_status.claim_id;
|
||||||
|
|
|
@ -44,6 +44,7 @@ typedef struct daemon_status_file {
|
||||||
ND_UUID host_id; // the machine guid of the agent
|
ND_UUID host_id; // the machine guid of the agent
|
||||||
ND_UUID node_id; // the Netdata Cloud node id of the agent
|
ND_UUID node_id; // the Netdata Cloud node id of the agent
|
||||||
ND_UUID claim_id; // the Netdata Cloud claim id of the agent
|
ND_UUID claim_id; // the Netdata Cloud claim id of the agent
|
||||||
|
ND_UUID machine_id; // the unique machine id of the system
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
usec_t init_started_ut;
|
usec_t init_started_ut;
|
||||||
|
|
|
@ -902,6 +902,7 @@ int netdata_main(int argc, char **argv) {
|
||||||
|
|
||||||
delta_startup_time("web server sockets");
|
delta_startup_time("web server sockets");
|
||||||
if(web_server_mode != WEB_SERVER_MODE_NONE) {
|
if(web_server_mode != WEB_SERVER_MODE_NONE) {
|
||||||
|
errno_clear();
|
||||||
if (!api_listen_sockets_setup()) {
|
if (!api_listen_sockets_setup()) {
|
||||||
exit_initiated_add(EXIT_REASON_ALREADY_RUNNING);
|
exit_initiated_add(EXIT_REASON_ALREADY_RUNNING);
|
||||||
daemon_status_file_update_status(DAEMON_STATUS_NONE);
|
daemon_status_file_update_status(DAEMON_STATUS_NONE);
|
||||||
|
|
186
src/libnetdata/os/machine_id.c
Normal file
186
src/libnetdata/os/machine_id.c
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "machine_id.h"
|
||||||
|
#include "libnetdata/libnetdata.h"
|
||||||
|
|
||||||
|
static ND_UUID cached_machine_id = { 0 };
|
||||||
|
static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
|
||||||
|
|
||||||
|
#if defined(OS_LINUX)
|
||||||
|
|
||||||
|
static ND_UUID get_machine_id(void) {
|
||||||
|
ND_UUID machine_id = { 0 };
|
||||||
|
|
||||||
|
// Try systemd machine-id locations first
|
||||||
|
const char *locations[] = {
|
||||||
|
"/etc/machine-id", // systemd standard location
|
||||||
|
"/var/lib/dbus/machine-id", // fallback for older systems or different distros
|
||||||
|
"/sys/class/dmi/id/product_uuid" // hardware-based UUID from DMI
|
||||||
|
};
|
||||||
|
|
||||||
|
for (size_t i = 0; i < _countof(locations); i++) {
|
||||||
|
char filename[FILENAME_MAX + 1];
|
||||||
|
snprintfz(filename, sizeof(filename), "%s%s",
|
||||||
|
netdata_configured_host_prefix ? netdata_configured_host_prefix : "", locations[i]);
|
||||||
|
|
||||||
|
char buf[128];
|
||||||
|
if (read_txt_file(filename, buf, sizeof(buf)) == 0) {
|
||||||
|
if (uuid_parse(trim(buf), machine_id.uuid) == 0)
|
||||||
|
return machine_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no reliable machine ID could be found, return NO_MACHINE_ID
|
||||||
|
return NO_MACHINE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(OS_FREEBSD)
|
||||||
|
|
||||||
|
static ND_UUID get_machine_id(void) {
|
||||||
|
ND_UUID machine_id = { 0 };
|
||||||
|
char buf[128];
|
||||||
|
|
||||||
|
// Try FreeBSD host ID first
|
||||||
|
char filename[FILENAME_MAX + 1];
|
||||||
|
snprintfz(filename, sizeof(filename), "%s/etc/hostid",
|
||||||
|
netdata_configured_host_prefix ? netdata_configured_host_prefix : "");
|
||||||
|
|
||||||
|
if (read_txt_file(filename, buf, sizeof(buf)) == 0) {
|
||||||
|
if (uuid_parse(trim(buf), machine_id.uuid) == 0)
|
||||||
|
return machine_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: Read system kern.hostuuid sysctl
|
||||||
|
size_t len = sizeof(buf);
|
||||||
|
if (sysctlbyname("kern.hostuuid", buf, &len, NULL, 0) == 0) {
|
||||||
|
if (uuid_parse(trim(buf), machine_id.uuid) == 0)
|
||||||
|
return machine_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no reliable machine ID could be found, return NO_MACHINE_ID
|
||||||
|
return NO_MACHINE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(OS_MACOS)
|
||||||
|
|
||||||
|
#include <IOKit/IOKitLib.h>
|
||||||
|
|
||||||
|
static ND_UUID get_machine_id(void) {
|
||||||
|
ND_UUID machine_id = { 0 };
|
||||||
|
|
||||||
|
// First try to get the platform UUID
|
||||||
|
io_registry_entry_t ioRegistryRoot = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/");
|
||||||
|
if (ioRegistryRoot) {
|
||||||
|
CFStringRef uuidCf = (CFStringRef) IORegistryEntryCreateCFProperty(
|
||||||
|
ioRegistryRoot,
|
||||||
|
CFSTR(kIOPlatformUUIDKey),
|
||||||
|
kCFAllocatorDefault,
|
||||||
|
0);
|
||||||
|
|
||||||
|
if (uuidCf) {
|
||||||
|
char uuid_str[UUID_STR_LEN];
|
||||||
|
if (CFStringGetCString(uuidCf, uuid_str, sizeof(uuid_str), kCFStringEncodingUTF8)) {
|
||||||
|
if (uuid_parse(uuid_str, machine_id.uuid) == 0) {
|
||||||
|
CFRelease(uuidCf);
|
||||||
|
IOObjectRelease(ioRegistryRoot);
|
||||||
|
return machine_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CFRelease(uuidCf);
|
||||||
|
}
|
||||||
|
IOObjectRelease(ioRegistryRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to IOPlatformExpertDevice's IOPlatformSerialNumber
|
||||||
|
io_service_t platformExpert = IOServiceGetMatchingService(
|
||||||
|
kIOMasterPortDefault,
|
||||||
|
IOServiceMatching("IOPlatformExpertDevice"));
|
||||||
|
|
||||||
|
if (platformExpert) {
|
||||||
|
CFStringRef serialNumberCf = (CFStringRef) IORegistryEntryCreateCFProperty(
|
||||||
|
platformExpert,
|
||||||
|
CFSTR(kIOPlatformSerialNumberKey),
|
||||||
|
kCFAllocatorDefault,
|
||||||
|
0);
|
||||||
|
|
||||||
|
if (serialNumberCf) {
|
||||||
|
char serial_str[128];
|
||||||
|
if (CFStringGetCString(serialNumberCf, serial_str, sizeof(serial_str), kCFStringEncodingUTF8)) {
|
||||||
|
// Hardware serial numbers are considered reliable and stable
|
||||||
|
if (strlen(serial_str) > 0) {
|
||||||
|
// Generate a UUID from the serial number
|
||||||
|
char uuid_input[150];
|
||||||
|
snprintfz(uuid_input, sizeof(uuid_input), "mac-serial:%s", serial_str);
|
||||||
|
machine_id = UUID_generate_from_hash(uuid_input, strlen(uuid_input));
|
||||||
|
|
||||||
|
CFRelease(serialNumberCf);
|
||||||
|
IOObjectRelease(platformExpert);
|
||||||
|
return machine_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CFRelease(serialNumberCf);
|
||||||
|
}
|
||||||
|
IOObjectRelease(platformExpert);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no reliable machine ID could be found, return NO_MACHINE_ID
|
||||||
|
return NO_MACHINE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(OS_WINDOWS)
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
static ND_UUID get_machine_id(void) {
|
||||||
|
ND_UUID machine_id = { 0 };
|
||||||
|
HKEY hKey;
|
||||||
|
|
||||||
|
// Try to get MachineGuid from registry - this is the most reliable machine ID on Windows
|
||||||
|
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Cryptography", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
|
||||||
|
WCHAR guidW[64];
|
||||||
|
DWORD guidSize = sizeof(guidW);
|
||||||
|
DWORD type = REG_SZ;
|
||||||
|
|
||||||
|
if (RegQueryValueExW(hKey, L"MachineGuid", NULL, &type, (LPBYTE)guidW, &guidSize) == ERROR_SUCCESS) {
|
||||||
|
char guid_str[UUID_STR_LEN];
|
||||||
|
|
||||||
|
// Convert GUID to UTF-8
|
||||||
|
if (WideCharToMultiByte(CP_UTF8, 0, guidW, -1, guid_str, sizeof(guid_str), NULL, NULL) > 0) {
|
||||||
|
if (uuid_parse(guid_str, machine_id.uuid) == 0) {
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
return machine_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no reliable machine ID could be found, return NO_MACHINE_ID
|
||||||
|
return NO_MACHINE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // OS_WINDOWS
|
||||||
|
|
||||||
|
ND_UUID os_machine_id(void) {
|
||||||
|
// Fast path - return cached value if available
|
||||||
|
if(!UUIDiszero(cached_machine_id))
|
||||||
|
return cached_machine_id;
|
||||||
|
|
||||||
|
spinlock_lock(&spinlock);
|
||||||
|
|
||||||
|
// Check again under lock in case another thread set it
|
||||||
|
if(UUIDiszero(cached_machine_id)) {
|
||||||
|
cached_machine_id = get_machine_id();
|
||||||
|
|
||||||
|
// Log the result if debugging is enabled
|
||||||
|
if(UUIDeq(cached_machine_id, NO_MACHINE_ID))
|
||||||
|
nd_log(NDLS_DAEMON, NDLP_WARNING, "OS_MACHINE_ID: Could not detect a reliable machine ID");
|
||||||
|
else {
|
||||||
|
char buf[UUID_STR_LEN];
|
||||||
|
uuid_unparse_lower(cached_machine_id.uuid, buf);
|
||||||
|
nd_log(NDLS_DAEMON, NDLP_WARNING, "OS_MACHINE_ID: machine ID found '%s'", buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spinlock_unlock(&spinlock);
|
||||||
|
return cached_machine_id;
|
||||||
|
}
|
28
src/libnetdata/os/machine_id.h
Normal file
28
src/libnetdata/os/machine_id.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#ifndef NETDATA_MACHINE_ID_H
|
||||||
|
#define NETDATA_MACHINE_ID_H
|
||||||
|
|
||||||
|
#include "libnetdata/libnetdata.h"
|
||||||
|
#include "libnetdata/uuid/uuid.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special value returned when a reliable machine ID cannot be determined
|
||||||
|
*/
|
||||||
|
#define NO_MACHINE_ID (ND_UUID){ .parts = { .hig64 = 1, .low64 = 1 } }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the machine ID
|
||||||
|
*
|
||||||
|
* Returns a UUID that uniquely identifies the machine.
|
||||||
|
* On Linux, this is the systemd machine-id.
|
||||||
|
* On other systems, it uses platform-specific values.
|
||||||
|
*
|
||||||
|
* The value is cached after first call.
|
||||||
|
* Returns NO_MACHINE_ID when a reliable machine ID cannot be detected.
|
||||||
|
*
|
||||||
|
* @return ND_UUID The machine ID
|
||||||
|
*/
|
||||||
|
ND_UUID os_machine_id(void);
|
||||||
|
|
||||||
|
#endif // NETDATA_MACHINE_ID_H
|
|
@ -40,6 +40,7 @@
|
||||||
#include "run_dir.h"
|
#include "run_dir.h"
|
||||||
#include "file_lock.h"
|
#include "file_lock.h"
|
||||||
#include "mmap_limit.h"
|
#include "mmap_limit.h"
|
||||||
|
#include "machine_id.h"
|
||||||
|
|
||||||
// this includes windows.h to the whole of netdata
|
// this includes windows.h to the whole of netdata
|
||||||
// so various conflicts arise
|
// so various conflicts arise
|
||||||
|
|
Loading…
Add table
Reference in a new issue