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