| 1 | /* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab |
| 2 | |
| 3 | This program is free software; you can redistribute it and/or modify |
| 4 | it under the terms of the GNU General Public License as published by |
| 5 | the Free Software Foundation; version 2 of the License. |
| 6 | |
| 7 | This program is distributed in the hope that it will be useful, |
| 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | GNU General Public License for more details. |
| 11 | |
| 12 | You should have received a copy of the GNU General Public License |
| 13 | along with this program; if not, write to the Free Software |
| 14 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ |
| 15 | |
| 16 | #include "feedback.h" |
| 17 | |
| 18 | #ifdef HAVE_UNISTD_H |
| 19 | #include <unistd.h> |
| 20 | #endif |
| 21 | |
| 22 | #if defined (_WIN32) |
| 23 | #define HAVE_SYS_UTSNAME_H |
| 24 | |
| 25 | #ifndef VER_SUITE_WH_SERVER |
| 26 | #define VER_SUITE_WH_SERVER 0x00008000 |
| 27 | #endif |
| 28 | |
| 29 | struct utsname { |
| 30 | char sysname[16]; // Name of this implementation of the operating system. |
| 31 | char nodename[16]; // Name of this node within the communications |
| 32 | // network to which this node is attached, if any. |
| 33 | char release[16]; // Current release level of this implementation. |
| 34 | char version[256]; // Current version level of this release. |
| 35 | char machine[16]; // Name of the hardware type on which the system is running. |
| 36 | }; |
| 37 | |
| 38 | /* Get commonly used name for Windows version */ |
| 39 | static const char *get_os_version_name(OSVERSIONINFOEX *ver) |
| 40 | { |
| 41 | DWORD major = ver->dwMajorVersion; |
| 42 | DWORD minor = ver->dwMinorVersion; |
| 43 | if (major == 10 && minor == 0) |
| 44 | { |
| 45 | return (ver->wProductType == VER_NT_WORKSTATION) ? |
| 46 | "Windows 10" : "Windows Server 2016" ; |
| 47 | } |
| 48 | if (major == 6 && minor == 3) |
| 49 | { |
| 50 | return (ver->wProductType == VER_NT_WORKSTATION)? |
| 51 | "Windows 8.1" :"Windows Server 2012 R2" ; |
| 52 | } |
| 53 | if (major == 6 && minor == 2) |
| 54 | { |
| 55 | return (ver->wProductType == VER_NT_WORKSTATION)? |
| 56 | "Windows 8" :"Windows Server 2012" ; |
| 57 | } |
| 58 | if (major == 6 && minor == 1) |
| 59 | { |
| 60 | return (ver->wProductType == VER_NT_WORKSTATION)? |
| 61 | "Windows 7" :"Windows Server 2008 R2" ; |
| 62 | } |
| 63 | if (major == 6 && minor == 0) |
| 64 | { |
| 65 | return (ver->wProductType == VER_NT_WORKSTATION)? |
| 66 | "Windows Vista" :"Windows Server 2008" ; |
| 67 | } |
| 68 | if (major == 5 && minor == 2) |
| 69 | { |
| 70 | if (GetSystemMetrics(SM_SERVERR2) != 0) |
| 71 | return "Windows Server 2003 R2" ; |
| 72 | if (ver->wSuiteMask & VER_SUITE_WH_SERVER) |
| 73 | return "Windows Home Server" ; |
| 74 | SYSTEM_INFO sysinfo; |
| 75 | GetSystemInfo(&sysinfo); |
| 76 | if (ver->wProductType == VER_NT_WORKSTATION && |
| 77 | sysinfo.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64) |
| 78 | return "Windows XP Professional x64 Edition" ; |
| 79 | |
| 80 | return "Windows Server 2003" ; |
| 81 | } |
| 82 | if (major == 5 && minor == 1) |
| 83 | return "Windows XP" ; |
| 84 | if (major == 5 && minor == 0) |
| 85 | return "Windows 2000" ; |
| 86 | |
| 87 | return "" ; |
| 88 | } |
| 89 | |
| 90 | |
| 91 | static int uname(struct utsname *buf) |
| 92 | { |
| 93 | OSVERSIONINFOEX ver; |
| 94 | ver.dwOSVersionInfoSize = (DWORD)sizeof(ver); |
| 95 | /* GetVersionEx got deprecated, we need it anyway, so disable deprecation warnings. */ |
| 96 | #ifdef _MSC_VER |
| 97 | #pragma warning (disable : 4996) |
| 98 | #endif |
| 99 | #ifdef __clang__ |
| 100 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
| 101 | #endif |
| 102 | if (!GetVersionEx((OSVERSIONINFO *)&ver)) |
| 103 | return -1; |
| 104 | |
| 105 | buf->nodename[0]= 0; |
| 106 | strcpy(buf->sysname, "Windows" ); |
| 107 | sprintf(buf->release, "%d.%d" , (int)ver.dwMajorVersion, (int)ver.dwMinorVersion); |
| 108 | |
| 109 | const char *version_str= get_os_version_name(&ver); |
| 110 | if(version_str && version_str[0]) |
| 111 | sprintf(buf->version, "%s %s" ,version_str, ver.szCSDVersion); |
| 112 | else |
| 113 | { |
| 114 | /* Fallback for unknown versions, e.g "Windows <major_ver>.<minor_ver>" */ |
| 115 | sprintf(buf->version, "Windows %d.%d%s" , |
| 116 | (int)ver.dwMajorVersion, (int)ver.dwMinorVersion, |
| 117 | (ver.wProductType == VER_NT_WORKSTATION ? "" : " Server" )); |
| 118 | } |
| 119 | |
| 120 | #ifdef _WIN64 |
| 121 | strcpy(buf->machine, "x64" ); |
| 122 | #else |
| 123 | BOOL isX64; |
| 124 | if (IsWow64Process(GetCurrentProcess(), &isX64) && isX64) |
| 125 | strcpy(buf->machine, "x64" ); |
| 126 | else |
| 127 | strcpy(buf->machine,"x86" ); |
| 128 | #endif |
| 129 | return 0; |
| 130 | } |
| 131 | |
| 132 | #elif defined(HAVE_SYS_UTSNAME_H) |
| 133 | #include <sys/utsname.h> |
| 134 | #endif |
| 135 | |
| 136 | #ifdef HAVE_SYS_UTSNAME_H |
| 137 | static bool have_ubuf= false; |
| 138 | static struct utsname ubuf; |
| 139 | #endif |
| 140 | |
| 141 | #ifdef TARGET_OS_LINUX |
| 142 | #include <glob.h> |
| 143 | static bool have_distribution= false; |
| 144 | static char distribution[256]; |
| 145 | |
| 146 | static const char *masks[]= { |
| 147 | "/etc/*-version" , "/etc/*-release" , |
| 148 | "/etc/*_version" , "/etc/*_release" |
| 149 | }; |
| 150 | #endif |
| 151 | |
| 152 | bool schema_table_store_record(THD *thd, TABLE *table); |
| 153 | |
| 154 | namespace feedback { |
| 155 | |
| 156 | /* |
| 157 | convenience macros for inserting rows into I_S table. |
| 158 | */ |
| 159 | #define INSERT2(NAME,LEN,VALUE) \ |
| 160 | do { \ |
| 161 | table->field[0]->store(NAME, (uint) LEN, system_charset_info); \ |
| 162 | table->field[1]->store VALUE; \ |
| 163 | if (schema_table_store_record(thd, table)) \ |
| 164 | return 1; \ |
| 165 | } while (0) |
| 166 | |
| 167 | #define INSERT1(NAME,VALUE) \ |
| 168 | do { \ |
| 169 | table->field[0]->store(NAME, (uint) sizeof(NAME)-1, system_charset_info); \ |
| 170 | table->field[1]->store VALUE; \ |
| 171 | if (schema_table_store_record(thd, table)) \ |
| 172 | return 1; \ |
| 173 | } while (0) |
| 174 | |
| 175 | static const bool UNSIGNED= true; ///< used below when inserting integers |
| 176 | |
| 177 | /** |
| 178 | callback for fill_plugins() |
| 179 | */ |
| 180 | static my_bool show_plugins(THD *thd, plugin_ref plugin, void *arg) |
| 181 | { |
| 182 | TABLE *table= (TABLE*) arg; |
| 183 | char name[NAME_LEN*2]; |
| 184 | size_t name_len; |
| 185 | char version[20]; |
| 186 | size_t version_len; |
| 187 | |
| 188 | name_len= my_snprintf(name, sizeof(name), "%s version" , |
| 189 | plugin_name(plugin)->str); |
| 190 | |
| 191 | version_len= my_snprintf(version, sizeof(version), "%d.%d" , |
| 192 | (plugin_decl(plugin)->version) >> 8, |
| 193 | (plugin_decl(plugin)->version) & 0xff); |
| 194 | |
| 195 | INSERT2(name, name_len, |
| 196 | (version, (uint)version_len, system_charset_info)); |
| 197 | |
| 198 | name_len= my_snprintf(name, sizeof(name), "%s used" , |
| 199 | plugin_name(plugin)->str); |
| 200 | |
| 201 | INSERT2(name, name_len, (plugin_ref_to_int(plugin)->locks_total, UNSIGNED)); |
| 202 | |
| 203 | return 0; |
| 204 | } |
| 205 | |
| 206 | /** |
| 207 | inserts all plugins, their versions, and usage counters |
| 208 | */ |
| 209 | int fill_plugin_version(THD *thd, TABLE_LIST *tables) |
| 210 | { |
| 211 | return plugin_foreach_with_mask(thd, show_plugins, MYSQL_ANY_PLUGIN, |
| 212 | ~PLUGIN_IS_FREED, tables->table); |
| 213 | } |
| 214 | |
| 215 | #if defined(_SC_PAGE_SIZE) && !defined(_SC_PAGESIZE) |
| 216 | #define _SC_PAGESIZE _SC_PAGE_SIZE |
| 217 | #endif |
| 218 | |
| 219 | /** |
| 220 | return the amount of physical memory |
| 221 | */ |
| 222 | static ulonglong my_getphysmem() |
| 223 | { |
| 224 | #ifdef _WIN32 |
| 225 | MEMORYSTATUSEX memstatus; |
| 226 | memstatus.dwLength= sizeof(memstatus); |
| 227 | GlobalMemoryStatusEx(&memstatus); |
| 228 | return memstatus.ullTotalPhys; |
| 229 | #else |
| 230 | ulonglong pages= 0; |
| 231 | |
| 232 | #ifdef _SC_PHYS_PAGES |
| 233 | pages= sysconf(_SC_PHYS_PAGES); |
| 234 | #endif |
| 235 | |
| 236 | #ifdef _SC_PAGESIZE |
| 237 | return pages * sysconf(_SC_PAGESIZE); |
| 238 | #else |
| 239 | return pages * my_getpagesize(); |
| 240 | #endif |
| 241 | #endif |
| 242 | } |
| 243 | |
| 244 | /* get the number of (online) CPUs */ |
| 245 | int my_getncpus() |
| 246 | { |
| 247 | #ifdef _SC_NPROCESSORS_ONLN |
| 248 | return sysconf(_SC_NPROCESSORS_ONLN); |
| 249 | #elif defined(__WIN__) |
| 250 | SYSTEM_INFO sysinfo; |
| 251 | GetSystemInfo(&sysinfo); |
| 252 | return sysinfo.dwNumberOfProcessors; |
| 253 | #else |
| 254 | return 0; |
| 255 | #endif |
| 256 | } |
| 257 | |
| 258 | /** |
| 259 | Find the version of the kernel and the linux distribution |
| 260 | */ |
| 261 | int prepare_linux_info() |
| 262 | { |
| 263 | #ifdef HAVE_SYS_UTSNAME_H |
| 264 | have_ubuf= (uname(&ubuf) != -1); |
| 265 | #endif |
| 266 | |
| 267 | #ifdef TARGET_OS_LINUX |
| 268 | /* |
| 269 | let's try to find what linux distribution it is |
| 270 | we read *[-_]{release,version} file in /etc. |
| 271 | |
| 272 | Either it will be /etc/lsb-release, such as |
| 273 | |
| 274 | ==> /etc/lsb-release <== |
| 275 | DISTRIB_ID=Ubuntu |
| 276 | DISTRIB_RELEASE=8.04 |
| 277 | DISTRIB_CODENAME=hardy |
| 278 | DISTRIB_DESCRIPTION="Ubuntu 8.04.4 LTS" |
| 279 | |
| 280 | Or a one-liner with the description (/etc/SuSE-release has more |
| 281 | than one line, but the description is the first, so it can be |
| 282 | treated as a one-liner). |
| 283 | |
| 284 | We'll read lsb-release first, and if it's not found will search |
| 285 | for other files (*-version *-release *_version *_release) |
| 286 | */ |
| 287 | int fd; |
| 288 | have_distribution= false; |
| 289 | if ((fd= my_open("/etc/lsb-release" , O_RDONLY, MYF(0))) != -1) |
| 290 | { |
| 291 | /* Cool, LSB-compliant distribution! */ |
| 292 | size_t len= my_read(fd, (uchar*)distribution, sizeof(distribution)-1, MYF(0)); |
| 293 | my_close(fd, MYF(0)); |
| 294 | if (len != (size_t)-1) |
| 295 | { |
| 296 | distribution[len]= 0; // safety |
| 297 | char *found= strstr(distribution, "DISTRIB_DESCRIPTION=" ); |
| 298 | if (found) |
| 299 | { |
| 300 | have_distribution= true; |
| 301 | char *end= strstr(found, "\n" ); |
| 302 | if (end == NULL) |
| 303 | end= distribution + len; |
| 304 | found+= 20; |
| 305 | |
| 306 | if (*found == '"' && end[-1] == '"') |
| 307 | { |
| 308 | found++; |
| 309 | end--; |
| 310 | } |
| 311 | *end= 0; |
| 312 | |
| 313 | char *to= strmov(distribution, "lsb: " ); |
| 314 | memmove(to, found, end - found + 1); |
| 315 | } |
| 316 | } |
| 317 | } |
| 318 | |
| 319 | /* if not an LSB-compliant distribution */ |
| 320 | for (uint i= 0; !have_distribution && i < array_elements(masks); i++) |
| 321 | { |
| 322 | glob_t found; |
| 323 | if (glob(masks[i], GLOB_NOSORT, NULL, &found) == 0) |
| 324 | { |
| 325 | int fd; |
| 326 | if ((fd= my_open(found.gl_pathv[0], O_RDONLY, MYF(0))) != -1) |
| 327 | { |
| 328 | /* |
| 329 | +5 and -8 below cut the file name part out of the |
| 330 | full pathname that corresponds to the mask as above. |
| 331 | */ |
| 332 | char *to= strmov(distribution, found.gl_pathv[0] + 5) - 8; |
| 333 | *to++= ':'; |
| 334 | *to++= ' '; |
| 335 | |
| 336 | size_t to_len= distribution + sizeof(distribution) - 1 - to; |
| 337 | size_t len= my_read(fd, (uchar*)to, to_len, MYF(0)); |
| 338 | my_close(fd, MYF(0)); |
| 339 | if (len != (size_t)-1) |
| 340 | { |
| 341 | to[len]= 0; // safety |
| 342 | char *end= strstr(to, "\n" ); |
| 343 | if (end) |
| 344 | *end= 0; |
| 345 | have_distribution= true; |
| 346 | } |
| 347 | } |
| 348 | } |
| 349 | globfree(&found); |
| 350 | } |
| 351 | #endif |
| 352 | return 0; |
| 353 | } |
| 354 | |
| 355 | /** |
| 356 | Add the linux distribution and the kernel version |
| 357 | */ |
| 358 | int fill_linux_info(THD *thd, TABLE_LIST *tables) |
| 359 | { |
| 360 | #if defined(HAVE_SYS_UTSNAME_H) || defined(TARGET_OS_LINUX) |
| 361 | TABLE *table= tables->table; |
| 362 | CHARSET_INFO *cs= system_charset_info; |
| 363 | #endif |
| 364 | |
| 365 | #ifdef HAVE_SYS_UTSNAME_H |
| 366 | if (have_ubuf) |
| 367 | { |
| 368 | INSERT1("Uname_sysname" , (ubuf.sysname, (uint) strlen(ubuf.sysname), cs)); |
| 369 | INSERT1("Uname_release" , (ubuf.release, (uint) strlen(ubuf.release), cs)); |
| 370 | INSERT1("Uname_version" , (ubuf.version, (uint) strlen(ubuf.version), cs)); |
| 371 | INSERT1("Uname_machine" , (ubuf.machine, (uint) strlen(ubuf.machine), cs)); |
| 372 | } |
| 373 | #endif |
| 374 | |
| 375 | #ifdef TARGET_OS_LINUX |
| 376 | if (have_distribution) |
| 377 | INSERT1("Uname_distribution" , (distribution, strlen(distribution), cs)); |
| 378 | #endif |
| 379 | |
| 380 | return 0; |
| 381 | } |
| 382 | |
| 383 | /** |
| 384 | Adds varios bits of information to the I_S.FEEDBACK |
| 385 | */ |
| 386 | int fill_misc_data(THD *thd, TABLE_LIST *tables) |
| 387 | { |
| 388 | TABLE *table= tables->table; |
| 389 | |
| 390 | INSERT1("Cpu_count" , (my_getncpus(), UNSIGNED)); |
| 391 | INSERT1("Mem_total" , (my_getphysmem(), UNSIGNED)); |
| 392 | INSERT1("Now" , (thd->query_start(), UNSIGNED)); |
| 393 | |
| 394 | return 0; |
| 395 | } |
| 396 | |
| 397 | int fill_collation_statistics(THD *thd, TABLE_LIST *tables) |
| 398 | { |
| 399 | TABLE *table= tables->table; |
| 400 | for (uint id= 1; id < MY_ALL_CHARSETS_SIZE; id++) |
| 401 | { |
| 402 | ulonglong count; |
| 403 | if (my_collation_is_known_id(id) && |
| 404 | (count= my_collation_statistics_get_use_count(id))) |
| 405 | { |
| 406 | char name[MY_CS_NAME_SIZE + 32]; |
| 407 | size_t namelen= my_snprintf(name, sizeof(name), |
| 408 | "Collation used %s" , |
| 409 | get_charset_name(id)); |
| 410 | INSERT2(name, namelen, (count, UNSIGNED)); |
| 411 | } |
| 412 | } |
| 413 | return 0; |
| 414 | }; |
| 415 | |
| 416 | /** |
| 417 | calculates the server unique identifier |
| 418 | |
| 419 | UID is a base64 encoded SHA1 hash of the MAC address of one of |
| 420 | the interfaces, and the tcp port that the server is listening on |
| 421 | */ |
| 422 | int calculate_server_uid(char *dest) |
| 423 | { |
| 424 | uchar rawbuf[2 + 6]; |
| 425 | uchar shabuf[MY_SHA1_HASH_SIZE]; |
| 426 | |
| 427 | int2store(rawbuf, mysqld_port); |
| 428 | if (my_gethwaddr(rawbuf + 2)) |
| 429 | { |
| 430 | sql_print_error("feedback plugin: failed to retrieve the MAC address" ); |
| 431 | return 1; |
| 432 | } |
| 433 | |
| 434 | my_sha1((uint8*) shabuf, (char*) rawbuf, sizeof(rawbuf)); |
| 435 | |
| 436 | assert(my_base64_needed_encoded_length(sizeof(shabuf)) <= SERVER_UID_SIZE); |
| 437 | my_base64_encode(shabuf, sizeof(shabuf), dest); |
| 438 | |
| 439 | return 0; |
| 440 | } |
| 441 | |
| 442 | } // namespace feedback |
| 443 | |