/* 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));
}
Messung V0.5 in Prozent C=95 H=75 G=85
¤ Dauer der Verarbeitung: 0.18 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland