1 /* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
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, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    Without limiting anything contained in the foregoing, this file,
15    which is part of C Driver for MySQL (Connector/C), is also subject to the
16    Universal FOSS Exception, version 1.0, a copy of which can be found at
17    http://oss.oracle.com/licenses/universal-foss-exception.
18 
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22    GNU General Public License, version 2.0, for more details.
23 
24    You should have received a copy of the GNU General Public License
25    along with this program; if not, write to the Free Software
26    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
27 
28 #include "mysys_priv.h"
29 #include "my_sys.h"
30 #include "my_static.h"
31 #include "mysys_err.h"
32 #include "m_string.h"
33 #include "mysql/psi/mysql_stage.h"
34 #include "mysql/psi/mysql_file.h"
35 
36 #ifdef HAVE_SYS_RESOURCE_H
37 #include <sys/resource.h>
38 #endif
39 
40 #ifdef _WIN32
41 #include <locale.h>
42 #include <crtdbg.h>
43 /* WSAStartup needs winsock library*/
44 #pragma comment(lib, "ws2_32")
45 my_bool have_tcpip=0;
46 static void my_win_init();
47 my_bool win_init_get_system_time_as_file_time();
48 #endif
49 
50 #define SCALE_SEC       100
51 #define SCALE_USEC      10000
52 
53 my_bool my_init_done= FALSE;
54 ulong  my_thread_stack_size= 65536;
55 MYSQL_FILE *mysql_stdin= NULL;
56 static MYSQL_FILE instrumented_stdin;
57 
58 
atoi_octal(const char * str)59 static ulong atoi_octal(const char *str)
60 {
61   long int tmp;
62   while (*str && my_isspace(&my_charset_latin1, *str))
63     str++;
64   str2int(str,
65 	  (*str == '0' ? 8 : 10),       /* Octalt or decimalt */
66 	  0, INT_MAX, &tmp);
67   return (ulong) tmp;
68 }
69 
70 
71 #if defined(MY_MSCRT_DEBUG)
set_crt_report_leaks()72 int set_crt_report_leaks()
73 {
74   HANDLE hLogFile;
75 
76   _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF        // debug allocation on
77                  | _CRTDBG_LEAK_CHECK_DF     // leak checks on exit
78                  | _CRTDBG_CHECK_ALWAYS_DF   // memory check (slow)
79                  );
80 
81   return ((
82     NULL == (hLogFile= GetStdHandle(STD_ERROR_HANDLE)) ||
83     -1 == _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE) ||
84     _CRTDBG_HFILE_ERROR == _CrtSetReportFile(_CRT_WARN, hLogFile) ||
85     -1 == _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE) ||
86     _CRTDBG_HFILE_ERROR == _CrtSetReportFile(_CRT_ERROR, hLogFile) ||
87     -1 == _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE) ||
88     _CRTDBG_HFILE_ERROR == _CrtSetReportFile(_CRT_ASSERT, hLogFile)) ? 1 : 0);
89 }
90 #endif
91 
92 
93 /**
94   Initialize my_sys functions, resources and variables
95 
96   @return Initialization result
97     @retval FALSE Success
98     @retval TRUE  Error. Couldn't initialize environment
99 */
my_init()100 my_bool my_init()
101 {
102   char *str;
103 
104   if (my_init_done)
105     return FALSE;
106 
107   my_init_done= TRUE;
108 
109 #if defined(MY_MSCRT_DEBUG)
110   set_crt_report_leaks();
111 #endif
112 
113   my_umask= 0640;                       /* Default umask for new files */
114   my_umask_dir= 0750;                   /* Default umask for new directories */
115 
116   /* Default creation of new files */
117   if ((str= getenv("UMASK")) != 0)
118     my_umask= (int) (atoi_octal(str) | 0600);
119   /* Default creation of new dir's */
120   if ((str= getenv("UMASK_DIR")) != 0)
121     my_umask_dir= (int) (atoi_octal(str) | 0700);
122 
123   instrumented_stdin.m_file= stdin;
124   instrumented_stdin.m_psi= NULL;       /* not yet instrumented */
125   mysql_stdin= & instrumented_stdin;
126 
127   if (my_thread_global_init())
128     return TRUE;
129 
130   if (my_thread_init())
131     return TRUE;
132 
133   /* $HOME is needed early to parse configuration files located in ~/ */
134   if ((home_dir= getenv("HOME")) != 0)
135     home_dir= intern_filename(home_dir_buff, home_dir);
136 
137   {
138     DBUG_ENTER("my_init");
139     DBUG_PROCESS((char*) (my_progname ? my_progname : "unknown"));
140 #ifdef _WIN32
141     my_win_init();
142     if (win_init_get_system_time_as_file_time())
143       DBUG_RETURN(TRUE);
144 #endif
145     DBUG_PRINT("exit", ("home: '%s'", home_dir));
146     DBUG_RETURN(FALSE);
147   }
148 } /* my_init */
149 
150 
151 	/* End my_sys */
152 
my_end(int infoflag)153 void my_end(int infoflag)
154 {
155   /*
156     We do not use DBUG_ENTER here, as after cleanup DBUG is no longer
157     operational, so we cannot use DBUG_RETURN.
158   */
159 
160   FILE *info_file= (DBUG_FILE ? DBUG_FILE : stderr);
161 
162   if (!my_init_done)
163     return;
164 
165   if ((infoflag & MY_CHECK_ERROR) || (info_file != stderr))
166 
167   {					/* Test if some file is left open */
168     if (my_file_opened | my_stream_opened)
169     {
170       char ebuff[512];
171       my_snprintf(ebuff, sizeof(ebuff), EE(EE_OPEN_WARNING),
172                   my_file_opened, my_stream_opened);
173       my_message_stderr(EE_OPEN_WARNING, ebuff, MYF(0));
174       DBUG_PRINT("error", ("%s", ebuff));
175       my_print_open_files();
176     }
177   }
178   free_charsets();
179   my_error_unregister_all();
180   my_once_free();
181 
182   if ((infoflag & MY_GIVE_INFO) || (info_file != stderr))
183   {
184 #ifdef HAVE_GETRUSAGE
185     struct rusage rus;
186     if (!getrusage(RUSAGE_SELF, &rus))
187       fprintf(info_file,"\n\
188 User time %.2f, System time %.2f\n                              \
189 Maximum resident set size %ld, Integral resident set size %ld\n\
190 Non-physical pagefaults %ld, Physical pagefaults %ld, Swaps %ld\n\
191 Blocks in %ld out %ld, Messages in %ld out %ld, Signals %ld\n\
192 Voluntary context switches %ld, Involuntary context switches %ld\n",
193 	      (rus.ru_utime.tv_sec * SCALE_SEC +
194 	       rus.ru_utime.tv_usec / SCALE_USEC) / 100.0,
195 	      (rus.ru_stime.tv_sec * SCALE_SEC +
196 	       rus.ru_stime.tv_usec / SCALE_USEC) / 100.0,
197 	      rus.ru_maxrss, rus.ru_idrss,
198 	      rus.ru_minflt, rus.ru_majflt,
199 	      rus.ru_nswap, rus.ru_inblock, rus.ru_oublock,
200 	      rus.ru_msgsnd, rus.ru_msgrcv, rus.ru_nsignals,
201 	      rus.ru_nvcsw, rus.ru_nivcsw);
202 #endif
203 #if defined(_WIN32)
204    _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
205    _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR );
206    _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );
207    _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR );
208    _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
209    _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR );
210    _CrtCheckMemory();
211    _CrtDumpMemoryLeaks();
212 #endif
213   }
214 
215   if (!(infoflag & MY_DONT_FREE_DBUG))
216   {
217     DBUG_END();                /* Must be done before my_thread_end */
218   }
219 
220   my_thread_end();
221   my_thread_global_end();
222 
223 #ifdef _WIN32
224   if (have_tcpip)
225     WSACleanup();
226 #endif /* _WIN32 */
227 
228   my_init_done= FALSE;
229 } /* my_end */
230 
231 
232 #ifdef _WIN32
233 /*
234   my_parameter_handler
235 
236   Invalid parameter handler we will use instead of the one "baked"
237   into the CRT for MSC v8.  This one just prints out what invalid
238   parameter was encountered.  By providing this routine, routines like
239   lseek will return -1 when we expect them to instead of crash.
240 */
241 
my_parameter_handler(const wchar_t * expression,const wchar_t * function,const wchar_t * file,unsigned int line,uintptr_t pReserved)242 void my_parameter_handler(const wchar_t * expression, const wchar_t * function,
243                           const wchar_t * file, unsigned int line,
244                           uintptr_t pReserved)
245 {
246   DBUG_PRINT("my",("Expression: %s  function: %s  file: %s, line: %d",
247 		   expression, function, file, line));
248 }
249 
250 
251 #ifdef __MSVC_RUNTIME_CHECKS
252 #include <rtcapi.h>
253 
254 /* Turn off runtime checks for 'handle_rtc_failure' */
255 #pragma runtime_checks("", off)
256 
257 /*
258   handle_rtc_failure
259   Windows: run-time error checks are reported to ...
260 */
261 
handle_rtc_failure(int err_type,const char * file,int line,const char * module,const char * format,...)262 int handle_rtc_failure(int err_type, const char *file, int line,
263                        const char* module, const char *format, ...)
264 {
265   va_list args;
266   char   buff[2048];
267   size_t len;
268 
269   len= my_snprintf(buff, sizeof(buff), "At %s:%d: ", file, line);
270 
271   va_start(args, format);
272   vsnprintf(buff + len, sizeof(buff) - len, format, args);
273   va_end(args);
274 
275   my_message_local(ERROR_LEVEL, buff);
276 
277   return 0; /* Error is handled */
278 }
279 #pragma runtime_checks("", restore)
280 #endif
281 
282 #define OFFSET_TO_EPOC ((__int64) 134774 * 24 * 60 * 60 * 1000 * 1000 * 10)
283 #define MS 10000000
284 
win_init_time()285 static void win_init_time()
286 {
287   /* The following is used by time functions */
288   FILETIME ft;
289   LARGE_INTEGER li, t_cnt;
290 
291   assert(sizeof(LARGE_INTEGER) == sizeof(query_performance_frequency));
292 
293   if (QueryPerformanceFrequency((LARGE_INTEGER *)&query_performance_frequency) == 0)
294     query_performance_frequency= 0;
295   else
296   {
297     GetSystemTimeAsFileTime(&ft);
298     li.LowPart=  ft.dwLowDateTime;
299     li.HighPart= ft.dwHighDateTime;
300     query_performance_offset= li.QuadPart-OFFSET_TO_EPOC;
301     QueryPerformanceCounter(&t_cnt);
302     query_performance_offset-= (t_cnt.QuadPart /
303                                 query_performance_frequency * MS +
304                                 t_cnt.QuadPart %
305                                 query_performance_frequency * MS /
306                                 query_performance_frequency);
307   }
308 }
309 
310 
311 /*
312   Open HKEY_LOCAL_MACHINE\SOFTWARE\MySQL and set any strings found
313   there as environment variables
314 */
win_init_registry()315 static void win_init_registry()
316 {
317   HKEY key_handle;
318 
319   if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)"SOFTWARE\\MySQL",
320                     0, KEY_READ, &key_handle) == ERROR_SUCCESS)
321   {
322     LONG ret;
323     DWORD index= 0;
324     DWORD type;
325     char key_name[256], key_data[1024];
326     DWORD key_name_len= sizeof(key_name) - 1;
327     DWORD key_data_len= sizeof(key_data) - 1;
328 
329     while ((ret= RegEnumValue(key_handle, index++,
330                               key_name, &key_name_len,
331                               NULL, &type, (LPBYTE)&key_data,
332                               &key_data_len)) != ERROR_NO_MORE_ITEMS)
333     {
334       char env_string[sizeof(key_name) + sizeof(key_data) + 2];
335 
336       if (ret == ERROR_MORE_DATA)
337       {
338         /* Registry value larger than 'key_data', skip it */
339         DBUG_PRINT("error", ("Skipped registry value that was too large"));
340       }
341       else if (ret == ERROR_SUCCESS)
342       {
343         if (type == REG_SZ)
344         {
345           strxmov(env_string, key_name, "=", key_data, NullS);
346 
347           /* variable for putenv must be allocated ! */
348           putenv(strdup(env_string)) ;
349         }
350       }
351       else
352       {
353         /* Unhandled error, break out of loop */
354         break;
355       }
356 
357       key_name_len= sizeof(key_name) - 1;
358       key_data_len= sizeof(key_data) - 1;
359     }
360 
361     RegCloseKey(key_handle);
362   }
363 }
364 
365 
366 /*------------------------------------------------------------------
367   Name: CheckForTcpip| Desc: checks if tcpip has been installed on system
368   According to Microsoft Developers documentation the first registry
369   entry should be enough to check if TCP/IP is installed, but as expected
370   this doesn't work on all Win32 machines :(
371 ------------------------------------------------------------------*/
372 
373 #define TCPIPKEY  "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"
374 #define WINSOCK2KEY "SYSTEM\\CurrentControlSet\\Services\\Winsock2\\Parameters"
375 #define WINSOCKKEY  "SYSTEM\\CurrentControlSet\\Services\\Winsock\\Parameters"
376 
win32_have_tcpip()377 static my_bool win32_have_tcpip()
378 {
379   HKEY hTcpipRegKey;
380   if (RegOpenKeyEx ( HKEY_LOCAL_MACHINE, TCPIPKEY, 0, KEY_READ,
381 		      &hTcpipRegKey) != ERROR_SUCCESS)
382   {
383     if (RegOpenKeyEx ( HKEY_LOCAL_MACHINE, WINSOCK2KEY, 0, KEY_READ,
384 		      &hTcpipRegKey) != ERROR_SUCCESS)
385     {
386       if (RegOpenKeyEx ( HKEY_LOCAL_MACHINE, WINSOCKKEY, 0, KEY_READ,
387 			 &hTcpipRegKey) != ERROR_SUCCESS)
388 	if (!getenv("HAVE_TCPIP") || have_tcpip)	/* Provide a workaround */
389 	  return (FALSE);
390     }
391   }
392   RegCloseKey ( hTcpipRegKey);
393   return (TRUE);
394 }
395 
396 
win32_init_tcp_ip()397 static my_bool win32_init_tcp_ip()
398 {
399   if (win32_have_tcpip())
400   {
401     WORD wVersionRequested = MAKEWORD( 2, 2 );
402     WSADATA wsaData;
403  	/* Be a good citizen: maybe another lib has already initialised
404  		sockets, so dont clobber them unless necessary */
405     if (WSAStartup( wVersionRequested, &wsaData ))
406     {
407       /* Load failed, maybe because of previously loaded
408 	 incompatible version; try again */
409       WSACleanup( );
410       if (!WSAStartup( wVersionRequested, &wsaData ))
411 	have_tcpip=1;
412     }
413     else
414     {
415       if (wsaData.wVersion != wVersionRequested)
416       {
417 	/* Version is no good, try again */
418 	WSACleanup( );
419 	if (!WSAStartup( wVersionRequested, &wsaData ))
420 	  have_tcpip=1;
421       }
422       else
423 	have_tcpip=1;
424     }
425   }
426   return(0);
427 }
428 
429 
my_win_init()430 static void my_win_init()
431 {
432   DBUG_ENTER("my_win_init");
433 
434   /* this is required to make crt functions return -1 appropriately */
435   _set_invalid_parameter_handler(my_parameter_handler);
436 
437 #ifdef __MSVC_RUNTIME_CHECKS
438   /*
439     Install handler to send RTC (Runtime Error Check) warnings
440     to log file
441   */
442   _RTC_SetErrorFunc(handle_rtc_failure);
443 #endif
444 
445   _tzset();
446 
447   win_init_time();
448   win_init_registry();
449   win32_init_tcp_ip();
450 
451   DBUG_VOID_RETURN;
452 }
453 #endif /* _WIN32 */
454 
455 PSI_stage_info stage_waiting_for_table_level_lock=
456 {0, "Waiting for table level lock", 0};
457 
458 #ifdef HAVE_PSI_INTERFACE
459 
460 PSI_mutex_key key_BITMAP_mutex, key_IO_CACHE_append_buffer_lock,
461   key_IO_CACHE_SHARE_mutex, key_KEY_CACHE_cache_lock,
462   key_THR_LOCK_charset, key_THR_LOCK_heap,
463   key_THR_LOCK_lock, key_THR_LOCK_malloc,
464   key_THR_LOCK_mutex, key_THR_LOCK_myisam, key_THR_LOCK_net,
465   key_THR_LOCK_open, key_THR_LOCK_threads,
466   key_TMPDIR_mutex, key_THR_LOCK_myisam_mmap;
467 
468 static PSI_mutex_info all_mysys_mutexes[]=
469 {
470   { &key_BITMAP_mutex, "BITMAP::mutex", 0},
471   { &key_IO_CACHE_append_buffer_lock, "IO_CACHE::append_buffer_lock", 0},
472   { &key_IO_CACHE_SHARE_mutex, "IO_CACHE::SHARE_mutex", 0},
473   { &key_KEY_CACHE_cache_lock, "KEY_CACHE::cache_lock", 0},
474   { &key_THR_LOCK_charset, "THR_LOCK_charset", PSI_FLAG_GLOBAL},
475   { &key_THR_LOCK_heap, "THR_LOCK_heap", PSI_FLAG_GLOBAL},
476   { &key_THR_LOCK_lock, "THR_LOCK_lock", PSI_FLAG_GLOBAL},
477   { &key_THR_LOCK_malloc, "THR_LOCK_malloc", PSI_FLAG_GLOBAL},
478   { &key_THR_LOCK_mutex, "THR_LOCK::mutex", 0},
479   { &key_THR_LOCK_myisam, "THR_LOCK_myisam", PSI_FLAG_GLOBAL},
480   { &key_THR_LOCK_net, "THR_LOCK_net", PSI_FLAG_GLOBAL},
481   { &key_THR_LOCK_open, "THR_LOCK_open", PSI_FLAG_GLOBAL},
482   { &key_THR_LOCK_threads, "THR_LOCK_threads", PSI_FLAG_GLOBAL},
483   { &key_TMPDIR_mutex, "TMPDIR_mutex", PSI_FLAG_GLOBAL},
484   { &key_THR_LOCK_myisam_mmap, "THR_LOCK_myisam_mmap", PSI_FLAG_GLOBAL}
485 };
486 
487 PSI_rwlock_key key_SAFE_HASH_lock;
488 
489 static PSI_rwlock_info all_mysys_rwlocks[]=
490 {
491   { &key_SAFE_HASH_lock, "SAFE_HASH::lock", 0}
492 };
493 
494 PSI_cond_key key_IO_CACHE_SHARE_cond,
495   key_IO_CACHE_SHARE_cond_writer,
496   key_THR_COND_threads;
497 
498 static PSI_cond_info all_mysys_conds[]=
499 {
500   { &key_IO_CACHE_SHARE_cond, "IO_CACHE_SHARE::cond", 0},
501   { &key_IO_CACHE_SHARE_cond_writer, "IO_CACHE_SHARE::cond_writer", 0},
502   { &key_THR_COND_threads, "THR_COND_threads", 0}
503 };
504 
505 #ifdef HAVE_LINUX_LARGE_PAGES
506 PSI_file_key key_file_proc_meminfo;
507 #endif /* HAVE_LINUX_LARGE_PAGES */
508 PSI_file_key key_file_charset, key_file_cnf;
509 
510 static PSI_file_info all_mysys_files[]=
511 {
512 #ifdef HAVE_LINUX_LARGE_PAGES
513   { &key_file_proc_meminfo, "proc_meminfo", 0},
514 #endif /* HAVE_LINUX_LARGE_PAGES */
515   { &key_file_charset, "charset", 0},
516   { &key_file_cnf, "cnf", 0}
517 };
518 
519 PSI_stage_info *all_mysys_stages[]=
520 {
521   & stage_waiting_for_table_level_lock
522 };
523 
524 static PSI_memory_info all_mysys_memory[]=
525 {
526 #ifdef _WIN32
527   { &key_memory_win_SECURITY_ATTRIBUTES, "win_SECURITY_ATTRIBUTES", 0},
528   { &key_memory_win_PACL, "win_PACL", 0},
529   { &key_memory_win_IP_ADAPTER_ADDRESSES, "win_IP_ADAPTER_ADDRESSES", 0},
530 #endif
531 
532   { &key_memory_max_alloca, "max_alloca", 0},
533   { &key_memory_charset_file, "charset_file", 0},
534   { &key_memory_charset_loader, "charset_loader", 0},
535   { &key_memory_lf_node, "lf_node", 0},
536   { &key_memory_lf_dynarray, "lf_dynarray", 0},
537   { &key_memory_lf_slist, "lf_slist", 0},
538   { &key_memory_LIST, "LIST", 0},
539   { &key_memory_IO_CACHE, "IO_CACHE", 0},
540   { &key_memory_KEY_CACHE, "KEY_CACHE", 0},
541   { &key_memory_SAFE_HASH_ENTRY, "SAFE_HASH_ENTRY", 0},
542   { &key_memory_MY_TMPDIR_full_list, "MY_TMPDIR::full_list", 0},
543   { &key_memory_MY_BITMAP_bitmap, "MY_BITMAP::bitmap", 0},
544   { &key_memory_my_compress_alloc, "my_compress_alloc", 0},
545   { &key_memory_pack_frm, "pack_frm", 0},
546   { &key_memory_my_err_head, "my_err_head", 0},
547   { &key_memory_my_file_info, "my_file_info", 0},
548   { &key_memory_MY_DIR, "MY_DIR", 0},
549   { &key_memory_MY_STAT, "MY_STAT", 0},
550   { &key_memory_QUEUE, "QUEUE", 0},
551   { &key_memory_DYNAMIC_STRING, "DYNAMIC_STRING", 0},
552   { &key_memory_TREE, "TREE", 0}
553 };
554 
my_init_mysys_psi_keys()555 void my_init_mysys_psi_keys()
556 {
557   const char* category= "mysys";
558   int count;
559 
560   count= sizeof(all_mysys_mutexes)/sizeof(all_mysys_mutexes[0]);
561   mysql_mutex_register(category, all_mysys_mutexes, count);
562 
563   count= sizeof(all_mysys_rwlocks)/sizeof(all_mysys_rwlocks[0]);
564   mysql_rwlock_register(category, all_mysys_rwlocks, count);
565 
566   count= sizeof(all_mysys_conds)/sizeof(all_mysys_conds[0]);
567   mysql_cond_register(category, all_mysys_conds, count);
568 
569   count= sizeof(all_mysys_files)/sizeof(all_mysys_files[0]);
570   mysql_file_register(category, all_mysys_files, count);
571 
572   count= array_elements(all_mysys_stages);
573   mysql_stage_register(category, all_mysys_stages, count);
574 
575   count= array_elements(all_mysys_memory);
576   mysql_memory_register(category, all_mysys_memory, count);
577 }
578 #endif /* HAVE_PSI_INTERFACE */
579 
580