1 /*
2    Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software Foundation,
22    51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
23 
24 #include "sql_priv.h"                         // SHOW_MY_BOOL
25 #include "unireg.h"
26 #include "my_global.h"                       // REQUIRED by m_string.h
27 #include "sql_class.h"                          // set_var.h: THD
28 #include "sys_vars_shared.h"
29 #include "sql_locale.h"
30 #include "sql_plugin.h"
31 #include "sql_parse.h"          // check_table_access
32 #include "sql_base.h"                           // close_mysql_tables
33 #include "key.h"                                // key_copy
34 #include "sql_show.h"           // remove_status_vars, add_status_vars
35 #include "strfunc.h"            // find_set
36 #include "sql_acl.h"                       // *_ACL
37 #include "records.h"          // init_read_record, end_read_record
38 #include <my_pthread.h>
39 #include <my_getopt.h>
40 #include "sql_audit.h"
41 #include <mysql/plugin_auth.h>
42 #include "lock.h"                               // MYSQL_LOCK_IGNORE_TIMEOUT
43 #include <mysql/plugin_validate_password.h>
44 #include "my_default.h"
45 #include "debug_sync.h"
46 
47 #include <algorithm>
48 
49 using std::min;
50 using std::max;
51 
52 #define REPORT_TO_LOG  1
53 #define REPORT_TO_USER 2
54 
55 extern struct st_mysql_plugin *mysql_optional_plugins[];
56 extern struct st_mysql_plugin *mysql_mandatory_plugins[];
57 
58 /**
59   @note The order of the enumeration is critical.
60   @see construct_options
61 */
62 const char *global_plugin_typelib_names[]=
63   { "OFF", "ON", "FORCE", "FORCE_PLUS_PERMANENT", NULL };
64 static TYPELIB global_plugin_typelib=
65   { array_elements(global_plugin_typelib_names)-1,
66     "", global_plugin_typelib_names, NULL };
67 
68 static I_List<i_string> opt_plugin_load_list;
69 I_List<i_string> *opt_plugin_load_list_ptr= &opt_plugin_load_list;
70 char *opt_plugin_dir_ptr;
71 char opt_plugin_dir[FN_REFLEN];
72 /*
73   When you ad a new plugin type, add both a string and make sure that the
74   init and deinit array are correctly updated.
75 */
76 const LEX_STRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]=
77 {
78   { C_STRING_WITH_LEN("UDF") },
79   { C_STRING_WITH_LEN("STORAGE ENGINE") },
80   { C_STRING_WITH_LEN("FTPARSER") },
81   { C_STRING_WITH_LEN("DAEMON") },
82   { C_STRING_WITH_LEN("INFORMATION SCHEMA") },
83   { C_STRING_WITH_LEN("AUDIT") },
84   { C_STRING_WITH_LEN("REPLICATION") },
85   { C_STRING_WITH_LEN("AUTHENTICATION") },
86   { C_STRING_WITH_LEN("VALIDATE PASSWORD") }
87 };
88 
89 extern int initialize_schema_table(st_plugin_int *plugin);
90 extern int finalize_schema_table(st_plugin_int *plugin);
91 
92 extern int initialize_audit_plugin(st_plugin_int *plugin);
93 extern int finalize_audit_plugin(st_plugin_int *plugin);
94 
95 /*
96   The number of elements in both plugin_type_initialize and
97   plugin_type_deinitialize should equal to the number of plugins
98   defined.
99 */
100 plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
101 {
102   0,ha_initialize_handlerton,0,0,initialize_schema_table,
103   initialize_audit_plugin,0,0,0
104 };
105 
106 plugin_type_init plugin_type_deinitialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
107 {
108   0,ha_finalize_handlerton,0,0,finalize_schema_table,
109   finalize_audit_plugin,0,0,0
110 };
111 
112 #ifdef HAVE_DLOPEN
113 static const char *plugin_interface_version_sym=
114                    "_mysql_plugin_interface_version_";
115 static const char *sizeof_st_plugin_sym=
116                    "_mysql_sizeof_struct_st_plugin_";
117 static const char *plugin_declarations_sym= "_mysql_plugin_declarations_";
118 static int min_plugin_interface_version= MYSQL_PLUGIN_INTERFACE_VERSION & ~0xFF;
119 #endif
120 
121 static void*	innodb_callback_data;
122 
123 /* Note that 'int version' must be the first field of every plugin
124    sub-structure (plugin->info).
125 */
126 static int min_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
127 {
128   0x0000,
129   MYSQL_HANDLERTON_INTERFACE_VERSION,
130   MYSQL_FTPARSER_INTERFACE_VERSION,
131   MYSQL_DAEMON_INTERFACE_VERSION,
132   MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
133   MYSQL_AUDIT_INTERFACE_VERSION,
134   MYSQL_REPLICATION_INTERFACE_VERSION,
135   MYSQL_AUTHENTICATION_INTERFACE_VERSION,
136   MYSQL_VALIDATE_PASSWORD_INTERFACE_VERSION
137 };
138 static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
139 {
140   0x0000, /* UDF: not implemented */
141   MYSQL_HANDLERTON_INTERFACE_VERSION,
142   MYSQL_FTPARSER_INTERFACE_VERSION,
143   MYSQL_DAEMON_INTERFACE_VERSION,
144   MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
145   MYSQL_AUDIT_INTERFACE_VERSION,
146   MYSQL_REPLICATION_INTERFACE_VERSION,
147   MYSQL_AUTHENTICATION_INTERFACE_VERSION,
148   MYSQL_VALIDATE_PASSWORD_INTERFACE_VERSION
149 };
150 
151 /* support for Services */
152 
153 #include "sql_plugin_services.h"
154 
155 /*
156   A mutex LOCK_plugin_delete must be acquired before calling plugin_del
157   function.
158 */
159 mysql_mutex_t LOCK_plugin_delete;
160 
161 /*
162   A mutex LOCK_plugin must be acquired before accessing the
163   following variables/structures.
164   We are always manipulating ref count, so a rwlock here is unneccessary.
165 */
166 mysql_mutex_t LOCK_plugin;
167 static DYNAMIC_ARRAY plugin_dl_array;
168 static DYNAMIC_ARRAY plugin_array;
169 static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM];
170 static bool reap_needed= false;
171 static int plugin_array_version=0;
172 
173 static bool initialized= 0;
174 
175 /*
176   write-lock on LOCK_system_variables_hash is required before modifying
177   the following variables/structures
178 */
179 static MEM_ROOT plugin_mem_root;
180 static uint global_variables_dynamic_size= 0;
181 static HASH bookmark_hash;
182 /** Hash for system variables of string type with MEMALLOC flag. */
183 static HASH malloced_string_type_sysvars_bookmark_hash;
184 
185 
186 /*
187   hidden part of opaque value passed to variable check functions.
188   Used to provide a object-like structure to non C++ consumers.
189 */
190 struct st_item_value_holder : public st_mysql_value
191 {
192   Item *item;
193 };
194 
195 
196 /*
197   stored in bookmark_hash, this structure is never removed from the
198   hash and is used to mark a single offset for a thd local variable
199   even if plugins have been uninstalled and reinstalled, repeatedly.
200   This structure is allocated from plugin_mem_root.
201 
202   The key format is as follows:
203     1 byte         - variable type code
204     name_len bytes - variable name
205     '\0'           - end of key
206 */
207 struct st_bookmark
208 {
209   uint name_len;
210   int offset;
211   uint version;
212   char key[1];
213 };
214 
215 
216 /*
217   skeleton of a plugin variable - portion of structure common to all.
218 */
219 struct st_mysql_sys_var
220 {
221   MYSQL_PLUGIN_VAR_HEADER;
222 };
223 
224 static SHOW_TYPE pluginvar_show_type(st_mysql_sys_var *plugin_var);
225 
226 
227 /*
228   sys_var class for access to all plugin variables visible to the user
229 */
230 class sys_var_pluginvar: public sys_var
231 {
232 public:
233   struct st_plugin_int *plugin;
234   struct st_mysql_sys_var *plugin_var;
235   /**
236     variable name from whatever is hard-coded in the plugin source
237     and doesn't have pluginname- prefix is replaced by an allocated name
238     with a plugin prefix. When plugin is uninstalled we need to restore the
239     pointer to point to the hard-coded value, because plugin may be
240     installed/uninstalled many times without reloading the shared object.
241   */
242   const char *orig_pluginvar_name;
243 
operator new(size_t size,MEM_ROOT * mem_root)244   static void *operator new(size_t size, MEM_ROOT *mem_root)
245   { return (void*) alloc_root(mem_root, size); }
operator delete(void * ptr_arg,size_t size)246   static void operator delete(void *ptr_arg,size_t size)
247   { TRASH(ptr_arg, size); }
248 
sys_var_pluginvar(sys_var_chain * chain,const char * name_arg,struct st_mysql_sys_var * plugin_var_arg)249   sys_var_pluginvar(sys_var_chain *chain, const char *name_arg,
250                     struct st_mysql_sys_var *plugin_var_arg)
251     :sys_var(chain, name_arg, plugin_var_arg->comment,
252              (plugin_var_arg->flags & PLUGIN_VAR_THDLOCAL ? SESSION : GLOBAL) |
253              (plugin_var_arg->flags & PLUGIN_VAR_READONLY ? READONLY : 0),
254              0, -1, NO_ARG, pluginvar_show_type(plugin_var_arg), 0, 0,
255              VARIABLE_NOT_IN_BINLOG, NULL, NULL, NULL, PARSE_NORMAL),
256     plugin_var(plugin_var_arg), orig_pluginvar_name(plugin_var_arg->name)
257   { plugin_var->name= name_arg; }
cast_pluginvar()258   sys_var_pluginvar *cast_pluginvar() { return this; }
259   bool check_update_type(Item_result type);
260   SHOW_TYPE show_type();
261   uchar* real_value_ptr(THD *thd, enum_var_type type);
262   TYPELIB* plugin_var_typelib(void);
263   uchar* do_value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
session_value_ptr(THD * thd,LEX_STRING * base)264   uchar* session_value_ptr(THD *thd, LEX_STRING *base)
265   { return do_value_ptr(thd, OPT_SESSION, base); }
global_value_ptr(THD * thd,LEX_STRING * base)266   uchar* global_value_ptr(THD *thd, LEX_STRING *base)
267   { return do_value_ptr(thd, OPT_GLOBAL, base); }
268   bool do_check(THD *thd, set_var *var);
session_save_default(THD * thd,set_var * var)269   virtual void session_save_default(THD *thd, set_var *var) {}
global_save_default(THD * thd,set_var * var)270   virtual void global_save_default(THD *thd, set_var *var) {}
271   bool session_update(THD *thd, set_var *var);
272   bool global_update(THD *thd, set_var *var);
273 };
274 
275 
276 /* prototypes */
277 static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv);
278 static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv,
279                              const char *list);
280 static my_bool check_if_option_is_deprecated(int optid,
281                                              const struct my_option *opt,
282                                              char *argument);
283 static int test_plugin_options(MEM_ROOT *, struct st_plugin_int *,
284                                int *, char **);
285 static bool register_builtin(struct st_mysql_plugin *, struct st_plugin_int *,
286                              struct st_plugin_int **);
287 static void unlock_variables(THD *thd, struct system_variables *vars);
288 static void cleanup_variables(THD *thd, struct system_variables *vars);
289 static void plugin_vars_free_values(sys_var *vars);
290 static bool plugin_var_memalloc_session_update(THD *thd,
291                                                struct st_mysql_sys_var *var,
292                                                char **dest, const char *value);
293 static bool plugin_var_memalloc_global_update(THD *thd,
294                                               struct st_mysql_sys_var *var,
295                                               char **dest, const char *value);
296 static void plugin_var_memalloc_free(struct system_variables *vars);
297 static void restore_pluginvar_names(sys_var *first);
298 static void plugin_opt_set_limits(struct my_option *,
299                                   const struct st_mysql_sys_var *);
300 #define my_intern_plugin_lock(A,B) intern_plugin_lock(A,B)
301 #define my_intern_plugin_lock_ci(A,B) intern_plugin_lock(A,B)
302 static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin);
303 static void intern_plugin_unlock(LEX *lex, plugin_ref plugin);
304 static void reap_plugins(void);
305 
report_error(int where_to,uint error,...)306 static void report_error(int where_to, uint error, ...)
307 {
308   va_list args;
309   if (where_to & REPORT_TO_USER)
310   {
311     va_start(args, error);
312     my_printv_error(error, ER(error), MYF(0), args);
313     va_end(args);
314   }
315   if (where_to & REPORT_TO_LOG)
316   {
317     va_start(args, error);
318     error_log_print(ERROR_LEVEL, ER_DEFAULT(error), args);
319     va_end(args);
320   }
321 }
322 
323 /**
324    Check if the provided path is valid in the sense that it does cause
325    a relative reference outside the directory.
326 
327    @note Currently, this function only check if there are any
328    characters in FN_DIRSEP in the string, but it might change in the
329    future.
330 
331    @code
332    check_valid_path("../foo.so") -> true
333    check_valid_path("foo.so") -> false
334    @endcode
335  */
check_valid_path(const char * path,size_t len)336 bool check_valid_path(const char *path, size_t len)
337 {
338   size_t prefix= my_strcspn(files_charset_info, path, path + len, FN_DIRSEP,
339                             strlen(FN_DIRSEP));
340   return  prefix < len;
341 }
342 
343 
344 /****************************************************************************
345   Value type thunks, allows the C world to play in the C++ world
346 ****************************************************************************/
347 
item_value_type(struct st_mysql_value * value)348 static int item_value_type(struct st_mysql_value *value)
349 {
350   switch (((st_item_value_holder*)value)->item->result_type()) {
351   case INT_RESULT:
352     return MYSQL_VALUE_TYPE_INT;
353   case REAL_RESULT:
354     return MYSQL_VALUE_TYPE_REAL;
355   default:
356     return MYSQL_VALUE_TYPE_STRING;
357   }
358 }
359 
item_val_str(struct st_mysql_value * value,char * buffer,int * length)360 static const char *item_val_str(struct st_mysql_value *value,
361                                 char *buffer, int *length)
362 {
363   String str(buffer, *length, system_charset_info), *res;
364   if (!(res= ((st_item_value_holder*)value)->item->val_str(&str)))
365     return NULL;
366   *length= res->length();
367   if (res->c_ptr_quick() == buffer)
368     return buffer;
369 
370   /*
371     Lets be nice and create a temporary string since the
372     buffer was too small
373   */
374   return current_thd->strmake(res->c_ptr_quick(), res->length());
375 }
376 
377 
item_val_int(struct st_mysql_value * value,long long * buf)378 static int item_val_int(struct st_mysql_value *value, long long *buf)
379 {
380   Item *item= ((st_item_value_holder*)value)->item;
381   *buf= item->val_int();
382   if (item->is_null())
383     return 1;
384   return 0;
385 }
386 
item_is_unsigned(struct st_mysql_value * value)387 static int item_is_unsigned(struct st_mysql_value *value)
388 {
389   Item *item= ((st_item_value_holder*)value)->item;
390   return item->unsigned_flag;
391 }
392 
item_val_real(struct st_mysql_value * value,double * buf)393 static int item_val_real(struct st_mysql_value *value, double *buf)
394 {
395   Item *item= ((st_item_value_holder*)value)->item;
396   *buf= item->val_real();
397   if (item->is_null())
398     return 1;
399   return 0;
400 }
401 
402 
403 /****************************************************************************
404   Plugin support code
405 ****************************************************************************/
406 
407 #ifdef HAVE_DLOPEN
408 
plugin_dl_find(const LEX_STRING * dl)409 static struct st_plugin_dl *plugin_dl_find(const LEX_STRING *dl)
410 {
411   uint i;
412   struct st_plugin_dl *tmp;
413   DBUG_ENTER("plugin_dl_find");
414   for (i= 0; i < plugin_dl_array.elements; i++)
415   {
416     tmp= *dynamic_element(&plugin_dl_array, i, struct st_plugin_dl **);
417     if (tmp->ref_count &&
418         ! my_strnncoll(files_charset_info,
419                        (const uchar *)dl->str, dl->length,
420                        (const uchar *)tmp->dl.str, tmp->dl.length))
421       DBUG_RETURN(tmp);
422   }
423   DBUG_RETURN(0);
424 }
425 
426 
plugin_dl_insert_or_reuse(struct st_plugin_dl * plugin_dl)427 static st_plugin_dl *plugin_dl_insert_or_reuse(struct st_plugin_dl *plugin_dl)
428 {
429   uint i;
430   struct st_plugin_dl *tmp;
431   DBUG_ENTER("plugin_dl_insert_or_reuse");
432   for (i= 0; i < plugin_dl_array.elements; i++)
433   {
434     tmp= *dynamic_element(&plugin_dl_array, i, struct st_plugin_dl **);
435     if (! tmp->ref_count)
436     {
437       memcpy(tmp, plugin_dl, sizeof(struct st_plugin_dl));
438       DBUG_RETURN(tmp);
439     }
440   }
441   if (insert_dynamic(&plugin_dl_array, &plugin_dl))
442     DBUG_RETURN(0);
443   tmp= *dynamic_element(&plugin_dl_array, plugin_dl_array.elements - 1,
444                         struct st_plugin_dl **)=
445       (struct st_plugin_dl *) memdup_root(&plugin_mem_root, (uchar*)plugin_dl,
446                                            sizeof(struct st_plugin_dl));
447   DBUG_RETURN(tmp);
448 }
449 #endif /* HAVE_DLOPEN */
450 
451 
free_plugin_mem(struct st_plugin_dl * p)452 static inline void free_plugin_mem(struct st_plugin_dl *p)
453 {
454 #ifdef HAVE_DLOPEN
455   if (p->handle)
456     dlclose(p->handle);
457 #endif
458   my_free(p->dl.str);
459   if (p->version != MYSQL_PLUGIN_INTERFACE_VERSION)
460     my_free(p->plugins);
461 }
462 
463 
plugin_dl_add(const LEX_STRING * dl,int report,bool allow_dl_path)464 static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl,
465                                    int report,
466                                    bool allow_dl_path)
467 {
468 #ifdef HAVE_DLOPEN
469   char dlpath[FN_REFLEN];
470   uint dummy_errors, dlpathlen, i;
471   struct st_plugin_dl *tmp, plugin_dl;
472   size_t dl_fname_pos= 0;
473   void *sym;
474   DBUG_ENTER("plugin_dl_add");
475   DBUG_PRINT("enter", ("dl->str: '%s', dl->length: %d",
476                        dl->str, (int) dl->length));
477 
478   if (allow_dl_path)
479     dl_fname_pos = dirname_length(dl->str);
480 
481   if (!dl_fname_pos) {
482     size_t plugin_dir_len= strlen(opt_plugin_dir);
483     /*
484       Ensure that the dll doesn't have a path.
485       This is done to ensure that only approved libraries from the
486       plugin directory are used (to make this even remotely secure).
487     */
488     if (check_valid_path(dl->str, dl->length) ||
489         check_string_char_length((LEX_STRING *) dl, "", NAME_CHAR_LEN,
490                                  system_charset_info, 1) ||
491         plugin_dir_len + dl->length + 1 >= FN_REFLEN)
492     {
493       report_error(report, ER_UDF_NO_PATHS);
494       DBUG_RETURN(0);
495     }
496     /* Compile dll path */
497     dlpathlen=
498       strxnmov(dlpath,
499                sizeof(dlpath) - 1,
500                opt_plugin_dir,
501                "/",
502                dl->str,
503                NullS) -
504       dlpath;
505   }
506   else
507   {
508     if (dl->length + 1 >= sizeof(dlpath))
509     {
510       report_error(report, ER_UDF_NO_PATHS);
511       DBUG_RETURN(0);
512     }
513     strncpy(dlpath, dl->str, sizeof(dlpath) - 1);
514     dlpathlen= dl->length;
515   }
516 
517   (void) unpack_filename(dlpath, dlpath);
518 
519   /* If this dll is already loaded just increase ref_count. */
520   if ((tmp= plugin_dl_find(dl)))
521   {
522     tmp->ref_count++;
523     DBUG_RETURN(tmp);
524   }
525   memset(&plugin_dl, 0, sizeof(plugin_dl));
526   plugin_dl.ref_count= 1;
527   /* Open new dll handle */
528   if (!(plugin_dl.handle= dlopen(dlpath, RTLD_NOW)))
529   {
530     const char *errmsg;
531     int error_number= dlopen_errno;
532     DLERROR_GENERATE(errmsg, error_number);
533 
534     if (!strncmp(dlpath, errmsg, dlpathlen))
535     { // if errmsg starts from dlpath, trim this prefix.
536       errmsg+=dlpathlen;
537       if (*errmsg == ':') errmsg++;
538       if (*errmsg == ' ') errmsg++;
539     }
540     report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, error_number, errmsg);
541     DBUG_RETURN(0);
542   }
543   /* Determine interface version */
544   if (!(sym= dlsym(plugin_dl.handle, plugin_interface_version_sym)))
545   {
546     free_plugin_mem(&plugin_dl);
547     report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_interface_version_sym);
548     DBUG_RETURN(0);
549   }
550   plugin_dl.version= *(int *)sym;
551   /* Versioning */
552   if (plugin_dl.version < min_plugin_interface_version ||
553       (plugin_dl.version >> 8) > (MYSQL_PLUGIN_INTERFACE_VERSION >> 8))
554   {
555     free_plugin_mem(&plugin_dl);
556     report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0,
557                  "plugin interface version mismatch");
558     DBUG_RETURN(0);
559   }
560 
561   /* link the services in */
562   for (i= 0; i < array_elements(list_of_services); i++)
563   {
564     if ((sym= dlsym(plugin_dl.handle, list_of_services[i].name)))
565     {
566       uint ver= (uint)(intptr)*(void**)sym;
567       if ((*(void**)sym) != list_of_services[i].service && /* already replaced */
568           (ver > list_of_services[i].version ||
569            (ver >> 8) < (list_of_services[i].version >> 8)))
570       {
571         char buf[MYSQL_ERRMSG_SIZE];
572         my_snprintf(buf, sizeof(buf),
573                     "service '%s' interface version mismatch",
574                     list_of_services[i].name);
575         report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0, buf);
576         DBUG_RETURN(0);
577       }
578       *(void**)sym= list_of_services[i].service;
579     }
580   }
581 
582   /* Find plugin declarations */
583   if (!(sym= dlsym(plugin_dl.handle, plugin_declarations_sym)))
584   {
585     free_plugin_mem(&plugin_dl);
586     report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_declarations_sym);
587     DBUG_RETURN(0);
588   }
589 
590   if (plugin_dl.version != MYSQL_PLUGIN_INTERFACE_VERSION)
591   {
592     uint sizeof_st_plugin;
593     struct st_mysql_plugin *old, *cur;
594     char *ptr= (char *)sym;
595 
596     if ((sym= dlsym(plugin_dl.handle, sizeof_st_plugin_sym)))
597       sizeof_st_plugin= *(int *)sym;
598     else
599     {
600 #ifdef ERROR_ON_NO_SIZEOF_PLUGIN_SYMBOL
601       report_error(report, ER_CANT_FIND_DL_ENTRY, sizeof_st_plugin_sym);
602       DBUG_RETURN(0);
603 #else
604       /*
605         When the following assert starts failing, we'll have to switch
606         to the upper branch of the #ifdef
607       */
608       DBUG_ASSERT(min_plugin_interface_version == 0);
609       sizeof_st_plugin= (int)offsetof(struct st_mysql_plugin, version);
610 #endif
611     }
612 
613     /*
614       What's the purpose of this loop? If the goal is to catch a
615       missing 0 record at the end of a list, it will fail miserably
616       since the compiler is likely to optimize this away. /Matz
617      */
618     for (i= 0;
619          ((struct st_mysql_plugin *)(ptr+i*sizeof_st_plugin))->info;
620          i++)
621       /* no op */;
622 
623     cur= (struct st_mysql_plugin*)
624       my_malloc((i+1)*sizeof(struct st_mysql_plugin), MYF(MY_ZEROFILL|MY_WME));
625     if (!cur)
626     {
627       free_plugin_mem(&plugin_dl);
628       report_error(report, ER_OUTOFMEMORY,
629                    static_cast<int>(plugin_dl.dl.length));
630       DBUG_RETURN(0);
631     }
632     /*
633       All st_plugin fields not initialized in the plugin explicitly, are
634       set to 0. It matches C standard behaviour for struct initializers that
635       have less values than the struct definition.
636     */
637     for (i=0;
638          (old=(struct st_mysql_plugin *)(ptr+i*sizeof_st_plugin))->info;
639          i++)
640       memcpy(cur+i, old, min<size_t>(sizeof(cur[i]), sizeof_st_plugin));
641 
642     sym= cur;
643   }
644   plugin_dl.plugins= (struct st_mysql_plugin *)sym;
645 
646   /*
647     If report is REPORT_TO_USER, we were called from
648     mysql_install_plugin. Otherwise, we are called directly or
649     indirectly from plugin_init.
650    */
651   if (report == REPORT_TO_USER)
652   {
653     st_mysql_plugin *plugin= plugin_dl.plugins;
654     for ( ; plugin->info ; ++plugin)
655       if (plugin->flags & PLUGIN_OPT_NO_INSTALL)
656       {
657         report_error(report, ER_PLUGIN_NO_INSTALL, plugin->name);
658         free_plugin_mem(&plugin_dl);
659         DBUG_RETURN(0);
660    }
661   }
662 
663   /* Duplicate and convert dll name */
664   plugin_dl.dl.length= dl->length * files_charset_info->mbmaxlen + 1;
665   if (! (plugin_dl.dl.str= (char*) my_malloc(plugin_dl.dl.length, MYF(0))))
666   {
667     free_plugin_mem(&plugin_dl);
668     report_error(report, ER_OUTOFMEMORY,
669                  static_cast<int>(plugin_dl.dl.length));
670     DBUG_RETURN(0);
671   }
672   plugin_dl.dl.length= copy_and_convert(plugin_dl.dl.str, plugin_dl.dl.length,
673     files_charset_info, dl->str, dl->length, system_charset_info,
674     &dummy_errors);
675   plugin_dl.dl.str[plugin_dl.dl.length]= 0;
676   /* Add this dll to array */
677   if (! (tmp= plugin_dl_insert_or_reuse(&plugin_dl)))
678   {
679     free_plugin_mem(&plugin_dl);
680     report_error(report, ER_OUTOFMEMORY,
681                  static_cast<int>(sizeof(struct st_plugin_dl)));
682     DBUG_RETURN(0);
683   }
684   DBUG_RETURN(tmp);
685 #else
686   DBUG_ENTER("plugin_dl_add");
687   report_error(report, ER_FEATURE_DISABLED, "plugin", "HAVE_DLOPEN");
688   DBUG_RETURN(0);
689 #endif
690 }
691 
692 
plugin_dl_del(const LEX_STRING * dl)693 static void plugin_dl_del(const LEX_STRING *dl)
694 {
695 #ifdef HAVE_DLOPEN
696   uint i;
697   DBUG_ENTER("plugin_dl_del");
698 
699   mysql_mutex_assert_owner(&LOCK_plugin);
700 
701   for (i= 0; i < plugin_dl_array.elements; i++)
702   {
703     struct st_plugin_dl *tmp= *dynamic_element(&plugin_dl_array, i,
704                                                struct st_plugin_dl **);
705     if (tmp->ref_count &&
706         ! my_strnncoll(files_charset_info,
707                        (const uchar *)dl->str, dl->length,
708                        (const uchar *)tmp->dl.str, tmp->dl.length))
709     {
710       /* Do not remove this element, unless no other plugin uses this dll. */
711       if (! --tmp->ref_count)
712       {
713         free_plugin_mem(tmp);
714         memset(tmp, 0, sizeof(struct st_plugin_dl));
715       }
716       break;
717     }
718   }
719   DBUG_VOID_RETURN;
720 #endif
721 }
722 
723 
plugin_find_internal(const LEX_STRING * name,int type)724 static struct st_plugin_int *plugin_find_internal(const LEX_STRING *name, int type)
725 {
726   uint i;
727   DBUG_ENTER("plugin_find_internal");
728   if (!initialized || !name->str)
729     DBUG_RETURN(0);
730 
731   mysql_mutex_assert_owner(&LOCK_plugin);
732 
733   if (type == MYSQL_ANY_PLUGIN)
734   {
735     for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
736     {
737       struct st_plugin_int *plugin= (st_plugin_int *)
738         my_hash_search(&plugin_hash[i], (const uchar *)name->str, name->length);
739       if (plugin)
740         DBUG_RETURN(plugin);
741     }
742   }
743   else
744     DBUG_RETURN((st_plugin_int *)
745         my_hash_search(&plugin_hash[type], (const uchar *)name->str,
746                        name->length));
747   DBUG_RETURN(0);
748 }
749 
750 
plugin_status(const LEX_STRING * name,int type)751 static SHOW_COMP_OPTION plugin_status(const LEX_STRING *name, int type)
752 {
753   SHOW_COMP_OPTION rc= SHOW_OPTION_NO;
754   struct st_plugin_int *plugin;
755   DBUG_ENTER("plugin_is_ready");
756   mysql_mutex_lock(&LOCK_plugin);
757   if ((plugin= plugin_find_internal(name, type)))
758   {
759     rc= SHOW_OPTION_DISABLED;
760     if (plugin->state == PLUGIN_IS_READY)
761       rc= SHOW_OPTION_YES;
762   }
763   mysql_mutex_unlock(&LOCK_plugin);
764   DBUG_RETURN(rc);
765 }
766 
767 
plugin_is_ready(const LEX_STRING * name,int type)768 bool plugin_is_ready(const LEX_STRING *name, int type)
769 {
770   bool rc= FALSE;
771   if (plugin_status(name, type) == SHOW_OPTION_YES)
772     rc= TRUE;
773   return rc;
774 }
775 
776 
plugin_status(const char * name,size_t len,int type)777 SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type)
778 {
779   LEX_STRING plugin_name= { (char *) name, len };
780   return plugin_status(&plugin_name, type);
781 }
782 
783 
intern_plugin_lock(LEX * lex,plugin_ref rc)784 static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc)
785 {
786   st_plugin_int *pi= plugin_ref_to_int(rc);
787   DBUG_ENTER("intern_plugin_lock");
788 
789   mysql_mutex_assert_owner(&LOCK_plugin);
790 
791   if (pi->state & (PLUGIN_IS_READY | PLUGIN_IS_UNINITIALIZED))
792   {
793     plugin_ref plugin;
794 #ifdef DBUG_OFF
795     /* built-in plugins don't need ref counting */
796     if (!pi->plugin_dl)
797       DBUG_RETURN(pi);
798 
799     plugin= pi;
800 #else
801     /*
802       For debugging, we do an additional malloc which allows the
803       memory manager and/or valgrind to track locked references and
804       double unlocks to aid resolving reference counting problems.
805     */
806     if (!(plugin= (plugin_ref) my_malloc(sizeof(pi), MYF(MY_WME))))
807       DBUG_RETURN(NULL);
808 
809     *plugin= pi;
810 #endif
811     pi->ref_count++;
812     DBUG_PRINT("info",("thd: %p, plugin: \"%s\", ref_count: %d",
813                        current_thd, pi->name.str, pi->ref_count));
814     if (lex)
815       insert_dynamic(&lex->plugins, &plugin);
816     DBUG_RETURN(plugin);
817   }
818   DBUG_RETURN(NULL);
819 }
820 
821 
plugin_lock(THD * thd,plugin_ref * ptr)822 plugin_ref plugin_lock(THD *thd, plugin_ref *ptr)
823 {
824   LEX *lex= thd ? thd->lex : 0;
825   plugin_ref rc;
826   DBUG_ENTER("plugin_lock");
827   mysql_mutex_lock(&LOCK_plugin);
828   rc= my_intern_plugin_lock_ci(lex, *ptr);
829   mysql_mutex_unlock(&LOCK_plugin);
830   DBUG_RETURN(rc);
831 }
832 
833 
plugin_lock_by_name(THD * thd,const LEX_STRING * name,int type)834 plugin_ref plugin_lock_by_name(THD *thd, const LEX_STRING *name, int type)
835 {
836   LEX *lex= thd ? thd->lex : 0;
837   plugin_ref rc= NULL;
838   st_plugin_int *plugin;
839   DBUG_ENTER("plugin_lock_by_name");
840   mysql_mutex_lock(&LOCK_plugin);
841   if ((plugin= plugin_find_internal(name, type)))
842     rc= my_intern_plugin_lock_ci(lex, plugin_int_to_ref(plugin));
843   mysql_mutex_unlock(&LOCK_plugin);
844   DBUG_RETURN(rc);
845 }
846 
847 
plugin_insert_or_reuse(struct st_plugin_int * plugin)848 static st_plugin_int *plugin_insert_or_reuse(struct st_plugin_int *plugin)
849 {
850   uint i;
851   struct st_plugin_int *tmp;
852   DBUG_ENTER("plugin_insert_or_reuse");
853   for (i= 0; i < plugin_array.elements; i++)
854   {
855     tmp= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
856     if (tmp->state == PLUGIN_IS_FREED)
857     {
858       memcpy(tmp, plugin, sizeof(struct st_plugin_int));
859       DBUG_RETURN(tmp);
860     }
861   }
862   if (insert_dynamic(&plugin_array, &plugin))
863     DBUG_RETURN(0);
864   tmp= *dynamic_element(&plugin_array, plugin_array.elements - 1,
865                         struct st_plugin_int **)=
866        (struct st_plugin_int *) memdup_root(&plugin_mem_root, (uchar*)plugin,
867                                             sizeof(struct st_plugin_int));
868   DBUG_RETURN(tmp);
869 }
870 
871 
872 /*
873   NOTE
874     Requires that a write-lock is held on LOCK_system_variables_hash
875 */
plugin_add(MEM_ROOT * tmp_root,const LEX_STRING * name,const LEX_STRING * dl,int * argc,char ** argv,int report,bool allow_path)876 static bool plugin_add(MEM_ROOT *tmp_root,
877                        const LEX_STRING *name, const LEX_STRING *dl,
878                        int *argc, char **argv, int report, bool allow_path)
879 {
880   struct st_plugin_int tmp;
881   struct st_mysql_plugin *plugin;
882   DBUG_ENTER("plugin_add");
883   if (plugin_find_internal(name, MYSQL_ANY_PLUGIN))
884   {
885     report_error(report, ER_UDF_EXISTS, name->str);
886     DBUG_RETURN(TRUE);
887   }
888   /* Clear the whole struct to catch future extensions. */
889   memset(&tmp, 0, sizeof(tmp));
890   if (! (tmp.plugin_dl= plugin_dl_add(dl, report, allow_path)))
891     DBUG_RETURN(TRUE);
892   /* Find plugin by name */
893   for (plugin= tmp.plugin_dl->plugins; plugin->info; plugin++)
894   {
895     uint name_len= strlen(plugin->name);
896     if (plugin->type >= 0 && plugin->type < MYSQL_MAX_PLUGIN_TYPE_NUM &&
897         ! my_strnncoll(system_charset_info,
898                        (const uchar *)name->str, name->length,
899                        (const uchar *)plugin->name,
900                        name_len))
901     {
902       struct st_plugin_int *tmp_plugin_ptr;
903       if (*(int*)plugin->info <
904           min_plugin_info_interface_version[plugin->type] ||
905           ((*(int*)plugin->info) >> 8) >
906           (cur_plugin_info_interface_version[plugin->type] >> 8))
907       {
908         char buf[256];
909         strxnmov(buf, sizeof(buf) - 1, "API version for ",
910                  plugin_type_names[plugin->type].str,
911                  " plugin is too different", NullS);
912         report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, 0, buf);
913         goto err;
914       }
915       tmp.plugin= plugin;
916       tmp.name.str= (char *)plugin->name;
917       tmp.name.length= name_len;
918       tmp.ref_count= 0;
919       tmp.state= PLUGIN_IS_UNINITIALIZED;
920       tmp.load_option= PLUGIN_ON;
921       if (test_plugin_options(tmp_root, &tmp, argc, argv))
922         tmp.state= PLUGIN_IS_DISABLED;
923 
924       if ((tmp_plugin_ptr= plugin_insert_or_reuse(&tmp)))
925       {
926         plugin_array_version++;
927         if (!my_hash_insert(&plugin_hash[plugin->type], (uchar*)tmp_plugin_ptr))
928         {
929           init_alloc_root(&tmp_plugin_ptr->mem_root, 4096, 4096);
930           DBUG_RETURN(FALSE);
931         }
932         tmp_plugin_ptr->state= PLUGIN_IS_FREED;
933       }
934       mysql_del_sys_var_chain(tmp.system_vars);
935       restore_pluginvar_names(tmp.system_vars);
936       goto err;
937 
938       /* plugin was disabled */
939       plugin_dl_del(dl);
940       DBUG_RETURN(FALSE);
941     }
942   }
943   report_error(report, ER_CANT_FIND_DL_ENTRY, name->str);
944 err:
945   plugin_dl_del(dl);
946   DBUG_RETURN(TRUE);
947 }
948 
949 
plugin_deinitialize(struct st_plugin_int * plugin,bool ref_check)950 static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check)
951 {
952   /*
953     we don't want to hold the LOCK_plugin mutex as it may cause
954     deinitialization to deadlock if plugins have worker threads
955     with plugin locks
956   */
957   mysql_mutex_assert_not_owner(&LOCK_plugin);
958 
959   if (plugin->plugin->status_vars)
960   {
961 #ifdef FIX_LATER
962     /**
963       @todo
964       unfortunately, status variables were introduced without a
965       pluginname_ namespace, that is pluginname_ was not added automatically
966       to status variable names. It should be fixed together with the next
967       incompatible API change.
968     */
969     SHOW_VAR array[2]= {
970       {plugin->plugin->name, (char*)plugin->plugin->status_vars, SHOW_ARRAY},
971       {0, 0, SHOW_UNDEF}
972     };
973     remove_status_vars(array);
974 #else
975     remove_status_vars(plugin->plugin->status_vars);
976 #endif /* FIX_LATER */
977   }
978 
979   if (plugin_type_deinitialize[plugin->plugin->type])
980   {
981     if ((*plugin_type_deinitialize[plugin->plugin->type])(plugin))
982     {
983       sql_print_error("Plugin '%s' of type %s failed deinitialization",
984                       plugin->name.str, plugin_type_names[plugin->plugin->type].str);
985     }
986   }
987   else if (plugin->plugin->deinit)
988   {
989     DBUG_PRINT("info", ("Deinitializing plugin: '%s'", plugin->name.str));
990     if (plugin->plugin->deinit(plugin))
991     {
992       DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.",
993                              plugin->name.str));
994     }
995   }
996   plugin->state= PLUGIN_IS_UNINITIALIZED;
997 
998   /*
999     We do the check here because NDB has a worker THD which doesn't
1000     exit until NDB is shut down.
1001   */
1002   if (ref_check && plugin->ref_count)
1003     sql_print_error("Plugin '%s' has ref_count=%d after deinitialization.",
1004                     plugin->name.str, plugin->ref_count);
1005 }
1006 
plugin_del(struct st_plugin_int * plugin)1007 static void plugin_del(struct st_plugin_int *plugin)
1008 {
1009   DBUG_ENTER("plugin_del(plugin)");
1010   mysql_mutex_assert_owner(&LOCK_plugin);
1011   mysql_mutex_assert_owner(&LOCK_plugin_delete);
1012   /* Free allocated strings before deleting the plugin. */
1013   mysql_rwlock_wrlock(&LOCK_system_variables_hash);
1014   mysql_del_sys_var_chain(plugin->system_vars);
1015   mysql_rwlock_unlock(&LOCK_system_variables_hash);
1016   restore_pluginvar_names(plugin->system_vars);
1017   plugin_vars_free_values(plugin->system_vars);
1018   my_hash_delete(&plugin_hash[plugin->plugin->type], (uchar*)plugin);
1019   if (plugin->plugin_dl)
1020     plugin_dl_del(&plugin->plugin_dl->dl);
1021   plugin->state= PLUGIN_IS_FREED;
1022   plugin_array_version++;
1023   free_root(&plugin->mem_root, MYF(0));
1024   DBUG_VOID_RETURN;
1025 }
1026 
reap_plugins(void)1027 static void reap_plugins(void)
1028 {
1029   uint count, idx;
1030   struct st_plugin_int *plugin, **reap, **list;
1031 
1032   mysql_mutex_assert_owner(&LOCK_plugin);
1033 
1034   if (!reap_needed)
1035     return;
1036 
1037   reap_needed= false;
1038   count= plugin_array.elements;
1039   reap= (struct st_plugin_int **)my_alloca(sizeof(plugin)*(count+1));
1040   *(reap++)= NULL;
1041 
1042   for (idx= 0; idx < count; idx++)
1043   {
1044     plugin= *dynamic_element(&plugin_array, idx, struct st_plugin_int **);
1045     if (plugin->state == PLUGIN_IS_DELETED && !plugin->ref_count)
1046     {
1047       /* change the status flag to prevent reaping by another thread */
1048       plugin->state= PLUGIN_IS_DYING;
1049       *(reap++)= plugin;
1050     }
1051   }
1052 
1053   mysql_mutex_unlock(&LOCK_plugin);
1054 
1055   list= reap;
1056   while ((plugin= *(--list)))
1057   {
1058     if (!opt_bootstrap)
1059       sql_print_information("Shutting down plugin '%s'", plugin->name.str);
1060     plugin_deinitialize(plugin, true);
1061   }
1062 
1063   mysql_mutex_lock(&LOCK_plugin_delete);
1064   mysql_mutex_lock(&LOCK_plugin);
1065 
1066   while ((plugin= *(--reap)))
1067     plugin_del(plugin);
1068 
1069   mysql_mutex_unlock(&LOCK_plugin_delete);
1070 
1071   my_afree(reap);
1072 }
1073 
intern_plugin_unlock(LEX * lex,plugin_ref plugin)1074 static void intern_plugin_unlock(LEX *lex, plugin_ref plugin)
1075 {
1076   int i;
1077   st_plugin_int *pi;
1078   DBUG_ENTER("intern_plugin_unlock");
1079 
1080   mysql_mutex_assert_owner(&LOCK_plugin);
1081 
1082   if (!plugin)
1083     DBUG_VOID_RETURN;
1084 
1085   pi= plugin_ref_to_int(plugin);
1086 
1087 #ifdef DBUG_OFF
1088   if (!pi->plugin_dl)
1089     DBUG_VOID_RETURN;
1090 #else
1091   my_free(plugin);
1092 #endif
1093 
1094   DBUG_PRINT("info",("unlocking plugin, name= %s, ref_count= %d",
1095                      pi->name.str, pi->ref_count));
1096   if (lex)
1097   {
1098     /*
1099       Remove one instance of this plugin from the use list.
1100       We are searching backwards so that plugins locked last
1101       could be unlocked faster - optimizing for LIFO semantics.
1102     */
1103     for (i= lex->plugins.elements - 1; i >= 0; i--)
1104       if (plugin == *dynamic_element(&lex->plugins, i, plugin_ref*))
1105       {
1106         delete_dynamic_element(&lex->plugins, i);
1107         break;
1108       }
1109     DBUG_ASSERT(i >= 0);
1110   }
1111 
1112   DBUG_ASSERT(pi->ref_count);
1113   pi->ref_count--;
1114 
1115   if (pi->state == PLUGIN_IS_DELETED && !pi->ref_count)
1116     reap_needed= true;
1117 
1118   DBUG_VOID_RETURN;
1119 }
1120 
1121 
plugin_unlock(THD * thd,plugin_ref plugin)1122 void plugin_unlock(THD *thd, plugin_ref plugin)
1123 {
1124   LEX *lex= thd ? thd->lex : 0;
1125   DBUG_ENTER("plugin_unlock");
1126   if (!plugin)
1127     DBUG_VOID_RETURN;
1128 #ifdef DBUG_OFF
1129   /* built-in plugins don't need ref counting */
1130   if (!plugin_dlib(plugin))
1131     DBUG_VOID_RETURN;
1132 #endif
1133   mysql_mutex_lock(&LOCK_plugin);
1134   intern_plugin_unlock(lex, plugin);
1135   reap_plugins();
1136   mysql_mutex_unlock(&LOCK_plugin);
1137   DBUG_VOID_RETURN;
1138 }
1139 
1140 
plugin_unlock_list(THD * thd,plugin_ref * list,uint count)1141 void plugin_unlock_list(THD *thd, plugin_ref *list, uint count)
1142 {
1143   LEX *lex= thd ? thd->lex : 0;
1144   DBUG_ENTER("plugin_unlock_list");
1145   DBUG_ASSERT(list);
1146 
1147   /*
1148     In unit tests, LOCK_plugin may be uninitialized, so do not lock it.
1149     Besides: there's no point in locking it, if there are no plugins to unlock.
1150    */
1151   if (count == 0)
1152     DBUG_VOID_RETURN;
1153 
1154   mysql_mutex_lock(&LOCK_plugin);
1155   while (count--)
1156     intern_plugin_unlock(lex, *list++);
1157   reap_plugins();
1158   mysql_mutex_unlock(&LOCK_plugin);
1159   DBUG_VOID_RETURN;
1160 }
1161 
plugin_initialize(struct st_plugin_int * plugin)1162 static int plugin_initialize(struct st_plugin_int *plugin)
1163 {
1164   int ret= 1;
1165   DBUG_ENTER("plugin_initialize");
1166 
1167   mysql_mutex_assert_owner(&LOCK_plugin);
1168   uint state= plugin->state;
1169   DBUG_ASSERT(state == PLUGIN_IS_UNINITIALIZED);
1170 
1171   mysql_mutex_unlock(&LOCK_plugin);
1172   if (plugin_type_initialize[plugin->plugin->type])
1173   {
1174     if ((*plugin_type_initialize[plugin->plugin->type])(plugin))
1175     {
1176       sql_print_error("Plugin '%s' registration as a %s failed.",
1177                       plugin->name.str, plugin_type_names[plugin->plugin->type].str);
1178       goto err;
1179     }
1180 
1181     /* FIXME: Need better solution to transfer the callback function
1182     array to memcached */
1183     if (strcmp(plugin->name.str, "InnoDB") == 0) {
1184       innodb_callback_data = ((handlerton*)plugin->data)->data;
1185     }
1186   }
1187   else if (plugin->plugin->init)
1188   {
1189     if (strcmp(plugin->name.str, "daemon_memcached") == 0) {
1190        plugin->data = (void*)innodb_callback_data;
1191     }
1192 
1193     if (plugin->plugin->init(plugin))
1194     {
1195       sql_print_error("Plugin '%s' init function returned error.",
1196                       plugin->name.str);
1197       goto err;
1198     }
1199   }
1200   state= PLUGIN_IS_READY; // plugin->init() succeeded
1201 
1202   if (plugin->plugin->status_vars)
1203   {
1204 #ifdef FIX_LATER
1205     /*
1206       We have a problem right now where we can not prepend without
1207       breaking backwards compatibility. We will fix this shortly so
1208       that engines have "use names" and we wil use those for
1209       CREATE TABLE, and use the plugin name then for adding automatic
1210       variable names.
1211     */
1212     SHOW_VAR array[2]= {
1213       {plugin->plugin->name, (char*)plugin->plugin->status_vars, SHOW_ARRAY},
1214       {0, 0, SHOW_UNDEF}
1215     };
1216     if (add_status_vars(array)) // add_status_vars makes a copy
1217       goto err;
1218 #else
1219     if (add_status_vars(plugin->plugin->status_vars))
1220       goto err;
1221 #endif /* FIX_LATER */
1222   }
1223 
1224   /*
1225     set the plugin attribute of plugin's sys vars so they are pointing
1226     to the active plugin
1227   */
1228   if (plugin->system_vars)
1229   {
1230     sys_var_pluginvar *var= plugin->system_vars->cast_pluginvar();
1231     for (;;)
1232     {
1233       var->plugin= plugin;
1234       if (!var->next)
1235         break;
1236       var= var->next->cast_pluginvar();
1237     }
1238   }
1239 
1240   ret= 0;
1241 
1242 err:
1243   mysql_mutex_lock(&LOCK_plugin);
1244   plugin->state= state;
1245 
1246   DBUG_RETURN(ret);
1247 }
1248 
1249 
1250 extern "C" uchar *get_plugin_hash_key(const uchar *, size_t *, my_bool);
1251 extern "C" uchar *get_bookmark_hash_key(const uchar *, size_t *, my_bool);
1252 
1253 
get_plugin_hash_key(const uchar * buff,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))1254 uchar *get_plugin_hash_key(const uchar *buff, size_t *length,
1255                            my_bool not_used MY_ATTRIBUTE((unused)))
1256 {
1257   struct st_plugin_int *plugin= (st_plugin_int *)buff;
1258   *length= (uint)plugin->name.length;
1259   return((uchar *)plugin->name.str);
1260 }
1261 
1262 
get_bookmark_hash_key(const uchar * buff,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))1263 uchar *get_bookmark_hash_key(const uchar *buff, size_t *length,
1264                              my_bool not_used MY_ATTRIBUTE((unused)))
1265 {
1266   struct st_bookmark *var= (st_bookmark *)buff;
1267   *length= var->name_len + 1;
1268   return (uchar*) var->key;
1269 }
1270 
convert_dash_to_underscore(char * str,int len)1271 static inline void convert_dash_to_underscore(char *str, int len)
1272 {
1273   for (char *p= str; p <= str+len; p++)
1274     if (*p == '-')
1275       *p= '_';
1276 }
1277 
convert_underscore_to_dash(char * str,int len)1278 static inline void convert_underscore_to_dash(char *str, int len)
1279 {
1280   for (char *p= str; p <= str+len; p++)
1281     if (*p == '_')
1282       *p= '-';
1283 }
1284 
1285 #ifdef HAVE_PSI_INTERFACE
1286 static PSI_mutex_key key_LOCK_plugin;
1287 static PSI_mutex_key key_LOCK_plugin_delete;
1288 
1289 static PSI_mutex_info all_plugin_mutexes[]=
1290 {
1291   { &key_LOCK_plugin, "LOCK_plugin", PSI_FLAG_GLOBAL},
1292   { &key_LOCK_plugin_delete, "LOCK_plugin_delete", PSI_FLAG_GLOBAL}
1293 };
1294 
init_plugin_psi_keys(void)1295 static void init_plugin_psi_keys(void)
1296 {
1297   const char* category= "sql";
1298   int count;
1299 
1300   count= array_elements(all_plugin_mutexes);
1301   mysql_mutex_register(category, all_plugin_mutexes, count);
1302 }
1303 #endif /* HAVE_PSI_INTERFACE */
1304 
1305 /*
1306   The logic is that we first load and initialize all compiled in plugins.
1307   From there we load up the dynamic types (assuming we have not been told to
1308   skip this part).
1309 
1310   Finally we initialize everything, aka the dynamic that have yet to initialize.
1311 */
plugin_init(int * argc,char ** argv,int flags)1312 int plugin_init(int *argc, char **argv, int flags)
1313 {
1314   uint i;
1315   bool is_myisam;
1316   struct st_mysql_plugin **builtins;
1317   struct st_mysql_plugin *plugin;
1318   struct st_plugin_int tmp, *plugin_ptr, **reap;
1319   MEM_ROOT tmp_root;
1320   bool reaped_mandatory_plugin= false;
1321   bool mandatory= true;
1322   DBUG_ENTER("plugin_init");
1323 
1324   if (initialized)
1325     DBUG_RETURN(0);
1326 
1327 #ifdef HAVE_PSI_INTERFACE
1328   init_plugin_psi_keys();
1329 #endif
1330 
1331   init_alloc_root(&plugin_mem_root, 4096, 4096);
1332   init_alloc_root(&tmp_root, 4096, 4096);
1333 
1334   if (my_hash_init(&bookmark_hash, &my_charset_bin, 16, 0, 0,
1335                    get_bookmark_hash_key, NULL, HASH_UNIQUE))
1336       goto err;
1337 
1338   if (my_hash_init(&malloced_string_type_sysvars_bookmark_hash, &my_charset_bin,
1339                    16, 0, 0, get_bookmark_hash_key, NULL, HASH_UNIQUE))
1340       goto err;
1341 
1342   mysql_mutex_init(key_LOCK_plugin, &LOCK_plugin, MY_MUTEX_INIT_FAST);
1343   mysql_mutex_init(key_LOCK_plugin_delete, &LOCK_plugin_delete, MY_MUTEX_INIT_FAST);
1344 
1345   if (my_init_dynamic_array(&plugin_dl_array,
1346                             sizeof(struct st_plugin_dl *),16,16) ||
1347       my_init_dynamic_array(&plugin_array,
1348                             sizeof(struct st_plugin_int *),16,16))
1349     goto err;
1350 
1351   for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
1352   {
1353     if (my_hash_init(&plugin_hash[i], system_charset_info, 16, 0, 0,
1354                      get_plugin_hash_key, NULL, HASH_UNIQUE))
1355       goto err;
1356   }
1357 
1358   mysql_mutex_lock(&LOCK_plugin);
1359 
1360   initialized= 1;
1361 
1362   /*
1363     First we register builtin plugins
1364   */
1365   for (builtins= mysql_mandatory_plugins; *builtins || mandatory; builtins++)
1366   {
1367     if (!*builtins)
1368     {
1369       builtins= mysql_optional_plugins;
1370       mandatory= false;
1371       if (!*builtins)
1372         break;
1373     }
1374     for (plugin= *builtins; plugin->info; plugin++)
1375     {
1376       memset(&tmp, 0, sizeof(tmp));
1377       tmp.plugin= plugin;
1378       tmp.name.str= (char *)plugin->name;
1379       tmp.name.length= strlen(plugin->name);
1380       tmp.state= 0;
1381       tmp.load_option= mandatory ? PLUGIN_FORCE : PLUGIN_ON;
1382 
1383       /*
1384         If the performance schema is compiled in,
1385         treat the storage engine plugin as 'mandatory',
1386         to suppress any plugin-level options such as '--performance-schema'.
1387         This is specific to the performance schema, and is done on purpose:
1388         the server-level option '--performance-schema' controls the overall
1389         performance schema initialization, which consists of much more that
1390         the underlying storage engine initialization.
1391         See mysqld.cc, set_vars.cc.
1392         Suppressing ways to interfere directly with the storage engine alone
1393         prevents awkward situations where:
1394         - the user wants the performance schema functionality, by using
1395           '--enable-performance-schema' (the server option),
1396         - yet disable explicitly a component needed for the functionality
1397           to work, by using '--skip-performance-schema' (the plugin)
1398       */
1399       if (!my_strcasecmp(&my_charset_latin1, plugin->name, "PERFORMANCE_SCHEMA"))
1400         tmp.load_option= PLUGIN_FORCE;
1401 
1402       free_root(&tmp_root, MYF(MY_MARK_BLOCKS_FREE));
1403       if (test_plugin_options(&tmp_root, &tmp, argc, argv))
1404         tmp.state= PLUGIN_IS_DISABLED;
1405       else
1406         tmp.state= PLUGIN_IS_UNINITIALIZED;
1407       if (register_builtin(plugin, &tmp, &plugin_ptr))
1408         goto err_unlock;
1409 
1410       /* only initialize MyISAM and CSV at this stage */
1411       if (!(is_myisam=
1412             !my_strcasecmp(&my_charset_latin1, plugin->name, "MyISAM")) &&
1413           my_strcasecmp(&my_charset_latin1, plugin->name, "CSV"))
1414         continue;
1415 
1416       if (plugin_ptr->state != PLUGIN_IS_UNINITIALIZED ||
1417           plugin_initialize(plugin_ptr))
1418         goto err_unlock;
1419 
1420       /*
1421         initialize the global default storage engine so that it may
1422         not be null in any child thread.
1423       */
1424       if (is_myisam)
1425       {
1426         DBUG_ASSERT(!global_system_variables.table_plugin);
1427         DBUG_ASSERT(!global_system_variables.temp_table_plugin);
1428         global_system_variables.table_plugin=
1429           my_intern_plugin_lock(NULL, plugin_int_to_ref(plugin_ptr));
1430         global_system_variables.temp_table_plugin=
1431           my_intern_plugin_lock(NULL, plugin_int_to_ref(plugin_ptr));
1432         DBUG_ASSERT(plugin_ptr->ref_count == 2);
1433       }
1434     }
1435   }
1436 
1437   /* should now be set to MyISAM storage engine */
1438   DBUG_ASSERT(global_system_variables.table_plugin);
1439   DBUG_ASSERT(global_system_variables.temp_table_plugin);
1440 
1441   mysql_mutex_unlock(&LOCK_plugin);
1442 
1443   /* Register all dynamic plugins */
1444   if (!(flags & PLUGIN_INIT_SKIP_DYNAMIC_LOADING))
1445   {
1446     I_List_iterator<i_string> iter(opt_plugin_load_list);
1447     i_string *item;
1448     while (NULL != (item= iter++))
1449       plugin_load_list(&tmp_root, argc, argv, item->ptr);
1450 
1451     if (!(flags & PLUGIN_INIT_SKIP_PLUGIN_TABLE))
1452       plugin_load(&tmp_root, argc, argv);
1453   }
1454 
1455   if (flags & PLUGIN_INIT_SKIP_INITIALIZATION)
1456     goto end;
1457 
1458   /*
1459     Now we initialize all remaining plugins
1460   */
1461 
1462   mysql_mutex_lock(&LOCK_plugin);
1463   reap= (st_plugin_int **) my_alloca((plugin_array.elements+1) * sizeof(void*));
1464   *(reap++)= NULL;
1465 
1466   for (i= 0; i < plugin_array.elements; i++)
1467   {
1468     plugin_ptr= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
1469     if (plugin_ptr->state == PLUGIN_IS_UNINITIALIZED)
1470     {
1471       if (plugin_initialize(plugin_ptr))
1472       {
1473         plugin_ptr->state= PLUGIN_IS_DYING;
1474         *(reap++)= plugin_ptr;
1475       }
1476     }
1477   }
1478 
1479   /*
1480     Check if any plugins have to be reaped
1481   */
1482   while ((plugin_ptr= *(--reap)))
1483   {
1484     mysql_mutex_unlock(&LOCK_plugin);
1485     if (plugin_ptr->load_option == PLUGIN_FORCE ||
1486         plugin_ptr->load_option == PLUGIN_FORCE_PLUS_PERMANENT)
1487       reaped_mandatory_plugin= TRUE;
1488     plugin_deinitialize(plugin_ptr, true);
1489     mysql_mutex_lock(&LOCK_plugin_delete);
1490     mysql_mutex_lock(&LOCK_plugin);
1491     plugin_del(plugin_ptr);
1492     mysql_mutex_unlock(&LOCK_plugin_delete);
1493   }
1494 
1495   mysql_mutex_unlock(&LOCK_plugin);
1496   my_afree(reap);
1497   if (reaped_mandatory_plugin)
1498     goto err;
1499 
1500 end:
1501   free_root(&tmp_root, MYF(0));
1502 
1503   DBUG_RETURN(0);
1504 
1505 err_unlock:
1506   mysql_mutex_unlock(&LOCK_plugin);
1507 err:
1508   free_root(&tmp_root, MYF(0));
1509   DBUG_RETURN(1);
1510 }
1511 
1512 
register_builtin(struct st_mysql_plugin * plugin,struct st_plugin_int * tmp,struct st_plugin_int ** ptr)1513 static bool register_builtin(struct st_mysql_plugin *plugin,
1514                              struct st_plugin_int *tmp,
1515                              struct st_plugin_int **ptr)
1516 {
1517   DBUG_ENTER("register_builtin");
1518   tmp->ref_count= 0;
1519   tmp->plugin_dl= 0;
1520 
1521   if (insert_dynamic(&plugin_array, &tmp))
1522     DBUG_RETURN(1);
1523 
1524   *ptr= *dynamic_element(&plugin_array, plugin_array.elements - 1,
1525                          struct st_plugin_int **)=
1526         (struct st_plugin_int *) memdup_root(&plugin_mem_root, (uchar*)tmp,
1527                                              sizeof(struct st_plugin_int));
1528 
1529   if (my_hash_insert(&plugin_hash[plugin->type],(uchar*) *ptr))
1530     DBUG_RETURN(1);
1531 
1532   DBUG_RETURN(0);
1533 }
1534 
1535 
1536 /*
1537   called only by plugin_init()
1538 */
plugin_load(MEM_ROOT * tmp_root,int * argc,char ** argv)1539 static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv)
1540 {
1541   THD thd;
1542   TABLE_LIST tables;
1543   TABLE *table;
1544   READ_RECORD read_record_info;
1545   int error;
1546   THD *new_thd= &thd;
1547   bool result;
1548 #ifdef EMBEDDED_LIBRARY
1549   No_such_table_error_handler error_handler;
1550 #endif /* EMBEDDED_LIBRARY */
1551   DBUG_ENTER("plugin_load");
1552 
1553   new_thd->thread_stack= (char*) &tables;
1554   new_thd->store_globals();
1555   new_thd->db= my_strdup("mysql", MYF(0));
1556   new_thd->db_length= 5;
1557   memset(&thd.net, 0, sizeof(thd.net));
1558   tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_READ);
1559 
1560 #ifdef EMBEDDED_LIBRARY
1561   /*
1562     When building an embedded library, if the mysql.plugin table
1563     does not exist, we silently ignore the missing table
1564   */
1565   new_thd->push_internal_handler(&error_handler);
1566 #endif /* EMBEDDED_LIBRARY */
1567 
1568   result= open_and_lock_tables(new_thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT);
1569 
1570 #ifdef EMBEDDED_LIBRARY
1571   new_thd->pop_internal_handler();
1572   if (error_handler.safely_trapped_errors())
1573     goto end;
1574 #endif /* EMBEDDED_LIBRARY */
1575 
1576   if (result)
1577   {
1578     DBUG_PRINT("error",("Can't open plugin table"));
1579     sql_print_error("Can't open the mysql.plugin table. Please "
1580                     "run mysql_upgrade to create it.");
1581     goto end;
1582   }
1583   table= tables.table;
1584   if (init_read_record(&read_record_info, new_thd, table, NULL, 1, 1, FALSE))
1585     goto end;
1586   table->use_all_columns();
1587   /*
1588     there're no other threads running yet, so we don't need a mutex.
1589     but plugin_add() before is designed to work in multi-threaded
1590     environment, and it uses mysql_mutex_assert_owner(), so we lock
1591     the mutex here to satisfy the assert
1592   */
1593   mysql_mutex_lock(&LOCK_plugin);
1594   while (!(error= read_record_info.read_record(&read_record_info)))
1595   {
1596     DBUG_PRINT("info", ("init plugin record"));
1597     String str_name, str_dl;
1598     get_field(tmp_root, table->field[0], &str_name);
1599     get_field(tmp_root, table->field[1], &str_dl);
1600 
1601     LEX_STRING name= {(char *)str_name.ptr(), str_name.length()};
1602     LEX_STRING dl= {(char *)str_dl.ptr(), str_dl.length()};
1603 
1604     if (plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG, false))
1605       sql_print_warning("Couldn't load plugin named '%s' with soname '%s'.",
1606                         str_name.c_ptr(), str_dl.c_ptr());
1607     free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
1608   }
1609   mysql_mutex_unlock(&LOCK_plugin);
1610   if (error > 0)
1611     sql_print_error(ER(ER_GET_ERRNO), my_errno);
1612   end_read_record(&read_record_info);
1613   table->m_needs_reopen= TRUE;                  // Force close to free memory
1614   close_mysql_tables(new_thd);
1615 end:
1616   /* Remember that we don't have a THD */
1617   my_pthread_setspecific_ptr(THR_THD, 0);
1618   DBUG_VOID_RETURN;
1619 }
1620 
1621 
1622 /*
1623   called only by plugin_init()
1624 */
plugin_load_list(MEM_ROOT * tmp_root,int * argc,char ** argv,const char * list)1625 static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv,
1626                              const char *list)
1627 {
1628   char buffer[FN_REFLEN];
1629   LEX_STRING name= {buffer, 0}, dl= {NULL, 0}, *str= &name;
1630   struct st_plugin_dl *plugin_dl;
1631   struct st_mysql_plugin *plugin;
1632   char *p= buffer;
1633   DBUG_ENTER("plugin_load_list");
1634   while (list)
1635   {
1636     if (p == buffer + sizeof(buffer) - 1)
1637     {
1638       sql_print_error("plugin-load parameter too long");
1639       DBUG_RETURN(TRUE);
1640     }
1641 
1642     switch ((*(p++)= *(list++))) {
1643     case '\0':
1644       list= NULL; /* terminate the loop */
1645       // fallthrough - the later 2 comments are required by GCC
1646 #ifndef __WIN__
1647       // fallthrough
1648     case ':':     /* can't use this as delimiter as it may be drive letter */
1649 #endif
1650       // fallthrough
1651     case ';':
1652       str->str[str->length]= '\0';
1653       if (str == &name)  // load all plugins in named module
1654       {
1655         if (!name.length)
1656         {
1657           p--;    /* reset pointer */
1658           continue;
1659         }
1660 
1661         dl= name;
1662         mysql_mutex_lock(&LOCK_plugin);
1663         if ((plugin_dl= plugin_dl_add(&dl, REPORT_TO_LOG, true)))
1664         {
1665           for (plugin= plugin_dl->plugins; plugin->info; plugin++)
1666           {
1667             name.str= (char *) plugin->name;
1668             name.length= strlen(name.str);
1669 
1670             free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
1671             if (plugin_add(tmp_root,
1672                            &name,
1673                            &dl,
1674                            argc,
1675                            argv,
1676                            REPORT_TO_LOG,
1677                            true))
1678               goto error;
1679           }
1680           plugin_dl_del(&dl); // reduce ref count
1681         }
1682       }
1683       else
1684       {
1685         free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
1686         mysql_mutex_lock(&LOCK_plugin);
1687         if (plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG, true))
1688           goto error;
1689       }
1690       mysql_mutex_unlock(&LOCK_plugin);
1691       name.length= dl.length= 0;
1692       dl.str= NULL; name.str= p= buffer;
1693       str= &name;
1694       continue;
1695     case '=':
1696     case '#':
1697       if (str == &name)
1698       {
1699         name.str[name.length]= '\0';
1700         str= &dl;
1701         str->str= p;
1702         continue;
1703       }
1704       // fallthrough
1705     default:
1706       str->length++;
1707       continue;
1708     }
1709   }
1710   DBUG_RETURN(FALSE);
1711 error:
1712   mysql_mutex_unlock(&LOCK_plugin);
1713   sql_print_error("Couldn't load plugin named '%s' with soname '%s'.",
1714                   name.str, dl.str);
1715   DBUG_RETURN(TRUE);
1716 }
1717 
1718 /*
1719   Shutdown memcached plugin before binlog shuts down
1720 */
memcached_shutdown(void)1721 void memcached_shutdown(void)
1722 {
1723   struct st_plugin_int *plugin;
1724   if (initialized)
1725   {
1726 
1727     for (uint i= 0; i < plugin_array.elements; i++)
1728     {
1729       plugin= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
1730 
1731       if (plugin->state == PLUGIN_IS_READY
1732 	  && strcmp(plugin->name.str, "daemon_memcached") == 0)
1733       {
1734 	plugin_deinitialize(plugin, true);
1735 
1736         mysql_mutex_lock(&LOCK_plugin_delete);
1737         mysql_mutex_lock(&LOCK_plugin);
1738 	plugin->state= PLUGIN_IS_DYING;
1739 	plugin_del(plugin);
1740         mysql_mutex_unlock(&LOCK_plugin);
1741         mysql_mutex_unlock(&LOCK_plugin_delete);
1742       }
1743     }
1744 
1745   }
1746 }
1747 
plugin_shutdown(void)1748 void plugin_shutdown(void)
1749 {
1750   uint i, count= plugin_array.elements;
1751   struct st_plugin_int **plugins, *plugin;
1752   struct st_plugin_dl **dl;
1753   bool skip_binlog = true;
1754 
1755   DBUG_ENTER("plugin_shutdown");
1756 
1757   if (initialized)
1758   {
1759     mysql_mutex_lock(&LOCK_plugin);
1760 
1761     reap_needed= true;
1762 
1763     /*
1764       We want to shut down plugins in a reasonable order, this will
1765       become important when we have plugins which depend upon each other.
1766       Circular references cannot be reaped so they are forced afterwards.
1767       TODO: Have an additional step here to notify all active plugins that
1768       shutdown is requested to allow plugins to deinitialize in parallel.
1769     */
1770     while (reap_needed && (count= plugin_array.elements))
1771     {
1772       reap_plugins();
1773       for (i= 0; i < count; i++)
1774       {
1775         plugin= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
1776 
1777 	if (plugin->state == PLUGIN_IS_READY
1778 	    && strcmp(plugin->name.str, "binlog") == 0 && skip_binlog)
1779 	{
1780 		skip_binlog = false;
1781 
1782 	} else if (plugin->state == PLUGIN_IS_READY)
1783         {
1784           plugin->state= PLUGIN_IS_DELETED;
1785           reap_needed= true;
1786         }
1787       }
1788       if (!reap_needed)
1789       {
1790         /*
1791           release any plugin references held.
1792         */
1793         unlock_variables(NULL, &global_system_variables);
1794         unlock_variables(NULL, &max_system_variables);
1795       }
1796     }
1797 
1798     plugins= (struct st_plugin_int **) my_alloca(sizeof(void*) * (count+1));
1799 
1800     /*
1801       If we have any plugins which did not die cleanly, we force shutdown
1802     */
1803     for (i= 0; i < count; i++)
1804     {
1805       plugins[i]= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
1806       /* change the state to ensure no reaping races */
1807       if (plugins[i]->state == PLUGIN_IS_DELETED)
1808         plugins[i]->state= PLUGIN_IS_DYING;
1809     }
1810     mysql_mutex_unlock(&LOCK_plugin);
1811 
1812     /*
1813       We loop through all plugins and call deinit() if they have one.
1814     */
1815     for (i= 0; i < count; i++)
1816       if (!(plugins[i]->state & (PLUGIN_IS_UNINITIALIZED | PLUGIN_IS_FREED |
1817                                  PLUGIN_IS_DISABLED)))
1818       {
1819         sql_print_warning("Plugin '%s' will be forced to shutdown",
1820                           plugins[i]->name.str);
1821         /*
1822           We are forcing deinit on plugins so we don't want to do a ref_count
1823           check until we have processed all the plugins.
1824         */
1825         plugin_deinitialize(plugins[i], false);
1826       }
1827 
1828     /*
1829       It's perfectly safe not to lock LOCK_plugin, LOCK_plugin_delete, as
1830       there're no concurrent threads anymore. But some functions called from
1831       here use mysql_mutex_assert_owner(), so we lock the mutex to satisfy it
1832     */
1833     mysql_mutex_lock(&LOCK_plugin_delete);
1834     mysql_mutex_lock(&LOCK_plugin);
1835 
1836     /*
1837       We defer checking ref_counts until after all plugins are deinitialized
1838       as some may have worker threads holding on to plugin references.
1839     */
1840     for (i= 0; i < count; i++)
1841     {
1842       if (plugins[i]->ref_count)
1843         sql_print_error("Plugin '%s' has ref_count=%d after shutdown.",
1844                         plugins[i]->name.str, plugins[i]->ref_count);
1845       if (plugins[i]->state & PLUGIN_IS_UNINITIALIZED)
1846         plugin_del(plugins[i]);
1847     }
1848 
1849     /*
1850       Now we can deallocate all memory.
1851     */
1852 
1853     cleanup_variables(NULL, &global_system_variables);
1854     cleanup_variables(NULL, &max_system_variables);
1855     mysql_mutex_unlock(&LOCK_plugin);
1856     mysql_mutex_unlock(&LOCK_plugin_delete);
1857 
1858     initialized= 0;
1859     mysql_mutex_destroy(&LOCK_plugin);
1860     mysql_mutex_destroy(&LOCK_plugin_delete);
1861 
1862     my_afree(plugins);
1863   }
1864 
1865   /* Dispose of the memory */
1866 
1867   for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
1868     my_hash_free(&plugin_hash[i]);
1869   delete_dynamic(&plugin_array);
1870 
1871   count= plugin_dl_array.elements;
1872   dl= (struct st_plugin_dl **)my_alloca(sizeof(void*) * count);
1873   for (i= 0; i < count; i++)
1874     dl[i]= *dynamic_element(&plugin_dl_array, i, struct st_plugin_dl **);
1875   for (i= 0; i < plugin_dl_array.elements; i++)
1876     free_plugin_mem(dl[i]);
1877   my_afree(dl);
1878   delete_dynamic(&plugin_dl_array);
1879 
1880   my_hash_free(&bookmark_hash);
1881   my_hash_free(&malloced_string_type_sysvars_bookmark_hash);
1882   free_root(&plugin_mem_root, MYF(0));
1883 
1884   global_variables_dynamic_size= 0;
1885 
1886   DBUG_VOID_RETURN;
1887 }
1888 
1889 
mysql_install_plugin(THD * thd,const LEX_STRING * name,const LEX_STRING * dl)1890 bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl)
1891 {
1892   TABLE_LIST tables;
1893   TABLE *table;
1894   int error, argc=orig_argc;
1895   char **argv=orig_argv;
1896   struct st_plugin_int *tmp;
1897   DBUG_ENTER("mysql_install_plugin");
1898 
1899   if (opt_noacl)
1900   {
1901     my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1902     DBUG_RETURN(TRUE);
1903   }
1904 
1905   tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);
1906   if (check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE))
1907     DBUG_RETURN(TRUE);
1908 
1909   /* need to open before acquiring LOCK_plugin or it will deadlock */
1910   if (! (table = open_ltable(thd, &tables, TL_WRITE,
1911                              MYSQL_LOCK_IGNORE_TIMEOUT)))
1912     DBUG_RETURN(TRUE);
1913 
1914   /*
1915     Pre-acquire audit plugins for events that may potentially occur
1916     during [UN]INSTALL PLUGIN.
1917 
1918     When audit event is triggered, audit subsystem acquires interested
1919     plugins by walking through plugin list. Evidently plugin list
1920     iterator protects plugin list by acquiring LOCK_plugin, see
1921     plugin_foreach_with_mask().
1922 
1923     On the other hand [UN]INSTALL PLUGIN is acquiring LOCK_plugin
1924     rather for a long time.
1925 
1926     When audit event is triggered during [UN]INSTALL PLUGIN, plugin
1927     list iterator acquires the same lock (within the same thread)
1928     second time.
1929 
1930     This hack should be removed when LOCK_plugin is fixed so it
1931     protects only what it supposed to protect.
1932   */
1933   mysql_audit_acquire_plugins(thd, MYSQL_AUDIT_GENERAL_CLASS);
1934 
1935   mysql_mutex_lock(&LOCK_plugin);
1936   DEBUG_SYNC(thd, "acquired_LOCK_plugin");
1937   mysql_rwlock_wrlock(&LOCK_system_variables_hash);
1938 
1939   if (my_load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv, NULL))
1940   {
1941     mysql_rwlock_unlock(&LOCK_system_variables_hash);
1942     report_error(REPORT_TO_USER, ER_PLUGIN_IS_NOT_LOADED, name->str);
1943     goto err;
1944   }
1945   error= plugin_add(thd->mem_root,
1946                     name,
1947                     dl,
1948                     &argc,
1949                     argv,
1950                     REPORT_TO_USER,
1951                     false);
1952   if (argv)
1953     free_defaults(argv);
1954   mysql_rwlock_unlock(&LOCK_system_variables_hash);
1955 
1956   if (error || !(tmp= plugin_find_internal(name, MYSQL_ANY_PLUGIN)))
1957     goto err;
1958 
1959   if (tmp->state == PLUGIN_IS_DISABLED)
1960   {
1961     push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
1962                         ER_CANT_INITIALIZE_UDF, ER(ER_CANT_INITIALIZE_UDF),
1963                         name->str, "Plugin is disabled");
1964   }
1965   else
1966   {
1967     if (plugin_initialize(tmp))
1968     {
1969       mysql_mutex_unlock(&LOCK_plugin);
1970       my_error(ER_CANT_INITIALIZE_UDF, MYF(0), name->str,
1971                "Plugin initialization function failed.");
1972       goto deinit;
1973     }
1974   }
1975 
1976   /*
1977     We do not replicate the INSTALL PLUGIN statement. Disable binlogging
1978     of the insert into the plugin table, so that it is not replicated in
1979     row based mode.
1980   */
1981   mysql_mutex_unlock(&LOCK_plugin);
1982   tmp_disable_binlog(thd);
1983   table->use_all_columns();
1984   restore_record(table, s->default_values);
1985   table->field[0]->store(name->str, name->length, system_charset_info);
1986   table->field[1]->store(dl->str, dl->length, files_charset_info);
1987   error= table->file->ha_write_row(table->record[0]);
1988   reenable_binlog(thd);
1989   if (error)
1990   {
1991     table->file->print_error(error, MYF(0));
1992     goto deinit;
1993   }
1994   DBUG_RETURN(FALSE);
1995 deinit:
1996   mysql_mutex_lock(&LOCK_plugin);
1997   tmp->state= PLUGIN_IS_DELETED;
1998   reap_needed= true;
1999   reap_plugins();
2000 err:
2001   mysql_mutex_unlock(&LOCK_plugin);
2002   DBUG_RETURN(TRUE);
2003 }
2004 
2005 
mysql_uninstall_plugin(THD * thd,const LEX_STRING * name)2006 bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name)
2007 {
2008   TABLE *table;
2009   TABLE_LIST tables;
2010   struct st_plugin_int *plugin;
2011   DBUG_ENTER("mysql_uninstall_plugin");
2012 
2013   if (opt_noacl)
2014   {
2015     my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
2016     DBUG_RETURN(TRUE);
2017   }
2018 
2019   tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);
2020 
2021   if (check_table_access(thd, DELETE_ACL, &tables, FALSE, 1, FALSE))
2022     DBUG_RETURN(TRUE);
2023 
2024   /* need to open before acquiring LOCK_plugin or it will deadlock */
2025   if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
2026     DBUG_RETURN(TRUE);
2027 
2028   if (!table->key_info)
2029   {
2030     my_error(ER_MISSING_KEY, MYF(0), table->s->db.str,
2031              table->s->table_name.str);
2032     DBUG_RETURN(TRUE);
2033   }
2034 
2035   /*
2036     Pre-acquire audit plugins for events that may potentially occur
2037     during [UN]INSTALL PLUGIN.
2038 
2039     When audit event is triggered, audit subsystem acquires interested
2040     plugins by walking through plugin list. Evidently plugin list
2041     iterator protects plugin list by acquiring LOCK_plugin, see
2042     plugin_foreach_with_mask().
2043 
2044     On the other hand [UN]INSTALL PLUGIN is acquiring LOCK_plugin
2045     rather for a long time.
2046 
2047     When audit event is triggered during [UN]INSTALL PLUGIN, plugin
2048     list iterator acquires the same lock (within the same thread)
2049     second time.
2050 
2051     This hack should be removed when LOCK_plugin is fixed so it
2052     protects only what it supposed to protect.
2053   */
2054   mysql_audit_acquire_plugins(thd, MYSQL_AUDIT_GENERAL_CLASS);
2055 
2056   mysql_mutex_lock(&LOCK_plugin);
2057   if (!(plugin= plugin_find_internal(name, MYSQL_ANY_PLUGIN)) ||
2058       plugin->state & (PLUGIN_IS_UNINITIALIZED | PLUGIN_IS_DYING))
2059   {
2060     my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PLUGIN", name->str);
2061     goto err;
2062   }
2063   if (!plugin->plugin_dl)
2064   {
2065     push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
2066                  WARN_PLUGIN_DELETE_BUILTIN, ER(WARN_PLUGIN_DELETE_BUILTIN));
2067     my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PLUGIN", name->str);
2068     goto err;
2069   }
2070   if (plugin->load_option == PLUGIN_FORCE_PLUS_PERMANENT)
2071   {
2072     my_error(ER_PLUGIN_IS_PERMANENT, MYF(0), name->str);
2073     goto err;
2074   }
2075   /*
2076     Error message for ER_PLUGIN_IS_PERMANENT is not suitable for
2077     plugins marked as not dynamically uninstallable, so we have a
2078     separate one instead of changing the old one.
2079    */
2080   if (plugin->plugin->flags & PLUGIN_OPT_NO_UNINSTALL)
2081   {
2082     my_error(ER_PLUGIN_NO_UNINSTALL, MYF(0), plugin->plugin->name);
2083     goto err;
2084   }
2085 
2086 #ifdef HAVE_REPLICATION
2087   /* Block Uninstallation of semi_sync plugins (Master/Slave)
2088      when they are busy
2089    */
2090   char buff[20];
2091   /*
2092     Master: If there are active semi sync slaves for this Master,
2093     then that means it is busy and rpl_semi_sync_master plugin
2094     cannot be uninstalled. To check whether the master
2095     has any semi sync slaves or not, check Rpl_semi_sync_master_cliens
2096     status variable value, if it is not 0, that means it is busy.
2097   */
2098   if (!strcmp(name->str, "rpl_semi_sync_master") &&
2099       get_status_var(thd,
2100                      plugin->plugin->status_vars,
2101                      "Rpl_semi_sync_master_clients",buff) &&
2102       strcmp(buff,"0") )
2103   {
2104     my_error(ER_PLUGIN_CANNOT_BE_UNINSTALLED, MYF(0), name->str,
2105              "Stop any active semisynchronous slaves of this master first.");
2106     goto err;
2107   }
2108   /* Slave: If there is semi sync enabled IO thread active on this Slave,
2109     then that means plugin is busy and rpl_semi_sync_slave plugin
2110     cannot be uninstalled. To check whether semi sync
2111     IO thread is active or not, check Rpl_semi_sync_slave_status status
2112     variable value, if it is ON, that means it is busy.
2113   */
2114   if (!strcmp(name->str, "rpl_semi_sync_slave") &&
2115       get_status_var(thd, plugin->plugin->status_vars,
2116                      "Rpl_semi_sync_slave_status", buff) &&
2117       !strcmp(buff,"ON") )
2118   {
2119     my_error(ER_PLUGIN_CANNOT_BE_UNINSTALLED, MYF(0), name->str,
2120              "Stop any active semisynchronous I/O threads on this slave first.");
2121     goto err;
2122   }
2123 #endif
2124 
2125   plugin->state= PLUGIN_IS_DELETED;
2126   if (plugin->ref_count)
2127     push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
2128                  WARN_PLUGIN_BUSY, ER(WARN_PLUGIN_BUSY));
2129   else
2130     reap_needed= true;
2131   reap_plugins();
2132   mysql_mutex_unlock(&LOCK_plugin);
2133 
2134   uchar user_key[MAX_KEY_LENGTH];
2135   table->use_all_columns();
2136   table->field[0]->store(name->str, name->length, system_charset_info);
2137   key_copy(user_key, table->record[0], table->key_info,
2138            table->key_info->key_length);
2139   if (! table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
2140                                            HA_WHOLE_KEY, HA_READ_KEY_EXACT))
2141   {
2142     int error;
2143     /*
2144       We do not replicate the UNINSTALL PLUGIN statement. Disable binlogging
2145       of the delete from the plugin table, so that it is not replicated in
2146       row based mode.
2147     */
2148     tmp_disable_binlog(thd);
2149     error= table->file->ha_delete_row(table->record[0]);
2150     reenable_binlog(thd);
2151     if (error)
2152     {
2153       table->file->print_error(error, MYF(0));
2154       DBUG_RETURN(TRUE);
2155     }
2156   }
2157   DBUG_RETURN(FALSE);
2158 err:
2159   mysql_mutex_unlock(&LOCK_plugin);
2160   DBUG_RETURN(TRUE);
2161 }
2162 
2163 
plugin_foreach_with_mask(THD * thd,plugin_foreach_func * func,int type,uint state_mask,void * arg)2164 bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
2165                        int type, uint state_mask, void *arg)
2166 {
2167   uint idx, total;
2168   struct st_plugin_int *plugin, **plugins;
2169   int version=plugin_array_version;
2170   DBUG_ENTER("plugin_foreach_with_mask");
2171 
2172   if (!initialized)
2173     DBUG_RETURN(FALSE);
2174 
2175   state_mask= ~state_mask; // do it only once
2176 
2177   mysql_mutex_lock(&LOCK_plugin);
2178   total= type == MYSQL_ANY_PLUGIN ? plugin_array.elements
2179                                   : plugin_hash[type].records;
2180   /*
2181     Do the alloca out here in case we do have a working alloca:
2182         leaving the nested stack frame invalidates alloca allocation.
2183   */
2184   plugins=(struct st_plugin_int **)my_alloca(total*sizeof(plugin));
2185   if (type == MYSQL_ANY_PLUGIN)
2186   {
2187     for (idx= 0; idx < total; idx++)
2188     {
2189       plugin= *dynamic_element(&plugin_array, idx, struct st_plugin_int **);
2190       plugins[idx]= !(plugin->state & state_mask) ? plugin : NULL;
2191     }
2192   }
2193   else
2194   {
2195     HASH *hash= plugin_hash + type;
2196     for (idx= 0; idx < total; idx++)
2197     {
2198       plugin= (struct st_plugin_int *) my_hash_element(hash, idx);
2199       plugins[idx]= !(plugin->state & state_mask) ? plugin : NULL;
2200     }
2201   }
2202   mysql_mutex_unlock(&LOCK_plugin);
2203 
2204   for (idx= 0; idx < total; idx++)
2205   {
2206     if (unlikely(version != plugin_array_version))
2207     {
2208       mysql_mutex_lock(&LOCK_plugin);
2209       for (uint i=idx; i < total; i++)
2210         if (plugins[i] && plugins[i]->state & state_mask)
2211           plugins[i]=0;
2212       mysql_mutex_unlock(&LOCK_plugin);
2213     }
2214     plugin= plugins[idx];
2215     /* It will stop iterating on first engine error when "func" returns TRUE */
2216     if (plugin && func(thd, plugin_int_to_ref(plugin), arg))
2217         goto err;
2218   }
2219 
2220   my_afree(plugins);
2221   DBUG_RETURN(FALSE);
2222 err:
2223   my_afree(plugins);
2224   DBUG_RETURN(TRUE);
2225 }
2226 
2227 
2228 /****************************************************************************
2229   Internal type declarations for variables support
2230 ****************************************************************************/
2231 
2232 #undef MYSQL_SYSVAR_NAME
2233 #define MYSQL_SYSVAR_NAME(name) name
2234 #define PLUGIN_VAR_TYPEMASK 0x007f
2235 
2236 #define EXTRA_OPTIONS 3 /* options for: 'foo', 'plugin-foo' and NULL */
2237 
2238 typedef DECLARE_MYSQL_SYSVAR_BASIC(sysvar_bool_t, my_bool);
2239 typedef DECLARE_MYSQL_THDVAR_BASIC(thdvar_bool_t, my_bool);
2240 typedef DECLARE_MYSQL_SYSVAR_BASIC(sysvar_str_t, char *);
2241 typedef DECLARE_MYSQL_THDVAR_BASIC(thdvar_str_t, char *);
2242 
2243 typedef DECLARE_MYSQL_SYSVAR_TYPELIB(sysvar_enum_t, unsigned long);
2244 typedef DECLARE_MYSQL_THDVAR_TYPELIB(thdvar_enum_t, unsigned long);
2245 typedef DECLARE_MYSQL_SYSVAR_TYPELIB(sysvar_set_t, ulonglong);
2246 typedef DECLARE_MYSQL_THDVAR_TYPELIB(thdvar_set_t, ulonglong);
2247 
2248 typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_int_t, int);
2249 typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_long_t, long);
2250 typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_longlong_t, longlong);
2251 typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_uint_t, uint);
2252 typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_ulong_t, ulong);
2253 typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_ulonglong_t, ulonglong);
2254 typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_double_t, double);
2255 
2256 typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_int_t, int);
2257 typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_long_t, long);
2258 typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_longlong_t, longlong);
2259 typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_uint_t, uint);
2260 typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_ulong_t, ulong);
2261 typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_ulonglong_t, ulonglong);
2262 typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_double_t, double);
2263 
2264 
2265 /****************************************************************************
2266   default variable data check and update functions
2267 ****************************************************************************/
2268 
check_func_bool(THD * thd,struct st_mysql_sys_var * var,void * save,st_mysql_value * value)2269 static int check_func_bool(THD *thd, struct st_mysql_sys_var *var,
2270                            void *save, st_mysql_value *value)
2271 {
2272   char buff[STRING_BUFFER_USUAL_SIZE];
2273   const char *str;
2274   int result, length;
2275   long long tmp;
2276 
2277   if (value->value_type(value) == MYSQL_VALUE_TYPE_STRING)
2278   {
2279     length= sizeof(buff);
2280     if (!(str= value->val_str(value, buff, &length)) ||
2281         (result= find_type(&bool_typelib, str, length, 1)-1) < 0)
2282       goto err;
2283   }
2284   else
2285   {
2286     if (value->val_int(value, &tmp) < 0)
2287       goto err;
2288     if (tmp > 1)
2289       goto err;
2290     result= (int) tmp;
2291   }
2292   *(my_bool *) save= result ? TRUE : FALSE;
2293   return 0;
2294 err:
2295   return 1;
2296 }
2297 
2298 #define CHECK_FUNC_RANGE(cfr_ctype, cfr_vmin, cfr_vmax, cfr_save, cfr_res) \
2299   do { \
2300     if (cfr_vmin && *(cfr_ctype *) cfr_save < *(cfr_ctype *) cfr_vmin) { \
2301       *(cfr_ctype *)cfr_save= *(cfr_ctype *) cfr_vmin; \
2302       cfr_res= TRUE; \
2303     } \
2304     if (cfr_vmax && *(cfr_ctype *) cfr_save > *(cfr_ctype *) cfr_vmax) { \
2305       *(cfr_ctype *) cfr_save= *(cfr_ctype *) cfr_vmax; \
2306       cfr_res= TRUE; \
2307     } \
2308   } while(0)
2309 
check_func_int(THD * thd,struct st_mysql_sys_var * var,void * save,st_mysql_value * value)2310 static int check_func_int(THD *thd, struct st_mysql_sys_var *var,
2311                           void *save, st_mysql_value *value)
2312 {
2313   my_bool fixed1, fixed2;
2314   long long orig, val;
2315   struct my_option options;
2316   const void *vmin, *vmax;
2317   value->val_int(value, &orig);
2318   val= orig;
2319   plugin_opt_set_limits(&options, var);
2320 
2321   vmin= getopt_constraint_get_min_value(var->name, 0, FALSE);
2322   vmax= getopt_constraint_get_max_value(var->name, 0, FALSE);
2323 
2324   if (var->flags & PLUGIN_VAR_UNSIGNED)
2325   {
2326     if ((fixed1= (!value->is_unsigned(value) && val < 0)))
2327       val=0;
2328     *(uint *)save= (uint) getopt_ull_limit_value((ulonglong) val, &options,
2329                                                    &fixed2);
2330 
2331     CHECK_FUNC_RANGE(uint, vmin, vmax, save, fixed1);
2332   }
2333   else
2334   {
2335     if ((fixed1= (value->is_unsigned(value) && val < 0)))
2336       val=LONGLONG_MAX;
2337     *(int *)save= (int) getopt_ll_limit_value(val, &options, &fixed2);
2338 
2339     CHECK_FUNC_RANGE(int, vmin, vmax, save, fixed1);
2340   }
2341 
2342   return throw_bounds_warning(thd, var->name, fixed1 || fixed2,
2343                               value->is_unsigned(value), (longlong) orig);
2344 }
2345 
2346 
check_func_long(THD * thd,struct st_mysql_sys_var * var,void * save,st_mysql_value * value)2347 static int check_func_long(THD *thd, struct st_mysql_sys_var *var,
2348                           void *save, st_mysql_value *value)
2349 {
2350   my_bool fixed1, fixed2;
2351   long long orig, val;
2352   struct my_option options;
2353   const void *vmin, *vmax;
2354   value->val_int(value, &orig);
2355   val= orig;
2356   plugin_opt_set_limits(&options, var);
2357 
2358   vmin= getopt_constraint_get_min_value(var->name, 0, FALSE);
2359   vmax= getopt_constraint_get_max_value(var->name, 0, FALSE);
2360 
2361   if (var->flags & PLUGIN_VAR_UNSIGNED)
2362   {
2363     if ((fixed1= (!value->is_unsigned(value) && val < 0)))
2364       val=0;
2365     *(ulong *)save= (ulong) getopt_ull_limit_value((ulonglong) val, &options,
2366                                                    &fixed2);
2367 
2368     CHECK_FUNC_RANGE(ulong, vmin, vmax, save, fixed1);
2369   }
2370   else
2371   {
2372     if ((fixed1= (value->is_unsigned(value) && val < 0)))
2373       val=LONGLONG_MAX;
2374     *(long *)save= (long) getopt_ll_limit_value(val, &options, &fixed2);
2375 
2376     CHECK_FUNC_RANGE(long, vmin, vmax, save, fixed1);
2377   }
2378 
2379   return throw_bounds_warning(thd, var->name, fixed1 || fixed2,
2380                               value->is_unsigned(value), (longlong) orig);
2381 }
2382 
2383 
check_func_longlong(THD * thd,struct st_mysql_sys_var * var,void * save,st_mysql_value * value)2384 static int check_func_longlong(THD *thd, struct st_mysql_sys_var *var,
2385                                void *save, st_mysql_value *value)
2386 {
2387   my_bool fixed1, fixed2;
2388   long long orig, val;
2389   struct my_option options;
2390   const void *vmin, *vmax;
2391   value->val_int(value, &orig);
2392   val= orig;
2393   plugin_opt_set_limits(&options, var);
2394 
2395   vmin= getopt_constraint_get_min_value(var->name, 0, FALSE);
2396   vmax= getopt_constraint_get_max_value(var->name, 0, FALSE);
2397 
2398   if (var->flags & PLUGIN_VAR_UNSIGNED)
2399   {
2400     if ((fixed1= (!value->is_unsigned(value) && val < 0)))
2401       val=0;
2402     *(ulonglong *)save= getopt_ull_limit_value((ulonglong) val, &options,
2403                                                &fixed2);
2404 
2405      CHECK_FUNC_RANGE(ulonglong, vmin, vmax, save, fixed1);
2406   }
2407   else
2408   {
2409     if ((fixed1= (value->is_unsigned(value) && val < 0)))
2410       val=LONGLONG_MAX;
2411     *(longlong *)save= getopt_ll_limit_value(val, &options, &fixed2);
2412 
2413     CHECK_FUNC_RANGE(longlong, vmin, vmax, save, fixed1);
2414   }
2415 
2416   return throw_bounds_warning(thd, var->name, fixed1 || fixed2,
2417                               value->is_unsigned(value), (longlong) orig);
2418 }
2419 
check_func_str(THD * thd,struct st_mysql_sys_var * var,void * save,st_mysql_value * value)2420 static int check_func_str(THD *thd, struct st_mysql_sys_var *var,
2421                           void *save, st_mysql_value *value)
2422 {
2423   char buff[STRING_BUFFER_USUAL_SIZE];
2424   const char *str;
2425   int length;
2426 
2427   length= sizeof(buff);
2428   if ((str= value->val_str(value, buff, &length)))
2429     str= thd->strmake(str, length);
2430   *(const char**)save= str;
2431   return 0;
2432 }
2433 
2434 
check_func_enum(THD * thd,struct st_mysql_sys_var * var,void * save,st_mysql_value * value)2435 static int check_func_enum(THD *thd, struct st_mysql_sys_var *var,
2436                            void *save, st_mysql_value *value)
2437 {
2438   char buff[STRING_BUFFER_USUAL_SIZE];
2439   const char *str;
2440   TYPELIB *typelib;
2441   long long tmp;
2442   long result;
2443   int length;
2444 
2445   if (var->flags & PLUGIN_VAR_THDLOCAL)
2446     typelib= ((thdvar_enum_t*) var)->typelib;
2447   else
2448     typelib= ((sysvar_enum_t*) var)->typelib;
2449 
2450   if (value->value_type(value) == MYSQL_VALUE_TYPE_STRING)
2451   {
2452     length= sizeof(buff);
2453     if (!(str= value->val_str(value, buff, &length)))
2454       goto err;
2455     if ((result= (long)find_type(typelib, str, length, 0) - 1) < 0)
2456       goto err;
2457   }
2458   else
2459   {
2460     if (value->val_int(value, &tmp))
2461       goto err;
2462     if (tmp < 0 || tmp >= typelib->count)
2463       goto err;
2464     result= (long) tmp;
2465   }
2466   *(long*)save= result;
2467   return 0;
2468 err:
2469   return 1;
2470 }
2471 
2472 
check_func_set(THD * thd,struct st_mysql_sys_var * var,void * save,st_mysql_value * value)2473 static int check_func_set(THD *thd, struct st_mysql_sys_var *var,
2474                           void *save, st_mysql_value *value)
2475 {
2476   char buff[STRING_BUFFER_USUAL_SIZE], *error= 0;
2477   const char *str;
2478   TYPELIB *typelib;
2479   ulonglong result;
2480   uint error_len= 0;                            // init as only set on error
2481   bool not_used;
2482   int length;
2483 
2484   if (var->flags & PLUGIN_VAR_THDLOCAL)
2485     typelib= ((thdvar_set_t*) var)->typelib;
2486   else
2487     typelib= ((sysvar_set_t*)var)->typelib;
2488 
2489   if (value->value_type(value) == MYSQL_VALUE_TYPE_STRING)
2490   {
2491     length= sizeof(buff);
2492     if (!(str= value->val_str(value, buff, &length)))
2493       goto err;
2494     result= find_set(typelib, str, length, NULL,
2495                      &error, &error_len, &not_used);
2496     if (error_len)
2497       goto err;
2498   }
2499   else
2500   {
2501     if (value->val_int(value, (long long *)&result))
2502       goto err;
2503     if (unlikely((result >= (1ULL << typelib->count)) &&
2504                  (typelib->count < sizeof(long)*8)))
2505       goto err;
2506   }
2507   *(ulonglong*)save= result;
2508   return 0;
2509 err:
2510   return 1;
2511 }
2512 
check_func_double(THD * thd,struct st_mysql_sys_var * var,void * save,st_mysql_value * value)2513 static int check_func_double(THD *thd, struct st_mysql_sys_var *var,
2514                              void *save, st_mysql_value *value)
2515 {
2516   double v;
2517   my_bool fixed;
2518   struct my_option option;
2519 
2520   value->val_real(value, &v);
2521   plugin_opt_set_limits(&option, var);
2522   *(double *) save= getopt_double_limit_value(v, &option, &fixed);
2523 
2524   return throw_bounds_warning(thd, var->name, fixed, v);
2525 }
2526 
2527 
update_func_bool(THD * thd,struct st_mysql_sys_var * var,void * tgt,const void * save)2528 static void update_func_bool(THD *thd, struct st_mysql_sys_var *var,
2529                              void *tgt, const void *save)
2530 {
2531   *(my_bool *) tgt= *(my_bool *) save ? TRUE : FALSE;
2532 }
2533 
2534 
update_func_int(THD * thd,struct st_mysql_sys_var * var,void * tgt,const void * save)2535 static void update_func_int(THD *thd, struct st_mysql_sys_var *var,
2536                              void *tgt, const void *save)
2537 {
2538   *(int *)tgt= *(int *) save;
2539 }
2540 
2541 
update_func_long(THD * thd,struct st_mysql_sys_var * var,void * tgt,const void * save)2542 static void update_func_long(THD *thd, struct st_mysql_sys_var *var,
2543                              void *tgt, const void *save)
2544 {
2545   *(long *)tgt= *(long *) save;
2546 }
2547 
2548 
update_func_longlong(THD * thd,struct st_mysql_sys_var * var,void * tgt,const void * save)2549 static void update_func_longlong(THD *thd, struct st_mysql_sys_var *var,
2550                              void *tgt, const void *save)
2551 {
2552   *(longlong *)tgt= *(ulonglong *) save;
2553 }
2554 
2555 
update_func_str(THD * thd,struct st_mysql_sys_var * var,void * tgt,const void * save)2556 static void update_func_str(THD *thd, struct st_mysql_sys_var *var,
2557                              void *tgt, const void *save)
2558 {
2559   *(char **) tgt= *(char **) save;
2560 }
2561 
update_func_double(THD * thd,struct st_mysql_sys_var * var,void * tgt,const void * save)2562 static void update_func_double(THD *thd, struct st_mysql_sys_var *var,
2563                                void *tgt, const void *save)
2564 {
2565   *(double *) tgt= *(double *) save;
2566 }
2567 
2568 /****************************************************************************
2569   System Variables support
2570 ****************************************************************************/
2571 
2572 
find_sys_var(THD * thd,const char * str,uint length)2573 sys_var *find_sys_var(THD *thd, const char *str, uint length)
2574 {
2575   sys_var *var;
2576   sys_var_pluginvar *pi= NULL;
2577   plugin_ref plugin;
2578   DBUG_ENTER("find_sys_var");
2579 
2580   const my_bool *hidden= getopt_constraint_get_hidden_value(str, 0, FALSE);
2581   if (hidden && *hidden)
2582   {
2583     var= NULL;
2584     goto exit;
2585   }
2586 
2587   mysql_mutex_lock(&LOCK_plugin);
2588   mysql_rwlock_rdlock(&LOCK_system_variables_hash);
2589   if ((var= intern_find_sys_var(str, length)) &&
2590       (pi= var->cast_pluginvar()))
2591   {
2592     mysql_rwlock_unlock(&LOCK_system_variables_hash);
2593     LEX *lex= thd ? thd->lex : 0;
2594     if (!(plugin= my_intern_plugin_lock(lex, plugin_int_to_ref(pi->plugin))))
2595       var= NULL; /* failed to lock it, it must be uninstalling */
2596     else
2597     if (!(plugin_state(plugin) & PLUGIN_IS_READY))
2598     {
2599       /* initialization not completed */
2600       var= NULL;
2601       intern_plugin_unlock(lex, plugin);
2602     }
2603   }
2604   else
2605     mysql_rwlock_unlock(&LOCK_system_variables_hash);
2606   mysql_mutex_unlock(&LOCK_plugin);
2607 
2608 exit:
2609   if (!var)
2610     my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (char*) str);
2611   DBUG_RETURN(var);
2612 }
2613 
2614 
2615 /*
2616   called by register_var, construct_options and test_plugin_options.
2617   Returns the 'bookmark' for the named variable.
2618   LOCK_system_variables_hash should be at least read locked
2619 */
find_bookmark(const char * plugin,const char * name,int flags)2620 static st_bookmark *find_bookmark(const char *plugin, const char *name,
2621                                   int flags)
2622 {
2623   st_bookmark *result= NULL;
2624   uint namelen, length, pluginlen= 0;
2625   char *varname, *p;
2626 
2627   if (!(flags & PLUGIN_VAR_THDLOCAL))
2628     return NULL;
2629 
2630   namelen= strlen(name);
2631   if (plugin)
2632     pluginlen= strlen(plugin) + 1;
2633   length= namelen + pluginlen + 2;
2634   varname= (char*) my_alloca(length);
2635 
2636   if (plugin)
2637   {
2638     strxmov(varname + 1, plugin, "_", name, NullS);
2639     for (p= varname + 1; *p; p++)
2640       if (*p == '-')
2641         *p= '_';
2642   }
2643   else
2644     memcpy(varname + 1, name, namelen + 1);
2645 
2646   varname[0]= flags & PLUGIN_VAR_TYPEMASK;
2647 
2648   result= (st_bookmark*) my_hash_search(&bookmark_hash,
2649                                         (const uchar*) varname, length - 1);
2650 
2651   my_afree(varname);
2652   return result;
2653 }
2654 
2655 
2656 /*
2657   returns a bookmark for thd-local variables, creating if neccessary.
2658   returns null for non thd-local variables.
2659   Requires that a write lock is obtained on LOCK_system_variables_hash
2660 */
register_var(const char * plugin,const char * name,int flags)2661 static st_bookmark *register_var(const char *plugin, const char *name,
2662                                  int flags)
2663 {
2664   uint length= strlen(plugin) + strlen(name) + 3, size= 0, offset, new_size;
2665   st_bookmark *result;
2666   char *varname, *p;
2667 
2668   if (!(flags & PLUGIN_VAR_THDLOCAL))
2669     return NULL;
2670 
2671   switch (flags & PLUGIN_VAR_TYPEMASK) {
2672   case PLUGIN_VAR_BOOL:
2673     size= sizeof(my_bool);
2674     break;
2675   case PLUGIN_VAR_INT:
2676     size= sizeof(int);
2677     break;
2678   case PLUGIN_VAR_LONG:
2679   case PLUGIN_VAR_ENUM:
2680     size= sizeof(long);
2681     break;
2682   case PLUGIN_VAR_LONGLONG:
2683   case PLUGIN_VAR_SET:
2684     size= sizeof(ulonglong);
2685     break;
2686   case PLUGIN_VAR_STR:
2687     size= sizeof(char*);
2688     break;
2689   case PLUGIN_VAR_DOUBLE:
2690     size= sizeof(double);
2691     break;
2692   default:
2693     DBUG_ASSERT(0);
2694     return NULL;
2695   };
2696 
2697   varname= ((char*) my_alloca(length));
2698   strxmov(varname + 1, plugin, "_", name, NullS);
2699   for (p= varname + 1; *p; p++)
2700     if (*p == '-')
2701       *p= '_';
2702 
2703   if (!(result= find_bookmark(NULL, varname + 1, flags)))
2704   {
2705     result= (st_bookmark*) alloc_root(&plugin_mem_root,
2706                                       sizeof(struct st_bookmark) + length-1);
2707     varname[0]= flags & PLUGIN_VAR_TYPEMASK;
2708     memcpy(result->key, varname, length);
2709     result->name_len= length - 2;
2710     result->offset= -1;
2711 
2712     DBUG_ASSERT(size && !(size & (size-1))); /* must be power of 2 */
2713 
2714     offset= global_system_variables.dynamic_variables_size;
2715     offset= (offset + size - 1) & ~(size - 1);
2716     result->offset= (int) offset;
2717 
2718     new_size= (offset + size + 63) & ~63;
2719 
2720     if (new_size > global_variables_dynamic_size)
2721     {
2722       global_system_variables.dynamic_variables_ptr= (char*)
2723         my_realloc(global_system_variables.dynamic_variables_ptr, new_size,
2724                    MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
2725       max_system_variables.dynamic_variables_ptr= (char*)
2726         my_realloc(max_system_variables.dynamic_variables_ptr, new_size,
2727                    MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
2728       /*
2729         Clear the new variable value space. This is required for string
2730         variables. If their value is non-NULL, it must point to a valid
2731         string.
2732       */
2733       memset(global_system_variables.dynamic_variables_ptr +
2734              global_variables_dynamic_size, 0,
2735              new_size - global_variables_dynamic_size);
2736       memset(max_system_variables.dynamic_variables_ptr +
2737              global_variables_dynamic_size, 0,
2738              new_size - global_variables_dynamic_size);
2739       global_variables_dynamic_size= new_size;
2740     }
2741 
2742     global_system_variables.dynamic_variables_head= offset;
2743     max_system_variables.dynamic_variables_head= offset;
2744     global_system_variables.dynamic_variables_size= offset + size;
2745     max_system_variables.dynamic_variables_size= offset + size;
2746     global_system_variables.dynamic_variables_version++;
2747     max_system_variables.dynamic_variables_version++;
2748 
2749     result->version= global_system_variables.dynamic_variables_version;
2750 
2751     /* this should succeed because we have already checked if a dup exists */
2752     if (my_hash_insert(&bookmark_hash, (uchar*) result))
2753     {
2754       fprintf(stderr, "failed to add placeholder to hash");
2755       DBUG_ASSERT(0);
2756     }
2757 
2758     /*
2759       Hashing vars of string type with MEMALLOC flag.
2760     */
2761     if (((flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR) &&
2762         (flags & PLUGIN_VAR_MEMALLOC) &&
2763         (my_hash_insert(&malloced_string_type_sysvars_bookmark_hash,
2764                         (uchar *)result)))
2765     {
2766       fprintf(stderr, "failed to add placeholder to"
2767                       " hash of malloced string type sysvars");
2768       DBUG_ASSERT(0);
2769     }
2770   }
2771   my_afree(varname);
2772   return result;
2773 }
2774 
restore_pluginvar_names(sys_var * first)2775 static void restore_pluginvar_names(sys_var *first)
2776 {
2777   for (sys_var *var= first; var; var= var->next)
2778   {
2779     sys_var_pluginvar *pv= var->cast_pluginvar();
2780     pv->plugin_var->name= pv->orig_pluginvar_name;
2781   }
2782 }
2783 
2784 
2785 /*
2786   returns a pointer to the memory which holds the thd-local variable or
2787   a pointer to the global variable if thd==null.
2788   If required, will sync with global variables if the requested variable
2789   has not yet been allocated in the current thread.
2790 */
intern_sys_var_ptr(THD * thd,int offset,bool global_lock)2791 static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock)
2792 {
2793   DBUG_ASSERT(offset >= 0);
2794   DBUG_ASSERT((uint)offset <= global_system_variables.dynamic_variables_head);
2795 
2796   if (!thd)
2797     return (uchar*) global_system_variables.dynamic_variables_ptr + offset;
2798 
2799   /*
2800     dynamic_variables_head points to the largest valid offset
2801   */
2802   if (!thd->variables.dynamic_variables_ptr ||
2803       (uint)offset > thd->variables.dynamic_variables_head)
2804   {
2805     uint idx;
2806 
2807     mysql_rwlock_rdlock(&LOCK_system_variables_hash);
2808 
2809     thd->variables.dynamic_variables_ptr= (char*)
2810       my_realloc(thd->variables.dynamic_variables_ptr,
2811                  global_variables_dynamic_size,
2812                  MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
2813 
2814     if (global_lock)
2815       mysql_mutex_lock(&LOCK_global_system_variables);
2816 
2817     mysql_mutex_assert_owner(&LOCK_global_system_variables);
2818 
2819     memcpy(thd->variables.dynamic_variables_ptr +
2820              thd->variables.dynamic_variables_size,
2821            global_system_variables.dynamic_variables_ptr +
2822              thd->variables.dynamic_variables_size,
2823            global_system_variables.dynamic_variables_size -
2824              thd->variables.dynamic_variables_size);
2825 
2826     /*
2827       now we need to iterate through any newly copied 'defaults'
2828       and if it is a string type with MEMALLOC flag, we need to strdup
2829     */
2830     for (idx= 0; idx < bookmark_hash.records; idx++)
2831     {
2832       sys_var_pluginvar *pi;
2833       sys_var *var;
2834       st_bookmark *v= (st_bookmark*) my_hash_element(&bookmark_hash,idx);
2835 
2836       if (v->version <= thd->variables.dynamic_variables_version ||
2837           !(var= intern_find_sys_var(v->key + 1, v->name_len)) ||
2838           !(pi= var->cast_pluginvar()) ||
2839           v->key[0] != (pi->plugin_var->flags & PLUGIN_VAR_TYPEMASK))
2840         continue;
2841 
2842       /* Here we do anything special that may be required of the data types */
2843 
2844       if ((pi->plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
2845           pi->plugin_var->flags & PLUGIN_VAR_MEMALLOC)
2846       {
2847          int varoff= *(int *) (pi->plugin_var + 1);
2848          char **thdvar= (char **) (thd->variables.
2849                                    dynamic_variables_ptr + varoff);
2850          char **sysvar= (char **) (global_system_variables.
2851                                    dynamic_variables_ptr + varoff);
2852          *thdvar= NULL;
2853          plugin_var_memalloc_session_update(thd, NULL, thdvar, *sysvar);
2854       }
2855     }
2856 
2857     if (global_lock)
2858       mysql_mutex_unlock(&LOCK_global_system_variables);
2859 
2860     thd->variables.dynamic_variables_version=
2861            global_system_variables.dynamic_variables_version;
2862     thd->variables.dynamic_variables_head=
2863            global_system_variables.dynamic_variables_head;
2864     thd->variables.dynamic_variables_size=
2865            global_system_variables.dynamic_variables_size;
2866 
2867     mysql_rwlock_unlock(&LOCK_system_variables_hash);
2868   }
2869   return (uchar*)thd->variables.dynamic_variables_ptr + offset;
2870 }
2871 
2872 
2873 /**
2874   For correctness and simplicity's sake, a pointer to a function
2875   must be compatible with pointed-to type, that is, the return and
2876   parameters types must be the same. Thus, a callback function is
2877   defined for each scalar type. The functions are assigned in
2878   construct_options to their respective types.
2879 */
2880 
mysql_sys_var_char(THD * thd,int offset)2881 static char *mysql_sys_var_char(THD* thd, int offset)
2882 {
2883   return (char *) intern_sys_var_ptr(thd, offset, true);
2884 }
2885 
mysql_sys_var_int(THD * thd,int offset)2886 static int *mysql_sys_var_int(THD* thd, int offset)
2887 {
2888   return (int *) intern_sys_var_ptr(thd, offset, true);
2889 }
2890 
mysql_sys_var_long(THD * thd,int offset)2891 static long *mysql_sys_var_long(THD* thd, int offset)
2892 {
2893   return (long *) intern_sys_var_ptr(thd, offset, true);
2894 }
2895 
mysql_sys_var_ulong(THD * thd,int offset)2896 static unsigned long *mysql_sys_var_ulong(THD* thd, int offset)
2897 {
2898   return (unsigned long *) intern_sys_var_ptr(thd, offset, true);
2899 }
2900 
mysql_sys_var_longlong(THD * thd,int offset)2901 static long long *mysql_sys_var_longlong(THD* thd, int offset)
2902 {
2903   return (long long *) intern_sys_var_ptr(thd, offset, true);
2904 }
2905 
mysql_sys_var_ulonglong(THD * thd,int offset)2906 static unsigned long long *mysql_sys_var_ulonglong(THD* thd, int offset)
2907 {
2908   return (unsigned long long *) intern_sys_var_ptr(thd, offset, true);
2909 }
2910 
mysql_sys_var_str(THD * thd,int offset)2911 static char **mysql_sys_var_str(THD* thd, int offset)
2912 {
2913   return (char **) intern_sys_var_ptr(thd, offset, true);
2914 }
2915 
mysql_sys_var_double(THD * thd,int offset)2916 static double *mysql_sys_var_double(THD* thd, int offset)
2917 {
2918   return (double *) intern_sys_var_ptr(thd, offset, true);
2919 }
2920 
plugin_thdvar_init(THD * thd,bool enable_plugins)2921 void plugin_thdvar_init(THD *thd, bool enable_plugins)
2922 {
2923   plugin_ref old_table_plugin= thd->variables.table_plugin;
2924   plugin_ref old_temp_table_plugin= thd->variables.temp_table_plugin;
2925   DBUG_ENTER("plugin_thdvar_init");
2926 
2927   thd->variables.table_plugin= NULL;
2928   thd->variables.temp_table_plugin= NULL;
2929   cleanup_variables(thd, &thd->variables);
2930 
2931   thd->variables= global_system_variables;
2932   thd->variables.table_plugin= NULL;
2933   thd->variables.temp_table_plugin= NULL;
2934 
2935   /* we are going to allocate these lazily */
2936   thd->variables.dynamic_variables_version= 0;
2937   thd->variables.dynamic_variables_size= 0;
2938   thd->variables.dynamic_variables_ptr= 0;
2939 
2940   if (enable_plugins)
2941   {
2942     mysql_mutex_lock(&LOCK_plugin);
2943     thd->variables.table_plugin=
2944       my_intern_plugin_lock(NULL, global_system_variables.table_plugin);
2945     intern_plugin_unlock(NULL, old_table_plugin);
2946     thd->variables.temp_table_plugin=
2947       my_intern_plugin_lock(NULL, global_system_variables.temp_table_plugin);
2948     intern_plugin_unlock(NULL, old_temp_table_plugin);
2949     mysql_mutex_unlock(&LOCK_plugin);
2950   }
2951   DBUG_VOID_RETURN;
2952 }
2953 
2954 
2955 /*
2956   Unlocks all system variables which hold a reference
2957 */
unlock_variables(THD * thd,struct system_variables * vars)2958 static void unlock_variables(THD *thd, struct system_variables *vars)
2959 {
2960   intern_plugin_unlock(NULL, vars->table_plugin);
2961   intern_plugin_unlock(NULL, vars->temp_table_plugin);
2962   vars->table_plugin= NULL;
2963   vars->temp_table_plugin= NULL;
2964 }
2965 
2966 
2967 /*
2968   Frees memory used by system variables
2969 
2970   Unlike plugin_vars_free_values() it frees all variables of all plugins,
2971   it's used on shutdown.
2972 */
cleanup_variables(THD * thd,struct system_variables * vars)2973 static void cleanup_variables(THD *thd, struct system_variables *vars)
2974 {
2975   if (thd)
2976     plugin_var_memalloc_free(&thd->variables);
2977 
2978   DBUG_ASSERT(vars->table_plugin == NULL);
2979   DBUG_ASSERT(vars->temp_table_plugin == NULL);
2980 
2981   my_free(vars->dynamic_variables_ptr);
2982   vars->dynamic_variables_ptr= NULL;
2983   vars->dynamic_variables_size= 0;
2984   vars->dynamic_variables_version= 0;
2985 }
2986 
2987 
plugin_thdvar_cleanup(THD * thd)2988 void plugin_thdvar_cleanup(THD *thd)
2989 {
2990   uint idx;
2991   plugin_ref *list;
2992   DBUG_ENTER("plugin_thdvar_cleanup");
2993 
2994   mysql_mutex_lock(&LOCK_plugin);
2995 
2996   unlock_variables(thd, &thd->variables);
2997   cleanup_variables(thd, &thd->variables);
2998 
2999   if ((idx= thd->lex->plugins.elements))
3000   {
3001     list= ((plugin_ref*) thd->lex->plugins.buffer) + idx - 1;
3002     DBUG_PRINT("info",("unlocking %d plugins", idx));
3003     while ((uchar*) list >= thd->lex->plugins.buffer)
3004       intern_plugin_unlock(thd->lex, *list--);
3005   }
3006 
3007   reap_plugins();
3008   mysql_mutex_unlock(&LOCK_plugin);
3009 
3010   reset_dynamic(&thd->lex->plugins);
3011 
3012   DBUG_VOID_RETURN;
3013 }
3014 
3015 
3016 /**
3017   @brief Free values of thread variables of a plugin.
3018 
3019   This must be called before a plugin is deleted. Otherwise its
3020   variables are no longer accessible and the value space is lost. Note
3021   that only string values with PLUGIN_VAR_MEMALLOC are allocated and
3022   must be freed.
3023 
3024   @param[in]        vars        Chain of system variables of a plugin
3025 */
3026 
plugin_vars_free_values(sys_var * vars)3027 static void plugin_vars_free_values(sys_var *vars)
3028 {
3029   DBUG_ENTER("plugin_vars_free_values");
3030 
3031   for (sys_var *var= vars; var; var= var->next)
3032   {
3033     sys_var_pluginvar *piv= var->cast_pluginvar();
3034     if (piv &&
3035         ((piv->plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR) &&
3036         (piv->plugin_var->flags & PLUGIN_VAR_MEMALLOC))
3037     {
3038       /* Free the string from global_system_variables. */
3039       char **valptr= (char**) piv->real_value_ptr(NULL, OPT_GLOBAL);
3040       DBUG_PRINT("plugin", ("freeing value for: '%s'  addr: 0x%lx",
3041                             var->name.str, (long) valptr));
3042       my_free(*valptr);
3043       *valptr= NULL;
3044     }
3045   }
3046   DBUG_VOID_RETURN;
3047 }
3048 
pluginvar_show_type(st_mysql_sys_var * plugin_var)3049 static SHOW_TYPE pluginvar_show_type(st_mysql_sys_var *plugin_var)
3050 {
3051   switch (plugin_var->flags & PLUGIN_VAR_TYPEMASK) {
3052   case PLUGIN_VAR_BOOL:
3053     return SHOW_MY_BOOL;
3054   case PLUGIN_VAR_INT:
3055     return SHOW_INT;
3056   case PLUGIN_VAR_LONG:
3057     return SHOW_LONG;
3058   case PLUGIN_VAR_LONGLONG:
3059     return SHOW_LONGLONG;
3060   case PLUGIN_VAR_STR:
3061     return SHOW_CHAR_PTR;
3062   case PLUGIN_VAR_ENUM:
3063   case PLUGIN_VAR_SET:
3064     return SHOW_CHAR;
3065   case PLUGIN_VAR_DOUBLE:
3066     return SHOW_DOUBLE;
3067   default:
3068     DBUG_ASSERT(0);
3069     return SHOW_UNDEF;
3070   }
3071 }
3072 
3073 
3074 /**
3075   Set value for thread local variable with PLUGIN_VAR_MEMALLOC flag.
3076 
3077   @param[in]     thd   Thread context.
3078   @param[in]     var   Plugin variable.
3079   @param[in,out] dest  Destination memory pointer.
3080   @param[in]     value '\0'-terminated new value.
3081 
3082   Most plugin variable values are stored on dynamic_variables_ptr.
3083   Releasing memory occupied by these values is as simple as freeing
3084   dynamic_variables_ptr.
3085 
3086   An exception to the rule are PLUGIN_VAR_MEMALLOC variables, which
3087   are stored on individual memory hunks. All of these hunks has to
3088   be freed when it comes to cleanup.
3089 
3090   It may happen that a plugin was uninstalled and descriptors of
3091   it's variables are lost. In this case it is impossible to locate
3092   corresponding values.
3093 
3094   In addition to allocating and setting variable value, new element
3095   is added to dynamic_variables_allocs list. When thread is done, it
3096   has to call plugin_var_memalloc_free() to release memory used by
3097   PLUGIN_VAR_MEMALLOC variables.
3098 
3099   If var is NULL, variable update function is not called. This is
3100   needed when we take snapshot of system variables during thread
3101   initialization.
3102 
3103   @note List element and variable value are stored on the same memory
3104   hunk. List element is followed by variable value.
3105 
3106   @return Completion status
3107   @retval false Success
3108   @retval true  Failure
3109 */
3110 
plugin_var_memalloc_session_update(THD * thd,struct st_mysql_sys_var * var,char ** dest,const char * value)3111 static bool plugin_var_memalloc_session_update(THD *thd,
3112                                                struct st_mysql_sys_var *var,
3113                                                char **dest, const char *value)
3114 
3115 {
3116   LIST *old_element= NULL;
3117   struct system_variables *vars= &thd->variables;
3118   DBUG_ENTER("plugin_var_memalloc_session_update");
3119 
3120   if (value)
3121   {
3122     size_t length= strlen(value) + 1;
3123     LIST *element;
3124     if (!(element= (LIST *) my_malloc(sizeof(LIST) + length, MYF(MY_WME))))
3125       DBUG_RETURN(true);
3126     memcpy(element + 1, value, length);
3127     value= (const char *) (element + 1);
3128     vars->dynamic_variables_allocs= list_add(vars->dynamic_variables_allocs,
3129                                              element);
3130   }
3131 
3132   if (*dest)
3133     old_element= (LIST *) (*dest - sizeof(LIST));
3134 
3135   if (var)
3136     var->update(thd, var, (void **) dest, (const void *) &value);
3137   else
3138     *dest= (char *) value;
3139 
3140   if (old_element)
3141   {
3142     vars->dynamic_variables_allocs= list_delete(vars->dynamic_variables_allocs,
3143                                                 old_element);
3144     my_free(old_element);
3145   }
3146   DBUG_RETURN(false);
3147 }
3148 
3149 
3150 /**
3151   Set value for a thread local variable.
3152 
3153   @param[in]     thd   Thread context.
3154   @param[in]     var   Plugin variable.
3155   @param[in,out] dest  Destination memory pointer.
3156   @param[in]     value New value.
3157 
3158   Note: new value should be '\0'-terminated for string variables.
3159 
3160   Used in plugin.h:THDVAR_SET(thd, name, value) macro.
3161 */
3162 
plugin_thdvar_safe_update(THD * thd,st_mysql_sys_var * var,char ** dest,const char * value)3163 void plugin_thdvar_safe_update(THD *thd, st_mysql_sys_var *var, char **dest, const char *value)
3164 {
3165   DBUG_ASSERT(current_thd == NULL || thd == current_thd);
3166 
3167   if (var->flags & PLUGIN_VAR_THDLOCAL)
3168   {
3169     if ((var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
3170         var->flags & PLUGIN_VAR_MEMALLOC)
3171       plugin_var_memalloc_session_update(thd, var, dest, value);
3172     else
3173       var->update(thd, var, dest, value);
3174   }
3175 }
3176 
3177 
3178 /**
3179   Free all elements allocated by plugin_var_memalloc_session_update().
3180 
3181   @param[in]     vars  system variables structure
3182 
3183   @see plugin_var_memalloc_session_update
3184 */
3185 
plugin_var_memalloc_free(struct system_variables * vars)3186 static void plugin_var_memalloc_free(struct system_variables *vars)
3187 {
3188   LIST *next, *root;
3189   DBUG_ENTER("plugin_var_memalloc_free");
3190   for (root= vars->dynamic_variables_allocs; root; root= next)
3191   {
3192     next= root->next;
3193     my_free(root);
3194   }
3195   vars->dynamic_variables_allocs= NULL;
3196   DBUG_VOID_RETURN;
3197 }
3198 
3199 
3200 /**
3201   Set value for global variable with PLUGIN_VAR_MEMALLOC flag.
3202 
3203   @param[in]     thd   Thread context.
3204   @param[in]     var   Plugin variable.
3205   @param[in,out] dest  Destination memory pointer.
3206   @param[in]     value '\0'-terminated new value.
3207 
3208   @return Completion status
3209   @retval false Success
3210   @retval true  Failure
3211 */
3212 
plugin_var_memalloc_global_update(THD * thd,struct st_mysql_sys_var * var,char ** dest,const char * value)3213 static bool plugin_var_memalloc_global_update(THD *thd,
3214                                               struct st_mysql_sys_var *var,
3215                                               char **dest, const char *value)
3216 {
3217   char *old_value= *dest;
3218   DBUG_ENTER("plugin_var_memalloc_global_update");
3219 
3220   if (value && !(value= my_strdup(value, MYF(MY_WME))))
3221     DBUG_RETURN(true);
3222 
3223   var->update(thd, var, (void **) dest, (const void *) &value);
3224 
3225   if (old_value)
3226     my_free(old_value);
3227 
3228   DBUG_RETURN(false);
3229 }
3230 
3231 
check_update_type(Item_result type)3232 bool sys_var_pluginvar::check_update_type(Item_result type)
3233 {
3234   switch (plugin_var->flags & PLUGIN_VAR_TYPEMASK) {
3235   case PLUGIN_VAR_INT:
3236   case PLUGIN_VAR_LONG:
3237   case PLUGIN_VAR_LONGLONG:
3238     return type != INT_RESULT;
3239   case PLUGIN_VAR_STR:
3240     return type != STRING_RESULT;
3241   case PLUGIN_VAR_ENUM:
3242   case PLUGIN_VAR_BOOL:
3243   case PLUGIN_VAR_SET:
3244     return type != STRING_RESULT && type != INT_RESULT;
3245   case PLUGIN_VAR_DOUBLE:
3246     return type != INT_RESULT && type != REAL_RESULT && type != DECIMAL_RESULT;
3247   default:
3248     return true;
3249   }
3250 }
3251 
3252 
real_value_ptr(THD * thd,enum_var_type type)3253 uchar* sys_var_pluginvar::real_value_ptr(THD *thd, enum_var_type type)
3254 {
3255   DBUG_ASSERT(thd || (type == OPT_GLOBAL));
3256   if (plugin_var->flags & PLUGIN_VAR_THDLOCAL)
3257   {
3258     if (type == OPT_GLOBAL)
3259       thd= NULL;
3260 
3261     return intern_sys_var_ptr(thd, *(int*) (plugin_var+1), false);
3262   }
3263   return *(uchar**) (plugin_var+1);
3264 }
3265 
3266 
plugin_var_typelib(void)3267 TYPELIB* sys_var_pluginvar::plugin_var_typelib(void)
3268 {
3269   switch (plugin_var->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_THDLOCAL)) {
3270   case PLUGIN_VAR_ENUM:
3271     return ((sysvar_enum_t *)plugin_var)->typelib;
3272   case PLUGIN_VAR_SET:
3273     return ((sysvar_set_t *)plugin_var)->typelib;
3274   case PLUGIN_VAR_ENUM | PLUGIN_VAR_THDLOCAL:
3275     return ((thdvar_enum_t *)plugin_var)->typelib;
3276   case PLUGIN_VAR_SET | PLUGIN_VAR_THDLOCAL:
3277     return ((thdvar_set_t *)plugin_var)->typelib;
3278   default:
3279     return NULL;
3280   }
3281   return NULL;	/* Keep compiler happy */
3282 }
3283 
3284 
do_value_ptr(THD * thd,enum_var_type type,LEX_STRING * base)3285 uchar* sys_var_pluginvar::do_value_ptr(THD *thd, enum_var_type type,
3286                                        LEX_STRING *base)
3287 {
3288   uchar* result;
3289 
3290   result= real_value_ptr(thd, type);
3291 
3292   if ((plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_ENUM)
3293     result= (uchar*) get_type(plugin_var_typelib(), *(ulong*)result);
3294   else if ((plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_SET)
3295     result= (uchar*) set_to_string(thd, 0, *(ulonglong*) result,
3296                                    plugin_var_typelib()->type_names);
3297   return result;
3298 }
3299 
do_check(THD * thd,set_var * var)3300 bool sys_var_pluginvar::do_check(THD *thd, set_var *var)
3301 {
3302   st_item_value_holder value;
3303   DBUG_ASSERT(!is_readonly());
3304   DBUG_ASSERT(plugin_var->check);
3305 
3306   value.value_type= item_value_type;
3307   value.val_str= item_val_str;
3308   value.val_int= item_val_int;
3309   value.val_real= item_val_real;
3310   value.is_unsigned= item_is_unsigned;
3311   value.item= var->value;
3312 
3313   return plugin_var->check(thd, plugin_var, &var->save_result, &value);
3314 }
3315 
session_update(THD * thd,set_var * var)3316 bool sys_var_pluginvar::session_update(THD *thd, set_var *var)
3317 {
3318   bool rc= false;
3319   DBUG_ASSERT(!is_readonly());
3320   DBUG_ASSERT(plugin_var->flags & PLUGIN_VAR_THDLOCAL);
3321   DBUG_ASSERT(thd == current_thd);
3322 
3323   mysql_mutex_lock(&LOCK_global_system_variables);
3324   void *tgt= real_value_ptr(thd, var->type);
3325   const void *src= var->value ? (void*)&var->save_result
3326                               : (void*)real_value_ptr(thd, OPT_GLOBAL);
3327   mysql_mutex_unlock(&LOCK_global_system_variables);
3328 
3329   if ((plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
3330       plugin_var->flags & PLUGIN_VAR_MEMALLOC)
3331     rc= plugin_var_memalloc_session_update(thd, plugin_var, (char **) tgt,
3332                                            *(const char **) src);
3333   else
3334     plugin_var->update(thd, plugin_var, tgt, src);
3335 
3336   return rc;
3337 }
3338 
global_update(THD * thd,set_var * var)3339 bool sys_var_pluginvar::global_update(THD *thd, set_var *var)
3340 {
3341   bool rc= false;
3342   DBUG_ASSERT(!is_readonly());
3343   mysql_mutex_assert_owner(&LOCK_global_system_variables);
3344 
3345   void *tgt= real_value_ptr(thd, var->type);
3346   const void *src= &var->save_result;
3347 
3348   if (!var->value)
3349   {
3350     switch (plugin_var->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_THDLOCAL)) {
3351     case PLUGIN_VAR_INT:
3352       src= &((sysvar_uint_t*) plugin_var)->def_val;
3353       break;
3354     case PLUGIN_VAR_LONG:
3355       src= &((sysvar_ulong_t*) plugin_var)->def_val;
3356       break;
3357     case PLUGIN_VAR_LONGLONG:
3358       src= &((sysvar_ulonglong_t*) plugin_var)->def_val;
3359       break;
3360     case PLUGIN_VAR_ENUM:
3361       src= &((sysvar_enum_t*) plugin_var)->def_val;
3362       break;
3363     case PLUGIN_VAR_SET:
3364       src= &((sysvar_set_t*) plugin_var)->def_val;
3365       break;
3366     case PLUGIN_VAR_BOOL:
3367       src= &((sysvar_bool_t*) plugin_var)->def_val;
3368       break;
3369     case PLUGIN_VAR_STR:
3370       src= &((sysvar_str_t*) plugin_var)->def_val;
3371       break;
3372     case PLUGIN_VAR_DOUBLE:
3373       src= &((sysvar_double_t*) plugin_var)->def_val;
3374       break;
3375     case PLUGIN_VAR_INT | PLUGIN_VAR_THDLOCAL:
3376       src= &((thdvar_uint_t*) plugin_var)->def_val;
3377       break;
3378     case PLUGIN_VAR_LONG | PLUGIN_VAR_THDLOCAL:
3379       src= &((thdvar_ulong_t*) plugin_var)->def_val;
3380       break;
3381     case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_THDLOCAL:
3382       src= &((thdvar_ulonglong_t*) plugin_var)->def_val;
3383       break;
3384     case PLUGIN_VAR_ENUM | PLUGIN_VAR_THDLOCAL:
3385       src= &((thdvar_enum_t*) plugin_var)->def_val;
3386       break;
3387     case PLUGIN_VAR_SET | PLUGIN_VAR_THDLOCAL:
3388       src= &((thdvar_set_t*) plugin_var)->def_val;
3389       break;
3390     case PLUGIN_VAR_BOOL | PLUGIN_VAR_THDLOCAL:
3391       src= &((thdvar_bool_t*) plugin_var)->def_val;
3392       break;
3393     case PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL:
3394       src= &((thdvar_str_t*) plugin_var)->def_val;
3395       break;
3396     case PLUGIN_VAR_DOUBLE | PLUGIN_VAR_THDLOCAL:
3397       src= &((thdvar_double_t*) plugin_var)->def_val;
3398       break;
3399     default:
3400       DBUG_ASSERT(0);
3401     }
3402   }
3403 
3404   if ((plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
3405       plugin_var->flags & PLUGIN_VAR_MEMALLOC)
3406     rc= plugin_var_memalloc_global_update(thd, plugin_var, (char **) tgt,
3407                                           *(const char **) src);
3408   else
3409     plugin_var->update(thd, plugin_var, tgt, src);
3410 
3411   return rc;
3412 }
3413 
3414 
3415 #define OPTION_SET_LIMITS(type, options, opt) \
3416   options->var_type= type; \
3417   options->def_value= (opt)->def_val; \
3418   options->min_value= (opt)->min_val; \
3419   options->max_value= (opt)->max_val; \
3420   options->block_size= (long) (opt)->blk_sz
3421 
3422 #define OPTION_SET_LIMITS_DOUBLE(options, opt) \
3423   options->var_type= GET_DOUBLE; \
3424   options->def_value= (longlong) getopt_double2ulonglong((opt)->def_val); \
3425   options->min_value= (longlong) getopt_double2ulonglong((opt)->min_val); \
3426   options->max_value= getopt_double2ulonglong((opt)->max_val); \
3427   options->block_size= (long) (opt)->blk_sz;
3428 
3429 
plugin_opt_set_limits(struct my_option * options,const struct st_mysql_sys_var * opt)3430 static void plugin_opt_set_limits(struct my_option *options,
3431                                   const struct st_mysql_sys_var *opt)
3432 {
3433   options->sub_size= 0;
3434 
3435   switch (opt->flags & (PLUGIN_VAR_TYPEMASK |
3436                         PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL)) {
3437   /* global system variables */
3438   case PLUGIN_VAR_INT:
3439     OPTION_SET_LIMITS(GET_INT, options, (sysvar_int_t*) opt);
3440     break;
3441   case PLUGIN_VAR_INT | PLUGIN_VAR_UNSIGNED:
3442     OPTION_SET_LIMITS(GET_UINT, options, (sysvar_uint_t*) opt);
3443     break;
3444   case PLUGIN_VAR_LONG:
3445     OPTION_SET_LIMITS(GET_LONG, options, (sysvar_long_t*) opt);
3446     break;
3447   case PLUGIN_VAR_LONG | PLUGIN_VAR_UNSIGNED:
3448     OPTION_SET_LIMITS(GET_ULONG, options, (sysvar_ulong_t*) opt);
3449     break;
3450   case PLUGIN_VAR_LONGLONG:
3451     OPTION_SET_LIMITS(GET_LL, options, (sysvar_longlong_t*) opt);
3452     break;
3453   case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_UNSIGNED:
3454     OPTION_SET_LIMITS(GET_ULL, options, (sysvar_ulonglong_t*) opt);
3455     break;
3456   case PLUGIN_VAR_ENUM:
3457     options->var_type= GET_ENUM;
3458     options->typelib= ((sysvar_enum_t*) opt)->typelib;
3459     options->def_value= ((sysvar_enum_t*) opt)->def_val;
3460     options->min_value= options->block_size= 0;
3461     options->max_value= options->typelib->count - 1;
3462     break;
3463   case PLUGIN_VAR_SET:
3464     options->var_type= GET_SET;
3465     options->typelib= ((sysvar_set_t*) opt)->typelib;
3466     options->def_value= ((sysvar_set_t*) opt)->def_val;
3467     options->min_value= options->block_size= 0;
3468     options->max_value= (1ULL << options->typelib->count) - 1;
3469     break;
3470   case PLUGIN_VAR_BOOL:
3471     options->var_type= GET_BOOL;
3472     options->def_value= ((sysvar_bool_t*) opt)->def_val;
3473     break;
3474   case PLUGIN_VAR_STR:
3475     options->var_type= ((opt->flags & PLUGIN_VAR_MEMALLOC) ?
3476                         GET_STR_ALLOC : GET_STR);
3477     options->def_value= (intptr) ((sysvar_str_t*) opt)->def_val;
3478     break;
3479   case PLUGIN_VAR_DOUBLE:
3480     OPTION_SET_LIMITS_DOUBLE(options, (sysvar_double_t*) opt);
3481     break;
3482   /* threadlocal variables */
3483   case PLUGIN_VAR_INT | PLUGIN_VAR_THDLOCAL:
3484     OPTION_SET_LIMITS(GET_INT, options, (thdvar_int_t*) opt);
3485     break;
3486   case PLUGIN_VAR_INT | PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL:
3487     OPTION_SET_LIMITS(GET_UINT, options, (thdvar_uint_t*) opt);
3488     break;
3489   case PLUGIN_VAR_LONG | PLUGIN_VAR_THDLOCAL:
3490     OPTION_SET_LIMITS(GET_LONG, options, (thdvar_long_t*) opt);
3491     break;
3492   case PLUGIN_VAR_LONG | PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL:
3493     OPTION_SET_LIMITS(GET_ULONG, options, (thdvar_ulong_t*) opt);
3494     break;
3495   case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_THDLOCAL:
3496     OPTION_SET_LIMITS(GET_LL, options, (thdvar_longlong_t*) opt);
3497     break;
3498   case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL:
3499     OPTION_SET_LIMITS(GET_ULL, options, (thdvar_ulonglong_t*) opt);
3500     break;
3501   case PLUGIN_VAR_DOUBLE | PLUGIN_VAR_THDLOCAL:
3502     OPTION_SET_LIMITS_DOUBLE(options, (thdvar_double_t*) opt);
3503     break;
3504   case PLUGIN_VAR_ENUM | PLUGIN_VAR_THDLOCAL:
3505     options->var_type= GET_ENUM;
3506     options->typelib= ((thdvar_enum_t*) opt)->typelib;
3507     options->def_value= ((thdvar_enum_t*) opt)->def_val;
3508     options->min_value= options->block_size= 0;
3509     options->max_value= options->typelib->count - 1;
3510     break;
3511   case PLUGIN_VAR_SET | PLUGIN_VAR_THDLOCAL:
3512     options->var_type= GET_SET;
3513     options->typelib= ((thdvar_set_t*) opt)->typelib;
3514     options->def_value= ((thdvar_set_t*) opt)->def_val;
3515     options->min_value= options->block_size= 0;
3516     options->max_value= (1ULL << options->typelib->count) - 1;
3517     break;
3518   case PLUGIN_VAR_BOOL | PLUGIN_VAR_THDLOCAL:
3519     options->var_type= GET_BOOL;
3520     options->def_value= ((thdvar_bool_t*) opt)->def_val;
3521     break;
3522   case PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL:
3523     options->var_type= ((opt->flags & PLUGIN_VAR_MEMALLOC) ?
3524                         GET_STR_ALLOC : GET_STR);
3525     options->def_value= (intptr) ((thdvar_str_t*) opt)->def_val;
3526     break;
3527   default:
3528     DBUG_ASSERT(0);
3529   }
3530   options->arg_type= REQUIRED_ARG;
3531   if (opt->flags & PLUGIN_VAR_NOCMDARG)
3532     options->arg_type= NO_ARG;
3533   if (opt->flags & PLUGIN_VAR_OPCMDARG)
3534     options->arg_type= OPT_ARG;
3535 }
3536 
3537 extern "C" my_bool get_one_plugin_option(int optid, const struct my_option *,
3538                                          char *);
3539 
get_one_plugin_option(int optid MY_ATTRIBUTE ((unused)),const struct my_option * opt,char * argument)3540 my_bool get_one_plugin_option(int optid MY_ATTRIBUTE((unused)),
3541                               const struct my_option *opt,
3542                               char *argument)
3543 {
3544   return 0;
3545 }
3546 
3547 
3548 /**
3549   Creates a set of my_option objects associated with a specified plugin-
3550   handle.
3551 
3552   @param mem_root Memory allocator to be used.
3553   @param tmp A pointer to a plugin handle
3554   @param[out] options A pointer to a pre-allocated static array
3555 
3556   The set is stored in the pre-allocated static array supplied to the function.
3557   The size of the array is calculated as (number_of_plugin_varaibles*2+3). The
3558   reason is that each option can have a prefix '--plugin-' in addtion to the
3559   shorter form '--&lt;plugin-name&gt;'. There is also space allocated for
3560   terminating NULL pointers.
3561 
3562   @return
3563     @retval -1 An error occurred
3564     @retval 0 Success
3565 */
3566 
construct_options(MEM_ROOT * mem_root,struct st_plugin_int * tmp,my_option * options)3567 static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp,
3568                              my_option *options)
3569 {
3570   const char *plugin_name= tmp->plugin->name;
3571   const LEX_STRING plugin_dash = { C_STRING_WITH_LEN("plugin-") };
3572   uint plugin_name_len= strlen(plugin_name);
3573   uint optnamelen;
3574   const int max_comment_len= 180;
3575   char *comment= (char *) alloc_root(mem_root, max_comment_len + 1);
3576   char *optname;
3577 
3578   int index= 0, offset= 0;
3579   st_mysql_sys_var *opt, **plugin_option;
3580   st_bookmark *v;
3581 
3582   /** Used to circumvent the const attribute on my_option::name */
3583   char *plugin_name_ptr, *plugin_name_with_prefix_ptr;
3584 
3585   DBUG_ENTER("construct_options");
3586 
3587   plugin_name_ptr= (char*) alloc_root(mem_root, plugin_name_len + 1);
3588   strcpy(plugin_name_ptr, plugin_name);
3589   my_casedn_str(&my_charset_latin1, plugin_name_ptr);
3590   convert_underscore_to_dash(plugin_name_ptr, plugin_name_len);
3591   plugin_name_with_prefix_ptr= (char*) alloc_root(mem_root,
3592                                                   plugin_name_len +
3593                                                   plugin_dash.length + 1);
3594   strxmov(plugin_name_with_prefix_ptr, plugin_dash.str, plugin_name_ptr, NullS);
3595 
3596   if (tmp->load_option != PLUGIN_FORCE &&
3597       tmp->load_option != PLUGIN_FORCE_PLUS_PERMANENT)
3598   {
3599     /* support --skip-plugin-foo syntax */
3600     options[0].name= plugin_name_ptr;
3601     options[1].name= plugin_name_with_prefix_ptr;
3602     options[0].id= 0;
3603     options[1].id= -1;
3604     options[0].var_type= options[1].var_type= GET_ENUM;
3605     options[0].arg_type= options[1].arg_type= OPT_ARG;
3606     options[0].def_value= options[1].def_value= 1; /* ON */
3607     options[0].typelib= options[1].typelib= &global_plugin_typelib;
3608 
3609     strxnmov(comment, max_comment_len, "Enable or disable ", plugin_name,
3610             " plugin. Possible values are ON, OFF, FORCE (don't start "
3611             "if the plugin fails to load).", NullS);
3612     options[0].comment= comment;
3613     /*
3614       Allocate temporary space for the value of the tristate.
3615       This option will have a limited lifetime and is not used beyond
3616       server initialization.
3617       GET_ENUM value is an unsigned long integer.
3618     */
3619     options[0].value= options[1].value=
3620                       (uchar **)alloc_root(mem_root, sizeof(ulong));
3621     *((ulong*) options[0].value)= (ulong) options[0].def_value;
3622 
3623     options+= 2;
3624   }
3625 
3626   if (!my_strcasecmp(&my_charset_latin1, plugin_name_ptr, "NDBCLUSTER"))
3627   {
3628     plugin_name_ptr= const_cast<char*>("ndb"); // Use legacy "ndb" prefix
3629     plugin_name_len= 3;
3630   }
3631 
3632   /*
3633     Two passes as the 2nd pass will take pointer addresses for use
3634     by my_getopt and register_var() in the first pass uses realloc
3635   */
3636 
3637   for (plugin_option= tmp->plugin->system_vars;
3638        plugin_option && *plugin_option; plugin_option++, index++)
3639   {
3640     opt= *plugin_option;
3641     if (!(opt->flags & PLUGIN_VAR_THDLOCAL))
3642       continue;
3643     if (!(register_var(plugin_name_ptr, opt->name, opt->flags)))
3644       continue;
3645     switch (opt->flags & PLUGIN_VAR_TYPEMASK) {
3646     case PLUGIN_VAR_BOOL:
3647       ((thdvar_bool_t *) opt)->resolve= mysql_sys_var_char;
3648       break;
3649     case PLUGIN_VAR_INT:
3650       ((thdvar_int_t *) opt)->resolve= mysql_sys_var_int;
3651       break;
3652     case PLUGIN_VAR_LONG:
3653       ((thdvar_long_t *) opt)->resolve= mysql_sys_var_long;
3654       break;
3655     case PLUGIN_VAR_LONGLONG:
3656       ((thdvar_longlong_t *) opt)->resolve= mysql_sys_var_longlong;
3657       break;
3658     case PLUGIN_VAR_STR:
3659       ((thdvar_str_t *) opt)->resolve= mysql_sys_var_str;
3660       break;
3661     case PLUGIN_VAR_ENUM:
3662       ((thdvar_enum_t *) opt)->resolve= mysql_sys_var_ulong;
3663       break;
3664     case PLUGIN_VAR_SET:
3665       ((thdvar_set_t *) opt)->resolve= mysql_sys_var_ulonglong;
3666       break;
3667     case PLUGIN_VAR_DOUBLE:
3668       ((thdvar_double_t *) opt)->resolve= mysql_sys_var_double;
3669       break;
3670     default:
3671       sql_print_error("Unknown variable type code 0x%x in plugin '%s'.",
3672                       opt->flags, plugin_name);
3673       DBUG_RETURN(-1);
3674     };
3675   }
3676 
3677   for (plugin_option= tmp->plugin->system_vars;
3678        plugin_option && *plugin_option; plugin_option++, index++)
3679   {
3680     switch ((opt= *plugin_option)->flags & PLUGIN_VAR_TYPEMASK) {
3681     case PLUGIN_VAR_BOOL:
3682       if (!opt->check)
3683         opt->check= check_func_bool;
3684       if (!opt->update)
3685         opt->update= update_func_bool;
3686       break;
3687     case PLUGIN_VAR_INT:
3688       if (!opt->check)
3689         opt->check= check_func_int;
3690       if (!opt->update)
3691         opt->update= update_func_int;
3692       break;
3693     case PLUGIN_VAR_LONG:
3694       if (!opt->check)
3695         opt->check= check_func_long;
3696       if (!opt->update)
3697         opt->update= update_func_long;
3698       break;
3699     case PLUGIN_VAR_LONGLONG:
3700       if (!opt->check)
3701         opt->check= check_func_longlong;
3702       if (!opt->update)
3703         opt->update= update_func_longlong;
3704       break;
3705     case PLUGIN_VAR_STR:
3706       if (!opt->check)
3707         opt->check= check_func_str;
3708       if (!opt->update)
3709       {
3710         opt->update= update_func_str;
3711         if (!(opt->flags & (PLUGIN_VAR_MEMALLOC | PLUGIN_VAR_READONLY)))
3712         {
3713           opt->flags|= PLUGIN_VAR_READONLY;
3714           sql_print_warning("Server variable %s of plugin %s was forced "
3715                             "to be read-only: string variable without "
3716                             "update_func and PLUGIN_VAR_MEMALLOC flag",
3717                             opt->name, plugin_name);
3718         }
3719       }
3720       break;
3721     case PLUGIN_VAR_ENUM:
3722       if (!opt->check)
3723         opt->check= check_func_enum;
3724       if (!opt->update)
3725         opt->update= update_func_long;
3726       break;
3727     case PLUGIN_VAR_SET:
3728       if (!opt->check)
3729         opt->check= check_func_set;
3730       if (!opt->update)
3731         opt->update= update_func_longlong;
3732       break;
3733     case PLUGIN_VAR_DOUBLE:
3734       if (!opt->check)
3735         opt->check= check_func_double;
3736       if (!opt->update)
3737         opt->update= update_func_double;
3738       break;
3739     default:
3740       sql_print_error("Unknown variable type code 0x%x in plugin '%s'.",
3741                       opt->flags, plugin_name);
3742       DBUG_RETURN(-1);
3743     }
3744 
3745     if ((opt->flags & (PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_THDLOCAL))
3746                     == PLUGIN_VAR_NOCMDOPT)
3747       continue;
3748 
3749     if (!opt->name)
3750     {
3751       sql_print_error("Missing variable name in plugin '%s'.",
3752                       plugin_name);
3753       DBUG_RETURN(-1);
3754     }
3755 
3756     if (!(opt->flags & PLUGIN_VAR_THDLOCAL))
3757     {
3758       optnamelen= strlen(opt->name);
3759       optname= (char*) alloc_root(mem_root, plugin_name_len + optnamelen + 2);
3760       strxmov(optname, plugin_name_ptr, "-", opt->name, NullS);
3761       optnamelen= plugin_name_len + optnamelen + 1;
3762     }
3763     else
3764     {
3765       /* this should not fail because register_var should create entry */
3766       if (!(v= find_bookmark(plugin_name_ptr, opt->name, opt->flags)))
3767       {
3768         sql_print_error("Thread local variable '%s' not allocated "
3769                         "in plugin '%s'.", opt->name, plugin_name);
3770         DBUG_RETURN(-1);
3771       }
3772 
3773       *(int*)(opt + 1)= offset= v->offset;
3774 
3775       if (opt->flags & PLUGIN_VAR_NOCMDOPT)
3776         continue;
3777 
3778       optname= (char*) memdup_root(mem_root, v->key + 1,
3779                                    (optnamelen= v->name_len) + 1);
3780     }
3781 
3782     convert_underscore_to_dash(optname, optnamelen);
3783 
3784     options->name= optname;
3785     options->comment= opt->comment;
3786     options->app_type= opt;
3787     options->id= 0;
3788 
3789     plugin_opt_set_limits(options, opt);
3790 
3791     if (opt->flags & PLUGIN_VAR_THDLOCAL)
3792       options->value= options->u_max_value= (uchar**)
3793         (global_system_variables.dynamic_variables_ptr + offset);
3794     else
3795       options->value= options->u_max_value= *(uchar***) (opt + 1);
3796 
3797     char *option_name_ptr;
3798     options[1]= options[0];
3799     options[1].id= -1;
3800     options[1].name= option_name_ptr= (char*) alloc_root(mem_root,
3801                                                         plugin_dash.length +
3802                                                         optnamelen + 1);
3803     options[1].comment= 0; /* Hidden from the help text */
3804     strxmov(option_name_ptr, plugin_dash.str, optname, NullS);
3805 
3806     options+= 2;
3807   }
3808 
3809   DBUG_RETURN(0);
3810 }
3811 
3812 
construct_help_options(MEM_ROOT * mem_root,struct st_plugin_int * p)3813 static my_option *construct_help_options(MEM_ROOT *mem_root,
3814                                          struct st_plugin_int *p)
3815 {
3816   st_mysql_sys_var **opt;
3817   my_option *opts;
3818   uint count= EXTRA_OPTIONS;
3819   DBUG_ENTER("construct_help_options");
3820 
3821   for (opt= p->plugin->system_vars; opt && *opt; opt++, count+= 2)
3822     ;
3823 
3824   if (!(opts= (my_option*) alloc_root(mem_root, sizeof(my_option) * count)))
3825     DBUG_RETURN(NULL);
3826 
3827   memset(opts, 0, sizeof(my_option) * count);
3828 
3829   /**
3830     some plugin variables (those that don't have PLUGIN_VAR_NOSYSVAR flag)
3831     have their names prefixed with the plugin name. Restore the names here
3832     to get the correct (not double-prefixed) help text.
3833     We won't need @@sysvars anymore and don't care about their proper names.
3834   */
3835   restore_pluginvar_names(p->system_vars);
3836 
3837   if (construct_options(mem_root, p, opts))
3838     DBUG_RETURN(NULL);
3839 
3840   DBUG_RETURN(opts);
3841 }
3842 
3843 
3844 /**
3845   Check option being used and raise deprecation warning if required.
3846 
3847   @param optid ID of the option that was passed through command line
3848   @param opt List of options
3849   @argument Status of the option : Enable or Disable
3850 
3851   A deprecation warning will be raised if --plugin-xxx type of option
3852   is used.
3853 
3854   @return Always returns success as purpose of the function is to raise
3855   warning only.
3856   @retval 0 Success
3857 */
3858 
check_if_option_is_deprecated(int optid,const struct my_option * opt,char * argument MY_ATTRIBUTE ((unused)))3859 static my_bool check_if_option_is_deprecated(int optid,
3860                                              const struct my_option *opt,
3861                                              char *argument MY_ATTRIBUTE((unused)))
3862 {
3863   if (optid == -1)
3864   {
3865     WARN_DEPRECATED(NULL, opt->name, (opt->name + strlen("plugin-")));
3866   }
3867   else if (!my_strcasecmp(&my_charset_latin1, opt->name, "innodb"))
3868   {
3869     sql_print_warning("The option innodb (skip-innodb) is deprecated and "
3870                       "will be removed in a future release");
3871   }
3872 
3873   return 0;
3874 }
3875 
3876 
3877 /**
3878   Create and register system variables supplied from the plugin and
3879   assigns initial values from corresponding command line arguments.
3880 
3881   @param tmp_root Temporary scratch space
3882   @param[out] plugin Internal plugin structure
3883   @param argc Number of command line arguments
3884   @param argv Command line argument vector
3885 
3886   The plugin will be updated with a policy on how to handle errors during
3887   initialization.
3888 
3889   @note Requires that a write-lock is held on LOCK_system_variables_hash
3890 
3891   @return How initialization of the plugin should be handled.
3892     @retval  0 Initialization should proceed.
3893     @retval  1 Plugin is disabled.
3894     @retval -1 An error has occurred.
3895 */
3896 
test_plugin_options(MEM_ROOT * tmp_root,struct st_plugin_int * tmp,int * argc,char ** argv)3897 static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
3898                                int *argc, char **argv)
3899 {
3900   struct sys_var_chain chain= { NULL, NULL };
3901   bool disable_plugin;
3902   enum_plugin_load_option plugin_load_option= tmp->load_option;
3903 
3904   MEM_ROOT *mem_root= alloc_root_inited(&tmp->mem_root) ?
3905                       &tmp->mem_root : &plugin_mem_root;
3906   st_mysql_sys_var **opt;
3907   my_option *opts= NULL;
3908   LEX_STRING plugin_name;
3909   char *varname;
3910   int error;
3911   sys_var *v MY_ATTRIBUTE((unused));
3912   struct st_bookmark *var;
3913   uint len, count= EXTRA_OPTIONS;
3914   DBUG_ENTER("test_plugin_options");
3915   DBUG_ASSERT(tmp->plugin && tmp->name.str);
3916 
3917   /*
3918     The 'federated' and 'ndbcluster' storage engines are always disabled by
3919     default.
3920   */
3921   if (!(my_strcasecmp(&my_charset_latin1, tmp->name.str, "federated") &&
3922       my_strcasecmp(&my_charset_latin1, tmp->name.str, "ndbcluster")))
3923     plugin_load_option= PLUGIN_OFF;
3924 
3925   for (opt= tmp->plugin->system_vars; opt && *opt; opt++)
3926     count+= 2; /* --{plugin}-{optname} and --plugin-{plugin}-{optname} */
3927 
3928   if (count > EXTRA_OPTIONS || (*argc > 1))
3929   {
3930     if (!(opts= (my_option*) alloc_root(tmp_root, sizeof(my_option) * count)))
3931     {
3932       sql_print_error("Out of memory for plugin '%s'.", tmp->name.str);
3933       DBUG_RETURN(-1);
3934     }
3935     memset(opts, 0, sizeof(my_option) * count);
3936 
3937     if (construct_options(tmp_root, tmp, opts))
3938     {
3939       sql_print_error("Bad options for plugin '%s'.", tmp->name.str);
3940       DBUG_RETURN(-1);
3941     }
3942 
3943     /*
3944       We adjust the default value to account for the hardcoded exceptions
3945       we have set for the federated and ndbcluster storage engines.
3946     */
3947     if (tmp->load_option != PLUGIN_FORCE &&
3948         tmp->load_option != PLUGIN_FORCE_PLUS_PERMANENT)
3949       opts[0].def_value= opts[1].def_value= plugin_load_option;
3950 
3951     error= handle_options(argc, &argv, opts, check_if_option_is_deprecated);
3952     (*argc)++; /* add back one for the program name */
3953 
3954     if (error)
3955     {
3956        sql_print_error("Parsing options for plugin '%s' failed.",
3957                        tmp->name.str);
3958        goto err;
3959     }
3960     /*
3961      Set plugin loading policy from option value. First element in the option
3962      list is always the <plugin name> option value.
3963     */
3964     if (tmp->load_option != PLUGIN_FORCE &&
3965         tmp->load_option != PLUGIN_FORCE_PLUS_PERMANENT)
3966       plugin_load_option= (enum_plugin_load_option) *(ulong*) opts[0].value;
3967   }
3968 
3969   disable_plugin= (plugin_load_option == PLUGIN_OFF);
3970   tmp->load_option= plugin_load_option;
3971 
3972   /*
3973     If the plugin is disabled it should not be initialized.
3974   */
3975   if (disable_plugin)
3976   {
3977     if (log_warnings)
3978       sql_print_information("Plugin '%s' is disabled.",
3979                             tmp->name.str);
3980     if (opts)
3981       my_cleanup_options(opts);
3982     DBUG_RETURN(1);
3983   }
3984 
3985   if (!my_strcasecmp(&my_charset_latin1, tmp->name.str, "NDBCLUSTER"))
3986   {
3987     plugin_name.str= const_cast<char*>("ndb"); // Use legacy "ndb" prefix
3988     plugin_name.length= 3;
3989   }
3990   else
3991     plugin_name= tmp->name;
3992 
3993   error= 1;
3994   for (opt= tmp->plugin->system_vars; opt && *opt; opt++)
3995   {
3996     st_mysql_sys_var *o;
3997     if (((o= *opt)->flags & PLUGIN_VAR_NOSYSVAR))
3998       continue;
3999     if ((var= find_bookmark(plugin_name.str, o->name, o->flags)))
4000       v= new (mem_root) sys_var_pluginvar(&chain, var->key + 1, o);
4001     else
4002     {
4003       len= plugin_name.length + strlen(o->name) + 2;
4004       varname= (char*) alloc_root(mem_root, len);
4005       strxmov(varname, plugin_name.str, "-", o->name, NullS);
4006       my_casedn_str(&my_charset_latin1, varname);
4007       convert_dash_to_underscore(varname, len-1);
4008       v= new (mem_root) sys_var_pluginvar(&chain, varname, o);
4009     }
4010     DBUG_ASSERT(v); /* check that an object was actually constructed */
4011   } /* end for */
4012   if (chain.first)
4013   {
4014     chain.last->next = NULL;
4015     if (mysql_add_sys_var_chain(chain.first))
4016     {
4017       sql_print_error("Plugin '%s' has conflicting system variables",
4018                       tmp->name.str);
4019       goto err;
4020     }
4021     tmp->system_vars= chain.first;
4022   }
4023   DBUG_RETURN(0);
4024 
4025 err:
4026   if (opts)
4027     my_cleanup_options(opts);
4028   DBUG_RETURN(error);
4029 }
4030 
4031 
4032 /****************************************************************************
4033   Help Verbose text with Plugin System Variables
4034 ****************************************************************************/
4035 
4036 
add_plugin_options(std::vector<my_option> * options,MEM_ROOT * mem_root)4037 void add_plugin_options(std::vector<my_option> *options, MEM_ROOT *mem_root)
4038 {
4039   struct st_plugin_int *p;
4040   my_option *opt;
4041 
4042   if (!initialized)
4043     return;
4044 
4045   for (uint idx= 0; idx < plugin_array.elements; idx++)
4046   {
4047     p= *dynamic_element(&plugin_array, idx, struct st_plugin_int **);
4048 
4049     if (!(opt= construct_help_options(mem_root, p)))
4050       continue;
4051 
4052     /* Only options with a non-NULL comment are displayed in help text */
4053     for (;opt->name; opt++)
4054       if (opt->comment)
4055         options->push_back(*opt);
4056   }
4057 }
4058 
4059 /**
4060   Searches for a correctly loaded plugin of a particular type by name
4061 
4062   @param plugin   the name of the plugin we're looking for
4063   @param type     type of the plugin (0-MYSQL_MAX_PLUGIN_TYPE_NUM)
4064   @return plugin, or NULL if not found
4065 */
plugin_find_by_type(LEX_STRING * plugin,int type)4066 struct st_plugin_int *plugin_find_by_type(LEX_STRING *plugin, int type)
4067 {
4068   st_plugin_int *ret;
4069   DBUG_ENTER("plugin_find_by_type");
4070 
4071   ret= plugin_find_internal(plugin, type);
4072   DBUG_RETURN(ret && ret->state == PLUGIN_IS_READY ? ret : NULL);
4073 }
4074 
4075 
4076 /**
4077   Locks the plugin strucutres so calls to plugin_find_inner can be issued.
4078 
4079   Must be followed by unlock_plugin_data.
4080 */
lock_plugin_data()4081 int lock_plugin_data()
4082 {
4083   DBUG_ENTER("lock_plugin_data");
4084   DBUG_RETURN(mysql_mutex_lock(&LOCK_plugin));
4085 }
4086 
4087 
4088 /**
4089   Unlocks the plugin strucutres as locked by lock_plugin_data()
4090 */
unlock_plugin_data()4091 int unlock_plugin_data()
4092 {
4093   DBUG_ENTER("unlock_plugin_data");
4094   DBUG_RETURN(mysql_mutex_unlock(&LOCK_plugin));
4095 }
4096 
4097 /**
4098   Create deep copy of system_variables instance.
4099 */
4100 struct system_variables *
copy_system_variables(THD * thd,bool enable_plugins)4101 copy_system_variables(THD *thd, bool enable_plugins)
4102 {
4103   struct system_variables *dst;
4104   ulong idx;
4105 
4106   DBUG_ASSERT(thd);
4107 
4108   dst= (struct system_variables *)
4109     my_malloc(sizeof(struct system_variables), MYF(MY_WME | MY_FAE));
4110   *dst = thd->variables;
4111 
4112   if (dst->dynamic_variables_ptr)
4113   {
4114     dst->dynamic_variables_ptr=
4115       (char *)my_malloc(dst->dynamic_variables_size, MYF(MY_WME | MY_FAE));
4116     memcpy(dst->dynamic_variables_ptr,
4117            thd->variables.dynamic_variables_ptr,
4118            thd->variables.dynamic_variables_size);
4119   }
4120 
4121   dst->dynamic_variables_allocs= thd->variables.dynamic_variables_allocs;
4122   thd->variables.dynamic_variables_allocs= NULL;
4123 
4124   mysql_rwlock_rdlock(&LOCK_system_variables_hash);
4125 
4126   /*
4127     Iterate through newly copied vars of string type with MEMALLOC
4128     flag and strdup value.
4129   */
4130   for (idx= 0; idx < malloced_string_type_sysvars_bookmark_hash.records; idx++)
4131   {
4132     char **var;
4133     st_bookmark *v=
4134       (st_bookmark*)my_hash_element(&malloced_string_type_sysvars_bookmark_hash,
4135                                     idx);
4136 
4137     if (thd->variables.dynamic_variables_ptr == NULL ||
4138         (uint) v->offset > thd->variables.dynamic_variables_head)
4139       continue;
4140 
4141     var= (char **) (thd->variables.dynamic_variables_ptr + v->offset);
4142 
4143     if (*var)
4144     {
4145       size_t length= strlen(*var) + 1;
4146       LIST *element;
4147       element= (LIST *) my_malloc(sizeof(LIST) + length, MYF(MY_FAE));
4148       memcpy(element + 1, *var, length);
4149       *var= (char *) (element + 1);
4150       thd->variables.dynamic_variables_allocs=
4151         list_add(thd->variables.dynamic_variables_allocs, element);
4152     }
4153   }
4154 
4155   mysql_rwlock_unlock(&LOCK_system_variables_hash);
4156 
4157   if (enable_plugins)
4158   {
4159     mysql_mutex_lock(&LOCK_plugin);
4160     dst->table_plugin=
4161       my_intern_plugin_lock(NULL, thd->variables.table_plugin);
4162     dst->temp_table_plugin=
4163       my_intern_plugin_lock(NULL, thd->variables.temp_table_plugin);
4164     mysql_mutex_unlock(&LOCK_plugin);
4165   }
4166 
4167   return dst;
4168 }
4169 
free_system_variables(struct system_variables * v,bool enable_plugins)4170 void free_system_variables(struct system_variables *v, bool enable_plugins)
4171 {
4172   DBUG_ASSERT(v);
4173 
4174   if (enable_plugins)
4175   {
4176     mysql_mutex_lock(&LOCK_plugin);
4177     intern_plugin_unlock(NULL, v->table_plugin);
4178     intern_plugin_unlock(NULL, v->temp_table_plugin);
4179     mysql_mutex_unlock(&LOCK_plugin);
4180   }
4181 
4182   plugin_var_memalloc_free(v);
4183 
4184   my_free(v->dynamic_variables_ptr);
4185 }
4186