From 61cf0c6187800157f0c9538ceaca011ed992445f Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Thu, 13 Jun 2013 18:30:15 +0200 Subject: [PATCH] Addition of interface for loading external modules as shared library objects --- .bzrignore | 1 + core/Makefile | 11 ++- core/load_utils.c | 237 ++++++++++++++++++++++++++++++++++++++++++++++ include/gw.h | 2 +- include/modules.h | 44 +++++++++ 5 files changed, 292 insertions(+), 3 deletions(-) create mode 100644 core/load_utils.c create mode 100644 include/modules.h diff --git a/.bzrignore b/.bzrignore index f8634da7f..d7e2c79c4 100644 --- a/.bzrignore +++ b/.bzrignore @@ -1 +1,2 @@ core/tags +gateway diff --git a/core/Makefile b/core/Makefile index a35b13be9..3cca2b937 100644 --- a/core/Makefile +++ b/core/Makefile @@ -13,14 +13,21 @@ # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Copyright SkySQL Ab 2013 +# +# Revision History +# Date Who Description +# 13/06/13 Mark Riddoch Addition of -rdynamic to allow libraries +# to resolve symbols in the main executable CC=cc CFLAGS=-c -I/usr/include -I../include +LDFLAGS=-rdynamic SRCS= atomic.c buffer.c spinlock.c gateway.c gateway_mysql_protocol.c gw_utils.c \ - utils.c dcb.c + utils.c dcb.c load_utils.c HDRS= ../include/atomic.h ../include/buffer.h ../include/dcb.h \ ../include/gateway_mysql.h ../include/gw.h ../include/mysql_protocol.h \ - ../include/session.h ../include/spinlock.h ../include/thread.h + ../include/session.h ../include/spinlock.h ../include/thread.h \ + ../include/modules.h OBJ=$(SRCS:.c=.o) LIBS=-lssl diff --git a/core/load_utils.c b/core/load_utils.c new file mode 100644 index 000000000..537b976e1 --- /dev/null +++ b/core/load_utils.c @@ -0,0 +1,237 @@ +/* + * This file is distributed as part of the SkySQL Gateway. It 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. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright SkySQL Ab 2013 + */ + +/* + * load_utils.c Utility functions to aid the loading of dynamic modules + * into the gateway + * + * Revision History + * + * Date Who Description + * 13/06/13 Mark Riddoch Initial implementation + * + */ +#include +#include +#include +#include +#include +#include +#include + +static MODULES *registered = NULL; + +static MODULES *find_module(const char *module); +static void register_module(const char *module, const char *type, void *dlhandle, char *version, void *modobj); +static void unregister_module(const char *module); + +/* + * Load the dynamic library related to a gateway module. The routine + * will look for library files in the current directory, + * $GATEWAY_HOME/modules and /usr/local/skysql/gateway/modules. + * + * @param module Name of the module to load + * @param type Type of module, used purely for registration + * @param entry Routine to call to extract entry points + * @return The module specific entry point structure or NULL + */ +void * +load_module(const char *module, const char *type, const char *entry) +{ +char *home, *version; +char fname[MAXPATHLEN]; +void *dlhandle, *sym; +char *(*ver)(); +void *(*ep)(), *modobj; +MODULES *mod; + + if ((mod = find_module(module)) == NULL) + { + /* + * The module is not already loaded + * + * Search of the shared object. + */ + sprintf(fname, "lib%s.so", module); + if (access(fname, F_OK) == -1) + { + if ((home = getenv("GATEWAY_HOME")) == NULL) + home = "/usr/local/skysql/gateway"; + sprintf(fname, "%s/modules/lib%s.so", home, module); + if (access(fname, F_OK) == -1) + { + fprintf(stderr, "Unable to find library for module: %s\n", module); + return NULL; + } + } + if ((dlhandle = dlopen(fname, RTLD_NOW|RTLD_LOCAL)) == NULL) + { + fprintf(stderr, "Unable to load library for module: %s, %s\n", module, dlerror()); + return NULL; + } + + if ((sym = dlsym(dlhandle, "version")) == NULL) + { + fprintf(stderr, "Version interface not supported by module: %s, %s\n", module, dlerror()); + dlclose(dlhandle); + return NULL; + } + ver = sym; + version = ver(); + + if ((sym = dlsym(dlhandle, entry)) == NULL) + { + fprintf(stderr, "Expected entry point interface missing from module: %s, %s\n", module, dlerror()); + dlclose(dlhandle); + return NULL; + } + ep = sym; + modobj = ep(); + + fprintf(stderr, "Loaded module %s: %s\n", module, version); + register_module(module, type, dlhandle, version, modobj); + } + else + { + /* + * The module is already loaded, get the entry points again and + * return a reference to the already loaded module. + */ + modobj = mod->modobj; + } + + return modobj; +} + +/* + * Unload a module. + * + * No errors are returned since it is not clear that much can be done + * to fix issues relating to unloading modules. + * + * @param module The name of the module + */ +void +unload_module(const char *module) +{ +MODULES *mod = find_module(module); +void *handle; + + if (!mod) + return; + handle = mod->handle; + unregister_module(module); + dlclose(handle); +} + +/* + * Find a module that has been previously loaded and return the handle for that + * library + * + * @param module The name of the module + * @return The module handle or NULL if it was not found + */ +static MODULES * +find_module(const char *module) +{ +MODULES *ptr = registered; + + while (registered) + if (strcmp(registered->module, module)) + return registered; + else + registered = registered->next; + return NULL; +} + +/* + * Register a newly loaded module. The registration allows for single copies + * to be loaded and cached entry point information to be return. + * + * @param module The name of the module loaded + * @param type The type of the module loaded + * @param dlhandle The handle returned by dlopen + * @param version The version string returned by the module + * @param modobj The module object + */ +static void +register_module(const char *module, const char *type, void *dlhandle, char *version, void *modobj) +{ +MODULES *mod; + + if ((mod = malloc(sizeof(MODULES))) == NULL) + return; + mod->module = strdup(module); + mod->type = strdup(type); + mod->handle = dlhandle; + mod->version = strdup(version); + mod->modobj = modobj; + mod->next = registered; + registered = mod; +} + +/* + * Unregister a module + * + * @param module The name of the module to remove + */ +static void +unregister_module(const char *module) +{ +MODULES *mod = find_module(module); +MODULES *ptr; + + if (!mod) + return; // Module not found + if (registered == mod) + registered = mod->next; + else + { + ptr = registered; + while (ptr && ptr->next != mod) + ptr = ptr->next; + } + + /* + * The module is now not in the linked list and all + * memory related to it can be freed + */ + free(mod->module); + free(mod->type); + free(mod->version); + free(mod); +} + +/* + * Print Modules + * + * Diagnostic routine to display all the loaded modules + */ +void +printModules() +{ +MODULES *ptr = registered; + + printf("%-15s | %-10s | Version\n", "Module Name", "Module Type"); + printf("-----------------------------------------------------\n"); + while (ptr) + { + printf("%-15s | %-10s | %s\n", ptr->module, ptr->type, ptr->version); + ptr = ptr->next; + } +} diff --git a/include/gw.h b/include/gw.h index d288a0b18..fdb0b34de 100644 --- a/include/gw.h +++ b/include/gw.h @@ -55,7 +55,7 @@ int handle_event_errors(DCB *dcb, int event); int handle_event_errors_backend(DCB *dcb, int event); void MySQLListener(int epfd, char *config_bind); int MySQLAccept(DCB *listener, int efd); -int gw_mysql_do_authentication(DCB *dcb); +int gw_mysql_do_authentication(DCB *dcb, GWBUF *); void gw_mysql_close(MySQLProtocol **ptr); char *gw_strend(register const char *s); int do_read_dcb(DCB *dcb); diff --git a/include/modules.h b/include/modules.h new file mode 100644 index 000000000..fd24b4e41 --- /dev/null +++ b/include/modules.h @@ -0,0 +1,44 @@ +#ifndef _MODULES_H +#define _MODULES_H +/* + * This file is distributed as part of the SkySQL Gateway. It 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. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright SkySQL Ab 2013 + */ + +/* + * The module interface used within the gateway + * + * Revision History + * + * Date Who Description + * 13/06/13 Mark Riddoch Initial implementation + * + */ + +typedef struct modules { + char *module; /* The name of the module */ + char *type; /* The module type */ + char *version; /* Module version */ + void *handle; /* The handle returned by dlopen */ + void *modobj; /* The module "object" this is the set of entry points */ + struct modules + *next; /* Next module in the linked list */ +} MODULES; + +extern void *load_module(const char *module, const char *type, const char *entry); +extern void unload_module(const char *module); +extern void printModules(); +#endif