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