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