#At file:///Users/cbell/source/bzr/mysql-wl-5710/ based on revid:sven.sandberg@strippedxw4a12u
3409 chuck.bell@stripped 2011-05-26
WL#5710 : mysql_plugin - enable or disable plugins
This patch adds a new client utility that enables or disables plugin
features. Currently, the client can enable or disable the thread_pool
and daemon_example plugins by default but the user can specify new plugins
via a configuration file.
added:
client/mysql_plugin.c
mysql-test/r/mysql_plugin.result
mysql-test/t/mysql_plugin-master.opt
mysql-test/t/mysql_plugin.test
modified:
client/CMakeLists.txt
include/my_global.h
mysql-test/mysql-test-run.pl
=== modified file 'client/CMakeLists.txt'
--- a/client/CMakeLists.txt 2010-09-20 14:17:32 +0000
+++ b/client/CMakeLists.txt 2011-05-26 19:25:47 +0000
@@ -54,6 +54,9 @@ ADD_DEPENDENCIES(mysql_upgrade GenFixPri
MYSQL_ADD_EXECUTABLE(mysqlshow mysqlshow.c)
TARGET_LINK_LIBRARIES(mysqlshow mysqlclient)
+MYSQL_ADD_EXECUTABLE(mysql_plugin mysql_plugin.c)
+TARGET_LINK_LIBRARIES(mysql_plugin mysqlclient)
+
MYSQL_ADD_EXECUTABLE(mysqlbinlog mysqlbinlog.cc)
TARGET_LINK_LIBRARIES(mysqlbinlog mysqlclient)
@@ -69,7 +72,7 @@ IF(WIN32)
MYSQL_ADD_EXECUTABLE(echo echo.c)
ENDIF(WIN32)
-SET_TARGET_PROPERTIES (mysqlcheck mysqldump mysqlimport mysql_upgrade mysqlshow mysqlslap
+SET_TARGET_PROPERTIES (mysqlcheck mysqldump mysqlimport mysql_upgrade mysqlshow mysqlslap mysql_plugin
PROPERTIES HAS_CXX TRUE)
ADD_DEFINITIONS(-DHAVE_DLOPEN)
=== added file 'client/mysql_plugin.c'
--- a/client/mysql_plugin.c 1970-01-01 00:00:00 +0000
+++ b/client/mysql_plugin.c 2011-05-26 19:25:47 +0000
@@ -0,0 +1,963 @@
+/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <m_string.h>
+#include <mysql.h>
+#include <my_getopt.h>
+#include <sys/stat.h>
+#include <my_global.h>
+#include <stdio.h>
+#include <string.h>
+
+
+#define SHOW_VERSION "1.0.0"
+#define PRINT_VERSION do { printf("%s Ver %s Distrib %s\n", \
+ my_progname, SHOW_VERSION, MYSQL_SERVER_VERSION); \
+ } while(0)
+
+/* Global variables. */
+static uint my_end_arg= 0;
+static uint opt_verbose=0;
+static uint opt_no_defaults= 0;
+static uint opt_print_defaults= 0;
+static char *opt_datadir=0, *opt_basedir=0,
+ *opt_plugin_dir=0, *opt_plugin_ini=0;
+static char bootstrap[FN_REFLEN];
+
+
+/* plugin struct */
+struct st_plugin
+{
+ const char *name; /* plugin name */
+ const char *so_name; /* plugin so (library) name */
+ const char *symbols[16]; /* symbols to load */
+};
+
+
+struct st_plugin *plugins_supported[256];
+int num_plugins;
+
+/*
+ plugins (plugins) supported.
+
+ NOTE: To add a new plugin, add rows to the #define below to define
+ and add a new element to this array. The code is designed to act
+ on these entries to insert/delete the correct values in the
+ mysql.plugin table.
+*/
+static struct st_plugin default_plugins[]= {
+ {
+ "thread_pool", "thread_pool" FN_SOEXT,
+ {
+ "thread_pool",
+ "TP_THREAD_STATE",
+ "TP_THREAD_GROUP_STATE",
+ "TP_THREAD_GROUP_STATS",
+ NULL
+ }
+ },
+ {
+ "daemon_example", "libdaemon_example" FN_SOEXT,
+ {
+ "daemon_example",
+ NULL
+ }
+ }
+};
+
+
+/* Options */
+static struct my_option my_long_options[] =
+{
+ {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG,
+ 0, 0, 0, 0, 0, 0},
+ {"basedir", 'b', "The basedir for the server.",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"datadir", 'd', "The datadir for the server.",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"plugin-dir", 'p', "The plugin dir for the server.",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"plugin-ini", 'i', "Read plugin information from configuration file "
+ "specified instead of from <plugin_dir>/plugin.ini.",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"no-defaults", 'n', "Do not read values from configuration file.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"print-defaults", 'P', "Show default values from configuration file.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"verbose", 'v',
+ "More verbose output; you can use this multiple times to get even more "
+ "verbose output.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+
+/* Methods */
+static my_bool
+get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
+ char *argument);
+static int check_options(int argc, char **argv, char *operation,
+ struct st_plugin **plugin);
+static int find_tool(const char *tool_name, char *tool_path);
+static int find_plugin(struct st_plugin *plugin, char *tp_path);
+static int run_command(char* cmd, char *mode);
+static int make_tempfile(char *filename, char *ext);
+static int get_default_values();
+static int load_plugin_data();
+
+
+int main(int argc,char *argv[])
+{
+ int i= 0;
+ int error= 0;
+ int ret= 0;
+ FILE *file;
+ char tp_path[FN_REFLEN];
+ char server_path[FN_REFLEN];
+ char query_str[512];
+ char operation[16];
+ char bootstrap_cmd[FN_REFLEN];
+ struct st_plugin *plugin= 0;
+
+ bzero(plugins_supported, 256);
+ MY_INIT(argv[0]);
+
+ /* Process options */
+ if ((handle_options(&argc, &argv, my_long_options, get_one_option)))
+ {
+ error= 1;
+ goto exit;
+ }
+
+ if (opt_print_defaults)
+ {
+ goto exit;
+ }
+
+ if (!opt_no_defaults && ((error= get_default_values())))
+ {
+ goto exit;
+ }
+
+ strcpy(operation, "");
+ if ((error = check_options(argc, argv, operation, &plugin)))
+ {
+ goto exit;
+ }
+ if (!plugin)
+ {
+ fprintf(stderr, "ERROR: No plugin specified or plugin does not "
+ "match information in plugin.ini file.\n");
+ error= 1;
+ goto exit;
+ }
+
+ i= (int)strlength(opt_basedir);
+ if (opt_basedir[i-1] != FN_LIBCHAR || opt_basedir[i-1] != FN_LIBCHAR2)
+ {
+ strcat(opt_basedir, FN_DIRSEP);
+ }
+
+ if (opt_verbose)
+ {
+ printf("# basedir = %s\n", opt_basedir);
+ printf("# plugin_dir = %s\n", opt_plugin_dir);
+ printf("# datadir = %s\n", opt_datadir);
+ printf("# plugin_ini = %s\n", opt_plugin_ini);
+ }
+
+ /* Attempt to access these directories. Error if access denied. */
+ if ((error= my_access(opt_basedir, F_OK)))
+ {
+ fprintf(stderr, "ERROR: Cannot access basedir at '%s'.\n",
+ opt_basedir);
+ error= 1;
+ goto exit;
+ }
+ if ((error= my_access(opt_plugin_dir, F_OK)))
+ {
+ fprintf(stderr, "ERROR: Cannot access plugin_dir at '%s'.\n",
+ opt_plugin_dir);
+ error= 1;
+ goto exit;
+ }
+ if ((error= my_access(opt_datadir, F_OK)))
+ {
+ fprintf(stderr, "ERROR: Cannot access datadir at '%s'.\n",
+ opt_datadir);
+ error= 1;
+ goto exit;
+ }
+ if ((error= my_access(opt_plugin_ini, F_OK)))
+ {
+ fprintf(stderr, "ERROR: Cannot access plugin_ini at '%s'.\n",
+ opt_plugin_ini);
+ error= 1;
+ goto exit;
+ }
+
+ /* Look for the utilities we need: mysqladmin, mysqld, and mysql. */
+ if (!find_tool("mysqld" FN_EXEEXT, server_path))
+ {
+ fprintf(stderr, "ERROR: Cannot find dependent tools. Ensure basedir is "
+ "set correctly.\n");
+ error= 1;
+ goto exit;
+ }
+
+ /* Look for the plugin. Abort if not found. */
+ if ((error= find_plugin(plugin, tp_path)))
+ {
+ fprintf(stderr, "ERROR: The plugin library %s is missing or in a different"
+ " location.\n", tp_path);
+ error= 1;
+ goto exit;
+ }
+
+ /* Create bootstrap file using a temporary file name */
+ if (!make_tempfile(bootstrap, "sql"))
+ {
+ error= 1;
+ goto exit;
+ }
+ file= fopen(bootstrap, "w+");
+ if (strcasecmp(operation, "enable") == 0)
+ {
+ /* Here we must do a 'safe' enable in case plugin is already enabled. */
+ int i= 0;
+ fprintf(file, "INSERT IGNORE INTO mysql.plugin VALUES ");
+ for (i= 0; i < array_elements(plugin->symbols); i++)
+ {
+ if (plugin->symbols[i] == NULL)
+ {
+ break;
+ }
+ if (i > 0)
+ {
+ fprintf(file, ", ");
+ }
+ fprintf(file, "('%s','%s')", plugin->symbols[i], plugin->so_name);
+ }
+ fprintf(file, ";\n");
+ if (opt_verbose)
+ {
+ printf("# Enabling %s...\n", plugin->name);
+ }
+ }
+ else
+ {
+ fprintf(file,
+ "DELETE FROM mysql.plugin WHERE name = '%s';", plugin->name);
+ {
+ printf("# Disabling %s...\n", plugin->name);
+ }
+ }
+ fclose(file);
+ if (opt_verbose)
+ {
+ char *ret= 0;
+ file = fopen(bootstrap, "r");
+ ret= fgets(query_str, 512, file);
+ if (ret == 0)
+ {
+ fprintf(stderr, "ERROR: Cannot read bootstrap file\n");
+ error= 1;
+ goto exit;
+ }
+ printf("# Query: %s", query_str);
+ fclose(file);
+ }
+
+ /* Build bootstrap command. */
+ snprintf(bootstrap_cmd, sizeof(bootstrap_cmd),
+ "%s --no-defaults --bootstrap --datadir=%s --basedir=%s"
+ " < %s", server_path, opt_datadir, opt_basedir, bootstrap);
+
+ /* Execute the command */
+ if (opt_verbose > 1)
+ {
+ printf("# Command: %s\n", bootstrap_cmd);
+ }
+ ret= run_command(bootstrap_cmd, "r");
+ if (ret != 0)
+ {
+ fprintf(stderr,
+ "ERROR: Unexpected result from bootstrap. Error code: %d.\n",
+ ret);
+ error= 1;
+ }
+
+ /* Remove file */
+ my_delete(bootstrap, MYF(0));
+ if (opt_verbose && error == 0)
+ {
+ printf("# Operation succeeded.\n");
+ }
+
+exit:
+ my_end(my_end_arg);
+ exit(error ? 1 : 0);
+ return 0; /* No compiler warnings */
+}
+
+
+/**
+ Get a temporary file name.
+
+ @param[out] filename The file name of the temporary file
+ @param[in] ext An extension for the file (optional)
+
+ @retval int error = 1, success = 0
+*/
+static int make_tempfile(char *filename, char *ext)
+{
+ int fd= 0;
+
+ if ((fd=create_temp_file(filename, NullS, ext, O_CREAT | O_WRONLY,
+ MYF(MY_WME))) < 0)
+ {
+ fprintf(stderr, "ERROR: Cannot generate temporary file. Error code: %d.\n",
+ fd);
+ return 0;
+ }
+ my_close(fd, MYF(0));
+ return 1;
+}
+
+
+/**
+ Get the value of an option from a string read from my_print_defaults output.
+
+ @param[in] line The line (string) read from the file
+ @param[in] item The option to search for (e.g. --datadir)
+
+ @returns NULL if not found, string containing value if found
+*/
+static char *get_value(char *line, char *item)
+{
+ char *destination= 0;
+ int item_len= (int)strlen(item);
+ int line_len = (int)strlen(line);
+
+ if ((strncasecmp(line, item, item_len) == 0))
+ {
+ int start= 0;
+ char *s= 0;
+
+ s = line + item_len + 1;
+ destination= my_strndup(s, line_len - start, MYF(MY_FAE));
+ destination[line_len - item_len - 2]= 0;
+ }
+ return destination;
+}
+
+/**
+ Get the default values from the my.cnf file.
+
+ @retval int error = 1, success = 0
+*/
+static int get_default_values()
+{
+ char tool_path[FN_REFLEN];
+ char defaults_cmd[FN_REFLEN];
+ char defaults_file[FN_REFLEN];
+ char line[FN_REFLEN];
+ int error= 0;
+ int ret= 0;
+ FILE *file;
+
+ if (!find_tool("my_print_defaults" FN_EXEEXT, tool_path))
+ {
+ fprintf(stderr, "WARNING: Cannot find my_print_defaults.\n");
+ }
+ else
+ {
+ if (!make_tempfile(defaults_file, "txt"))
+ {
+ error= 1;
+ goto exit;
+ }
+ snprintf(defaults_cmd, sizeof(defaults_cmd),
+ "%s mysqld > %s", tool_path, defaults_file);
+
+ /* Execute the command */
+ if (opt_verbose > 1)
+ {
+ printf("# Command: %s\n", defaults_cmd);
+ }
+ ret= run_command(defaults_cmd, "r");
+ if (ret != 0)
+ {
+ fprintf(stderr, "ERROR: my_print_defaults failed. Error code: %d.\n",
+ ret);
+ error= 1;
+ goto exit;
+ }
+ /* Now open the file and read the defaults we want. */
+ file= fopen(defaults_file, "r");
+ while (fgets(line, FN_REFLEN, file) != NULL)
+ {
+ char *value= 0;
+
+ if ((opt_datadir == 0) && ((value= get_value(line, "--datadir"))))
+ {
+ opt_datadir= my_strdup(value, MYF(MY_FAE));
+ }
+ if ((opt_basedir == 0) && ((value= get_value(line, "--basedir"))))
+ {
+ opt_basedir= my_strdup(value, MYF(MY_FAE));
+ }
+ if ((opt_plugin_dir == 0) && ((value= get_value(line, "--plugin_dir"))))
+ {
+ opt_plugin_dir= my_strdup(value, MYF(MY_FAE));
+ }
+ if ((opt_plugin_ini == 0) && ((value= get_value(line, "--plugin_ini"))))
+ {
+ opt_plugin_ini= my_strdup(value, MYF(MY_FAE));
+ }
+ }
+ fclose(file);
+ /* Remove file */
+ my_delete(defaults_file, MYF(0));
+ }
+exit:
+ return error;
+}
+
+/**
+ Print usage.
+*/
+static void usage(void)
+{
+ int i= 0;
+
+ PRINT_VERSION;
+ puts("Copyright (c) 2011, Oracle and/or its affiliates. "
+ "All rights reserved.\n");
+ puts("Enable or disable plugins.");
+ if (opt_plugin_dir)
+ {
+ /* load plugin data from plugin.ini file */
+ load_plugin_data();
+ }
+ else
+ {
+ /* load defaults */
+ for (i= 0; i < array_elements(default_plugins); i++)
+ {
+ plugins_supported[i]= &default_plugins[i];
+ num_plugins= i + 1;
+ }
+ }
+ puts("\nPlugins supported:");
+ for (i= 0; i < num_plugins; i++)
+ {
+ printf(" %s\n", plugins_supported[i]->name);
+ }
+ printf("\nUsage: %s [options] <plugin> ENABLE|DISABLE\n\nOptions:\n",
+ my_progname);
+ my_print_help(my_long_options);
+ puts("\n");
+}
+
+
+/**
+ Print the default values as read from the my.cnf file.
+*/
+static void print_default_values(void)
+{
+ printf("%s would have been started with the following arguments:\n",
+ my_progname);
+ get_default_values();
+ if (opt_datadir)
+ {
+ printf("--datadir=%s ", opt_datadir);
+ }
+ if (opt_basedir)
+ {
+ printf("--basedir=%s ", opt_basedir);
+ }
+ if (opt_plugin_dir)
+ {
+ printf("--plugin_dir=%s ", opt_plugin_dir);
+ }
+ printf("\n");
+}
+
+/**
+ Process the arguments and identify an option and store its value.
+
+ @param[in] optid The single character shortcut for the argument.
+ @param[in] my_option Structure of legal options.
+ @param[in] argument The argument value to process.
+*/
+static my_bool
+get_one_option(int optid,
+ const struct my_option *opt __attribute__((unused)),
+ char *argument)
+{
+ switch(optid) {
+ case 'n':
+ opt_no_defaults++;
+ break;
+ case 'P':
+ opt_print_defaults++;
+ print_default_values();
+ break;
+ case 'v':
+ opt_verbose++;
+ break;
+ case 'V':
+ PRINT_VERSION;
+ exit(0);
+ break;
+ case '?':
+ case 'I': /* Info */
+ usage();
+ exit(0);
+ case 'd':
+ opt_datadir= my_strdup(argument, MYF(MY_FAE));
+ break;
+ case 'b':
+ opt_basedir= my_strdup(argument, MYF(MY_FAE));
+ break;
+ case 'p':
+ opt_plugin_dir= my_strdup(argument, MYF(MY_FAE));
+ break;
+ case 'i':
+ opt_plugin_ini= my_strdup(argument, MYF(MY_FAE));
+ break;
+ }
+ return 0;
+}
+
+
+/**
+ Run a command in a shell.
+
+ This function will attempt to execute the command specified.
+
+ @param[in] cmd The command to execute.
+ @param[in] mode The mode for popen() (e.g. "r", "w", "rw")
+
+ @return int error code or 0 for success.
+*/
+static int run_command(char* cmd, char *mode)
+{
+ char buf[512]= {0};
+ FILE *res_file;
+ int error;
+
+ if (!(res_file= popen(cmd, mode)))
+ return -1;
+
+ if (opt_verbose)
+ {
+ while (fgets(buf, sizeof(buf), res_file))
+ {
+ fprintf(stdout, "%s", buf);
+ }
+ }
+ error= pclose(res_file);
+ return error;
+}
+
+
+/**
+ Check to see if a file exists.
+
+ @param[in] filename File to locate.
+
+ @retval int file not found = 1, file found = 0
+*/
+static int file_exists (char * filename)
+{
+ struct stat buf;
+ int i = stat (filename, &buf);
+ /* File found */
+ if (i == 0)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+
+/**
+ Search a specific path and sub directory for a file name.
+
+ @param[in] base_path Original path to use.
+ @param[in] tool_name Name of the tool to locate.
+ @param[in] subdir The sub directory to search.
+ @param[out] tool_path If tool found, return complete path.
+
+ @retval int error = 1, success = 0
+*/
+static int search_dir(char * base_path, const char *tool_name,
+ const char *subdir, char *tool_path)
+{
+ char new_path[FN_REFLEN];
+
+ strcpy(new_path, base_path);
+ strcat(new_path, subdir);
+ fn_format(new_path, new_path, "", tool_name, MY_UNPACK_FILENAME);
+ if (file_exists(new_path))
+ {
+ strcpy(tool_path, new_path);
+ return 1;
+ }
+ return 0;
+}
+
+
+/**
+ Search known common paths and sub directories for a file name.
+
+ @param[in] base_path Original path to use.
+ @param[in] tool_name Name of the tool to locate.
+ @param[out] tool_path If tool found, return complete path.
+
+ @retval int error = 1, success = 0
+*/
+static int search_paths(char *base_path, const char *tool_name,
+ char *tool_path)
+{
+ int i= 0;
+
+ static const char *paths[]= {
+ "", "/share/", "/scripts/", "/bin/", "/sbin/", "/libexec/",
+ "/mysql/", "/sql/",
+ };
+ for (i = 0 ; i < array_elements(paths); i++)
+ {
+ if (search_dir(base_path, tool_name, paths[i], tool_path))
+ return 1;
+ }
+ return 0;
+}
+
+
+/**
+ Locate the tool and form tool path.
+
+ @param[in] tool_name Name of the tool to locate.
+ @param[out] tool_path If tool found, return complete path.
+
+ @retval int error = 1, success = 0
+*/
+static int find_tool(const char *tool_name, char *tool_path)
+{
+ int i= 0;
+
+ char *paths[]= {
+ opt_basedir, "/usr", "/usr/local/mysql", "/usr/sbin", "/usr/share",
+ };
+ for (i= 0; i < array_elements(paths); i++)
+ {
+ if (paths[i] && (search_paths(paths[i], tool_name, tool_path)))
+ goto found;
+ }
+ return 0;
+found:
+ if (opt_verbose)
+ printf("# Found tool '%s' as '%s'.\n", tool_name, tool_path);
+ return 1;
+}
+
+
+/**
+ Check the options for validity.
+
+ This function checks the arguments for validity issuing the appropriate
+ error message if arguments are missing or invalid. On success, @operation
+ is set to either "ENABLE" or "DISABLE".
+
+ @param[in] argc The number of arguments.
+ @param[in] argv The arguments.
+ @param[out] operation The operation chosen (enable|disable)
+ @param[out] plugin The plugin selected
+
+ @retval int error = 1, success = 0
+*/
+static int check_options(int argc, char **argv, char *operation,
+ struct st_plugin **plugin)
+{
+ int i= 0; // loop counter
+ int j= 0;
+ int num_found= 0; // number of options found (shortcut loop)
+
+ for (i = 0; i < argc && num_found < 5; i++)
+ {
+ if (!argv[i])
+ {
+ continue;
+ }
+ if ((strcasecmp(argv[i], "ENABLE") == 0) ||
+ (strcasecmp(argv[i], "DISABLE") == 0))
+ {
+ strcpy(operation, argv[i]);
+ num_found++;
+ }
+ else if ((strncasecmp(argv[i], "--basedir=", 10) == 0) &&
+ !opt_basedir)
+ {
+ opt_basedir= my_strndup(argv[i]+10, strlen(argv[i])-10, MYF(MY_FAE));
+ num_found++;
+ }
+ else if ((strncasecmp(argv[i], "--datadir=", 10) == 0) &&
+ !opt_datadir)
+ {
+ opt_datadir= my_strndup(argv[i]+10, strlen(argv[i])-10, MYF(MY_FAE));
+ num_found++;
+ }
+ else if ((strncasecmp(argv[i], "--plugin-dir=", 13) == 0) &&
+ !opt_plugin_dir)
+ {
+ opt_plugin_dir= my_strndup(argv[i]+13, strlen(argv[i])-13, MYF(MY_FAE));
+ num_found++;
+ }
+ else
+ {
+ if (load_plugin_data())
+ {
+ return 1;
+ }
+ for (j= 0; j < num_plugins; j++)
+ {
+ if (strcasecmp(plugins_supported[j]->name, argv[i]) == 0)
+ {
+ *plugin = plugins_supported[j];
+ num_found++;
+ }
+ }
+ }
+ }
+
+ if (!opt_basedir)
+ {
+ fprintf(stderr, "ERROR: Missing --basedir option.\n");
+ return 1;
+ }
+
+ if (!opt_datadir)
+ {
+ fprintf(stderr, "ERROR: Missing --datadir option.\n");
+ return 1;
+ }
+
+ if (!opt_plugin_dir)
+ {
+ fprintf(stderr, "ERROR: Missing --plugin_dir option.\n");
+ return 1;
+ }
+
+ if ((strlen(operation) == 0))
+ {
+ fprintf(stderr, "ERROR: missing operation. Please specify either "
+ "'<plugin> ENABLE' or '<plugin> DISABLE'.\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ Find the plugin library.
+
+ This function attempts to use the @c plugin_dir option passed on the
+ command line to locate the plugin.
+
+ @param[in] plugin The plugin to find.
+ @param[out] tp_path The actual path to plugin with FN_SOEXT applied.
+
+ @retval st_plugin error = NULL, success = st_plugin data
+*/
+static int find_plugin(struct st_plugin *plugin, char *tp_path)
+{
+ /* Check for existance of plugin */
+ fn_format(tp_path, plugin->so_name, opt_plugin_dir, "", MYF(0));
+ if (!file_exists(tp_path))
+ {
+ return 1;
+ }
+ else if (opt_verbose)
+ {
+ printf("# Found plugin '%s' as '%s'\n", plugin->name, tp_path);
+ }
+ return 0;
+}
+
+
+/**
+ Read a plugin data element
+
+ This method takes as input a line from the plugin.ini file and splits it
+ into the st_plugin structure.
+
+ @retval int error = 1, success = 0
+*/
+void *read_plugin_data(char *line)
+{
+ const char delimiters[]= " ,";
+ char *token, *cp;
+ struct st_plugin *plugin_data;
+ int i= 0;
+
+ plugin_data= my_malloc(sizeof(struct st_plugin), MYF(MY_ZEROFILL));
+ cp= my_strdup(line, MYF(MY_FAE));
+ token= strtok (cp, delimiters);
+ if (token != NULL)
+ {
+ // read name
+ plugin_data->name= my_strdup(token, MYF(MY_WME));
+ // read so_name
+ token = strtok(NULL, delimiters);
+ if (token == NULL)
+ {
+ goto error;
+ }
+ plugin_data->so_name= my_strdup(token, MYF(MY_WME));
+ // read symbols
+ while (token != NULL)
+ {
+ token= strtok (NULL, delimiters);
+ if ((token != NULL) && (token[0] != '\n'))
+ {
+ plugin_data->symbols[i]= my_strdup(token, MYF(MY_WME));
+ i++;
+ }
+ else
+ {
+ plugin_data->symbols[i]= NULL;
+ }
+ }
+ }
+ else
+ {
+ goto error;
+ }
+ return plugin_data;
+
+error:
+ my_free(plugin_data);
+ return NULL;
+}
+
+
+/**
+ Read the plugin ini file.
+
+ This function attempts to read the plugin.ini file from the plugin_dir
+ path. If the file is not found, a new plugin.ini file will be created and
+ the default values inserted.
+
+ @retval int error = 1, success = 0
+*/
+static int load_plugin_data(void)
+{
+ FILE *file_ptr;
+ char path[FN_REFLEN];
+ char line[1024];
+ int i= 0;
+ int j= 0;
+ struct st_plugin *pi= 0;
+
+ if (opt_plugin_ini == 0)
+ {
+ fn_format(path, "plugin.ini", opt_plugin_dir, "", MYF(0));
+ opt_plugin_ini= my_strdup(path, MYF(MY_FAE));
+ }
+ if (!file_exists(opt_plugin_ini))
+ {
+ file_ptr = fopen(opt_plugin_ini, "w");
+ if (file_ptr == NULL)
+ {
+ fprintf(stderr, "ERROR: Cannot create %s. Check permissions.\n",
+ opt_plugin_ini);
+ return 1;
+ }
+ /* add comments for format first */
+ fputs("#\n", file_ptr);
+ fputs("# Plugin initialization file. Format using comma-separated "
+ "values:\n", file_ptr);
+ fputs("# name, libname, symbol, [symbol, ]\n", file_ptr);
+ fputs("# Note: trailing comma is required.\n", file_ptr);
+ fputs("#\n", file_ptr);
+ for (i= 0; i < array_elements(default_plugins); i++)
+ {
+ // write name
+ fputs(default_plugins[i].name, file_ptr);
+ fputs(", ", file_ptr);
+ // write so_name
+ fputs(default_plugins[i].so_name, file_ptr);
+ fputs(", ", file_ptr);
+ for (j=0; j < array_elements(default_plugins[i].symbols); j++)
+ {
+ if (default_plugins[i].symbols[j] != NULL)
+ {
+ fputs(default_plugins[i].symbols[j], file_ptr);
+ fputs(", ", file_ptr);
+ }
+ }
+ fputs("\n", file_ptr);
+ }
+ fclose(file_ptr);
+ }
+
+ file_ptr= fopen(opt_plugin_ini, "r");
+ if (file_ptr == NULL)
+ {
+ fprintf(stderr, "ERROR: Cannot read plugin.ini file.\n");
+ return 1;
+ }
+ i = 0;
+ while (1)
+ {
+ char *res;
+ res= fgets(line, sizeof(line), file_ptr);
+ if (res == NULL)
+ {
+ break;
+ }
+ if (line[0] == '#') // skip comment lines
+ {
+ continue;
+ }
+ pi= read_plugin_data(line);
+ if (pi != NULL)
+ {
+ plugins_supported[i]= pi;
+ i++;
+ num_plugins= i;
+ if (num_plugins >= 256)
+ {
+ fprintf(stderr, "ERROR: Max plugins reached.\n");
+ return 1;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ fclose(file_ptr);
+ return 0;
+}
+
+
=== modified file 'include/my_global.h'
--- a/include/my_global.h 2011-04-13 19:05:26 +0000
+++ b/include/my_global.h 2011-05-26 19:25:47 +0000
@@ -602,6 +602,8 @@ typedef SOCKET_SIZE_TYPE size_socket;
#define FN_LIBCHAR '\\'
#define FN_LIBCHAR2 '/'
#define FN_DIRSEP "/\\" /* Valid directory separators */
+#define FN_EXEEXT ".exe"
+#define FN_SOEXT ".dll"
#define FN_ROOTDIR "\\"
#define FN_DEVCHAR ':'
#define FN_NETWORK_DRIVES /* Uses \\ to indicate network drives */
@@ -610,6 +612,8 @@ typedef SOCKET_SIZE_TYPE size_socket;
#define FN_LIBCHAR '/'
#define FN_LIBCHAR2 '/'
#define FN_DIRSEP "/" /* Valid directory separators */
+#define FN_EXEEXT ""
+#define FN_SOEXT ".so"
#define FN_ROOTDIR "/"
#endif
=== modified file 'mysql-test/mysql-test-run.pl'
--- a/mysql-test/mysql-test-run.pl 2011-05-25 14:07:16 +0000
+++ b/mysql-test/mysql-test-run.pl 2011-05-26 19:25:47 +0000
@@ -168,6 +168,7 @@ my $opt_suites;
our $opt_verbose= 0; # Verbose output, enable with --verbose
our $exe_mysql;
+our $exe_mysql_plugin;
our $exe_mysqladmin;
our $exe_mysqltest;
our $exe_libtool;
@@ -1944,6 +1945,7 @@ sub executable_setup () {
# Look for the client binaries
$exe_mysqladmin= mtr_exe_exists("$path_client_bindir/mysqladmin");
$exe_mysql= mtr_exe_exists("$path_client_bindir/mysql");
+ $exe_mysql_plugin= mtr_exe_exists("$path_client_bindir/mysql_plugin");
if ( ! $opt_skip_ndbcluster )
{
@@ -2219,6 +2221,19 @@ sub environment_setup {
push(@ld_library_paths, "$basedir/storage/ndb/src/.libs");
}
+ # Add the path where daemon_example can be found
+ # --------------------------------------------------------------------------
+ if (my $lib_plugin= find_plugin("libdaemon_example", "plugin/daemon_example"))
+ {
+ my $lib_dirname= dirname($lib_plugin);
+ $ENV{'DAEMONEXAMPLE_PLUGIN_DIR'}= $lib_dirname;
+ }
+ else
+ {
+ $ENV{'DAEMON_EXAMPLE_PLUGIN_DIR'}="";
+ }
+
+ # --------------------------------------------------------------------------
# Plugin settings should no longer be added here, instead
# place definitions in include/plugin.defs.
# See comment in that file for details.
@@ -2349,6 +2364,7 @@ sub environment_setup {
$ENV{'MYSQLADMIN'}= native_path($exe_mysqladmin);
$ENV{'MYSQL_CLIENT_TEST'}= mysql_client_test_arguments();
$ENV{'EXE_MYSQL'}= $exe_mysql;
+ $ENV{'MYSQL_PLUGIN'}= $exe_mysql_plugin;
# ----------------------------------------------------
# bug25714 executable may _not_ exist in
=== added file 'mysql-test/r/mysql_plugin.result'
--- a/mysql-test/r/mysql_plugin.result 1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/mysql_plugin.result 2011-05-26 19:25:47 +0000
@@ -0,0 +1,82 @@
+#
+# Ensure the plugin isn't loaded.
+#
+SELECT * FROM mysql.plugin WHERE name = 'daemon_example' ORDER BY name;
+name dl
+#
+# Enable the plugin...
+#
+#
+# Ensure the plugin is now loaded.
+#
+SELECT * FROM mysql.plugin WHERE name = 'daemon_example' ORDER BY name;
+name dl
+daemon_example libdaemon_example.so
+#
+# Disable the plugin...
+#
+# Disabling daemon_example...
+#
+# Ensure the plugin isn't loaded.
+#
+SELECT * FROM mysql.plugin WHERE name = 'daemon_example' ORDER BY name;
+name dl
+#
+# Attempt to load non-existant plugin
+#
+ERROR: No plugin specified or plugin does not match information in plugin.ini file.
+#
+# Attempt to use non-existant plugin.ini file
+#
+ERROR: Cannot create /NOT/THERE/pi.ini. Check permissions.
+#
+# Attempt to omit the plugin
+#
+ERROR: No plugin specified or plugin does not match information in plugin.ini file.
+#
+# Attempt to omit DISABLE|ENABLE
+#
+ERROR: missing operation. Please specify either '<plugin> ENABLE' or '<plugin> DISABLE'.
+#
+# Attempt to use bad paths - datadir
+#
+ERROR: Cannot access datadir at '/data_not_there/'.
+#
+# Attempt to use bad paths - basedir
+#
+ERROR: Cannot access basedir at '/basedir_not_there/'.
+#
+# Attempt to use bad paths - plugin_dir
+#
+ERROR: Cannot create /plugin_not_there/plugin.ini. Check permissions.
+#
+# Show the help.
+#
+mysql_plugin Ver 1.0.0 Distrib 5.5.14
+Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+
+Enable or disable plugins.
+
+Plugins supported:
+ thread_pool
+ daemon_example
+
+Usage: mysql_plugin [options] <plugin> ENABLE|DISABLE
+
+Options:
+ -?, --help Display this help and exit.
+ -b, --basedir=name The basedir for the server.
+ -d, --datadir=name The datadir for the server.
+ -p, --plugin-dir=name
+ The plugin dir for the server.
+ -i, --plugin-ini=name
+ Read plugin information from configuration file specified
+ instead of from <plugin_dir>/plugin.ini.
+ -n, --no-defaults Do not read values from configuration file.
+ -P, --print-defaults
+ Show default values from configuration file.
+ -v, --verbose More verbose output; you can use this multiple times to
+ get even more verbose output.
+ -V, --version Output version information and exit.
+
+
=== added file 'mysql-test/t/mysql_plugin-master.opt'
--- a/mysql-test/t/mysql_plugin-master.opt 1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/mysql_plugin-master.opt 2011-05-26 19:25:47 +0000
@@ -0,0 +1 @@
+--plugin-dir=$DAEMONEXAMPLE_PLUGIN_DIR
=== added file 'mysql-test/t/mysql_plugin.test'
--- a/mysql-test/t/mysql_plugin.test 1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/mysql_plugin.test 2011-05-26 19:25:47 +0000
@@ -0,0 +1,169 @@
+#
+# Test mysql_plugin tool
+#
+
+#
+# Test currently does not run on Windows because in Windows build trees,
+# mysqld.exe is not placed in the ./sql folder - it is placed either
+# in ./sql/Debug or ./sql/release. If this behaviour is changed, this test
+# can run on Windows.
+#
+--source include/not_windows.inc
+
+# Add the datadir, basedir, plugin_dir to the bootstrap command
+let $MYSQLD_DATADIR= `select @@datadir`;
+let $MYSQLD_BASEDIR= `select @@basedir`;
+let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=$DAEMONEXAMPLE_PLUGIN_DIR;
+
+--echo #
+--echo # Ensure the plugin isn't loaded.
+--echo #
+SELECT * FROM mysql.plugin WHERE name = 'daemon_example' ORDER BY name;
+
+--echo #
+--echo # Enable the plugin...
+--echo #
+let $expect_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect;
+# MTR will remove this file later, but this might be too late.
+--error 0,1
+--remove_file $expect_file
+--write_file $expect_file
+wait
+EOF
+--shutdown_server 10
+--source include/wait_until_disconnected.inc
+
+#
+# Enable the plugin
+#
+--exec $MYSQLD_BOOTSTRAP_CMD ENABLE daemon_example
+
+#
+# Ensure enabling an enabled plugin doesn't fail
+--exec $MYSQLD_BOOTSTRAP_CMD ENABLE daemon_example
+
+#
+# Restart the server
+#
+--append_file $expect_file
+restart
+EOF
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--echo #
+--echo # Ensure the plugin is now loaded.
+--echo #
+--replace_regex /\.dll/.so/
+SELECT * FROM mysql.plugin WHERE name = 'daemon_example' ORDER BY name;
+
+--echo #
+--echo # Disable the plugin...
+--echo #
+# MTR will remove this file later, but this might be too late.
+--error 0,1
+--remove_file $expect_file
+--write_file $expect_file
+wait
+EOF
+--shutdown_server 10
+--source include/wait_until_disconnected.inc
+
+#
+# Disable the plugin
+#
+--exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example
+
+#
+# Restart the server
+#
+--append_file $expect_file
+restart
+EOF
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--echo #
+--echo # Ensure the plugin isn't loaded.
+--echo #
+SELECT * FROM mysql.plugin WHERE name = 'daemon_example' ORDER BY name;
+
+#
+# Stop the server for error conditions
+#
+let $expect_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect;
+# MTR will remove this file later, but this might be too late.
+--error 0,1
+--remove_file $expect_file
+--write_file $expect_file
+wait
+EOF
+--shutdown_server 10
+--source include/wait_until_disconnected.inc
+
+--echo #
+--echo # Attempt to load non-existant plugin
+--echo #
+--error 1,2,256
+--exec $MYSQLD_BOOTSTRAP_CMD DISABLE NOT_THERE_AT_ALL 2>&1
+
+--echo #
+--echo # Attempt to use non-existant plugin.ini file
+--echo #
+--error 1,2,256
+--exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example --plugin-ini=/NOT/THERE/pi.ini 2>&1
+
+--echo #
+--echo # Attempt to omit the plugin
+--echo #
+--error 1,2,256
+--exec $MYSQLD_BOOTSTRAP_CMD DISABLE 2>&1
+
+--echo #
+--echo # Attempt to omit DISABLE|ENABLE
+--echo #
+--error 1,2,256
+--exec $MYSQLD_BOOTSTRAP_CMD daemon_example 2>&1
+
+--echo #
+--echo # Attempt to use bad paths - datadir
+--echo #
+let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=/data_not_there/ --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=$DAEMONEXAMPLE_PLUGIN_DIR;
+--error 1,2,256
+--exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1
+
+--echo #
+--echo # Attempt to use bad paths - basedir
+--echo #
+let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=/basedir_not_there/ --plugin-dir=$DAEMONEXAMPLE_PLUGIN_DIR;
+--error 1,2,256
+--exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1
+
+--echo #
+--echo # Attempt to use bad paths - plugin_dir
+--echo #
+let $MYSQLD_BOOTSTRAP_CMD= $MYSQL_PLUGIN -n --datadir=$MYSQLD_DATADIR --basedir=$MYSQLD_BASEDIR/sql --plugin-dir=/plugin_not_there/;
+--error 1,2,256
+--exec $MYSQLD_BOOTSTRAP_CMD DISABLE daemon_example 2>&1
+
+--echo #
+--echo # Show the help.
+--echo #
+replace_result $MYSQL_PLUGIN mysql_plugin;
+--exec $MYSQL_PLUGIN --help
+
+#
+# Restart the server
+#
+--append_file $expect_file
+restart
+EOF
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+#
+# Cleanup
+# MTR will remove this file later, but this might be too late.
+--error 0,1
+--remove_file $expect_file
+
Attachment: [text/bzr-bundle] bzr/chuck.bell@oracle.com-20110526192547-z4abvt8klp00i0v3.bundle