diff options
Diffstat (limited to 'plugin/feedback/utils.cc')
-rw-r--r-- | plugin/feedback/utils.cc | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/plugin/feedback/utils.cc b/plugin/feedback/utils.cc new file mode 100644 index 00000000..c31422cc --- /dev/null +++ b/plugin/feedback/utils.cc @@ -0,0 +1,442 @@ +/* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + 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 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 Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include "feedback.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#if defined (_WIN32) +#define HAVE_SYS_UTSNAME_H + +#ifndef VER_SUITE_WH_SERVER +#define VER_SUITE_WH_SERVER 0x00008000 +#endif + +struct utsname { + char sysname[16]; // Name of this implementation of the operating system. + char nodename[16]; // Name of this node within the communications + // network to which this node is attached, if any. + char release[16]; // Current release level of this implementation. + char version[256]; // Current version level of this release. + char machine[16]; // Name of the hardware type on which the system is running. +}; + +/* Get commonly used name for Windows version */ +static const char *get_os_version_name(OSVERSIONINFOEX *ver) +{ + DWORD major = ver->dwMajorVersion; + DWORD minor = ver->dwMinorVersion; + if (major == 10 && minor == 0) + { + return (ver->wProductType == VER_NT_WORKSTATION) ? + "Windows 10" : "Windows Server 2016"; + } + if (major == 6 && minor == 3) + { + return (ver->wProductType == VER_NT_WORKSTATION)? + "Windows 8.1":"Windows Server 2012 R2"; + } + if (major == 6 && minor == 2) + { + return (ver->wProductType == VER_NT_WORKSTATION)? + "Windows 8":"Windows Server 2012"; + } + if (major == 6 && minor == 1) + { + return (ver->wProductType == VER_NT_WORKSTATION)? + "Windows 7":"Windows Server 2008 R2"; + } + if (major == 6 && minor == 0) + { + return (ver->wProductType == VER_NT_WORKSTATION)? + "Windows Vista":"Windows Server 2008"; + } + if (major == 5 && minor == 2) + { + if (GetSystemMetrics(SM_SERVERR2) != 0) + return "Windows Server 2003 R2"; + if (ver->wSuiteMask & VER_SUITE_WH_SERVER) + return "Windows Home Server"; + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + if (ver->wProductType == VER_NT_WORKSTATION && + sysinfo.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64) + return "Windows XP Professional x64 Edition"; + + return "Windows Server 2003"; + } + if (major == 5 && minor == 1) + return "Windows XP"; + if (major == 5 && minor == 0) + return "Windows 2000"; + + return ""; +} + + +static int uname(struct utsname *buf) +{ + OSVERSIONINFOEX ver; + ver.dwOSVersionInfoSize = (DWORD)sizeof(ver); + /* GetVersionEx got deprecated, we need it anyway, so disable deprecation warnings. */ +#ifdef _MSC_VER +#pragma warning (disable : 4996) +#endif +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + if (!GetVersionEx((OSVERSIONINFO *)&ver)) + return -1; + + buf->nodename[0]= 0; + strcpy(buf->sysname, "Windows"); + sprintf(buf->release, "%d.%d", (int)ver.dwMajorVersion, (int)ver.dwMinorVersion); + + const char *version_str= get_os_version_name(&ver); + if(version_str && version_str[0]) + sprintf(buf->version, "%s %s",version_str, ver.szCSDVersion); + else + { + /* Fallback for unknown versions, e.g "Windows <major_ver>.<minor_ver>" */ + sprintf(buf->version, "Windows %d.%d%s", + (int)ver.dwMajorVersion, (int)ver.dwMinorVersion, + (ver.wProductType == VER_NT_WORKSTATION ? "" : " Server")); + } + +#ifdef _WIN64 + strcpy(buf->machine, "x64"); +#else + BOOL isX64; + if (IsWow64Process(GetCurrentProcess(), &isX64) && isX64) + strcpy(buf->machine, "x64"); + else + strcpy(buf->machine,"x86"); +#endif + return 0; +} + +#elif defined(HAVE_SYS_UTSNAME_H) +#include <sys/utsname.h> +#endif + +#ifdef HAVE_SYS_UTSNAME_H +static bool have_ubuf= false; +static struct utsname ubuf; +#endif + +#ifdef TARGET_OS_LINUX +#include <glob.h> +static bool have_distribution= false; +static char distribution[256]; + +static const char *masks[]= { + "/etc/*-version", "/etc/*-release", + "/etc/*_version", "/etc/*_release" +}; +#endif + +bool schema_table_store_record(THD *thd, TABLE *table); + +namespace feedback { + +/* + convenience macros for inserting rows into I_S table. +*/ +#define INSERT2(NAME,LEN,VALUE) \ + do { \ + table->field[0]->store(NAME, (uint) LEN, system_charset_info); \ + table->field[1]->store VALUE; \ + if (schema_table_store_record(thd, table)) \ + return 1; \ + } while (0) + +#define INSERT1(NAME,VALUE) \ + do { \ + table->field[0]->store(NAME, (uint) sizeof(NAME)-1, system_charset_info); \ + table->field[1]->store VALUE; \ + if (schema_table_store_record(thd, table)) \ + return 1; \ + } while (0) + +static const bool UNSIGNED= true; ///< used below when inserting integers + +/** + callback for fill_plugins() +*/ +static my_bool show_plugins(THD *thd, plugin_ref plugin, void *arg) +{ + TABLE *table= (TABLE*) arg; + char name[NAME_LEN*2]; + size_t name_len; + char version[20]; + size_t version_len; + + name_len= my_snprintf(name, sizeof(name), "%s version", + plugin_name(plugin)->str); + + version_len= my_snprintf(version, sizeof(version), "%d.%d", + (plugin_decl(plugin)->version) >> 8, + (plugin_decl(plugin)->version) & 0xff); + + INSERT2(name, name_len, + (version, (uint)version_len, system_charset_info)); + + name_len= my_snprintf(name, sizeof(name), "%s used", + plugin_name(plugin)->str); + + INSERT2(name, name_len, (plugin_ref_to_int(plugin)->locks_total, UNSIGNED)); + + return 0; +} + +/** + inserts all plugins, their versions, and usage counters +*/ +int fill_plugin_version(THD *thd, TABLE_LIST *tables) +{ + return plugin_foreach_with_mask(thd, show_plugins, MYSQL_ANY_PLUGIN, + ~PLUGIN_IS_FREED, tables->table); +} + +#if defined(_SC_PAGE_SIZE) && !defined(_SC_PAGESIZE) +#define _SC_PAGESIZE _SC_PAGE_SIZE +#endif + +/** + return the amount of physical memory +*/ +static ulonglong my_getphysmem() +{ +#ifdef _WIN32 + MEMORYSTATUSEX memstatus; + memstatus.dwLength= sizeof(memstatus); + GlobalMemoryStatusEx(&memstatus); + return memstatus.ullTotalPhys; +#else + ulonglong pages= 0; + +#ifdef _SC_PHYS_PAGES + pages= sysconf(_SC_PHYS_PAGES); +#endif + +#ifdef _SC_PAGESIZE + return pages * sysconf(_SC_PAGESIZE); +#else + return pages * my_getpagesize(); +#endif +#endif +} + +/* get the number of (online) CPUs */ +int my_getncpus() +{ +#ifdef _SC_NPROCESSORS_ONLN + return sysconf(_SC_NPROCESSORS_ONLN); +#elif defined(_WIN32) + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + return sysinfo.dwNumberOfProcessors; +#else + return 0; +#endif +} + +/** + Find the version of the kernel and the linux distribution +*/ +int prepare_linux_info() +{ +#ifdef HAVE_SYS_UTSNAME_H + have_ubuf= (uname(&ubuf) != -1); +#endif + +#ifdef TARGET_OS_LINUX + /* + let's try to find what linux distribution it is + we read *[-_]{release,version} file in /etc. + + Either it will be /etc/lsb-release, such as + + ==> /etc/lsb-release <== + DISTRIB_ID=Ubuntu + DISTRIB_RELEASE=8.04 + DISTRIB_CODENAME=hardy + DISTRIB_DESCRIPTION="Ubuntu 8.04.4 LTS" + + Or a one-liner with the description (/etc/SuSE-release has more + than one line, but the description is the first, so it can be + treated as a one-liner). + + We'll read lsb-release first, and if it's not found will search + for other files (*-version *-release *_version *_release) +*/ + int fd; + have_distribution= false; + if ((fd= my_open("/etc/lsb-release", O_RDONLY, MYF(0))) != -1) + { + /* Cool, LSB-compliant distribution! */ + size_t len= my_read(fd, (uchar*)distribution, sizeof(distribution)-1, MYF(0)); + my_close(fd, MYF(0)); + if (len != (size_t)-1) + { + distribution[len]= 0; // safety + char *found= strstr(distribution, "DISTRIB_DESCRIPTION="); + if (found) + { + have_distribution= true; + char *end= strstr(found, "\n"); + if (end == NULL) + end= distribution + len; + found+= 20; + + if (*found == '"' && end[-1] == '"') + { + found++; + end--; + } + *end= 0; + + char *to= strmov(distribution, "lsb: "); + memmove(to, found, end - found + 1); + } + } + } + + /* if not an LSB-compliant distribution */ + for (uint i= 0; !have_distribution && i < array_elements(masks); i++) + { + glob_t found; + if (glob(masks[i], GLOB_NOSORT, NULL, &found) == 0) + { + int fd; + if ((fd= my_open(found.gl_pathv[0], O_RDONLY, MYF(0))) != -1) + { + /* + +5 and -8 below cut the file name part out of the + full pathname that corresponds to the mask as above. + */ + char *to= strmov(distribution, found.gl_pathv[0] + 5) - 8; + *to++= ':'; + *to++= ' '; + + size_t to_len= distribution + sizeof(distribution) - 1 - to; + size_t len= my_read(fd, (uchar*)to, to_len, MYF(0)); + my_close(fd, MYF(0)); + if (len != (size_t)-1) + { + to[len]= 0; // safety + char *end= strstr(to, "\n"); + if (end) + *end= 0; + have_distribution= true; + } + } + } + globfree(&found); + } +#endif + return 0; +} + +/** + Add the linux distribution and the kernel version +*/ +int fill_linux_info(THD *thd, TABLE_LIST *tables) +{ +#if defined(HAVE_SYS_UTSNAME_H) || defined(TARGET_OS_LINUX) + TABLE *table= tables->table; + CHARSET_INFO *cs= system_charset_info; +#endif + +#ifdef HAVE_SYS_UTSNAME_H + if (have_ubuf) + { + INSERT1("Uname_sysname", (ubuf.sysname, (uint) strlen(ubuf.sysname), cs)); + INSERT1("Uname_release", (ubuf.release, (uint) strlen(ubuf.release), cs)); + INSERT1("Uname_version", (ubuf.version, (uint) strlen(ubuf.version), cs)); + INSERT1("Uname_machine", (ubuf.machine, (uint) strlen(ubuf.machine), cs)); + } +#endif + +#ifdef TARGET_OS_LINUX + if (have_distribution) + INSERT1("Uname_distribution", (distribution, strlen(distribution), cs)); +#endif + + return 0; +} + +/** + Adds varios bits of information to the I_S.FEEDBACK +*/ +int fill_misc_data(THD *thd, TABLE_LIST *tables) +{ + TABLE *table= tables->table; + + INSERT1("Cpu_count", (my_getncpus(), UNSIGNED)); + INSERT1("Mem_total", (my_getphysmem(), UNSIGNED)); + INSERT1("Now", (thd->query_start(), UNSIGNED)); + + return 0; +} + +int fill_collation_statistics(THD *thd, TABLE_LIST *tables) +{ + TABLE *table= tables->table; + for (uint id= 1; id < MY_ALL_CHARSETS_SIZE; id++) + { + ulonglong count; + if (my_collation_is_known_id(id) && + (count= my_collation_statistics_get_use_count(id))) + { + char name[MY_CS_COLLATION_NAME_SIZE + 32]; + size_t namelen= my_snprintf(name, sizeof(name), + "Collation used %s", + get_charset_name(id)); + INSERT2(name, namelen, (count, UNSIGNED)); + } + } + return 0; +}; + +/** + calculates the server unique identifier + + UID is a base64 encoded SHA1 hash of the MAC address of one of + the interfaces, and the tcp port that the server is listening on +*/ +int calculate_server_uid(char *dest) +{ + uchar rawbuf[2 + 6]; + uchar shabuf[MY_SHA1_HASH_SIZE]; + + int2store(rawbuf, mysqld_port); + if (my_gethwaddr(rawbuf + 2)) + { + sql_print_error("feedback plugin: failed to retrieve the MAC address"); + return 1; + } + + my_sha1((uint8*) shabuf, (char*) rawbuf, sizeof(rawbuf)); + + assert(my_base64_needed_encoded_length(sizeof(shabuf)) <= SERVER_UID_SIZE); + my_base64_encode(shabuf, sizeof(shabuf), dest); + + return 0; +} + +} // namespace feedback |