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