1 /* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
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
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include "sql/persisted_variable.h"
24
25 #include "my_config.h"
26
27 #include <fcntl.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <algorithm>
32 #include <memory>
33 #include <new>
34 #include <utility>
35
36 #include "lex_string.h"
37 #include "m_ctype.h"
38 #include "m_string.h"
39 #include "my_compiler.h"
40 #include "my_dbug.h"
41 #include "my_default.h" // check_file_permissions
42 #include "my_getopt.h"
43 #include "my_io.h"
44 #include "my_loglevel.h"
45 #include "my_macros.h"
46 #include "my_sys.h"
47 #include "my_thread.h"
48 #include "mysql/components/services/log_builtins.h"
49 #include "mysql/components/services/log_shared.h"
50 #include "mysql/components/services/psi_file_bits.h"
51 #include "mysql/components/services/psi_memory_bits.h"
52 #include "mysql/components/services/psi_mutex_bits.h"
53 #include "mysql/components/services/system_variable_source_type.h"
54 #include "mysql/psi/mysql_file.h"
55 #include "mysql/psi/mysql_memory.h"
56 #include "mysql/psi/mysql_mutex.h"
57 #include "mysql/psi/psi_base.h"
58 #include "mysql/status_var.h"
59 #include "mysql_version.h"
60 #include "mysqld_error.h"
61 #include "prealloced_array.h"
62 #include "sql/auth/auth_acls.h"
63 #include "sql/auth/auth_internal.h"
64 #include "sql/auth/sql_security_ctx.h"
65 #include "sql/current_thd.h"
66 #include "sql/debug_sync.h" // DEBUG_SYNC
67 #include "sql/derror.h" // ER_THD
68 #include "sql/item.h"
69 #include "sql/json_dom.h"
70 #include "sql/log.h"
71 #include "sql/mysqld.h"
72 #include "sql/set_var.h"
73 #include "sql/sql_class.h"
74 #include "sql/sql_error.h"
75 #include "sql/sql_lex.h"
76 #include "sql/sql_list.h"
77 #include "sql/sql_show.h"
78 #include "sql/sys_vars_shared.h"
79 #include "sql/thr_malloc.h"
80 #include "sql_string.h"
81 #include "template_utils.h"
82 #include "thr_mutex.h"
83 #include "typelib.h"
84
85 using std::map;
86 using std::string;
87 using std::vector;
88
89 const string version("\"Version\"");
90 const string name("\"Name\"");
91 const string value("\"Value\"");
92 const string metadata("\"Metadata\"");
93 const string timestamp("\"Timestamp\"");
94 const string user("\"User\"");
95 const string host("\"Host\"");
96 const string mysqld_section("\"mysql_server\"");
97 const string static_section("\"mysql_server_static_options\"");
98 const string colon(" : ");
99 const string comma(" , ");
100 const string open_brace("{ ");
101 const string close_brace(" }");
102
103 const int file_version = 1;
104
105 PSI_file_key key_persist_file_cnf;
106
107 #ifdef HAVE_PSI_FILE_INTERFACE
108 static PSI_file_info all_persist_files[] = {
109 {&key_persist_file_cnf, "cnf", 0, 0, PSI_DOCUMENT_ME}};
110 #endif /* HAVE_PSI_FILE_INTERFACE */
111
112 PSI_mutex_key key_persist_file, key_persist_variables;
113
114 #ifdef HAVE_PSI_MUTEX_INTERFACE
115 static PSI_mutex_info all_persist_mutexes[] = {
116 {&key_persist_file, "m_LOCK_persist_file", 0, 0, PSI_DOCUMENT_ME},
117 {&key_persist_variables, "m_LOCK_persist_variables", 0, 0,
118 PSI_DOCUMENT_ME}};
119 #endif /* HAVE_PSI_MUTEX_INTERFACE */
120
121 PSI_memory_key key_memory_persisted_variables;
122
123 #ifdef HAVE_PSI_MEMORY_INTERFACE
124 static PSI_memory_info all_options[] = {
125 {&key_memory_persisted_variables, "persisted_options_root", 0,
126 PSI_FLAG_ONLY_GLOBAL_STAT, PSI_DOCUMENT_ME}};
127 #endif /* HAVE_PSI_MEMORY_INTERFACE */
128
129 #ifdef HAVE_PSI_INTERFACE
my_init_persist_psi_keys(void)130 void my_init_persist_psi_keys(void) {
131 const char *category MY_ATTRIBUTE((unused)) = "persist";
132 int count MY_ATTRIBUTE((unused));
133
134 #ifdef HAVE_PSI_FILE_INTERFACE
135 count = sizeof(all_persist_files) / sizeof(all_persist_files[0]);
136 mysql_file_register(category, all_persist_files, count);
137 #endif
138
139 #ifdef HAVE_PSI_MUTEX_INTERFACE
140 count = static_cast<int>(array_elements(all_persist_mutexes));
141 mysql_mutex_register(category, all_persist_mutexes, count);
142 #endif
143
144 #ifdef HAVE_PSI_MEMORY_INTERFACE
145 count = static_cast<int>(array_elements(all_options));
146 mysql_memory_register(category, all_options, count);
147 #endif
148 }
149 #endif
150
151 /** A comparison operator to sort persistent variables entries by timestamp */
152 struct sort_tv_by_timestamp {
operator ()sort_tv_by_timestamp153 bool operator()(const st_persist_var x, const st_persist_var y) const {
154 return x.timestamp < y.timestamp;
155 }
156 };
157
158 Persisted_variables_cache *Persisted_variables_cache::m_instance = nullptr;
159
160 /* Standard Constructors for st_persist_var */
161
st_persist_var()162 st_persist_var::st_persist_var() {
163 if (current_thd) {
164 timeval tv = current_thd->query_start_timeval_trunc(DATETIME_MAX_DECIMALS);
165 timestamp = tv.tv_sec * 1000000ULL + tv.tv_usec;
166 } else
167 timestamp = my_micro_time();
168 is_null = false;
169 }
170
st_persist_var(THD * thd)171 st_persist_var::st_persist_var(THD *thd) {
172 timeval tv = thd->query_start_timeval_trunc(DATETIME_MAX_DECIMALS);
173 timestamp = tv.tv_sec * 1000000ULL + tv.tv_usec;
174 user = thd->security_context()->user().str;
175 host = thd->security_context()->host().str;
176 is_null = false;
177 }
178
st_persist_var(const std::string key,const std::string value,const ulonglong timestamp,const std::string user,const std::string host,const bool is_null)179 st_persist_var::st_persist_var(const std::string key, const std::string value,
180 const ulonglong timestamp,
181 const std::string user, const std::string host,
182 const bool is_null) {
183 this->key = key;
184 this->value = value;
185 this->timestamp = timestamp;
186 this->user = user;
187 this->host = host;
188 this->is_null = is_null;
189 }
190
191 /**
192 Initialize class members. This function reads datadir if present in
193 config file or set at command line, in order to know from where to
194 load this config file. If datadir is not set then read from MYSQL_DATADIR.
195
196 @param [in] argc Pointer to argc of original program
197 @param [in] argv Pointer to argv of original program
198
199 @return 0 Success
200 @return 1 Failure
201
202 */
init(int * argc,char *** argv)203 int Persisted_variables_cache::init(int *argc, char ***argv) {
204 #ifdef HAVE_PSI_INTERFACE
205 my_init_persist_psi_keys();
206 #endif
207
208 int temp_argc = *argc;
209 MEM_ROOT alloc{PSI_NOT_INSTRUMENTED, 512};
210 char *ptr, **res, *datadir = nullptr;
211 char dir[FN_REFLEN] = {0}, local_datadir_buffer[FN_REFLEN] = {0};
212 const char *dirs = NULL;
213 bool persist_load = true;
214
215 my_option persist_options[] = {
216 {"persisted_globals_load", 0, "", &persist_load, &persist_load, nullptr,
217 GET_BOOL, OPT_ARG, 1, 0, 0, nullptr, 0, nullptr},
218 {"datadir", 0, "", &datadir, nullptr, nullptr, GET_STR, OPT_ARG, 0, 0, 0,
219 nullptr, 0, nullptr},
220 {nullptr, 0, nullptr, nullptr, nullptr, nullptr, GET_NO_ARG, NO_ARG, 0, 0,
221 0, nullptr, 0, nullptr}};
222
223 /* create temporary args list and pass it to handle_options */
224 init_alloc_root(key_memory_persisted_variables, &alloc, 512, 0);
225 if (!(ptr =
226 (char *)alloc.Alloc(sizeof(alloc) + (*argc + 1) * sizeof(char *))))
227 return 1;
228 memset(ptr, 0, (sizeof(char *) * (*argc + 1)));
229 res = (char **)(ptr);
230 memcpy((uchar *)res, (char *)(*argv), (*argc) * sizeof(char *));
231
232 my_getopt_skip_unknown = true;
233 if (my_handle_options(&temp_argc, &res, persist_options, nullptr, nullptr,
234 true, false)) {
235 free_root(&alloc, MYF(0));
236 return 1;
237 }
238 my_getopt_skip_unknown = false;
239 free_root(&alloc, MYF(0));
240
241 persisted_globals_load = persist_load;
242
243 if (!datadir) {
244 // mysql_real_data_home must be initialized at this point
245 DBUG_ASSERT(mysql_real_data_home[0]);
246 /*
247 mysql_home_ptr should also be initialized at this point.
248 See calculate_mysql_home_from_my_progname() for details
249 */
250 DBUG_ASSERT(mysql_home_ptr && mysql_home_ptr[0]);
251 convert_dirname(local_datadir_buffer, mysql_real_data_home, NullS);
252 (void)my_load_path(local_datadir_buffer, local_datadir_buffer,
253 mysql_home_ptr);
254 datadir = local_datadir_buffer;
255 }
256
257 dirs = datadir;
258 unpack_dirname(dir, dirs);
259 my_realpath(datadir_buffer, dir, MYF(0));
260 unpack_dirname(datadir_buffer, datadir_buffer);
261 if (fn_format(dir, MYSQL_PERSIST_CONFIG_NAME, datadir_buffer, ".cnf",
262 MY_UNPACK_FILENAME | MY_SAFE_PATH) == nullptr)
263 return 1;
264 m_persist_filename = string(dir);
265
266 mysql_mutex_init(key_persist_variables, &m_LOCK_persist_variables,
267 MY_MUTEX_INIT_FAST);
268
269 mysql_mutex_init(key_persist_file, &m_LOCK_persist_file, MY_MUTEX_INIT_FAST);
270
271 m_instance = this;
272 return 0;
273 }
274
275 /**
276 Return a singleton object
277 */
get_instance()278 Persisted_variables_cache *Persisted_variables_cache::get_instance() {
279 DBUG_ASSERT(m_instance != nullptr);
280 return m_instance;
281 }
282
283 /**
284 For boolean variable types do validation on what value is set for the
285 variable and then report error in case an invalid value is set.
286
287 @param [in] value Value which needs to be checked for.
288 @param [out] bool_str Target String into which correct value needs to be
289 stored after validation.
290
291 @return true Failure if value is set to anything other than "true", "on",
292 "1", "false" , "off", "0"
293 @return false Success
294 */
check_boolean_value(const char * value,String & bool_str)295 static bool check_boolean_value(const char *value, String &bool_str) {
296 bool ret = false;
297 bool result = get_bool_argument(value, &ret);
298 if (ret) return true;
299 if (result) {
300 bool_str = String("ON", system_charset_info);
301 } else {
302 bool_str = String("OFF", system_charset_info);
303 }
304 return false;
305 }
306
307 /**
308 Retrieve variables name/value and update the in-memory copy with
309 this new values. If value is default then remove this entry from
310 in-memory copy, else update existing key with new value
311
312 @param [in] thd Pointer to connection handler
313 @param [in] setvar Pointer to set_var which is being SET
314
315 @return true Failure
316 @return false Success
317 */
set_variable(THD * thd,set_var * setvar)318 bool Persisted_variables_cache::set_variable(THD *thd, set_var *setvar) {
319 char val_buf[1024] = {0};
320 String utf8_str;
321 bool is_null = false;
322
323 struct st_persist_var tmp_var(thd);
324 sys_var *system_var = setvar->var;
325
326 const char *var_name =
327 Persisted_variables_cache::get_variable_name(system_var);
328 const char *var_value = val_buf;
329 if (setvar->type == OPT_PERSIST_ONLY) {
330 String str(val_buf, sizeof(val_buf), system_charset_info), *res;
331 const CHARSET_INFO *tocs = &my_charset_utf8mb4_bin;
332 uint dummy_err;
333 String bool_str;
334 if (setvar->value) {
335 res = setvar->value->val_str(&str);
336 if (system_var->get_var_type() == GET_BOOL) {
337 if (res == nullptr ||
338 check_boolean_value(res->c_ptr_quick(), bool_str)) {
339 my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var_name,
340 (res ? res->c_ptr_quick() : "null"));
341 return true;
342 } else {
343 res = &bool_str;
344 }
345 }
346 if (res && res->length()) {
347 /*
348 value held by Item class can be of different charset,
349 so convert to utf8mb4
350 */
351 utf8_str.copy(res->ptr(), res->length(), res->charset(), tocs,
352 &dummy_err);
353 var_value = utf8_str.c_ptr_quick();
354 }
355 } else {
356 /* persist default value */
357 setvar->var->save_default(thd, setvar);
358 setvar->var->saved_value_to_string(thd, setvar, str.ptr());
359 res = &str;
360 if (system_var->get_var_type() == GET_BOOL) {
361 check_boolean_value(res->c_ptr_quick(), bool_str);
362 res = &bool_str;
363 }
364 utf8_str.copy(res->ptr(), res->length(), res->charset(), tocs,
365 &dummy_err);
366 var_value = utf8_str.c_ptr_quick();
367 }
368 } else {
369 Persisted_variables_cache::get_variable_value(thd, system_var, &utf8_str,
370 &is_null);
371 var_value = utf8_str.c_ptr_quick();
372 }
373
374 /* structured variables may have basename if specified */
375 tmp_var.key =
376 (setvar->base.str ? string(setvar->base.str).append(".").append(var_name)
377 : string(var_name));
378 tmp_var.value = var_value;
379 tmp_var.is_null = is_null;
380
381 /* modification to in-memory must be thread safe */
382 lock();
383 DEBUG_SYNC(thd, "in_set_persist_variables");
384 /* if present update variable with new value else insert into hash */
385 if ((setvar->type == OPT_PERSIST_ONLY && setvar->var->is_readonly()) ||
386 setvar->var->is_persist_readonly())
387 m_persist_ro_variables[tmp_var.key] = tmp_var;
388 else {
389 /*
390 if element is present remove from current position and insert
391 at end of vector to restore insertion order.
392 */
393 string str = tmp_var.key;
394 auto itt =
395 std::find_if(m_persist_variables.begin(), m_persist_variables.end(),
396 [str](st_persist_var const &s) { return s.key == str; });
397 if (itt != m_persist_variables.end()) m_persist_variables.erase(itt);
398 m_persist_variables.push_back(tmp_var);
399 /* for plugin variables update m_persist_plugin_variables */
400 if (setvar->var->cast_pluginvar()) {
401 auto it = std::find_if(
402 m_persist_plugin_variables.begin(), m_persist_plugin_variables.end(),
403 [str](st_persist_var const &s) { return s.key == str; });
404 if (it != m_persist_plugin_variables.end())
405 m_persist_plugin_variables.erase(it);
406 m_persist_plugin_variables.push_back(tmp_var);
407 }
408 }
409 unlock();
410 return false;
411 }
412
413 /**
414 Retrieve variables value from sys_var
415
416 @param [in] thd Pointer to connection handler
417 @param [in] system_var Pointer to sys_var which is being SET
418 @param [in] str Pointer to String instance into which value
419 is copied
420 @param [out] is_null Is value NULL or not.
421
422 @return
423 Pointer to String instance holding the value
424 */
get_variable_value(THD * thd,sys_var * system_var,String * str,bool * is_null)425 String *Persisted_variables_cache::get_variable_value(THD *thd,
426 sys_var *system_var,
427 String *str,
428 bool *is_null) {
429 const char *value;
430 char val_buf[1024];
431 size_t val_length;
432 char show_var_buffer[sizeof(SHOW_VAR)];
433 SHOW_VAR *show = (SHOW_VAR *)show_var_buffer;
434 const CHARSET_INFO *fromcs;
435 const CHARSET_INFO *tocs = &my_charset_utf8mb4_bin;
436 uint dummy_err;
437
438 show->type = SHOW_SYS;
439 show->name = system_var->name.str;
440 show->value = (char *)system_var;
441
442 mysql_mutex_lock(&LOCK_global_system_variables);
443 value = get_one_variable(thd, show, OPT_GLOBAL, show->type, nullptr, &fromcs,
444 val_buf, &val_length, is_null);
445 mysql_mutex_unlock(&LOCK_global_system_variables);
446
447 /* convert the retrieved value to utf8mb4 */
448 str->copy(value, val_length, fromcs, tocs, &dummy_err);
449 return str;
450 }
451
452 /**
453 Retrieve variables name from sys_var
454
455 @param [in] system_var Pointer to sys_var which is being SET
456 @return
457 Pointer to buffer holding the name
458 */
get_variable_name(sys_var * system_var)459 const char *Persisted_variables_cache::get_variable_name(sys_var *system_var) {
460 return system_var->name.str;
461 }
462
463 /**
464 Given information of variable which needs to be persisted, this function
465 will construct a json foematted string out of it.
466
467 Format will be as below for variable named "X":
468 "X" : {
469 "Value" : "value",
470 "Metadata" : {
471 "Timestamp" : timestamp_value,
472 "User" : "user_name",
473 "Host" : "host_name"
474 }
475 }
476
477 @param [in] name Variable name
478 @param [in] value Variable value
479 @param [in] timestamp Timestamp value when this variable was set
480 @param [in] user User who set this variable
481 @param [in] host Host on which this variable was set
482 @param [in] is_null Is variable value NULL or not.
483 @param [out] dest String object where json formatted string
484 is stored
485
486 @return
487 Pointer to String instance holding the json formatted string
488
489 */
construct_json_string(std::string name,std::string value,ulonglong timestamp,std::string user,std::string host,bool is_null,String * dest)490 String *Persisted_variables_cache::construct_json_string(
491 std::string name, std::string value, ulonglong timestamp, std::string user,
492 std::string host, bool is_null, String *dest) {
493 String str;
494 Json_wrapper vv;
495 std::unique_ptr<Json_string> var_name(new (std::nothrow) Json_string(name));
496 Json_wrapper vn(var_name.release());
497 vn.to_string(&str, true, String().ptr());
498 dest->append(str);
499 dest->append(string(colon + open_brace + ::value + colon).c_str());
500
501 /* reset str */
502 str = String();
503 if (is_null) {
504 std::unique_ptr<Json_null> var_null_val(new (std::nothrow) Json_null());
505 vv = Json_wrapper(std::move(var_null_val));
506 } else {
507 std::unique_ptr<Json_string> var_val(new (std::nothrow) Json_string(value));
508 vv = Json_wrapper(std::move(var_val));
509 }
510 vv.to_string(&str, true, String().ptr());
511 dest->append(str);
512 dest->append(comma.c_str());
513
514 /* reset str */
515 str = String();
516 dest->append(
517 string(metadata + colon + open_brace + ::timestamp + colon).c_str());
518 std::unique_ptr<Json_uint> var_ts(new (std::nothrow) Json_uint(timestamp));
519 Json_wrapper vt(var_ts.release());
520 vt.to_string(&str, true, String().ptr());
521 dest->append(str);
522 dest->append(comma.c_str());
523
524 /* reset str */
525 str = String();
526 dest->append(string(::user + colon).c_str());
527 std::unique_ptr<Json_string> var_user(new (std::nothrow) Json_string(user));
528 Json_wrapper vu(var_user.release());
529 vu.to_string(&str, true, String().ptr());
530 dest->append(str);
531 dest->append(comma.c_str());
532
533 /* reset str */
534 str = String();
535 dest->append(string(::host + colon).c_str());
536 std::unique_ptr<Json_string> var_host(new (std::nothrow) Json_string(host));
537 Json_wrapper vh(var_host.release());
538 vh.to_string(&str, true, String().ptr());
539 dest->append(str);
540 dest->append(string(close_brace + close_brace + comma).c_str());
541
542 return dest;
543 }
544
545 /**
546 Convert in-memory copy into a stream of characters and write this
547 stream to persisted config file
548
549 @return Error state
550 @retval true An error occurred
551 @retval false Success
552 */
flush_to_file()553 bool Persisted_variables_cache::flush_to_file() {
554 lock();
555 mysql_mutex_lock(&m_LOCK_persist_file);
556
557 string tmp_str(open_brace + version + colon + std::to_string(file_version) +
558 comma + mysqld_section + colon + open_brace);
559 String dest(tmp_str.c_str(), &my_charset_utf8mb4_bin);
560
561 for (auto iter = m_persist_variables.begin();
562 iter != m_persist_variables.end(); iter++) {
563 String json_formatted_string;
564 Persisted_variables_cache::construct_json_string(
565 iter->key, iter->value, iter->timestamp, iter->user, iter->host,
566 iter->is_null, &json_formatted_string);
567 dest.append(json_formatted_string.c_ptr_quick());
568 }
569
570 if (m_persist_ro_variables.size()) {
571 dest.append(string(static_section + colon + open_brace).c_str());
572 }
573
574 for (auto iter = m_persist_ro_variables.begin();
575 iter != m_persist_ro_variables.end(); iter++) {
576 String json_formatted_string;
577 Persisted_variables_cache::construct_json_string(
578 iter->second.key, iter->second.value, iter->second.timestamp,
579 iter->second.user, iter->second.host, iter->second.is_null,
580 &json_formatted_string);
581 dest.append(json_formatted_string.c_ptr_quick());
582 }
583
584 if (m_persist_ro_variables.size()) {
585 /* remove last " , " characters */
586 dest.chop();
587 dest.chop();
588 dest.chop();
589 dest.append(close_brace.c_str());
590 }
591 if (m_persist_variables.size() && !m_persist_ro_variables.size()) {
592 dest.chop();
593 dest.chop();
594 dest.chop();
595 }
596 dest.append(string(close_brace + close_brace).c_str());
597 /*
598 If file does not exists create one. When persisted_globals_load is 0
599 we dont read contents of mysqld-auto.cnf file, thus append any new
600 variables which are persisted to this file.
601 */
602 bool ret = false;
603
604 if (open_persist_file(O_CREAT | O_WRONLY)) {
605 ret = true;
606 } else {
607 /* write to file */
608 if (mysql_file_fputs(dest.c_ptr(), m_fd) < 0) {
609 ret = true;
610 }
611 }
612
613 close_persist_file();
614 mysql_mutex_unlock(&m_LOCK_persist_file);
615 unlock();
616 return ret;
617 }
618
619 /**
620 Open persisted config file
621
622 @param [in] flag File open mode
623 @return Error state
624 @retval true An error occurred
625 @retval false Success
626 */
open_persist_file(int flag)627 bool Persisted_variables_cache::open_persist_file(int flag) {
628 m_fd = mysql_file_fopen(key_persist_file_cnf, m_persist_filename.c_str(),
629 flag, MYF(0));
630 return (m_fd ? 0 : 1);
631 }
632
633 /**
634 Close persisted config file.
635 */
close_persist_file()636 void Persisted_variables_cache::close_persist_file() {
637 mysql_file_fclose(m_fd, MYF(0));
638 m_fd = nullptr;
639 }
640
641 /**
642 load_persist_file() read persisted config file
643
644 @return Error state
645 @retval true An error occurred
646 @retval false Success
647 */
load_persist_file()648 bool Persisted_variables_cache::load_persist_file() {
649 if (read_persist_file() > 0) return true;
650 return false;
651 }
652
653 /**
654 set_persist_options() will set the options read from persisted config file
655
656 This function does nothing when --no-defaults is set or if
657 persisted_globals_load is set to false
658
659 @param [in] plugin_options Flag which tells what options are being set.
660 If set to false non plugin variables are set
661 else plugin variables are set
662
663 @return Error state
664 @retval true An error occurred
665 @retval false Success
666 */
set_persist_options(bool plugin_options)667 bool Persisted_variables_cache::set_persist_options(bool plugin_options) {
668 THD *thd;
669 LEX lex_tmp, *sav_lex = nullptr;
670 List<set_var_base> tmp_var_list;
671 vector<st_persist_var> *persist_variables = nullptr;
672 bool result = false, new_thd = false;
673 const std::vector<std::string> priv_list = {
674 "ENCRYPTION_KEY_ADMIN", "ROLE_ADMIN", "SYSTEM_VARIABLES_ADMIN",
675 "AUDIT_ADMIN"};
676 const ulong static_priv_list = (SUPER_ACL | FILE_ACL);
677 Sctx_ptr<Security_context> ctx;
678 /*
679 if persisted_globals_load is set to false or --no-defaults is set
680 then do not set persistent options
681 */
682 if (no_defaults || !persisted_globals_load) return false;
683 /*
684 This function is called in only 2 places
685 1. During server startup.
686 2. During install plugin after server has started.
687 During server startup before server components are initialized
688 current_thd is NULL thus instantiate new temporary THD.
689 After server has started we have current_thd so make use of current_thd.
690 */
691 if (current_thd) {
692 thd = current_thd;
693 sav_lex = thd->lex;
694 thd->lex = &lex_tmp;
695 lex_start(thd);
696 } else {
697 if (!(thd = new THD)) {
698 LogErr(ERROR_LEVEL, ER_FAILED_TO_SET_PERSISTED_OPTIONS);
699 return true;
700 }
701 thd->thread_stack = (char *)&thd;
702 thd->set_new_thread_id();
703 thd->store_globals();
704 lex_start(thd);
705 /* create security context for bootstrap auth id */
706 Security_context_factory default_factory(
707 thd, "bootstrap", "localhost", Default_local_authid(thd),
708 Grant_temporary_dynamic_privileges(thd, priv_list),
709 Grant_temporary_static_privileges(thd, static_priv_list),
710 Drop_temporary_dynamic_privileges(priv_list));
711 ctx = default_factory.create(thd->mem_root);
712 /* attach this auth id to current security_context */
713 thd->set_security_context(ctx.get());
714 thd->real_id = my_thread_self();
715 new_thd = true;
716 alloc_and_copy_thd_dynamic_variables(thd, !plugin_options);
717 }
718 /*
719 locking is not needed as this function is executed only during server
720 bootstrap, but we take the lock to be on safer side.
721 */
722 lock();
723 assert_lock_owner();
724 /*
725 Based on plugin_options, we decide on what options to be set. If
726 plugin_options is false we set all non plugin variables and then
727 keep all plugin variables in a map. When the plugin is installed
728 plugin variables are read from the map and set.
729 */
730 persist_variables =
731 (plugin_options ? &m_persist_plugin_variables : &m_persist_variables);
732
733 /* create a sorted set of values sorted by timestamp */
734 std::multiset<st_persist_var, sort_tv_by_timestamp> sorted_vars(
735 persist_variables->begin(), persist_variables->end());
736
737 for (auto iter = sorted_vars.begin(); iter != sorted_vars.end(); iter++) {
738 Item *res = nullptr;
739 set_var *var = nullptr;
740 sys_var *sysvar = nullptr;
741 string var_name = iter->key;
742
743 LEX_CSTRING base_name = {var_name.c_str(), var_name.length()};
744
745 sysvar = intern_find_sys_var(var_name.c_str(), var_name.length());
746 if (sysvar == nullptr) {
747 /*
748 for plugin variables we report a warning in error log,
749 keep track of this variable so that it is set when plugin
750 is loaded and continue with remaining persisted variables
751 */
752 m_persist_plugin_variables.push_back(*iter);
753 LogErr(WARNING_LEVEL, ER_UNKNOWN_VARIABLE_IN_PERSISTED_CONFIG_FILE,
754 var_name.c_str());
755 continue;
756 }
757 switch (sysvar->show_type()) {
758 case SHOW_INT:
759 case SHOW_LONG:
760 case SHOW_LONGLONG:
761 case SHOW_HA_ROWS:
762 res = new (thd->mem_root)
763 Item_uint(iter->value.c_str(), (uint)iter->value.length());
764 break;
765 case SHOW_SIGNED_INT:
766 case SHOW_SIGNED_LONG:
767 case SHOW_SIGNED_LONGLONG:
768 res = new (thd->mem_root)
769 Item_int(iter->value.c_str(), (uint)iter->value.length());
770 break;
771 case SHOW_CHAR:
772 case SHOW_LEX_STRING:
773 case SHOW_BOOL:
774 case SHOW_MY_BOOL:
775 res = new (thd->mem_root) Item_string(
776 iter->value.c_str(), iter->value.length(), &my_charset_utf8mb4_bin);
777 break;
778 case SHOW_CHAR_PTR:
779 if (iter->is_null)
780 res = new (thd->mem_root) Item_null();
781 else
782 res = new (thd->mem_root)
783 Item_string(iter->value.c_str(), iter->value.length(),
784 &my_charset_utf8mb4_bin);
785 break;
786 case SHOW_DOUBLE:
787 res = new (thd->mem_root)
788 Item_float(iter->value.c_str(), (uint)iter->value.length());
789 break;
790 default:
791 my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), sysvar->name.str);
792 result = true;
793 goto err;
794 }
795
796 var = new (thd->mem_root) set_var(OPT_GLOBAL, sysvar, base_name, res);
797 tmp_var_list.push_back(var);
798
799 if (sql_set_variables(thd, &tmp_var_list, false)) {
800 /*
801 If there is a connection and an error occurred during install plugin
802 then report error at sql layer, else log the error in server log.
803 */
804 if (current_thd && plugin_options) {
805 if (thd->is_error())
806 LogErr(ERROR_LEVEL, ER_PERSIST_OPTION_STATUS,
807 thd->get_stmt_da()->message_text());
808 else
809 my_error(ER_CANT_SET_PERSISTED, MYF(0));
810 } else {
811 if (thd->is_error())
812 LogErr(ERROR_LEVEL, ER_PERSIST_OPTION_STATUS,
813 thd->get_stmt_da()->message_text());
814 else
815 LogErr(ERROR_LEVEL, ER_FAILED_TO_SET_PERSISTED_OPTIONS);
816 }
817 result = true;
818 goto err;
819 }
820 tmp_var_list.empty();
821 /*
822 Once persisted variables are SET in the server,
823 update variables source/user/timestamp/host from m_persist_variables.
824 */
825 auto it = std::find_if(
826 m_persist_variables.begin(), m_persist_variables.end(),
827 [var_name](st_persist_var const &s) { return s.key == var_name; });
828 if (it != m_persist_variables.end()) {
829 /* persisted variable is found */
830 sysvar->set_source(enum_variable_source::PERSISTED);
831 #ifndef DBUG_OFF
832 bool source_truncated =
833 #endif
834 sysvar->set_source_name(m_persist_filename.c_str());
835 DBUG_ASSERT(!source_truncated);
836 sysvar->set_timestamp(it->timestamp);
837 if (sysvar->set_user(it->user.c_str()))
838 LogErr(WARNING_LEVEL, ER_PERSIST_OPTION_USER_TRUNCATED,
839 var_name.c_str());
840 if (sysvar->set_host(it->host.c_str()))
841 LogErr(WARNING_LEVEL, ER_PERSIST_OPTION_HOST_TRUNCATED,
842 var_name.c_str());
843 }
844 }
845
846 err:
847 if (new_thd) {
848 /* check for warnings in DA */
849 Diagnostics_area::Sql_condition_iterator it =
850 thd->get_stmt_da()->sql_conditions();
851 const Sql_condition *err = nullptr;
852 while ((err = it++)) {
853 if (err->severity() == Sql_condition::SL_WARNING) {
854 // Rewrite error number for "deprecated" to error log equivalent.
855 if (err->mysql_errno() == ER_WARN_DEPRECATED_SYNTAX)
856 LogEvent()
857 .type(LOG_TYPE_ERROR)
858 .prio(WARNING_LEVEL)
859 .errcode(ER_SERVER_WARN_DEPRECATED)
860 .verbatim(err->message_text());
861 /*
862 Any other (unexpected) message is wrapped to preserve its
863 original error number, and to explain the issue.
864 This is a failsafe; "expected", that is to say, common
865 messages should be handled explicitly like the deprecation
866 warning above.
867 */
868 else
869 LogErr(WARNING_LEVEL, ER_ERROR_INFO_FROM_DA, err->mysql_errno(),
870 err->message_text());
871 }
872 }
873 thd->free_items();
874 lex_end(thd->lex);
875 thd->release_resources();
876 ctx.reset(nullptr);
877 delete thd;
878 } else {
879 thd->lex = sav_lex;
880 }
881 unlock();
882 return result;
883 }
884
885 /**
886 extract_variables_from_json() is used to extract all the variable information
887 which is in the form of Json_object.
888
889 New format for mysqld-auto.cnf is as below:
890 { "Version" : 1,
891 "mysql_server" :
892 { "variable_name" : {
893 "Value" : "variable_value",
894 "Metadata" : {
895 "Timestamp" : timestamp_value,
896 "User" : "user_name",
897 "Host" : "host_name"
898 }
899 }
900 }
901 { "variable_name" : {
902 ...
903
904 { "mysql_server_static_options" :
905 { "variable_name" : {
906 "Value" : "variable_value",
907 ...
908 }
909 ...
910 }
911
912 @param [in] dom Pointer to the Json_dom object which is an
913 internal representation of parsed json string
914 @param [in] is_read_only Bool value when set to TRUE extracts read only
915 variables and dynamic variables when set to FALSE.
916
917 @return 0 Success
918 @return 1 Failure
919 */
extract_variables_from_json(const Json_dom * dom,bool is_read_only)920 bool Persisted_variables_cache::extract_variables_from_json(const Json_dom *dom,
921 bool is_read_only) {
922 if (dom->json_type() != enum_json_type::J_OBJECT) goto err;
923 for (auto &var_iter : *down_cast<const Json_object *>(dom)) {
924 string var_value, var_user, var_host;
925 ulonglong timestamp = 0;
926 bool is_null = false;
927
928 const string &var_name = var_iter.first;
929 if (var_iter.second->json_type() != enum_json_type::J_OBJECT) goto err;
930 const Json_object *dom_obj =
931 down_cast<const Json_object *>(var_iter.second.get());
932
933 /**
934 Static variables by themselves is represented as a json object with key
935 "mysql_server_static_options" as parent element.
936 */
937 if (var_name == "mysql_server_static_options") {
938 if (extract_variables_from_json(dom_obj, true)) return true;
939 continue;
940 }
941
942 /**
943 Every Json object which represents Variable information must have only
944 2 elements which is
945 {
946 "Value" : "variable_value", -- 1st element
947 "Metadata" : { -- 2nd element
948 "Timestamp" : timestamp_value,
949 "User" : "user_name",
950 "Host" : "host_name"
951 }
952 }
953 */
954 if (dom_obj->depth() != 3 && dom_obj->cardinality() != 2) goto err;
955
956 Json_object::const_iterator var_properties_iter = dom_obj->begin();
957 /* extract variable value */
958 if (var_properties_iter->first != "Value") goto err;
959
960 const Json_dom *value = var_properties_iter->second.get();
961 /* if value is not in string form or null throw error. */
962 if (value->json_type() == enum_json_type::J_STRING) {
963 var_value = down_cast<const Json_string *>(value)->value();
964 } else if (value->json_type() == enum_json_type::J_NULL) {
965 var_value = "";
966 is_null = true;
967 } else {
968 goto err;
969 }
970
971 ++var_properties_iter;
972 /* extract metadata */
973 if (var_properties_iter->first != "Metadata") goto err;
974
975 if (var_properties_iter->second->json_type() != enum_json_type::J_OBJECT)
976 goto err;
977 dom_obj = down_cast<const Json_object *>(var_properties_iter->second.get());
978 if (dom_obj->depth() != 1 && dom_obj->cardinality() != 3) goto err;
979
980 for (auto &metadata_iter : *dom_obj) {
981 const string &metadata_type = metadata_iter.first;
982 const Json_dom *metadata_value = metadata_iter.second.get();
983 if (metadata_type == "Timestamp") {
984 if (metadata_value->json_type() != enum_json_type::J_UINT) goto err;
985 const Json_uint *i = down_cast<const Json_uint *>(metadata_value);
986 timestamp = i->value();
987 } else if (metadata_type == "User" || metadata_type == "Host") {
988 if (metadata_value->json_type() != enum_json_type::J_STRING) goto err;
989 const Json_string *i = down_cast<const Json_string *>(metadata_value);
990 if (metadata_type == "User")
991 var_user = i->value();
992 else
993 var_host = i->value();
994 } else {
995 goto err;
996 }
997 }
998 st_persist_var persist_var(var_name, var_value, timestamp, var_user,
999 var_host, is_null);
1000 lock();
1001 assert_lock_owner();
1002 if (is_read_only)
1003 m_persist_ro_variables[var_name] = persist_var;
1004 else
1005 m_persist_variables.push_back(persist_var);
1006 unlock();
1007 }
1008 return false;
1009
1010 err:
1011 LogErr(ERROR_LEVEL, ER_JSON_PARSE_ERROR);
1012 return true;
1013 }
1014
1015 /**
1016 read_persist_file() reads the persisted config file
1017
1018 This function does following:
1019 1. Read the persisted config file into a string buffer
1020 2. This string buffer is parsed with JSON parser to check
1021 if the format is correct or not.
1022 3. Check for correct group name.
1023 4. Extract key/value pair and populate in m_persist_variables,
1024 m_persist_ro_variables.
1025 mysqld-auto.cnf file will have variable properties like when a
1026 variable is set, by wholm and on what host this variable was set.
1027
1028 @return Error state
1029 @retval -1 or 1 Failure
1030 @retval 0 Success
1031 */
read_persist_file()1032 int Persisted_variables_cache::read_persist_file() {
1033 char buff[4096] = {0};
1034 string parsed_value;
1035 const char *error = nullptr;
1036 size_t offset = 0;
1037
1038 if ((check_file_permissions(m_persist_filename.c_str(), false)) < 2)
1039 return -1;
1040
1041 if (open_persist_file(O_RDONLY)) return -1;
1042 do {
1043 /* Read the persisted config file into a string buffer */
1044 parsed_value.append(buff);
1045 buff[0] = '\0';
1046 } while (mysql_file_fgets(buff, sizeof(buff) - 1, m_fd));
1047 close_persist_file();
1048
1049 /* parse the file contents to check if it is in json format or not */
1050 std::unique_ptr<Json_dom> json(Json_dom::parse(
1051 parsed_value.c_str(), parsed_value.length(), &error, &offset));
1052 if (!json.get()) {
1053 LogErr(ERROR_LEVEL, ER_JSON_PARSE_ERROR);
1054 return 1;
1055 }
1056 Json_object *json_obj = down_cast<Json_object *>(json.get());
1057 Json_object::const_iterator iter = json_obj->begin();
1058 if (iter->first != "Version") {
1059 LogErr(ERROR_LEVEL, ER_PERSIST_OPTION_STATUS,
1060 "Persisted config file corrupted.");
1061 return 1;
1062 }
1063 /* Check file version */
1064 Json_dom *dom_obj = iter->second.get();
1065 if (dom_obj->json_type() != enum_json_type::J_INT) {
1066 LogErr(ERROR_LEVEL, ER_PERSIST_OPTION_STATUS,
1067 "Persisted config file version invalid.");
1068 return 1;
1069 }
1070 Json_int *i = down_cast<Json_int *>(dom_obj);
1071 if (file_version != i->value()) {
1072 LogErr(ERROR_LEVEL, ER_PERSIST_OPTION_STATUS,
1073 "Persisted config file version invalid.");
1074 return 1;
1075 }
1076 ++iter;
1077 if (iter->first != "mysql_server") {
1078 LogErr(ERROR_LEVEL, ER_CONFIG_OPTION_WITHOUT_GROUP);
1079 return 1;
1080 }
1081 /* Extract key/value pair and populate in a global hash map */
1082 if (extract_variables_from_json(iter->second.get())) return 1;
1083 return 0;
1084 }
1085
1086 /**
1087 append_read_only_variables() does a lookup into persist_variables for read
1088 only variables and place them after the command line options with a separator
1089 "----persist-args-separator----"
1090
1091 This function does nothing when --no-defaults is set or if
1092 persisted_globals_load is disabled.
1093
1094 @param [in] argc Pointer to argc of original program
1095 @param [in] argv Pointer to argv of original program
1096 @param [in] plugin_options This flag tells wether options are
1097 handled during plugin install. If set to true options are handled as part of
1098 install plugin.
1099
1100 @return 0 Success
1101 @return 1 Failure
1102 */
append_read_only_variables(int * argc,char *** argv,bool plugin_options)1103 bool Persisted_variables_cache::append_read_only_variables(
1104 int *argc, char ***argv, bool plugin_options) {
1105 Prealloced_array<char *, 100> my_args(key_memory_persisted_variables);
1106 MEM_ROOT alloc;
1107
1108 if (*argc < 2 || no_defaults || !persisted_globals_load) return false;
1109
1110 init_alloc_root(key_memory_persisted_variables, &alloc, 512, 0);
1111
1112 /* create a set of values sorted by timestamp */
1113 std::multiset<st_persist_var, sort_tv_by_timestamp> sorted_vars;
1114 for (auto iter : m_persist_ro_variables) sorted_vars.insert(iter.second);
1115
1116 for (auto iter : sorted_vars) {
1117 string persist_option = "--loose_" + iter.key + "=" + iter.value;
1118 char *tmp;
1119
1120 if (nullptr == (tmp = strdup_root(&alloc, persist_option.c_str())) ||
1121 my_args.push_back(tmp))
1122 return true;
1123 }
1124 /*
1125 Update existing command line options if there are any persisted
1126 reasd only options to be appendded
1127 */
1128 if (my_args.size()) {
1129 char **res = new (&alloc) char *[my_args.size() + *argc + 2];
1130 if (res == nullptr) goto err;
1131 memset(res, 0, (sizeof(char *) * (my_args.size() + *argc + 2)));
1132 /* copy all arguments to new array */
1133 memcpy((uchar *)(res), (char *)(*argv), (*argc) * sizeof(char *));
1134
1135 if (!my_args.empty()) {
1136 /*
1137 Set args separator to know options set as part of command line and
1138 options set from persisted config file
1139 */
1140 set_persist_args_separator(&res[*argc]);
1141 /* copy arguments from persistent config file */
1142 memcpy((res + *argc + 1), &my_args[0], my_args.size() * sizeof(char *));
1143 }
1144 res[my_args.size() + *argc + 1] = nullptr; /* last null */
1145 (*argc) += (int)my_args.size() + 1;
1146 *argv = res;
1147 if (plugin_options)
1148 ro_persisted_plugin_argv_alloc =
1149 std::move(alloc); // Possibly overwrite previous.
1150 else
1151 ro_persisted_argv_alloc = std::move(alloc);
1152 return false;
1153 }
1154 return false;
1155
1156 err:
1157 LogErr(ERROR_LEVEL, ER_FAILED_TO_HANDLE_DEFAULTS_FILE);
1158 exit(1);
1159 }
1160
1161 /**
1162 reset_persisted_variables() does a lookup into persist_variables and remove
1163 the variable from the hash if present and flush the hash to file.
1164
1165 @param [in] thd Pointer to connection handle.
1166 @param [in] name Name of variable to remove, if NULL all
1167 variables are removed from config file.
1168 @param [in] if_exists Bool value when set to true reports
1169 warning else error if variable is not
1170 present in the config file.
1171
1172 @return 0 Success
1173 @return 1 Failure
1174 */
reset_persisted_variables(THD * thd,const char * name,bool if_exists)1175 bool Persisted_variables_cache::reset_persisted_variables(THD *thd,
1176 const char *name,
1177 bool if_exists) {
1178 bool result = false, flush = false, not_present = true;
1179 string var_name;
1180 bool reset_all = (name ? 0 : 1);
1181 var_name = (name ? name : string());
1182 /* update on m_persist_variables/m_persist_ro_variables must be thread safe */
1183 lock();
1184 auto it_ro = m_persist_ro_variables.find(var_name);
1185
1186 if (reset_all) {
1187 /* check for necessary privileges */
1188 if (!m_persist_variables.empty() && check_priv(thd, false)) goto end;
1189 if (!m_persist_ro_variables.empty() && check_priv(thd, true)) goto end;
1190
1191 if (!m_persist_variables.empty()) {
1192 m_persist_variables.clear();
1193 flush = true;
1194 }
1195 if (!m_persist_ro_variables.empty()) {
1196 m_persist_ro_variables.clear();
1197 flush = true;
1198 }
1199 /* remove plugin variables if any */
1200 if (!m_persist_plugin_variables.empty()) {
1201 m_persist_plugin_variables.clear();
1202 flush = true;
1203 }
1204 } else {
1205 auto checkvariable = [&var_name](st_persist_var const &s) -> bool {
1206 return s.key == var_name;
1207 };
1208 if (m_persist_variables.size()) {
1209 auto it = std::find_if(m_persist_variables.begin(),
1210 m_persist_variables.end(), checkvariable);
1211 if (it != m_persist_variables.end()) {
1212 /* if variable is present in config file remove it */
1213 if (check_priv(thd, false)) goto end;
1214 m_persist_variables.erase(it);
1215 flush = true;
1216 not_present = false;
1217 }
1218 }
1219 if (m_persist_plugin_variables.size()) {
1220 auto it = std::find_if(m_persist_plugin_variables.begin(),
1221 m_persist_plugin_variables.end(), checkvariable);
1222 if (it != m_persist_plugin_variables.end()) {
1223 if (check_priv(thd, false)) goto end;
1224 m_persist_plugin_variables.erase(it);
1225 flush = true;
1226 not_present = false;
1227 }
1228 }
1229 if (it_ro != m_persist_ro_variables.end()) {
1230 if (check_priv(thd, true)) goto end;
1231 /* if static variable is present in config file remove it */
1232 m_persist_ro_variables.erase(it_ro);
1233 flush = true;
1234 not_present = false;
1235 }
1236 if (not_present) {
1237 /* if not present and if exists is specified, report warning */
1238 if (if_exists) {
1239 push_warning_printf(
1240 thd, Sql_condition::SL_WARNING, ER_VAR_DOES_NOT_EXIST,
1241 ER_THD(thd, ER_VAR_DOES_NOT_EXIST), var_name.c_str());
1242 } else /* report error */
1243 {
1244 my_error(ER_VAR_DOES_NOT_EXIST, MYF(0), var_name.c_str());
1245 result = true;
1246 }
1247 }
1248 }
1249 unlock();
1250 if (flush) flush_to_file();
1251
1252 return result;
1253
1254 end:
1255 unlock();
1256 return true;
1257 }
1258
1259 /**
1260 Return in-memory copy persist_variables_
1261 */
get_persisted_variables()1262 vector<st_persist_var> *Persisted_variables_cache::get_persisted_variables() {
1263 return &m_persist_variables;
1264 }
1265
1266 /**
1267 Return in-memory copy for static persisted variables
1268 */
1269 map<string, st_persist_var>
get_persist_ro_variables()1270 *Persisted_variables_cache::get_persist_ro_variables() {
1271 return &m_persist_ro_variables;
1272 }
1273
cleanup()1274 void Persisted_variables_cache::cleanup() {
1275 mysql_mutex_destroy(&m_LOCK_persist_variables);
1276 mysql_mutex_destroy(&m_LOCK_persist_file);
1277 free_root(&ro_persisted_argv_alloc, MYF(0));
1278 free_root(&ro_persisted_plugin_argv_alloc, MYF(0));
1279 }
1280