1 /* Copyright (c) 2010, 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    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software Foundation,
21    51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22 
23 #include "bootstrap.h"
24 
25 #include "log.h"                 // sql_print_warning
26 #include "mysqld_thd_manager.h"  // Global_THD_manager
27 #include "bootstrap_impl.h"
28 #include "sql_initialize.h"
29 #include "sql_class.h"           // THD
30 #include "sql_connect.h"         // close_connection
31 #include "sql_parse.h"           // mysql_parse
32 
33 #include "pfs_file_provider.h"
34 #include "mysql/psi/mysql_file.h"
35 
36 static MYSQL_FILE *bootstrap_file= NULL;
37 static const char *bootstrap_query= NULL;
38 static int bootstrap_error= 0;
39 
40 
41 class Query_command_iterator: public Command_iterator
42 {
43 public:
Query_command_iterator(const char * query)44   Query_command_iterator(const char* query):
45     m_query(query), m_is_read(false) {}
next(std::string & query,int * read_error,int * query_source)46   virtual int next(std::string &query, int *read_error, int *query_source)
47   {
48     if (m_is_read)
49       return READ_BOOTSTRAP_EOF;
50 
51     query= m_query;
52     m_is_read= true;
53     *read_error= 0;
54     *query_source= QUERY_SOURCE_COMPILED;
55     return READ_BOOTSTRAP_SUCCESS;
56   }
57 private:
58   const char *m_query; // Owned externally.
59   bool m_is_read;
60 };
61 
62 
next(std::string & query,int * error,int * query_source)63 int File_command_iterator::next(std::string &query, int *error,
64                                 int *query_source)
65 {
66   static char query_buffer[MAX_BOOTSTRAP_QUERY_SIZE];
67   size_t length= 0;
68   int rc;
69   *query_source= QUERY_SOURCE_FILE;
70 
71   rc= read_bootstrap_query(query_buffer, &length, m_input, m_fgets_fn, error);
72   if (rc == READ_BOOTSTRAP_SUCCESS)
73     query.assign(query_buffer, length);
74   return rc;
75 }
76 
77 
mysql_file_fgets_fn(char * buffer,size_t size,MYSQL_FILE * input,int * error)78 char *mysql_file_fgets_fn(char *buffer, size_t size, MYSQL_FILE* input, int *error)
79 {
80   char *line= mysql_file_fgets(buffer, static_cast<int>(size), input);
81   if (error)
82     *error= (line == NULL) ? ferror(input->m_file) : 0;
83   return line;
84 }
85 
File_command_iterator(const char * file_name)86 File_command_iterator::File_command_iterator(const char *file_name)
87 {
88   is_allocated= false;
89   if (!(m_input= mysql_file_fopen(key_file_init, file_name,
90     O_RDONLY, MYF(MY_WME))))
91     return;
92   m_fgets_fn= mysql_file_fgets_fn;
93   is_allocated= true;
94 }
95 
~File_command_iterator()96 File_command_iterator::~File_command_iterator()
97 {
98   end();
99 }
100 
end(void)101 void File_command_iterator::end(void)
102 {
103   if (is_allocated)
104   {
105     mysql_file_fclose(m_input, MYF(0));
106     is_allocated= false;
107     m_input= NULL;
108   }
109 }
110 
111 Command_iterator *Command_iterator::current_iterator= NULL;
112 
handle_bootstrap_impl(THD * thd)113 static void handle_bootstrap_impl(THD *thd)
114 {
115   std::string query;
116 
117   DBUG_ENTER("handle_bootstrap");
118   File_command_iterator file_iter(bootstrap_file, mysql_file_fgets_fn);
119   Compiled_in_command_iterator comp_iter;
120   Query_command_iterator query_iter(bootstrap_query);
121   bool has_binlog_option= thd->variables.option_bits & OPTION_BIN_LOG;
122   int query_source, last_query_source= -1;
123 
124   thd->thread_stack= (char*) &thd;
125   thd->security_context()->assign_user(STRING_WITH_LEN("boot"));
126   thd->security_context()->assign_priv_user("", 0);
127   thd->security_context()->assign_priv_host("", 0);
128   /*
129     Make the "client" handle multiple results. This is necessary
130     to enable stored procedures with SELECTs and Dynamic SQL
131     in init-file.
132   */
133     thd->get_protocol_classic()->add_client_capability(
134     CLIENT_MULTI_RESULTS);
135 
136   thd->init_for_queries();
137 
138   /*
139     If a single bootstrap query is submitted, execute it regardless of the
140     command line options. If no query is submitted, read commands from the
141     executable or from file depending on option.
142   */
143   if (bootstrap_query)
144   {
145     Command_iterator::current_iterator= &query_iter;
146     bootstrap_query= NULL;
147   }
148   else
149   {
150     if (opt_initialize)
151       Command_iterator::current_iterator= &comp_iter;
152     else
153       Command_iterator::current_iterator= &file_iter;
154   }
155 
156   Command_iterator::current_iterator->begin();
157   for ( ; ; )
158   {
159     int error= 0;
160     int rc;
161 
162     rc= Command_iterator::current_iterator->next(query, &error, &query_source);
163 
164     /*
165       The server must avoid logging compiled statements into the binary log
166       (and generating GTIDs for them when GTID_MODE is ON) during bootstrap/
167       initialize procedures.
168       We will disable SQL_LOG_BIN session variable before processing compiled
169       statements, and will re-enable it before processing statements of the
170       initialization file.
171     */
172     if (has_binlog_option && query_source != last_query_source)
173     {
174       switch (query_source)
175       {
176       case QUERY_SOURCE_COMPILED:
177         thd->variables.option_bits&= ~OPTION_BIN_LOG;
178         break;
179       case QUERY_SOURCE_FILE:
180         /*
181           Some compiled script might have disable binary logging session
182           variable during compiled scripts. Enabling it again as it was
183           enabled before applying the compiled statements.
184         */
185         thd->variables.sql_log_bin= true;
186         thd->variables.option_bits|= OPTION_BIN_LOG;
187         break;
188       default:
189         assert(false);
190         break;
191       }
192     }
193     last_query_source= query_source;
194 
195     if (rc == READ_BOOTSTRAP_EOF)
196       break;
197     /*
198       Check for bootstrap file errors. SQL syntax errors will be
199       caught below.
200     */
201     if (rc != READ_BOOTSTRAP_SUCCESS)
202     {
203       /*
204         mysql_parse() may have set a successful error status for the previous
205         query. We must clear the error status to report the bootstrap error.
206       */
207       thd->get_stmt_da()->reset_diagnostics_area();
208 
209       /* Get the nearest query text for reference. */
210       const char *err_ptr= query.c_str() + (query.length() <= MAX_BOOTSTRAP_ERROR_LEN ?
211                                         0 : (query.length() - MAX_BOOTSTRAP_ERROR_LEN));
212       switch (rc)
213       {
214       case READ_BOOTSTRAP_ERROR:
215         my_printf_error(ER_UNKNOWN_ERROR,
216                         "Bootstrap file error, return code (%d). "
217                         "Nearest query: '%s'", MYF(0), error, err_ptr);
218         break;
219 
220       case READ_BOOTSTRAP_QUERY_SIZE:
221         my_printf_error(ER_UNKNOWN_ERROR, "Bootstrap file error. Query size "
222                         "exceeded %d bytes near '%s'.", MYF(0),
223                         MAX_BOOTSTRAP_LINE_SIZE, err_ptr);
224         break;
225 
226       default:
227         assert(false);
228         break;
229       }
230 
231       thd->send_statement_status();
232       bootstrap_error= 1;
233       break;
234     }
235 
236     char *query_copy= static_cast<char*>(thd->alloc(query.length() + 1));
237     if (query_copy == NULL)
238     {
239       bootstrap_error= 1;
240       break;
241     }
242     memcpy(query_copy, query.c_str(), query.length());
243     query_copy[query.length()]= '\0';
244     thd->set_query(query_copy, query.length());
245     thd->set_query_id(next_query_id());
246     DBUG_PRINT("query",("%-.4096s",thd->query().str));
247 #if defined(ENABLED_PROFILING)
248     thd->profiling.start_new_query();
249     thd->profiling.set_query_source(thd->query().str, thd->query().length);
250 #endif
251 
252     thd->set_time();
253     Parser_state parser_state;
254     if (parser_state.init(thd, thd->query().str, thd->query().length))
255     {
256       thd->send_statement_status();
257       bootstrap_error= 1;
258       break;
259     }
260 
261     mysql_parse(thd, &parser_state, true);
262 
263     bootstrap_error= thd->is_error();
264     thd->send_statement_status();
265 
266 #if defined(ENABLED_PROFILING)
267     thd->profiling.finish_current_query();
268 #endif
269 
270     if (bootstrap_error)
271       break;
272 
273     free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
274 
275     /*
276       If the last statement has enabled the session binary logging while
277       processing queries that are compiled and must not be binary logged,
278       we must disable binary logging again.
279     */
280     if (last_query_source == QUERY_SOURCE_COMPILED &&
281         thd->variables.option_bits & OPTION_BIN_LOG)
282       thd->variables.option_bits&= ~OPTION_BIN_LOG;
283 
284   }
285 
286   Command_iterator::current_iterator->end();
287 
288   /*
289     We should re-enable SQL_LOG_BIN session if it was enabled by default
290     but disabled during bootstrap/initialization.
291   */
292   if (has_binlog_option)
293   {
294     thd->variables.sql_log_bin= true;
295     thd->variables.option_bits|= OPTION_BIN_LOG;
296   }
297 
298   DBUG_VOID_RETURN;
299 }
300 
301 
302 /**
303   Execute commands from bootstrap_file.
304 
305   Used when creating the initial grant tables.
306 */
307 
308 namespace {
handle_bootstrap(void * arg)309 extern "C" void *handle_bootstrap(void *arg)
310 {
311   THD *thd=(THD*) arg;
312 
313   mysql_thread_set_psi_id(thd->thread_id());
314 
315   /* The following must be called before DBUG_ENTER */
316   thd->thread_stack= (char*) &thd;
317   if (my_thread_init() || thd->store_globals())
318   {
319 #ifndef EMBEDDED_LIBRARY
320     close_connection(thd, ER_OUT_OF_RESOURCES);
321 #endif
322     thd->fatal_error();
323     bootstrap_error= 1;
324     thd->get_protocol_classic()->end_net();
325   }
326   else
327   {
328     Global_THD_manager *thd_manager= Global_THD_manager::get_instance();
329     thd_manager->add_thd(thd);
330 
331     handle_bootstrap_impl(thd);
332 
333     thd->get_protocol_classic()->end_net();
334     thd->release_resources();
335     thd_manager->remove_thd(thd);
336   }
337   my_thread_end();
338   return 0;
339 }
340 } // namespace
341 
342 
bootstrap(MYSQL_FILE * file)343 int bootstrap(MYSQL_FILE *file)
344 {
345   DBUG_ENTER("bootstrap");
346 
347   THD *thd= new THD;
348   thd->bootstrap= 1;
349   thd->get_protocol_classic()->init_net(NULL);
350   thd->security_context()->set_master_access(~(ulong)0);
351 
352   thd->set_new_thread_id();
353 
354   DBUG_EXECUTE_IF("bootstrap_crash", DBUG_SUICIDE(););
355   DBUG_EXECUTE_IF("bootstrap_hang", {
356     while (1)
357       my_sleep(1000000);
358   });
359   DBUG_EXECUTE_IF("bootstrap_buffer_overrun", {
360     int *mem = static_cast<int *>(my_malloc(PSI_NOT_INSTRUMENTED, 127, 0));
361     // Allocations are usually aligned, so even if 127 bytes were requested,
362     // it's mostly safe to assume there are 128 bytes. Writing into the last
363     // byte is safe for the rest of the code, but still enough to trigger
364     // AddressSanitizer (ASAN) or Valgrind.
365     my_atomic_store32(mem + (128 / sizeof(*mem)) - 1, 1);
366     my_free(mem);
367   });
368 
369   bootstrap_file=file;
370 
371   my_thread_attr_t thr_attr;
372   my_thread_attr_init(&thr_attr);
373 #ifndef _WIN32
374   pthread_attr_setscope(&thr_attr, PTHREAD_SCOPE_SYSTEM);
375 #endif
376   my_thread_attr_setdetachstate(&thr_attr, MY_THREAD_CREATE_JOINABLE);
377   my_thread_handle thread_handle;
378   // What about setting THD::real_id?
379   int error= mysql_thread_create(key_thread_bootstrap,
380                                  &thread_handle, &thr_attr, handle_bootstrap, thd);
381   if (error)
382   {
383     sql_print_warning("Can't create thread to handle bootstrap (errno= %d)",
384                       error);
385     DBUG_RETURN(-1);
386   }
387   /* Wait for thread to die */
388   my_thread_join(&thread_handle, NULL);
389   delete thd;
390   DBUG_RETURN(bootstrap_error);
391 }
392 
bootstrap_single_query(const char * query)393 int bootstrap_single_query(const char* query)
394 {
395   bootstrap_query= query;
396   return bootstrap(NULL);
397 }
398