/* Copyright (c) 2010, 2021, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ #include "bootstrap.h" #include "log.h" // sql_print_warning #include "mysqld_thd_manager.h" // Global_THD_manager #include "bootstrap_impl.h" #include "sql_initialize.h" #include "sql_class.h" // THD #include "sql_connect.h" // close_connection #include "sql_parse.h" // mysql_parse #include "pfs_file_provider.h" #include "mysql/psi/mysql_file.h" static MYSQL_FILE *bootstrap_file= NULL; static const char *bootstrap_query= NULL; static int bootstrap_error= 0; class Query_command_iterator: public Command_iterator { public: Query_command_iterator(const char* query): m_query(query), m_is_read(false) {} virtual int next(std::string &query, int *read_error, int *query_source) { if (m_is_read) return READ_BOOTSTRAP_EOF; query= m_query; m_is_read= true; *read_error= 0; *query_source= QUERY_SOURCE_COMPILED; return READ_BOOTSTRAP_SUCCESS; } private: const char *m_query; // Owned externally. bool m_is_read; }; int File_command_iterator::next(std::string &query, int *error, int *query_source) { static char query_buffer[MAX_BOOTSTRAP_QUERY_SIZE]; size_t length= 0; int rc; *query_source= QUERY_SOURCE_FILE; rc= read_bootstrap_query(query_buffer, &length, m_input, m_fgets_fn, error); if (rc == READ_BOOTSTRAP_SUCCESS) query.assign(query_buffer, length); return rc; } char *mysql_file_fgets_fn(char *buffer, size_t size, MYSQL_FILE* input, int *error) { char *line= mysql_file_fgets(buffer, static_cast(size), input); if (error) *error= (line == NULL) ? ferror(input->m_file) : 0; return line; } File_command_iterator::File_command_iterator(const char *file_name) { is_allocated= false; if (!(m_input= mysql_file_fopen(key_file_init, file_name, O_RDONLY, MYF(MY_WME)))) return; m_fgets_fn= mysql_file_fgets_fn; is_allocated= true; } File_command_iterator::~File_command_iterator() { end(); } void File_command_iterator::end(void) { if (is_allocated) { mysql_file_fclose(m_input, MYF(0)); is_allocated= false; m_input= NULL; } } Command_iterator *Command_iterator::current_iterator= NULL; static void handle_bootstrap_impl(THD *thd) { std::string query; DBUG_ENTER("handle_bootstrap"); File_command_iterator file_iter(bootstrap_file, mysql_file_fgets_fn); Compiled_in_command_iterator comp_iter; Query_command_iterator query_iter(bootstrap_query); bool has_binlog_option= thd->variables.option_bits & OPTION_BIN_LOG; int query_source, last_query_source= -1; thd->thread_stack= (char*) &thd; thd->security_context()->assign_user(STRING_WITH_LEN("boot")); thd->security_context()->assign_priv_user("", 0); thd->security_context()->assign_priv_host("", 0); /* Make the "client" handle multiple results. This is necessary to enable stored procedures with SELECTs and Dynamic SQL in init-file. */ thd->get_protocol_classic()->add_client_capability( CLIENT_MULTI_RESULTS); thd->init_for_queries(); /* If a single bootstrap query is submitted, execute it regardless of the command line options. If no query is submitted, read commands from the executable or from file depending on option. */ if (bootstrap_query) { Command_iterator::current_iterator= &query_iter; bootstrap_query= NULL; } else { if (opt_initialize) Command_iterator::current_iterator= &comp_iter; else Command_iterator::current_iterator= &file_iter; } Command_iterator::current_iterator->begin(); for ( ; ; ) { int error= 0; int rc; rc= Command_iterator::current_iterator->next(query, &error, &query_source); /* The server must avoid logging compiled statements into the binary log (and generating GTIDs for them when GTID_MODE is ON) during bootstrap/ initialize procedures. We will disable SQL_LOG_BIN session variable before processing compiled statements, and will re-enable it before processing statements of the initialization file. */ if (has_binlog_option && query_source != last_query_source) { switch (query_source) { case QUERY_SOURCE_COMPILED: thd->variables.option_bits&= ~OPTION_BIN_LOG; break; case QUERY_SOURCE_FILE: /* Some compiled script might have disable binary logging session variable during compiled scripts. Enabling it again as it was enabled before applying the compiled statements. */ thd->variables.sql_log_bin= true; thd->variables.option_bits|= OPTION_BIN_LOG; break; default: assert(false); break; } } last_query_source= query_source; if (rc == READ_BOOTSTRAP_EOF) break; /* Check for bootstrap file errors. SQL syntax errors will be caught below. */ if (rc != READ_BOOTSTRAP_SUCCESS) { /* mysql_parse() may have set a successful error status for the previous query. We must clear the error status to report the bootstrap error. */ thd->get_stmt_da()->reset_diagnostics_area(); /* Get the nearest query text for reference. */ const char *err_ptr= query.c_str() + (query.length() <= MAX_BOOTSTRAP_ERROR_LEN ? 0 : (query.length() - MAX_BOOTSTRAP_ERROR_LEN)); switch (rc) { case READ_BOOTSTRAP_ERROR: my_printf_error(ER_UNKNOWN_ERROR, "Bootstrap file error, return code (%d). " "Nearest query: '%s'", MYF(0), error, err_ptr); break; case READ_BOOTSTRAP_QUERY_SIZE: my_printf_error(ER_UNKNOWN_ERROR, "Bootstrap file error. Query size " "exceeded %d bytes near '%s'.", MYF(0), MAX_BOOTSTRAP_LINE_SIZE, err_ptr); break; default: assert(false); break; } thd->send_statement_status(); bootstrap_error= 1; break; } char *query_copy= static_cast(thd->alloc(query.length() + 1)); if (query_copy == NULL) { bootstrap_error= 1; break; } memcpy(query_copy, query.c_str(), query.length()); query_copy[query.length()]= '\0'; thd->set_query(query_copy, query.length()); thd->set_query_id(next_query_id()); DBUG_PRINT("query",("%-.4096s",thd->query().str)); #if defined(ENABLED_PROFILING) thd->profiling.start_new_query(); thd->profiling.set_query_source(thd->query().str, thd->query().length); #endif thd->set_time(); Parser_state parser_state; if (parser_state.init(thd, thd->query().str, thd->query().length)) { thd->send_statement_status(); bootstrap_error= 1; break; } mysql_parse(thd, &parser_state, true); bootstrap_error= thd->is_error(); thd->send_statement_status(); #if defined(ENABLED_PROFILING) thd->profiling.finish_current_query(); #endif if (bootstrap_error) break; free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); /* If the last statement has enabled the session binary logging while processing queries that are compiled and must not be binary logged, we must disable binary logging again. */ if (last_query_source == QUERY_SOURCE_COMPILED && thd->variables.option_bits & OPTION_BIN_LOG) thd->variables.option_bits&= ~OPTION_BIN_LOG; } Command_iterator::current_iterator->end(); /* We should re-enable SQL_LOG_BIN session if it was enabled by default but disabled during bootstrap/initialization. */ if (has_binlog_option) { thd->variables.sql_log_bin= true; thd->variables.option_bits|= OPTION_BIN_LOG; } DBUG_VOID_RETURN; } /** Execute commands from bootstrap_file. Used when creating the initial grant tables. */ namespace { extern "C" void *handle_bootstrap(void *arg) { THD *thd=(THD*) arg; mysql_thread_set_psi_id(thd->thread_id()); /* The following must be called before DBUG_ENTER */ thd->thread_stack= (char*) &thd; if (my_thread_init() || thd->store_globals()) { #ifndef EMBEDDED_LIBRARY close_connection(thd, ER_OUT_OF_RESOURCES); #endif thd->fatal_error(); bootstrap_error= 1; thd->get_protocol_classic()->end_net(); } else { Global_THD_manager *thd_manager= Global_THD_manager::get_instance(); thd_manager->add_thd(thd); handle_bootstrap_impl(thd); thd->get_protocol_classic()->end_net(); thd->release_resources(); thd_manager->remove_thd(thd); } my_thread_end(); return 0; } } // namespace int bootstrap(MYSQL_FILE *file) { DBUG_ENTER("bootstrap"); THD *thd= new THD; thd->bootstrap= 1; thd->get_protocol_classic()->init_net(NULL); thd->security_context()->set_master_access(~(ulong)0); thd->set_new_thread_id(); DBUG_EXECUTE_IF("bootstrap_crash", DBUG_SUICIDE();); DBUG_EXECUTE_IF("bootstrap_hang", { while (1) my_sleep(1000000); }); DBUG_EXECUTE_IF("bootstrap_buffer_overrun", { int *mem = static_cast(my_malloc(PSI_NOT_INSTRUMENTED, 127, 0)); // Allocations are usually aligned, so even if 127 bytes were requested, // it's mostly safe to assume there are 128 bytes. Writing into the last // byte is safe for the rest of the code, but still enough to trigger // AddressSanitizer (ASAN) or Valgrind. my_atomic_store32(mem + (128 / sizeof(*mem)) - 1, 1); my_free(mem); }); bootstrap_file=file; my_thread_attr_t thr_attr; my_thread_attr_init(&thr_attr); #ifndef _WIN32 pthread_attr_setscope(&thr_attr, PTHREAD_SCOPE_SYSTEM); #endif my_thread_attr_setdetachstate(&thr_attr, MY_THREAD_CREATE_JOINABLE); my_thread_handle thread_handle; // What about setting THD::real_id? int error= mysql_thread_create(key_thread_bootstrap, &thread_handle, &thr_attr, handle_bootstrap, thd); if (error) { sql_print_warning("Can't create thread to handle bootstrap (errno= %d)", error); DBUG_RETURN(-1); } /* Wait for thread to die */ my_thread_join(&thread_handle, NULL); delete thd; DBUG_RETURN(bootstrap_error); } int bootstrap_single_query(const char* query) { bootstrap_query= query; return bootstrap(NULL); }