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, ¬_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 '--<plugin-name>'. 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