mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 12:56:14 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			509 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			509 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab
 | 
						|
   Copyright (c) 2010, 2011, Oracle and/or its affiliates.
 | 
						|
 | 
						|
   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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
 | 
						|
 | 
						|
/**
 | 
						|
  @file
 | 
						|
  
 | 
						|
  Support code for the client side (libmysql) plugins
 | 
						|
 | 
						|
  Client plugins are somewhat different from server plugins, they are simpler.
 | 
						|
 | 
						|
  They do not need to be installed or in any way explicitly loaded on the
 | 
						|
  client, they are loaded automatically on demand.
 | 
						|
  One client plugin per shared object, soname *must* match the plugin name.
 | 
						|
 | 
						|
  There is no reference counting and no unloading either.
 | 
						|
*/
 | 
						|
 | 
						|
#include <my_global.h>
 | 
						|
#include "mysql.h"
 | 
						|
#include <my_sys.h>
 | 
						|
#include <m_string.h>
 | 
						|
#include <my_pthread.h>
 | 
						|
 | 
						|
#include <sql_common.h>
 | 
						|
#include "errmsg.h"
 | 
						|
#include <mysql/client_plugin.h>
 | 
						|
 | 
						|
PSI_memory_key key_memory_root;
 | 
						|
PSI_memory_key key_memory_load_env_plugins;
 | 
						|
 | 
						|
#ifdef HAVE_PSI_INTERFACE
 | 
						|
PSI_mutex_key key_mutex_LOCK_load_client_plugin;
 | 
						|
 | 
						|
static PSI_mutex_info all_client_plugin_mutexes[]=
 | 
						|
{
 | 
						|
  {&key_mutex_LOCK_load_client_plugin, "LOCK_load_client_plugin", PSI_FLAG_GLOBAL}
 | 
						|
};
 | 
						|
 | 
						|
static PSI_memory_info all_client_plugin_memory[]=
 | 
						|
{
 | 
						|
  {&key_memory_root, "root", PSI_FLAG_GLOBAL},
 | 
						|
  {&key_memory_load_env_plugins, "load_env_plugins", PSI_FLAG_GLOBAL}
 | 
						|
};
 | 
						|
 | 
						|
static void init_client_plugin_psi_keys()
 | 
						|
{
 | 
						|
  const char* category= "sql";
 | 
						|
  int count;
 | 
						|
 | 
						|
  count= array_elements(all_client_plugin_mutexes);
 | 
						|
  mysql_mutex_register(category, all_client_plugin_mutexes, count);
 | 
						|
 | 
						|
  count= array_elements(all_client_plugin_memory);
 | 
						|
  mysql_memory_register(category, all_client_plugin_memory, count);
 | 
						|
}
 | 
						|
#endif /* HAVE_PSI_INTERFACE */
 | 
						|
 | 
						|
struct st_client_plugin_int {
 | 
						|
  struct st_client_plugin_int *next;
 | 
						|
  void   *dlhandle;
 | 
						|
  struct st_mysql_client_plugin *plugin;
 | 
						|
};
 | 
						|
 | 
						|
static my_bool initialized= 0;
 | 
						|
static MEM_ROOT mem_root;
 | 
						|
 | 
						|
#define plugin_declarations_sym "_mysql_client_plugin_declaration_"
 | 
						|
 | 
						|
static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]=
 | 
						|
{
 | 
						|
  0, /* these two are taken by Connector/C */
 | 
						|
  0, /* these two are taken by Connector/C */
 | 
						|
  MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
  Loaded plugins are stored in a linked list.
 | 
						|
  The list is append-only, the elements are added to the head (like in a stack).
 | 
						|
  The elements are added under a mutex, but the list can be read and traversed
 | 
						|
  without any mutex because once an element is added to the list, it stays
 | 
						|
  there. The main purpose of a mutex is to prevent two threads from
 | 
						|
  loading the same plugin twice in parallel.
 | 
						|
*/
 | 
						|
struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS];
 | 
						|
static mysql_mutex_t LOCK_load_client_plugin;
 | 
						|
 | 
						|
static int is_not_initialized(MYSQL *mysql, const char *name)
 | 
						|
{
 | 
						|
  DBUG_ENTER("is_not_initialized");
 | 
						|
 | 
						|
  if (initialized)
 | 
						|
    DBUG_RETURN(0);
 | 
						|
 | 
						|
  set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
 | 
						|
                           unknown_sqlstate, ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
 | 
						|
                           name, "not initialized");
 | 
						|
  DBUG_RETURN(1);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  finds a plugin in the list
 | 
						|
 | 
						|
  @param name   plugin name to search for
 | 
						|
  @param type   plugin type
 | 
						|
 | 
						|
  @note this does NOT necessarily need a mutex, take care!
 | 
						|
  
 | 
						|
  @retval a pointer to a found plugin or 0
 | 
						|
*/
 | 
						|
static struct st_mysql_client_plugin *
 | 
						|
find_plugin(const char *name, int type)
 | 
						|
{
 | 
						|
  struct st_client_plugin_int *p;
 | 
						|
  DBUG_ENTER("find_plugin");
 | 
						|
 | 
						|
  DBUG_ASSERT(initialized);
 | 
						|
  DBUG_ASSERT(type >= 0 && type < MYSQL_CLIENT_MAX_PLUGINS);
 | 
						|
  if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS)
 | 
						|
    DBUG_RETURN(0);
 | 
						|
 | 
						|
  for (p= plugin_list[type]; p; p= p->next)
 | 
						|
  {
 | 
						|
    if (strcmp(p->plugin->name, name) == 0)
 | 
						|
      DBUG_RETURN(p->plugin);
 | 
						|
  }
 | 
						|
  DBUG_RETURN(NULL);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  verifies the plugin and adds it to the list
 | 
						|
 | 
						|
  @param mysql          MYSQL structure (for error reporting)
 | 
						|
  @param plugin         plugin to install
 | 
						|
  @param dlhandle       a handle to the shared object (returned by dlopen)
 | 
						|
                        or 0 if the plugin was not dynamically loaded
 | 
						|
  @param argc           number of arguments in the 'va_list args'
 | 
						|
  @param args           arguments passed to the plugin initialization function
 | 
						|
 | 
						|
  @retval a pointer to an installed plugin or 0
 | 
						|
*/
 | 
						|
static struct st_mysql_client_plugin *
 | 
						|
add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle,
 | 
						|
           int argc, va_list args)
 | 
						|
{
 | 
						|
  const char *errmsg;
 | 
						|
  struct st_client_plugin_int plugin_int, *p;
 | 
						|
  char errbuf[1024];
 | 
						|
  DBUG_ENTER("add_plugin");
 | 
						|
 | 
						|
  DBUG_ASSERT(initialized);
 | 
						|
 | 
						|
  plugin_int.plugin= plugin;
 | 
						|
  plugin_int.dlhandle= dlhandle;
 | 
						|
 | 
						|
  if (plugin->type >= MYSQL_CLIENT_MAX_PLUGINS)
 | 
						|
  {
 | 
						|
    errmsg= "Unknown client plugin type";
 | 
						|
    goto err1;
 | 
						|
  }
 | 
						|
 | 
						|
  if (plugin->interface_version >> 8 != plugin_version[plugin->type] >> 8)
 | 
						|
  {
 | 
						|
    errmsg= "Incompatible client plugin interface";
 | 
						|
    goto err1;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Call the plugin initialization function, if any */
 | 
						|
  if (plugin->init && plugin->init(errbuf, sizeof(errbuf), argc, args))
 | 
						|
  {
 | 
						|
    errmsg= errbuf;
 | 
						|
    goto err1;
 | 
						|
  }
 | 
						|
 | 
						|
  p= (struct st_client_plugin_int *)
 | 
						|
    memdup_root(&mem_root, &plugin_int, sizeof(plugin_int));
 | 
						|
 | 
						|
  if (!p)
 | 
						|
  {
 | 
						|
    errmsg= "Out of memory";
 | 
						|
    goto err2;
 | 
						|
  }
 | 
						|
 | 
						|
  mysql_mutex_assert_owner(&LOCK_load_client_plugin);
 | 
						|
 | 
						|
  p->next= plugin_list[plugin->type];
 | 
						|
  plugin_list[plugin->type]= p;
 | 
						|
  net_clear_error(&mysql->net);
 | 
						|
 | 
						|
  DBUG_RETURN(plugin);
 | 
						|
 | 
						|
err2:
 | 
						|
  if (plugin->deinit)
 | 
						|
    plugin->deinit();
 | 
						|
err1:
 | 
						|
  set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
 | 
						|
                           ER(CR_AUTH_PLUGIN_CANNOT_LOAD), plugin->name,
 | 
						|
                           errmsg);
 | 
						|
  if (dlhandle)
 | 
						|
    (void)dlclose(dlhandle);
 | 
						|
  DBUG_RETURN(NULL);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Loads plugins which are specified in the environment variable
 | 
						|
  LIBMYSQL_PLUGINS.
 | 
						|
  
 | 
						|
  Multiple plugins must be separated by semicolon. This function doesn't
 | 
						|
  return or log an error.
 | 
						|
 | 
						|
  The function is be called by mysql_client_plugin_init
 | 
						|
 | 
						|
  @todo
 | 
						|
  Support extended syntax, passing parameters to plugins, for example
 | 
						|
  LIBMYSQL_PLUGINS="plugin1(param1,param2);plugin2;..."
 | 
						|
  or
 | 
						|
  LIBMYSQL_PLUGINS="plugin1=int:param1,str:param2;plugin2;..."
 | 
						|
*/
 | 
						|
static void load_env_plugins(MYSQL *mysql)
 | 
						|
{
 | 
						|
  char *plugs, *free_env, *s= getenv("LIBMYSQL_PLUGINS");
 | 
						|
  DBUG_ENTER("load_env_plugins");
 | 
						|
 | 
						|
  /* no plugins to load */
 | 
						|
  if (!s)
 | 
						|
    DBUG_VOID_RETURN;
 | 
						|
 | 
						|
  free_env= plugs= my_strdup(key_memory_load_env_plugins, s, MYF(MY_WME));
 | 
						|
 | 
						|
  do {
 | 
						|
    if ((s= strchr(plugs, ';')))
 | 
						|
      *s= '\0';
 | 
						|
    mysql_load_plugin(mysql, plugs, -1, 0);
 | 
						|
    plugs= s + 1;
 | 
						|
  } while (s);
 | 
						|
 | 
						|
  my_free(free_env);
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
/********** extern functions to be used by libmysql *********************/
 | 
						|
 | 
						|
/**
 | 
						|
  Initializes the client plugin layer.
 | 
						|
 | 
						|
  This function must be called before any other client plugin function.
 | 
						|
 | 
						|
  @retval 0    successful
 | 
						|
  @retval != 0 error occurred
 | 
						|
*/
 | 
						|
int mysql_client_plugin_init()
 | 
						|
{
 | 
						|
  MYSQL mysql;
 | 
						|
  struct st_mysql_client_plugin **builtin;
 | 
						|
  va_list unused;
 | 
						|
  DBUG_ENTER("mysql_client_plugin_init");
 | 
						|
 | 
						|
  if (initialized)
 | 
						|
    DBUG_RETURN(0);
 | 
						|
 | 
						|
#ifdef HAVE_PSI_INTERFACE
 | 
						|
  init_client_plugin_psi_keys();
 | 
						|
#endif /* HAVE_PSI_INTERFACE */
 | 
						|
 | 
						|
  bzero(&mysql, sizeof(mysql)); /* dummy mysql for set_mysql_extended_error */
 | 
						|
  bzero(&unused, sizeof unused);
 | 
						|
 | 
						|
  mysql_mutex_init(key_mutex_LOCK_load_client_plugin,
 | 
						|
                   &LOCK_load_client_plugin, MY_MUTEX_INIT_SLOW);
 | 
						|
  init_alloc_root(key_memory_root, &mem_root, 128, 128, MYF(0));
 | 
						|
 | 
						|
  bzero(&plugin_list, sizeof(plugin_list));
 | 
						|
 | 
						|
  initialized= 1;
 | 
						|
 | 
						|
  mysql_mutex_lock(&LOCK_load_client_plugin);
 | 
						|
 | 
						|
  for (builtin= mysql_client_builtins; *builtin; builtin++)
 | 
						|
    add_plugin(&mysql, *builtin, 0, 0, unused);
 | 
						|
 | 
						|
  mysql_mutex_unlock(&LOCK_load_client_plugin);
 | 
						|
 | 
						|
  load_env_plugins(&mysql);
 | 
						|
 | 
						|
  DBUG_RETURN(0);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Deinitializes the client plugin layer.
 | 
						|
 | 
						|
  Unloades all client plugins and frees any associated resources.
 | 
						|
*/
 | 
						|
void mysql_client_plugin_deinit()
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  struct st_client_plugin_int *p;
 | 
						|
  DBUG_ENTER("mysql_client_plugin_deinit");
 | 
						|
 | 
						|
  if (!initialized)
 | 
						|
    DBUG_VOID_RETURN;
 | 
						|
 | 
						|
  for (i=0; i < MYSQL_CLIENT_MAX_PLUGINS; i++)
 | 
						|
    for (p= plugin_list[i]; p; p= p->next)
 | 
						|
    {
 | 
						|
      if (p->plugin->deinit)
 | 
						|
        p->plugin->deinit();
 | 
						|
      if (p->dlhandle)
 | 
						|
        (void)dlclose(p->dlhandle);
 | 
						|
    }
 | 
						|
 | 
						|
  bzero(&plugin_list, sizeof(plugin_list));
 | 
						|
  initialized= 0;
 | 
						|
  free_root(&mem_root, MYF(0));
 | 
						|
  mysql_mutex_destroy(&LOCK_load_client_plugin);
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
/************* public facing functions, for client consumption *********/
 | 
						|
 | 
						|
/* see <mysql/client_plugin.h> for a full description */
 | 
						|
struct st_mysql_client_plugin *
 | 
						|
mysql_client_register_plugin(MYSQL *mysql,
 | 
						|
                             struct st_mysql_client_plugin *plugin)
 | 
						|
{
 | 
						|
  DBUG_ENTER("mysql_client_register_plugin");
 | 
						|
 | 
						|
  if (is_not_initialized(mysql, plugin->name))
 | 
						|
    DBUG_RETURN(NULL);
 | 
						|
 | 
						|
  mysql_mutex_lock(&LOCK_load_client_plugin);
 | 
						|
 | 
						|
  /* make sure the plugin wasn't loaded meanwhile */
 | 
						|
  if (find_plugin(plugin->name, plugin->type))
 | 
						|
  {
 | 
						|
    set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
 | 
						|
                             unknown_sqlstate, ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
 | 
						|
                             plugin->name, "it is already loaded");
 | 
						|
    plugin= NULL;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    va_list unused;
 | 
						|
    bzero(&unused, sizeof unused);
 | 
						|
    plugin= add_plugin(mysql, plugin, 0, 0, unused);
 | 
						|
  }
 | 
						|
 | 
						|
  mysql_mutex_unlock(&LOCK_load_client_plugin);
 | 
						|
  DBUG_RETURN(plugin);
 | 
						|
}
 | 
						|
 | 
						|
/* see <mysql/client_plugin.h> for a full description */
 | 
						|
struct st_mysql_client_plugin *
 | 
						|
mysql_load_plugin_v(MYSQL *mysql, const char *name, int type,
 | 
						|
                    int argc, va_list args)
 | 
						|
{
 | 
						|
  const char *errmsg;
 | 
						|
  char dlpath[FN_REFLEN+1];
 | 
						|
  void *sym, *dlhandle;
 | 
						|
  struct st_mysql_client_plugin *plugin;
 | 
						|
  DBUG_ENTER("mysql_load_plugin_v");
 | 
						|
 | 
						|
  DBUG_PRINT ("entry", ("name=%s type=%d int argc=%d", name, type, argc));
 | 
						|
  if (is_not_initialized(mysql, name))
 | 
						|
  {
 | 
						|
    DBUG_PRINT ("leave", ("mysql not initialized"));
 | 
						|
    DBUG_RETURN (NULL);
 | 
						|
  }
 | 
						|
 | 
						|
  mysql_mutex_lock(&LOCK_load_client_plugin);
 | 
						|
 | 
						|
  /* make sure the plugin wasn't loaded meanwhile */
 | 
						|
  if (type >= 0 && find_plugin(name, type))
 | 
						|
  {
 | 
						|
    errmsg= "it is already loaded";
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Compile dll path */
 | 
						|
  strxnmov(dlpath, sizeof(dlpath) - 1,
 | 
						|
           mysql->options.extension && mysql->options.extension->plugin_dir ?
 | 
						|
           mysql->options.extension->plugin_dir : PLUGINDIR, "/",
 | 
						|
           name, SO_EXT, NullS);
 | 
						|
 | 
						|
  if (strpbrk(name, "()[]!@#$%^&/*;.,'?\\"))
 | 
						|
  {
 | 
						|
    errmsg= "invalid plugin name";
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_PRINT ("info", ("dlopeninig %s", dlpath));
 | 
						|
  /* Open new dll handle */
 | 
						|
  if (!(dlhandle= dlopen(dlpath, RTLD_NOW)))
 | 
						|
  {
 | 
						|
    DBUG_PRINT ("info", ("failed to dlopen"));
 | 
						|
    errmsg= dlerror();
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!(sym= dlsym(dlhandle, plugin_declarations_sym)))
 | 
						|
  {
 | 
						|
    errmsg= "not a plugin";
 | 
						|
    goto errc;
 | 
						|
  }
 | 
						|
 | 
						|
  plugin= (struct st_mysql_client_plugin*)sym;
 | 
						|
 | 
						|
  if (type >=0 && type != plugin->type)
 | 
						|
  {
 | 
						|
    errmsg= "type mismatch";
 | 
						|
    goto errc;
 | 
						|
  }
 | 
						|
 | 
						|
  if (strcmp(name, plugin->name))
 | 
						|
  {
 | 
						|
    errmsg= "name mismatch";
 | 
						|
    goto errc;
 | 
						|
  }
 | 
						|
 | 
						|
  if (type < 0 && find_plugin(name, plugin->type))
 | 
						|
  {
 | 
						|
    errmsg= "it is already loaded";
 | 
						|
    goto errc;
 | 
						|
  }
 | 
						|
 | 
						|
  plugin= add_plugin(mysql, plugin, dlhandle, argc, args);
 | 
						|
 | 
						|
  mysql_mutex_unlock(&LOCK_load_client_plugin);
 | 
						|
 | 
						|
  DBUG_PRINT ("leave", ("plugin loaded ok"));
 | 
						|
  DBUG_RETURN (plugin);
 | 
						|
 | 
						|
errc:
 | 
						|
  dlclose(dlhandle);
 | 
						|
err:
 | 
						|
  mysql_mutex_unlock(&LOCK_load_client_plugin);
 | 
						|
  DBUG_PRINT ("leave", ("plugin load error : %s", errmsg));
 | 
						|
  set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
 | 
						|
                           ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, errmsg);
 | 
						|
  DBUG_RETURN (NULL);
 | 
						|
}
 | 
						|
 | 
						|
/* see <mysql/client_plugin.h> for a full description */
 | 
						|
struct st_mysql_client_plugin *
 | 
						|
mysql_load_plugin(MYSQL *mysql, const char *name, int type, int argc, ...)
 | 
						|
{
 | 
						|
  struct st_mysql_client_plugin *p;
 | 
						|
  va_list args;
 | 
						|
  DBUG_ENTER("mysql_load_plugin");
 | 
						|
 | 
						|
  va_start(args, argc);
 | 
						|
  p= mysql_load_plugin_v(mysql, name, type, argc, args);
 | 
						|
  va_end(args);
 | 
						|
  DBUG_RETURN(p);
 | 
						|
}
 | 
						|
 | 
						|
/* see <mysql/client_plugin.h> for a full description */
 | 
						|
struct st_mysql_client_plugin *
 | 
						|
mysql_client_find_plugin(MYSQL *mysql, const char *name, int type)
 | 
						|
{
 | 
						|
  struct st_mysql_client_plugin *p;
 | 
						|
  DBUG_ENTER("mysql_client_find_plugin");
 | 
						|
 | 
						|
  DBUG_PRINT ("entry", ("name=%s, type=%d", name, type));
 | 
						|
  if (is_not_initialized(mysql, name))
 | 
						|
    DBUG_RETURN (NULL);
 | 
						|
 | 
						|
  if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS)
 | 
						|
  {
 | 
						|
    set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
 | 
						|
                             ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name,
 | 
						|
                             "invalid type");
 | 
						|
  }
 | 
						|
 | 
						|
  if ((p= find_plugin(name, type)))
 | 
						|
  {
 | 
						|
    DBUG_PRINT ("leave", ("found %p", p));
 | 
						|
    DBUG_RETURN (p);
 | 
						|
  }
 | 
						|
 | 
						|
  /* not found, load it */
 | 
						|
  p= mysql_load_plugin(mysql, name, type, 0);
 | 
						|
  DBUG_PRINT ("leave", ("loaded %p", p));
 | 
						|
  DBUG_RETURN (p);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* see <mysql/client_plugin.h> for a full description */
 | 
						|
int mysql_plugin_options(struct st_mysql_client_plugin *plugin,
 | 
						|
                                 const char *option,
 | 
						|
                                 const void *value)
 | 
						|
{
 | 
						|
  DBUG_ENTER("mysql_plugin_options");
 | 
						|
  /* does the plugin support options call? */
 | 
						|
  if (!plugin || !plugin->options)
 | 
						|
    DBUG_RETURN(1);
 | 
						|
  DBUG_RETURN(plugin->options(option, value));
 | 
						|
}
 |