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
29struct 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 */
39static 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
91static 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
137static bool have_ubuf= false;
138static struct utsname ubuf;
139#endif
140
141#ifdef TARGET_OS_LINUX
142#include <glob.h>
143static bool have_distribution= false;
144static char distribution[256];
145
146static const char *masks[]= {
147 "/etc/*-version", "/etc/*-release",
148 "/etc/*_version", "/etc/*_release"
149};
150#endif
151
152bool schema_table_store_record(THD *thd, TABLE *table);
153
154namespace 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
175static const bool UNSIGNED= true; ///< used below when inserting integers
176
177/**
178 callback for fill_plugins()
179*/
180static 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*/
209int 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*/
222static 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 */
245int 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*/
261int 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*/
358int 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*/
386int 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
397int 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*/
422int 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