From d4ca93886613bff7dce5708883304477911d60c4 Mon Sep 17 00:00:00 2001 From: Love Hornquist Astrand Date: Fri, 17 Jul 2009 15:18:00 -0700 Subject: [PATCH] Only load plugins once and never unload them Its expensive to load and unload plugins all the time, so lets stop doing that. Run over the plugin directory and load all plugins and remember them all. In the future, something should watch the directory and if it changes, load the new plugins that was put there. --- lib/krb5/plugin.c | 199 +++++++++++++++++++++++++++------------------- 1 file changed, 116 insertions(+), 83 deletions(-) diff --git a/lib/krb5/plugin.c b/lib/krb5/plugin.c index 844cb7ab8..7b8466b82 100644 --- a/lib/krb5/plugin.c +++ b/lib/krb5/plugin.c @@ -40,19 +40,28 @@ struct krb5_plugin { void *symbol; - void *dsohandle; struct krb5_plugin *next; }; struct plugin { - enum krb5_plugin_type type; - void *name; - void *symbol; + enum { DSO, SYMBOL } type; + union { + struct { + char *path; + void *dsohandle; + } dso; + struct { + enum krb5_plugin_type type; + char *name; + char *symbol; + } symbol; + } u; struct plugin *next; }; static HEIMDAL_MUTEX plugin_mutex = HEIMDAL_MUTEX_INITIALIZER; static struct plugin *registered = NULL; +static int plugins_needs_scan = 1; static const char *sysplugin_dirs[] = { LIBDIR "/plugin/krb5", @@ -85,14 +94,12 @@ _krb5_plugin_get_next(struct krb5_plugin *p) #ifdef HAVE_DLOPEN static krb5_error_code -loadlib(krb5_context context, - enum krb5_plugin_type type, - const char *name, - const char *lib, - struct krb5_plugin **e) +loadlib(krb5_context context, char *path) { - *e = calloc(1, sizeof(**e)); - if (*e == NULL) { + struct plugin *e; + + e = calloc(1, sizeof(*e)); + if (e == NULL) { krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } @@ -103,24 +110,13 @@ loadlib(krb5_context context, #ifndef RTLD_LOCAL #define RTLD_LOCAL 0 #endif + e->type = DSO; + /* ignore error from dlopen, and just keep it as negative cache entry */ + e->u.dso.dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY); + e->u.dso.path = path; - (*e)->dsohandle = dlopen(lib, RTLD_LOCAL|RTLD_LAZY); - if ((*e)->dsohandle == NULL) { - free(*e); - *e = NULL; - krb5_set_error_message(context, ENOMEM, "Failed to load %s: %s", - lib, dlerror()); - return ENOMEM; - } - - /* dlsym doesn't care about the type */ - (*e)->symbol = dlsym((*e)->dsohandle, name); - if ((*e)->symbol == NULL) { - dlclose((*e)->dsohandle); - free(*e); - krb5_clear_error_message(context); - return ENOMEM; - } + e->next = registered; + registered = e->next; return 0; } @@ -146,26 +142,35 @@ krb5_plugin_register(krb5_context context, { struct plugin *e; + HEIMDAL_MUTEX_lock(&plugin_mutex); + /* check for duplicates */ - for (e = registered; e != NULL; e = e->next) - if (e->type == type && strcmp(e->name,name)== 0 && e->symbol == symbol) + for (e = registered; e != NULL; e = e->next) { + if (e->type == SYMBOL && + strcmp(e->u.symbol.name, name) == 0 && + e->u.symbol.type == type && e->u.symbol.symbol == symbol) { + HEIMDAL_MUTEX_unlock(&plugin_mutex); return 0; + } + } e = calloc(1, sizeof(*e)); if (e == NULL) { + HEIMDAL_MUTEX_unlock(&plugin_mutex); krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } - e->type = type; - e->name = strdup(name); - if (e->name == NULL) { + e->type = SYMBOL; + e->u.symbol.type = type; + e->u.symbol.name = strdup(name); + if (e->u.symbol.name == NULL) { + HEIMDAL_MUTEX_unlock(&plugin_mutex); free(e); krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } - e->symbol = symbol; + e->u.symbol.symbol = symbol; - HEIMDAL_MUTEX_lock(&plugin_mutex); e->next = registered; registered = e; HEIMDAL_MUTEX_unlock(&plugin_mutex); @@ -173,41 +178,19 @@ krb5_plugin_register(krb5_context context, return 0; } -krb5_error_code -_krb5_plugin_find(krb5_context context, - enum krb5_plugin_type type, - const char *name, - struct krb5_plugin **list) +static krb5_error_code +load_plugins(krb5_context context) { - struct krb5_plugin *e; - struct plugin *p; + struct plugin *e; krb5_error_code ret; char **dirs = NULL, **di; struct dirent *entry; char *path; DIR *d = NULL; - *list = NULL; - - HEIMDAL_MUTEX_lock(&plugin_mutex); - - for (p = registered; p != NULL; p = p->next) { - if (p->type != type || strcmp(p->name, name) != 0) - continue; - - e = calloc(1, sizeof(*e)); - if (e == NULL) { - HEIMDAL_MUTEX_unlock(&plugin_mutex); - ret = ENOMEM; - krb5_set_error_message(context, ret, "malloc: out of memory"); - goto out; - } - e->symbol = p->symbol; - e->dsohandle = NULL; - e->next = *list; - *list = e; - } - HEIMDAL_MUTEX_unlock(&plugin_mutex); + if (!plugins_needs_scan) + return 0; + plugins_needs_scan = 0; #ifdef HAVE_DLOPEN @@ -244,21 +227,83 @@ _krb5_plugin_find(krb5_context context, if (path == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); - goto out; + return ret; + } + + /* check if already tried */ + for (e = registered; e != NULL; e = e->next) + if (e->type == DSO && strcmp(e->u.dso.path, path) == 0) + break; + if (e == NULL) { + ret = loadlib(context, path); + if (ret) + free(path); } - ret = loadlib(context, type, name, path, &e); - free(path); - if (ret) - continue; - - e->next = *list; - *list = e; } closedir(d); } if (dirs != rk_UNCONST(sysplugin_dirs)) krb5_config_free_strings(dirs); #endif /* HAVE_DLOPEN */ + return 0; +} + +static krb5_error_code +add_symbol(krb5_context context, struct krb5_plugin **list, void *symbol) +{ + struct krb5_plugin *e; + + e = calloc(1, sizeof(*e)); + if (e == NULL) { + krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); + return ENOMEM; + } + e->symbol = symbol; + e->next = *list; + *list = e; + return 0; +} + +krb5_error_code +_krb5_plugin_find(krb5_context context, + enum krb5_plugin_type type, + const char *name, + struct krb5_plugin **list) +{ + struct plugin *e; + krb5_error_code ret; + + *list = NULL; + + HEIMDAL_MUTEX_lock(&plugin_mutex); + + load_plugins(context); + + for (ret = 0, e = registered; e != NULL; e = e->next) { + switch(e->type) { + case DSO: { + void *sym; + if (e->u.dso.dsohandle == NULL) + continue; + sym = dlsym(e->u.dso.dsohandle, name); + if (sym) + ret = add_symbol(context, list, sym); + break; + } + case SYMBOL: + if (strcmp(e->u.symbol.name, name) == 0 && e->u.symbol.type == type) + ret = add_symbol(context, list, e->u.symbol.symbol); + break; + } + if (ret) { + _krb5_plugin_free(*list); + *list = NULL; + } + } + + HEIMDAL_MUTEX_unlock(&plugin_mutex); + if (ret) + return ret; if (*list == NULL) { krb5_set_error_message(context, ENOENT, "Did not find a plugin for %s", name); @@ -266,16 +311,6 @@ _krb5_plugin_find(krb5_context context, } return 0; - -out: - if (dirs != rk_UNCONST(sysplugin_dirs)) - krb5_config_free_strings(dirs); - if (d) - closedir(d); - _krb5_plugin_free(*list); - *list = NULL; - - return ret; } void @@ -284,8 +319,6 @@ _krb5_plugin_free(struct krb5_plugin *list) struct krb5_plugin *next; while (list) { next = list->next; - if (list->dsohandle) - dlclose(list->dsohandle); free(list); list = next; }