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 | |