diff --git a/CMakeLists.txt b/CMakeLists.txt index e3ab47ea19..6af81a2fb6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -987,6 +987,8 @@ set(LIBNETDATA_FILES src/libnetdata/memory/alignment.h src/libnetdata/os/get_system_pagesize.c src/libnetdata/os/get_system_pagesize.h + src/libnetdata/os/hostname.c + src/libnetdata/os/hostname.h ) set(LIBH2O_FILES diff --git a/packaging/cmake/config.cmake.h.in b/packaging/cmake/config.cmake.h.in index 292474da8c..32bad0b85a 100644 --- a/packaging/cmake/config.cmake.h.in +++ b/packaging/cmake/config.cmake.h.in @@ -68,6 +68,7 @@ #cmakedefine HAVE_SETENV #cmakedefine HAVE_DLSYM #cmakedefine HAVE_LIBCURL +#cmakedefine HAVE_LIBICONV #cmakedefine HAVE_ARC4RANDOM_BUF #cmakedefine HAVE_ARC4RANDOM_UNIFORM diff --git a/src/daemon/config/netdata-conf-global.c b/src/daemon/config/netdata-conf-global.c index cf0d609439..7c41586e87 100644 --- a/src/daemon/config/netdata-conf-global.c +++ b/src/daemon/config/netdata-conf-global.c @@ -40,22 +40,6 @@ skip: return processors; } -static int get_hostname(char *buf, size_t buf_size) { - if (netdata_configured_host_prefix && *netdata_configured_host_prefix) { - char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s/etc/hostname", netdata_configured_host_prefix); - - if (!read_txt_file(filename, buf, buf_size)) { - trim(buf); - return 0; - } - } - - int rc = gethostname(buf, buf_size); - buf[buf_size - 1] = '\0'; - return rc; -} - void netdata_conf_glibc_malloc_initialize(size_t wanted_arenas, size_t trim_threshold __maybe_unused) { wanted_arenas = config_get_number(CONFIG_SECTION_GLOBAL, "glibc malloc arena max for plugins", wanted_arenas); if(wanted_arenas < 1 || wanted_arenas > os_get_system_cpus_cached(true)) { @@ -119,8 +103,8 @@ void netdata_conf_section_global(void) { netdata_configured_host_prefix = config_get(CONFIG_SECTION_GLOBAL, "host access prefix", ""); (void) verify_netdata_host_prefix(true); - char buf[HOSTNAME_MAX + 1]; - if (get_hostname(buf, sizeof(buf))) + char buf[HOST_NAME_MAX * 4 + 1]; + if (!os_hostname(buf, sizeof(buf), netdata_configured_host_prefix)) netdata_log_error("Cannot get machine hostname."); netdata_configured_hostname = config_get(CONFIG_SECTION_GLOBAL, "hostname", buf); diff --git a/src/daemon/pulse/pulse-daemon-memory-system.c b/src/daemon/pulse/pulse-daemon-memory-system.c index 8ce7e8766d..ed41b04b62 100644 --- a/src/daemon/pulse/pulse-daemon-memory-system.c +++ b/src/daemon/pulse/pulse-daemon-memory-system.c @@ -107,7 +107,7 @@ cleanup: } #endif // HAVE_C_MALLOC_INFO -void pulse_daemon_memory_system_do(bool extended) { +void pulse_daemon_memory_system_do(bool extended __maybe_unused) { #ifdef HAVE_C_MALLOC_INFO size_t glibc_arenas, glibc_allocated_arenas, glibc_unused_fast, glibc_unused_rest, glibc_allocated_mmap; diff --git a/src/database/rrdlabels.c b/src/database/rrdlabels.c index 24c9db7343..37072d0068 100644 --- a/src/database/rrdlabels.c +++ b/src/database/rrdlabels.c @@ -1577,6 +1577,10 @@ int rrdlabels_unittest_sanitization() { const unsigned char invalid5[] = "app.clewd修改\xe7\x89_fd_open_limits"; errors += rrdlabels_unittest_sanitize_value((const char *)invalid5, "app.clewd修改e789_fd_open_limits"); + // invalid UTF8 No 6 + const unsigned char invalid6[] = "\260\327\312\300\322\242"; + errors += rrdlabels_unittest_sanitize_value((const char *)invalid6, "d7cac0Ң"); + return errors; } diff --git a/src/libnetdata/common.h b/src/libnetdata/common.h index 6124b8d835..13f877c809 100644 --- a/src/libnetdata/common.h +++ b/src/libnetdata/common.h @@ -400,6 +400,12 @@ typedef uint32_t uid_t; // -------------------------------------------------------------------------------------------------------------------- +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX 256 +#endif + +// -------------------------------------------------------------------------------------------------------------------- + #if defined(OS_WINDOWS) #include <windows.h> #include <wctype.h> diff --git a/src/libnetdata/os/hostname.c b/src/libnetdata/os/hostname.c new file mode 100644 index 0000000000..62ec4d0752 --- /dev/null +++ b/src/libnetdata/os/hostname.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "libnetdata/libnetdata.h" + +#if defined(OS_WINDOWS) +bool os_hostname(char *dst, size_t dst_size, const char *filesystem_root __maybe_unused) { + WCHAR wbuf[HOST_NAME_MAX * 4 + 1]; + char buf[HOST_NAME_MAX * 4 + 1]; + DWORD size = _countof(wbuf); + bool success = false; + + // First try DNS hostname + if (GetComputerNameExW(ComputerNameDnsHostname, wbuf, &size)) { + success = true; + } + // Then try NetBIOS name + else { + size = _countof(wbuf); + if (GetComputerNameW(wbuf, &size)) { + success = true; + } + } + + if (success) { + // Convert UTF-16 to UTF-8 + int utf8_size = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, NULL, 0, NULL, NULL); + if (utf8_size > 0 && utf8_size <= (int)sizeof(buf)) { + WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, utf8_size, NULL, NULL); + } + else { + success = false; + } + } + + if (!success) { + // Try getting the machine GUID first + HKEY hKey; + 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) { + // Convert GUID to UTF-8 + if (WideCharToMultiByte(CP_UTF8, 0, guidW, -1, buf, sizeof(buf), NULL, NULL) > 0) { + success = true; + } + } + RegCloseKey(hKey); + } + + if (!success) { + // If machine GUID fails, try getting volume serial number of C: drive + WCHAR rootPath[] = L"C:\\"; + DWORD serialNumber; + if (GetVolumeInformationW(rootPath, NULL, 0, &serialNumber, NULL, NULL, NULL, 0)) { + snprintf(buf, sizeof(buf), "host%08lx", (long unsigned)serialNumber); + success = true; + } + else { + // Last resort: use process ID + snprintf(buf, sizeof(buf), "host%lu", (long unsigned)GetCurrentProcessId()); + } + } + } + + char *hostname = trim(buf); + rrdlabels_sanitize_value(dst, hostname, dst_size); + return *dst != '\0'; +} +#else // !OS_WINDOWS + +#ifdef HAVE_LIBICONV +#include <iconv.h> + +static const char *get_current_locale(void) { + const char *locale = getenv("LC_ALL"); // LC_ALL overrides all other locale settings + + if (!locale || !*locale) { + locale = getenv("LC_CTYPE"); // Check LC_CTYPE for character encoding + + if (!locale || !*locale) + locale = getenv("LANG"); // Fallback to LANG + } + + return locale; +} + +static const char *get_encoding_from_locale(const char *locale) { + if(!locale || !*locale) + return NULL; + + const char *dot = strchr(locale, '.'); + if (dot) + return dot + 1; + + return locale; +} + +// Function to convert from a source encoding to UTF-8 +static bool iconv_convert_to_utf8(const char *input, const char *src_encoding, char *output, size_t output_size) { + iconv_t cd = iconv_open("UTF-8", src_encoding); + if (cd == (iconv_t)-1) { + int i = errno; + return false; + } + + char *input_ptr = (char *)input; // iconv() may modify this pointer + char *output_ptr = output; // iconv() modifies this pointer + size_t input_len = strlen(input); + size_t output_len = output_size; + + // Perform the conversion + if (iconv(cd, &input_ptr, &input_len, &output_ptr, &output_len) == (size_t)-1) { + iconv_close(cd); + return false; + } + + // Null-terminate the output string + *output_ptr = '\0'; + + iconv_close(cd); + return true; +} +#endif + +bool os_hostname(char *dst, size_t dst_size, const char *filesystem_root) { + *dst = '\0'; + + char buf[HOST_NAME_MAX * 4 + 1]; + *buf = '\0'; + + if (filesystem_root && *filesystem_root) { + char filename[FILENAME_MAX + 1]; + snprintfz(filename, FILENAME_MAX, "%s/etc/hostname", netdata_configured_host_prefix); + + if (read_txt_file(filename, buf, sizeof(buf))) + *buf = '\0'; + } + + if(!*buf && gethostname(buf, sizeof(buf)) != 0) + snprintf(buf, sizeof(buf), "host%ld", gethostid()); + + char *original_hostname = trim(buf); + +#ifdef HAVE_LIBICONV + const char *locale = get_current_locale(); + if (locale && *locale) { + char utf8_output[HOST_NAME_MAX * 4 + 1] = ""; + if(iconv_convert_to_utf8(original_hostname, get_encoding_from_locale(locale), utf8_output, sizeof(utf8_output))) { + rrdlabels_sanitize_value(dst, trim(utf8_output), dst_size); + return *dst != '\0'; + } + } +#endif + + rrdlabels_sanitize_value(dst, original_hostname, dst_size); + return *dst != '\0'; +} + +#endif // !OS_WINDOWS diff --git a/src/libnetdata/os/hostname.h b/src/libnetdata/os/hostname.h new file mode 100644 index 0000000000..77c8b2391b --- /dev/null +++ b/src/libnetdata/os/hostname.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef NETDATA_HOSTNAME_H +#define NETDATA_HOSTNAME_H + +#include "libnetdata/libnetdata.h" + +/** + * Get system hostname in UTF-8 encoding + * + * @param dst Buffer to store the hostname + * @param dst_size Size of the destination buffer + * @param filesystem_root Optional root directory (for Unix-like systems, when running in a container) + * @return true on success, false on failure + * + * The function guarantees to return a UTF-8 encoded hostname. + * On Windows, filesystem_root is ignored. + */ + bool os_hostname(char *dst, size_t dst_size, const char *filesystem_root); + +#endif //NETDATA_HOSTNAME_H diff --git a/src/libnetdata/os/os.h b/src/libnetdata/os/os.h index 4168e72333..ee01b18a21 100644 --- a/src/libnetdata/os/os.h +++ b/src/libnetdata/os/os.h @@ -23,6 +23,7 @@ #include "sleep.h" #include "uuid_generate.h" #include "setenv.h" +#include "hostname.h" #include "os-freebsd-wrappers.h" #include "os-macos-wrappers.h" #include "os-windows-wrappers.h" diff --git a/src/web/api/formatters/rrd2json.h b/src/web/api/formatters/rrd2json.h index cf3492ff28..a5f633228f 100644 --- a/src/web/api/formatters/rrd2json.h +++ b/src/web/api/formatters/rrd2json.h @@ -18,8 +18,6 @@ #include "web/server/web_client.h" -#define HOSTNAME_MAX 1024 - void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb); int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, struct query_target *qt, time_t *latest_timestamp);