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 02110-1335 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 */
get_os_version_name(OSVERSIONINFOEX * ver)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 
uname(struct utsname * buf)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 */
show_plugins(THD * thd,plugin_ref plugin,void * arg)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 */
fill_plugin_version(THD * thd,TABLE_LIST * tables)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 */
my_getphysmem()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 */
my_getncpus()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 */
prepare_linux_info()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 */
fill_linux_info(THD * thd,TABLE_LIST * tables)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 */
fill_misc_data(THD * thd,TABLE_LIST * tables)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 
fill_collation_statistics(THD * thd,TABLE_LIST * tables)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 */
calculate_server_uid(char * dest)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