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.
This commit is contained in:
Love Hornquist Astrand
2009-07-17 15:18:00 -07:00
parent de5110c05a
commit d4ca938866

View File

@@ -40,19 +40,28 @@
struct krb5_plugin { struct krb5_plugin {
void *symbol; void *symbol;
void *dsohandle;
struct krb5_plugin *next; struct krb5_plugin *next;
}; };
struct plugin { struct plugin {
enum krb5_plugin_type type; enum { DSO, SYMBOL } type;
void *name; union {
void *symbol; struct {
char *path;
void *dsohandle;
} dso;
struct {
enum krb5_plugin_type type;
char *name;
char *symbol;
} symbol;
} u;
struct plugin *next; struct plugin *next;
}; };
static HEIMDAL_MUTEX plugin_mutex = HEIMDAL_MUTEX_INITIALIZER; static HEIMDAL_MUTEX plugin_mutex = HEIMDAL_MUTEX_INITIALIZER;
static struct plugin *registered = NULL; static struct plugin *registered = NULL;
static int plugins_needs_scan = 1;
static const char *sysplugin_dirs[] = { static const char *sysplugin_dirs[] = {
LIBDIR "/plugin/krb5", LIBDIR "/plugin/krb5",
@@ -85,14 +94,12 @@ _krb5_plugin_get_next(struct krb5_plugin *p)
#ifdef HAVE_DLOPEN #ifdef HAVE_DLOPEN
static krb5_error_code static krb5_error_code
loadlib(krb5_context context, loadlib(krb5_context context, char *path)
enum krb5_plugin_type type,
const char *name,
const char *lib,
struct krb5_plugin **e)
{ {
*e = calloc(1, sizeof(**e)); struct plugin *e;
if (*e == NULL) {
e = calloc(1, sizeof(*e));
if (e == NULL) {
krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM; return ENOMEM;
} }
@@ -103,24 +110,13 @@ loadlib(krb5_context context,
#ifndef RTLD_LOCAL #ifndef RTLD_LOCAL
#define RTLD_LOCAL 0 #define RTLD_LOCAL 0
#endif #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); e->next = registered;
if ((*e)->dsohandle == NULL) { registered = e->next;
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;
}
return 0; return 0;
} }
@@ -146,26 +142,35 @@ krb5_plugin_register(krb5_context context,
{ {
struct plugin *e; struct plugin *e;
HEIMDAL_MUTEX_lock(&plugin_mutex);
/* check for duplicates */ /* check for duplicates */
for (e = registered; e != NULL; e = e->next) for (e = registered; e != NULL; e = e->next) {
if (e->type == type && strcmp(e->name,name)== 0 && e->symbol == symbol) 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; return 0;
}
}
e = calloc(1, sizeof(*e)); e = calloc(1, sizeof(*e));
if (e == NULL) { if (e == NULL) {
HEIMDAL_MUTEX_unlock(&plugin_mutex);
krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM; return ENOMEM;
} }
e->type = type; e->type = SYMBOL;
e->name = strdup(name); e->u.symbol.type = type;
if (e->name == NULL) { e->u.symbol.name = strdup(name);
if (e->u.symbol.name == NULL) {
HEIMDAL_MUTEX_unlock(&plugin_mutex);
free(e); free(e);
krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM; return ENOMEM;
} }
e->symbol = symbol; e->u.symbol.symbol = symbol;
HEIMDAL_MUTEX_lock(&plugin_mutex);
e->next = registered; e->next = registered;
registered = e; registered = e;
HEIMDAL_MUTEX_unlock(&plugin_mutex); HEIMDAL_MUTEX_unlock(&plugin_mutex);
@@ -173,41 +178,19 @@ krb5_plugin_register(krb5_context context,
return 0; return 0;
} }
krb5_error_code static krb5_error_code
_krb5_plugin_find(krb5_context context, load_plugins(krb5_context context)
enum krb5_plugin_type type,
const char *name,
struct krb5_plugin **list)
{ {
struct krb5_plugin *e; struct plugin *e;
struct plugin *p;
krb5_error_code ret; krb5_error_code ret;
char **dirs = NULL, **di; char **dirs = NULL, **di;
struct dirent *entry; struct dirent *entry;
char *path; char *path;
DIR *d = NULL; DIR *d = NULL;
*list = NULL; if (!plugins_needs_scan)
return 0;
HEIMDAL_MUTEX_lock(&plugin_mutex); plugins_needs_scan = 0;
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);
#ifdef HAVE_DLOPEN #ifdef HAVE_DLOPEN
@@ -244,21 +227,83 @@ _krb5_plugin_find(krb5_context context,
if (path == NULL) { if (path == NULL) {
ret = ENOMEM; ret = ENOMEM;
krb5_set_error_message(context, ret, "malloc: out of memory"); 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); closedir(d);
} }
if (dirs != rk_UNCONST(sysplugin_dirs)) if (dirs != rk_UNCONST(sysplugin_dirs))
krb5_config_free_strings(dirs); krb5_config_free_strings(dirs);
#endif /* HAVE_DLOPEN */ #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) { if (*list == NULL) {
krb5_set_error_message(context, ENOENT, "Did not find a plugin for %s", name); 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; 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 void
@@ -284,8 +319,6 @@ _krb5_plugin_free(struct krb5_plugin *list)
struct krb5_plugin *next; struct krb5_plugin *next;
while (list) { while (list) {
next = list->next; next = list->next;
if (list->dsohandle)
dlclose(list->dsohandle);
free(list); free(list);
list = next; list = next;
} }