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