library/base/util_functions.cpp (627 lines of code) (raw):

/* * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2.0, * as published by the Free Software Foundation. * * This program is designed to work with certain software (including * but not limited to OpenSSL) that is licensed under separate terms, as * designated in a particular file or component or in included license * documentation. The authors of MySQL hereby grant you an additional * permission to link the program and your derivative works with the * separately licensed software that they have either included with * the program or referenced in the documentation. * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License, version 2.0, for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <glib/gstdio.h> #include <cstdio> #include <sstream> #include <fstream> #include <vector> #include "base/log.h" #include "base/common.h" #include "base/string_utilities.h" #include "base/file_utilities.h" #include "workbench/wb_version.h" // Windows includes #ifdef _MSC_VER #include <windows.h> #include <direct.h> #include <tchar.h> #include <strsafe.h> #else // unix/linux includes #include <string.h> #include <stdlib.h> #include <ctype.h> #include <assert.h> #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <sys/time.h> #ifdef HAVE_SYS_SELECT_H #include <sys/select.h> #endif #include <errno.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> #include <sys/utsname.h> // uname() #include <fcntl.h> #define SIZE_T size_t #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #endif #if defined(__APPLE__) && defined(__MACH__) #include <sys/sysctl.h> #include <mach/machine.h> #endif #include "base/file_functions.h" #include "base/util_functions.h" DEFAULT_LOG_DOMAIN(DOMAIN_BASE) struct hardware_info { std::string _cpu; std::string _clock; unsigned int _cpu_count; std::int64_t _memory_in_bytes; }; //---------------------------------------------------------------------------------------------------------------------- char *auto_line_break(const char *txt, unsigned int width, char sep) { char *dst = (char *)g_malloc((width + 2) * 80); unsigned int i, o = 0, p = 0, w = 0, l = (unsigned int)strlen(txt); for (i = 0; i < l;) { w++; if (w > width) { #if defined(_MSC_VER) dst[p + o] = '\r'; dst[p + o + 1] = '\n'; o += 1; #else dst[p + o] = '\n'; #endif i = p + 1; w = 0; } else { dst[i + o] = txt[i]; if (txt[i] == sep) p = i; i++; } } dst[i + o] = 0; return dst; } //---------------------------------------------------------------------------------------------------------------------- int str_is_numeric(const char *str) { unsigned int len = (unsigned int)strlen(str); unsigned int i; for (i = 0; i < len; i++) if (g_ascii_digit_value(str[i]) == -1) return 0; return 1; } //---------------------------------------------------------------------------------------------------------------------- char *str_toupper(char *str) { char *s = str; while (*s) { *s = (char)toupper(*s); s++; } return str; } //---------------------------------------------------------------------------------------------------------------------- #if defined(_MSC_VER) #define BUFSIZE 256 #define VER_SUITE_WH_SERVER 0x00008000 int get_value_from_registry(HKEY root_key, const char *sub_key, const char *key, const char *def, char *value, int target_size) { HKEY hSubKey; DWORD dwType; DWORD dwSize; LONG retval; unsigned char Buffer[512]; // Explicitly link to ANSI version, we are using ANSI key names only here. if ((retval = RegOpenKeyExA(root_key, sub_key, 0, KEY_READ, &hSubKey)) == ERROR_SUCCESS) { dwSize = 512; if ((RegQueryValueExA(hSubKey, key, NULL, &dwType, Buffer, &dwSize)) == ERROR_SUCCESS) { if (dwType == REG_DWORD) { sprintf_s(value, target_size, "%d", Buffer[0] + Buffer[1] * 256 + Buffer[2] * 65536 + Buffer[3] * 16777216); } else { StringCchCopy((LPTSTR)value, BUFSIZE, (LPTSTR)Buffer); } } else { StringCchCopy((LPTSTR)value, BUFSIZE, (LPTSTR)def); } return 0; } else { StringCchCopy((LPTSTR)value, BUFSIZE, (LPTSTR) ""); return retval; } } //---------------------------------------------------------------------------------------------------------------------- int set_value_to_registry(HKEY root_key, const char *sub_key, const char *key, const char *value) { HKEY hSubKey; LONG retval; DWORD dwDispo; if ((retval = RegCreateKeyExA(root_key, sub_key, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hSubKey, &dwDispo)) == ERROR_SUCCESS) { retval = RegSetValueExA(hSubKey, key, 0, REG_SZ, (const BYTE *)value, (DWORD)strlen(value) + 1); if (retval != ERROR_SUCCESS) { return GetLastError(); } else { return 0; } } else { return -1; } } //---------------------------------------------------------------------------------------------------------------------- typedef void(WINAPI *PGNSI)(LPSYSTEM_INFO); typedef BOOL(WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD); std::string get_local_os_name() { std::string result; char buffer[BUFSIZE]; std::string product_name; if (get_value_from_registry(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "ProductName", "", buffer, BUFSIZE) == 0) product_name = buffer; std::string csd_version; if (get_value_from_registry(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "CSDVersion", "", buffer, BUFSIZE) == 0) csd_version = buffer; if (!product_name.empty()) { result = (base::hasPrefix(product_name, "Microsoft") ? "" : "Microsoft ") + product_name; if (!csd_version.empty()) result += " " + csd_version; } return result; } //---------------------------------------------------------------------------------------------------------------------- std::string get_local_hardware_info() { char *hardware_string; SYSTEM_INFO sysinfo; MEMORYSTATUSEX memstat; char processor_name[BUFSIZE]; char processor_mhz[BUFSIZE]; char total_phys_ram[BUFSIZE]; DWORDLONG total_phys_ram_val; int target_size; GetSystemInfo(&sysinfo); memstat.dwLength = sizeof(memstat); GlobalMemoryStatusEx(&memstat); ZeroMemory(processor_mhz, sizeof(processor_mhz)); ZeroMemory(processor_name, sizeof(processor_name)); get_value_from_registry(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", "~MHz", "", processor_mhz, BUFSIZE); get_value_from_registry(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", "ProcessorNameString", "", processor_name, BUFSIZE); if (!processor_name[0]) { get_value_from_registry(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", "Identifier", "", processor_name, BUFSIZE); sprintf_s(processor_name, BUFSIZE, "%s %s", processor_name, processor_mhz); } base::trim(processor_name); total_phys_ram_val = memstat.ullTotalPhys; if (total_phys_ram_val >= 1072410624 / 1.9) { sprintf_s(total_phys_ram, BUFSIZE, "%1.1f GiB RAM", (double)total_phys_ram_val / 1072410624.0); } else if (total_phys_ram_val >= 1047276 / 1.9) { sprintf_s(total_phys_ram, BUFSIZE, "%1.0f MiB RAM", (double)total_phys_ram_val / 1047276.0); } else { sprintf_s(total_phys_ram, BUFSIZE, "%lld B RAM", total_phys_ram_val); } target_size = 16 + (int)strlen(processor_name) + (int)strlen(total_phys_ram); hardware_string = (char *)g_malloc(target_size); if (sysinfo.dwNumberOfProcessors > 1) { sprintf_s(hardware_string, target_size, "%dx %s, %s", sysinfo.dwNumberOfProcessors, processor_name, total_phys_ram); } else { sprintf_s(hardware_string, target_size, "%s, %s", processor_name, total_phys_ram); } return hardware_string; } #else #if defined(__APPLE__) && defined(__MACH__) std::string get_local_os_name() { struct utsname info; if (uname(&info) < 0) return "unknown"; char *dot_position = strstr(info.release, "."); if (dot_position == NULL) return "unknown"; *dot_position = 0; int version = base::atoi<int>(info.release, 0); switch (version) { case 23: return std::string("macOS 14.x Sonoma ") + info.machine; case 22: return std::string("macOS 13.x Ventura ") + info.machine; case 21: return std::string("macOS 12.x Monterey ") + info.machine; case 20: return std::string("macOS 11.x Big Sur ") + info.machine; case 19: return std::string("macOS 10.15.x Catalina ") + info.machine; } return "unknown"; } static const char *get_cpu_type_name(int cpu_type, int cpu_subtype) { switch (cpu_type) { case CPU_TYPE_I386: switch (cpu_subtype) { case CPU_SUBTYPE_386: return "80386"; case CPU_SUBTYPE_486: return "80486"; case CPU_SUBTYPE_486SX: return "80486SX"; case CPU_SUBTYPE_PENT: return "Pentium"; case CPU_SUBTYPE_PENTPRO: return "Pentium Pro"; case CPU_SUBTYPE_PENTII_M3: return "Pentium II"; case CPU_SUBTYPE_PENTII_M5: return "Pentium II"; #ifdef CPU_SUBTYPE_CELERON case CPU_SUBTYPE_CELERON: return "Celeron"; #endif #ifdef CPU_SUBTYPE_CELERON_MOBILE case CPU_SUBTYPE_CELERON_MOBILE: return "Celeron Mobile"; #endif #ifdef CPU_SUBTYPE_PENTIUM_3 case CPU_SUBTYPE_PENTIUM_3: return "Pentium III"; #endif #ifdef CPU_SUBTYPE_PENTIUM_3_M case CPU_SUBTYPE_PENTIUM_3_M: return "Pentium III M"; #endif #ifdef CPU_SUBTYPE_PENTIUM_3_XEON case CPU_SUBTYPE_PENTIUM_3_XEON: return "Pentium III Xeon"; #endif #ifdef CPU_SUBTYPE_PENTIUM_M case CPU_SUBTYPE_PENTIUM_M: return "Pentium M"; #endif #ifdef CPU_SUBTYPE_PENTIUM_4 case CPU_SUBTYPE_PENTIUM_4: return "Pentium 4"; #endif #ifdef CPU_SUBTYPE_PENTIUM_4_M case CPU_SUBTYPE_PENTIUM_4_M: return "Pentium 4M"; #endif #ifdef CPU_SUBTYPE_ITANIUM case CPU_SUBTYPE_ITANIUM: return "Itanium"; #endif #ifdef CPU_SUBTYPE_ITANIUM_2 case CPU_SUBTYPE_ITANIUM_2: return "Itanium 2"; #endif #ifdef CPU_SUBTYPE_XEON case CPU_SUBTYPE_XEON: return "Xeon"; #endif #ifdef CPU_SUBTYPE_XEON_MP case CPU_SUBTYPE_XEON_MP: return "Xeon MP"; #endif default: return "x86"; } break; case CPU_TYPE_POWERPC: switch (cpu_subtype) { case CPU_SUBTYPE_POWERPC_601: return "PowerPC 601"; case CPU_SUBTYPE_POWERPC_602: return "PowerPC 602"; case CPU_SUBTYPE_POWERPC_603: return "PowerPC 603"; case CPU_SUBTYPE_POWERPC_603e: return "PowerPC 603e"; case CPU_SUBTYPE_POWERPC_603ev: return "PowerPC 603ev"; case CPU_SUBTYPE_POWERPC_604: return "PowerPC 604"; case CPU_SUBTYPE_POWERPC_604e: return "PowerPC 604e"; case CPU_SUBTYPE_POWERPC_620: return "PowerPC 620"; case CPU_SUBTYPE_POWERPC_750: return "PowerPC G3"; case CPU_SUBTYPE_POWERPC_7400: return "PowerPC G4"; case CPU_SUBTYPE_POWERPC_7450: return "PowerPC G4"; case CPU_SUBTYPE_POWERPC_970: return "PowerPC G5"; default: return "PowerPC"; } break; } return "Unknown"; } //---------------------------------------------------------------------------------------------------------------------- // macOS static int _get_hardware_info(hardware_info &info) { int mib[2]; size_t length; int tmp; char *mclass; int cpu_type, cpu_subtype; if (sysctlbyname("machdep.cpu.brand_string", NULL, &length, NULL, 0) != -1) { char *cpu = (char *)g_malloc(length + 1); sysctlbyname("machdep.cpu.brand_string", cpu, &length, NULL, 0); info._cpu = base::trim(cpu, " \n"); g_free(cpu); } else { // get machine class mib[0] = CTL_HW; mib[1] = HW_MACHINE; sysctl(mib, 2, NULL, &length, NULL, 0); mclass = (char *)g_malloc(length * sizeof(char *)); sysctl(mib, 2, mclass, &length, NULL, 0); // get machine arch length = sizeof(cpu_type); sysctlbyname("hw.cputype", &cpu_type, &length, NULL, 0); length = sizeof(cpu_subtype); sysctlbyname("hw.cpusubtype", &cpu_subtype, &length, NULL, 0); info._cpu = base::strfmt("%s (%s)", mclass, get_cpu_type_name(cpu_type, cpu_subtype)); g_free(mclass); // get cpu clock mib[0] = CTL_HW; mib[1] = HW_CPU_FREQ; length = sizeof(tmp); if (sysctl(mib, 2, &tmp, &length, NULL, 0) < 0) info._clock = base::strfmt("?"); else info._clock = base::strfmt("%.01f", (double)tmp / 1000000.0); } // get cpu count mib[0] = CTL_HW; mib[1] = HW_NCPU; length = sizeof(info._cpu_count); if (sysctl(mib, 2, &info._cpu_count, &length, NULL, 0) < 0) info._cpu_count = 1; // get memory size info._memory_in_bytes = get_physical_memory_size(); return 0; } //---------------------------------------------------------------------------------------------------------------------- #else // Linux/Unix version std::string get_local_os_name() { auto is_debian_based = [](struct utsname &info) -> bool { return strstr(info.version, "Ubuntu") or strstr(info.version, "Debian"); }; // get distro name and version - Debian-based systems auto get_lsb_release_param = [](char param) -> std::string { char cmd[] = "lsb_release -_"; cmd[sizeof(cmd) - 2] = param; // replace _ with param int rc; char *stdo; std::string result; GError *error; if (g_spawn_command_line_sync(cmd, &stdo, NULL, &rc, &error) && stdo) { char *d = strchr(stdo, ':'); if (d) result = base::trim(g_strchug(d + 1)); g_free(stdo); return result; } else { logError("Error executing lsb_release -%c: %s\n", param, error->message); return std::string("unknown"); } }; // get distro name and version - Red-Hat-based systems auto cat_redhat_release = []() -> std::string { std::ifstream is; try { is.open("/etc/redhat-release", std::ifstream::in); char buf[256]; is.getline(buf, 256); return std::string(buf); } catch (const std::ios_base::failure &e) { logError("Error reading /etc/redhat-release: %s\n", e.what()); return std::string("unknown"); } }; struct utsname info; if (uname(&info) < 0) return "unknown"; else if (is_debian_based(info)) return get_lsb_release_param('i') + ' ' + get_lsb_release_param('r') + ' ' + info.machine; else return cat_redhat_release() + ' ' + info.machine; } //---------------------------------------------------------------------------------------------------------------------- static int _get_hardware_info(hardware_info &info) { FILE *proc; char line[256]; // fetch processor info from /proc/cpuinfo proc = fopen("/proc/cpuinfo", "r"); if (!proc) { return -1; } info._cpu_count = 0; while (!feof(proc)) { if (!fgets(line, sizeof(line), proc)) break; if (base::hasPrefix(line, "model name")) { info._cpu_count++; info._cpu = base::trim(base::split(line, ":")[1], " \n"); } else if (base::hasPrefix(line, "cpu MHz")) { info._clock = base::trim(base::split(line, ":")[1], " \n"); } } fclose(proc); info._memory_in_bytes = get_physical_memory_size(); return 0; } #endif //---------------------------------------------------------------------------------------------------------------------- std::string get_local_hardware_info() { std::stringstream hardware_string; hardware_info info; _get_hardware_info(info); if (info._cpu_count > 1) hardware_string << info._cpu_count << "x "; hardware_string << info._cpu; if (!info._clock.empty()) hardware_string << " (" << info._clock << "MHz)"; hardware_string << " - " << base::sizefmt(info._memory_in_bytes, false) << " RAM"; return hardware_string.str(); } #endif //---------------------------------------------------------------------------------------------------------------------- std::int64_t get_physical_memory_size() { #if defined(_MSC_VER) MEMORYSTATUS memstat; GlobalMemoryStatus(&memstat); return memstat.dwTotalPhys; #elif defined(__APPLE__) std::uint64_t mem64; int mib[2]; int mem32; size_t length; mib[0] = CTL_HW; mib[1] = HW_MEMSIZE; length = sizeof(mem64); if (sysctl(mib, 2, &mem64, &length, NULL, 0) < 0) { mib[0] = CTL_HW; mib[1] = HW_PHYSMEM; length = sizeof(mem32); sysctl(mib, 2, &mem32, &length, NULL, 0); mem64 = mem32; } return mem64; #else FILE *proc; std::int64_t mem64; mem64 = 0; // fetch physical memory info from /proc/meminfo proc = fopen("/proc/meminfo", "r"); if (proc) { char line[1024]; char *ptr, *end; while (fgets(line, sizeof(line), proc)) { if (strncasecmp(line, "MemTotal:", sizeof("MemTotal:") - 1) == 0) { char *line_end = line + strlen(line); ptr = strchr(line, ':') + 1; while (*ptr && *ptr == ' ') ptr++; end = strchr(ptr, ' '); if (end) *end = 0; if (end < line_end) end++; if (strstr(end, "gB") || strstr(end, "GB")) mem64 = strtoul(base::trim(ptr).c_str(), NULL, 10) * 1024 * 1024 * 1024LL; else if (strstr(end, "mB") || strstr(end, "MB")) mem64 = strtoul(base::trim(ptr).c_str(), NULL, 10) * 1024 * 1024LL; else if (strstr(end, "kB") || strstr(end, "KB")) mem64 = strtoul(base::trim(ptr).c_str(), NULL, 10) * 1024LL; else mem64 = strtoul(base::trim(ptr).c_str(), NULL, 10); break; } } fclose(proc); } return mem64; #endif } //---------------------------------------------------------------------------------------------------------------------- std::int64_t get_file_size(const char *filename) { #if _MSC_VER DWORD dwSizeLow; DWORD dwSizeHigh = 0; HANDLE hfile; std::wstring name = base::string_to_wstring(filename); hfile = CreateFile(name.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hfile != INVALID_HANDLE_VALUE) { dwSizeLow = GetFileSize(hfile, &dwSizeHigh); CloseHandle(hfile); if ((dwSizeLow == INVALID_FILE_SIZE) && (GetLastError()) != NO_ERROR) { return -1; } else { return (((std::int64_t)dwSizeHigh << 32) + dwSizeLow); } } else return -1; #else //! WINDOWS struct stat buf; char *local_filename; if (!(local_filename = g_filename_from_utf8(filename, -1, NULL, NULL, NULL))) return -1; if (stat(local_filename, &buf) < 0) { g_free(local_filename); return -1; } g_free(local_filename); return buf.st_size; #endif //! WINDOWS } // note, needle has to be ascii! char *strcasestr_len(const char *haystack, int haystack_len, const char *needle) { gssize needle_len = (gssize)strlen(needle); int i; if (needle_len > haystack_len) return NULL; i = 0; while (i <= haystack_len - needle_len) { if (g_ascii_strncasecmp(needle, haystack + i, needle_len) == 0) return (char *)haystack + i; i++; } return NULL; } //---------------------------------------------------------------------------------------------------------------------- const char *strfindword(const char *str, const char *word) { const char *result = NULL; const char *ptr; size_t wordlen = strlen(word); ptr = str; for (;;) { // find match ptr = strcasestr_len(ptr, (int)strlen(ptr), word); if (!ptr) break; // check if its acceptable if ((ptr == str || !isalnum(*(ptr - 1))) && // space or any other non-alpha-numeric before (!isalnum(*(ptr + wordlen)) || *(ptr + wordlen) == '\0')) // space or any other non-alpha-numeric after { result = ptr; break; }; ptr += wordlen; } return result; } //-------------------------------------------------------------------------------------------------- // TODO: move to file_functions /** * Copies all files non-recursively from source to target. Target will be created on the fly. */ int copy_folder(const char *source_folder, const char *target_folder) { const char *entry; GDir *dir; // Create target folder. if (!g_file_test(target_folder, G_FILE_TEST_IS_DIR)) if (g_mkdir(target_folder, 0700) < 0) return 0; dir = g_dir_open(source_folder, 0, NULL); if (dir) { while ((entry = g_dir_read_name(dir)) != NULL) { char *source = g_build_filename(source_folder, entry, NULL); char *target = g_build_filename(target_folder, entry, NULL); if (!base::copyFile(source, target)) { logWarning("Could not copy file %s to %s: %s\n", source, target, g_strerror(errno)); g_free(source); g_free(target); g_dir_close(dir); return 0; } g_free(source); g_free(target); } g_dir_close(dir); } else { logWarning("Could not open directory %s\n", source_folder); return 0; } return 1; } //-------------------------------------------------------------------------------------------------- namespace base { double timestamp() { #if defined(_MSC_VER) return (double)GetTickCount() / 1000.0; #else struct timeval tv; gettimeofday(&tv, NULL); double seconds = tv.tv_sec; double fraction = tv.tv_usec / (double)(1000000); return seconds + fraction; #endif } //-------------------------------------------------------------------------------------------------- std::string fmttime(time_t t, const char *fmt) { char date[100]; #ifdef _MSC_VER errno_t err; #else int err; #endif struct tm newtime; if (t == 0) time(&t); #ifdef _MSC_VER err = localtime_s(&newtime, &t); #else localtime_r(&t, &newtime); err = 0; #endif if (!err) strftime(date, sizeof(date), fmt, &newtime); else date[0] = 0; return date; } BASELIBRARY_PUBLIC_FUNC std::string getVersion(void) { return strfmt("%u.%u.%u", APP_MAJOR_NUMBER, APP_MINOR_NUMBER, APP_RELEASE_NUMBER); } } // namespace base //--------------------------------------------------------------------------------------------------