MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
client_plugin.c
Go to the documentation of this file.
1 /* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
30 #include <my_global.h>
31 #include "mysql.h"
32 #include <my_sys.h>
33 #include <m_string.h>
34 #include <my_pthread.h>
35 
36 #include <sql_common.h>
37 #include "errmsg.h"
38 #include <mysql/client_plugin.h>
39 
41  struct st_client_plugin_int *next;
42  void *dlhandle;
43  struct st_mysql_client_plugin *plugin;
44 };
45 
46 static my_bool initialized= 0;
47 static MEM_ROOT mem_root;
48 
49 static const char *plugin_declarations_sym= "_mysql_client_plugin_declaration_";
50 static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]=
51 {
52  0, /* these two are taken by Connector/C */
53  0, /* these two are taken by Connector/C */
54  MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION
55 };
56 
57 /*
58  Loaded plugins are stored in a linked list.
59  The list is append-only, the elements are added to the head (like in a stack).
60  The elements are added under a mutex, but the list can be read and traversed
61  without any mutex because once an element is added to the list, it stays
62  there. The main purpose of a mutex is to prevent two threads from
63  loading the same plugin twice in parallel.
64 */
65 struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS];
66 static mysql_mutex_t LOCK_load_client_plugin;
67 
68 static int is_not_initialized(MYSQL *mysql, const char *name)
69 {
70  if (initialized)
71  return 0;
72 
73  set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
74  unknown_sqlstate, ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
75  name, "not initialized");
76  return 1;
77 }
78 
89 static struct st_mysql_client_plugin *
90 find_plugin(const char *name, int type)
91 {
92  struct st_client_plugin_int *p;
93 
94  DBUG_ASSERT(initialized);
95  DBUG_ASSERT(type >= 0 && type < MYSQL_CLIENT_MAX_PLUGINS);
96  if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS)
97  return 0;
98 
99  for (p= plugin_list[type]; p; p= p->next)
100  {
101  if (strcmp(p->plugin->name, name) == 0)
102  return p->plugin;
103  }
104  return NULL;
105 }
106 
119 static struct st_mysql_client_plugin *
120 do_add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin,
121  void *dlhandle,
122  int argc, va_list args)
123 {
124  const char *errmsg;
125  struct st_client_plugin_int plugin_int, *p;
126  char errbuf[1024];
127 
128  DBUG_ASSERT(initialized);
129 
130  plugin_int.plugin= plugin;
131  plugin_int.dlhandle= dlhandle;
132 
133  if (plugin->type >= MYSQL_CLIENT_MAX_PLUGINS)
134  {
135  errmsg= "Unknown client plugin type";
136  goto err1;
137  }
138 
139  if (plugin->interface_version < plugin_version[plugin->type] ||
140  (plugin->interface_version >> 8) >
141  (plugin_version[plugin->type] >> 8))
142  {
143  errmsg= "Incompatible client plugin interface";
144  goto err1;
145  }
146 
147  /* Call the plugin initialization function, if any */
148  if (plugin->init && plugin->init(errbuf, sizeof(errbuf), argc, args))
149  {
150  errmsg= errbuf;
151  goto err1;
152  }
153 
154  p= (struct st_client_plugin_int *)
155  memdup_root(&mem_root, &plugin_int, sizeof(plugin_int));
156 
157  if (!p)
158  {
159  errmsg= "Out of memory";
160  goto err2;
161  }
162 
163  mysql_mutex_assert_owner(&LOCK_load_client_plugin);
164 
165  p->next= plugin_list[plugin->type];
166  plugin_list[plugin->type]= p;
167  net_clear_error(&mysql->net);
168 
169  return plugin;
170 
171 err2:
172  if (plugin->deinit)
173  plugin->deinit();
174 err1:
175  set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
176  ER(CR_AUTH_PLUGIN_CANNOT_LOAD), plugin->name,
177  errmsg);
178  if (dlhandle)
179  dlclose(dlhandle);
180  return NULL;
181 }
182 
183 
184 static struct st_mysql_client_plugin *
185 add_plugin_noargs(MYSQL *mysql, struct st_mysql_client_plugin *plugin,
186  void *dlhandle,
187  int argc, ...)
188 {
189  struct st_mysql_client_plugin *retval= NULL;
190  va_list ap;
191  va_start(ap, argc);
192  retval= do_add_plugin(mysql, plugin, dlhandle, argc, ap);
193  va_end(ap);
194  return retval;
195 }
196 
197 
198 static struct st_mysql_client_plugin *
199 add_plugin_withargs(MYSQL *mysql, struct st_mysql_client_plugin *plugin,
200  void *dlhandle,
201  int argc, va_list args)
202 {
203  return do_add_plugin(mysql, plugin, dlhandle, argc, args);
204 }
205 
206 
207 
223 static void load_env_plugins(MYSQL *mysql)
224 {
225  char *plugs, *free_env, *s= getenv("LIBMYSQL_PLUGINS");
226  char *enable_cleartext_plugin= getenv("LIBMYSQL_ENABLE_CLEARTEXT_PLUGIN");
227 
228  if (enable_cleartext_plugin && strchr("1Yy", enable_cleartext_plugin[0]))
229  libmysql_cleartext_plugin_enabled= 1;
230 
231  /* no plugins to load */
232  if(!s)
233  return;
234 
235  free_env= plugs= my_strdup(s, MYF(MY_WME));
236 
237  do {
238  if ((s= strchr(plugs, ';')))
239  *s= '\0';
240  mysql_load_plugin(mysql, plugs, -1, 0);
241  plugs= s + 1;
242  } while (s);
243 
244  my_free(free_env);
245 
246 }
247 
248 /********** extern functions to be used by libmysql *********************/
249 
259 {
260  MYSQL mysql;
261  struct st_mysql_client_plugin **builtin;
262 
263  if (initialized)
264  return 0;
265 
266  memset(&mysql, 0, sizeof(mysql)); /* dummy mysql for set_mysql_extended_error */
267 
268  mysql_mutex_init(0, &LOCK_load_client_plugin, MY_MUTEX_INIT_SLOW);
269  init_alloc_root(&mem_root, 128, 128);
270 
271  memset(&plugin_list, 0, sizeof(plugin_list));
272 
273  initialized= 1;
274 
275  mysql_mutex_lock(&LOCK_load_client_plugin);
276 
277  for (builtin= mysql_client_builtins; *builtin; builtin++)
278  add_plugin_noargs(&mysql, *builtin, 0, 0);
279 
280  mysql_mutex_unlock(&LOCK_load_client_plugin);
281 
282  load_env_plugins(&mysql);
283 
284  return 0;
285 }
286 
293 {
294  int i;
295  struct st_client_plugin_int *p;
296 
297  if (!initialized)
298  return;
299 
300  for (i=0; i < MYSQL_CLIENT_MAX_PLUGINS; i++)
301  for (p= plugin_list[i]; p; p= p->next)
302  {
303  if (p->plugin->deinit)
304  p->plugin->deinit();
305  if (p->dlhandle)
306  dlclose(p->dlhandle);
307  }
308 
309  memset(&plugin_list, 0, sizeof(plugin_list));
310  initialized= 0;
311  free_root(&mem_root, MYF(0));
312  mysql_mutex_destroy(&LOCK_load_client_plugin);
313 }
314 
315 /************* public facing functions, for client consumption *********/
316 
317 /* see <mysql/client_plugin.h> for a full description */
318 struct st_mysql_client_plugin *
320  struct st_mysql_client_plugin *plugin)
321 {
322  if (is_not_initialized(mysql, plugin->name))
323  return NULL;
324 
325  mysql_mutex_lock(&LOCK_load_client_plugin);
326 
327  /* make sure the plugin wasn't loaded meanwhile */
328  if (find_plugin(plugin->name, plugin->type))
329  {
330  set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
331  unknown_sqlstate, ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
332  plugin->name, "it is already loaded");
333  plugin= NULL;
334  }
335  else
336  plugin= add_plugin_noargs(mysql, plugin, 0, 0);
337 
338  mysql_mutex_unlock(&LOCK_load_client_plugin);
339  return plugin;
340 }
341 
342 /* see <mysql/client_plugin.h> for a full description */
343 struct st_mysql_client_plugin *
344 mysql_load_plugin_v(MYSQL *mysql, const char *name, int type,
345  int argc, va_list args)
346 {
347  const char *errmsg;
348  char dlpath[FN_REFLEN+1];
349  void *sym, *dlhandle;
350  struct st_mysql_client_plugin *plugin;
351  const char *plugindir;
352 #ifdef _WIN32
353  char win_errormsg[2048];
354 #endif
355 
356  DBUG_ENTER ("mysql_load_plugin_v");
357  DBUG_PRINT ("entry", ("name=%s type=%d int argc=%d", name, type, argc));
358  if (is_not_initialized(mysql, name))
359  {
360  DBUG_PRINT ("leave", ("mysql not initialized"));
361  DBUG_RETURN (NULL);
362  }
363 
364  mysql_mutex_lock(&LOCK_load_client_plugin);
365 
366  /* make sure the plugin wasn't loaded meanwhile */
367  if (type >= 0 && find_plugin(name, type))
368  {
369  errmsg= "it is already loaded";
370  goto err;
371  }
372 
373  if (mysql->options.extension && mysql->options.extension->plugin_dir)
374  {
375  plugindir= mysql->options.extension->plugin_dir;
376  }
377  else
378  {
379  plugindir= getenv("LIBMYSQL_PLUGIN_DIR");
380  if (!plugindir)
381  {
382  plugindir= PLUGINDIR;
383  }
384  }
385 
386  /* Compile dll path */
387  strxnmov(dlpath, sizeof(dlpath) - 1,
388  plugindir, "/",
389  name, SO_EXT, NullS);
390 
391  DBUG_PRINT ("info", ("dlopeninig %s", dlpath));
392  /* Open new dll handle */
393  if (!(dlhandle= dlopen(dlpath, RTLD_NOW)))
394  {
395 #if defined(__APPLE__)
396  /* Apple supports plugins with .so also, so try this as well */
397  strxnmov(dlpath, sizeof(dlpath) - 1,
398  mysql->options.extension && mysql->options.extension->plugin_dir ?
399  mysql->options.extension->plugin_dir : PLUGINDIR, "/",
400  name, ".so", NullS);
401  if ((dlhandle= dlopen(dlpath, RTLD_NOW)))
402  goto have_plugin;
403 #endif
404 
405 #ifdef _WIN32
406  /* There should be no win32 calls between failed dlopen() and GetLastError() */
407  if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
408  0, GetLastError(), 0, win_errormsg, 2048, NULL))
409  errmsg= win_errormsg;
410  else
411  errmsg= "";
412 #else
413  errmsg= dlerror();
414 #endif
415  DBUG_PRINT ("info", ("failed to dlopen"));
416  goto err;
417  }
418 
419 #if defined(__APPLE__)
420 have_plugin:
421 #endif
422  if (!(sym= dlsym(dlhandle, plugin_declarations_sym)))
423  {
424  errmsg= "not a plugin";
425  dlclose(dlhandle);
426  goto err;
427  }
428 
429  plugin= (struct st_mysql_client_plugin*)sym;
430 
431  if (type >=0 && type != plugin->type)
432  {
433  errmsg= "type mismatch";
434  goto err;
435  }
436 
437  if (strcmp(name, plugin->name))
438  {
439  errmsg= "name mismatch";
440  goto err;
441  }
442 
443  if (type < 0 && find_plugin(name, plugin->type))
444  {
445  errmsg= "it is already loaded";
446  goto err;
447  }
448 
449  plugin= add_plugin_withargs(mysql, plugin, dlhandle, argc, args);
450 
451  mysql_mutex_unlock(&LOCK_load_client_plugin);
452 
453  DBUG_PRINT ("leave", ("plugin loaded ok"));
454  DBUG_RETURN (plugin);
455 
456 err:
457  mysql_mutex_unlock(&LOCK_load_client_plugin);
458  DBUG_PRINT ("leave", ("plugin load error : %s", errmsg));
459  set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
460  ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, errmsg);
461  DBUG_RETURN (NULL);
462 }
463 
464 /* see <mysql/client_plugin.h> for a full description */
465 struct st_mysql_client_plugin *
466 mysql_load_plugin(MYSQL *mysql, const char *name, int type, int argc, ...)
467 {
468  struct st_mysql_client_plugin *p;
469  va_list args;
470  va_start(args, argc);
471  p= mysql_load_plugin_v(mysql, name, type, argc, args);
472  va_end(args);
473  return p;
474 }
475 
476 /* see <mysql/client_plugin.h> for a full description */
477 struct st_mysql_client_plugin *
478 mysql_client_find_plugin(MYSQL *mysql, const char *name, int type)
479 {
480  struct st_mysql_client_plugin *p;
481 
482  DBUG_ENTER ("mysql_client_find_plugin");
483  DBUG_PRINT ("entry", ("name=%s, type=%d", name, type));
484  if (is_not_initialized(mysql, name))
485  DBUG_RETURN (NULL);
486 
487  if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS)
488  {
489  set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
490  ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name,
491  "invalid type");
492  }
493 
494  if ((p= find_plugin(name, type)))
495  {
496  DBUG_PRINT ("leave", ("found %p", p));
497  DBUG_RETURN (p);
498  }
499 
500  /* not found, load it */
501  p= mysql_load_plugin(mysql, name, type, 0);
502  DBUG_PRINT ("leave", ("loaded %p", p));
503  DBUG_RETURN (p);
504 }
505 
506 
507 /* see <mysql/client_plugin.h> for a full description */
509  const char *option,
510  const void *value)
511 {
512  DBUG_ENTER("mysql_plugin_options");
513  /* does the plugin support options call? */
514  if (!plugin || !plugin->options)
515  DBUG_RETURN(1);
516  DBUG_RETURN(plugin->options(option, value));
517 }