mirror of
https://github.com/lxsang/ant-http
synced 2024-11-16 00:28:21 +01:00
252 lines
7.1 KiB
C
252 lines
7.1 KiB
C
#include <dlfcn.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <sys/sendfile.h>
|
|
#include <unistd.h>
|
|
#include "plugin_manager.h"
|
|
#include "lib/utils.h"
|
|
#include "lib/handle.h"
|
|
#include "http_server.h"
|
|
static void unload_plugin_by_name(const char *);
|
|
static void *plugin_from_file(char *name, char *path, dictionary_t conf);
|
|
|
|
/**
|
|
* Plugin table to store the loaded plugin
|
|
*/
|
|
static struct plugin_entry *plugin_table[HASHSIZE];
|
|
|
|
/**
|
|
* Locate a plugin in the plugin table
|
|
* @param s plugin name
|
|
* @return a plugin entry in the plugin table
|
|
*/
|
|
struct plugin_entry *plugin_lookup(char *s)
|
|
{
|
|
struct plugin_entry *np;
|
|
for (np = plugin_table[hash(s, HASHSIZE)]; np != NULL; np = np->next)
|
|
if (strcmp(s, np->pname) == 0)
|
|
return np; /* found */
|
|
return NULL; /* not found */
|
|
}
|
|
|
|
/**
|
|
* Load a plugin to the plugin table
|
|
* Only load when not available in the plugin table
|
|
* @param name plugin name
|
|
* @param config: plugin configuration
|
|
* @return pointer to the loaded plugin
|
|
*/
|
|
struct plugin_entry *plugin_load(char *name, dictionary_t pconf)
|
|
{
|
|
char *pname = NULL;
|
|
char path[BUFFLEN];
|
|
struct plugin_entry *np;
|
|
unsigned hashval;
|
|
plugin_header_t *(*metafn)();
|
|
plugin_header_t *meta = NULL;
|
|
config_t *sconf = config();
|
|
int fromfd, tofd;
|
|
char *error;
|
|
struct stat st;
|
|
int is_tmp = 0;
|
|
if (pconf)
|
|
{
|
|
pname = dvalue(pconf, "name");
|
|
}
|
|
if ((np = plugin_lookup(name)) == NULL)
|
|
{ /* not found */
|
|
LOG("Loading plugin: %s...", name);
|
|
np = (struct plugin_entry *)malloc(sizeof(*np));
|
|
if (np == NULL || name == NULL)
|
|
{
|
|
if (np)
|
|
free(np);
|
|
return NULL;
|
|
}
|
|
|
|
(void)snprintf(path, sizeof(path), "%s/%s%s", sconf->plugins_dir, name, sconf->plugins_ext);
|
|
if (pname && strcmp(name, pname) != 0)
|
|
{
|
|
// copy plugin file to tmpdir
|
|
(void)snprintf(path, sizeof(path), "%s/%s%s", sconf->plugins_dir, pname, sconf->plugins_ext);
|
|
LOG("Original plugin file: %s", path);
|
|
if ((fromfd = open(path, O_RDONLY)) < 0)
|
|
{
|
|
ERROR("Unable to open file for reading %s: %s", path, strerror(errno));
|
|
return NULL;
|
|
}
|
|
if (stat(path, &st) != 0)
|
|
{
|
|
close(fromfd);
|
|
ERROR("Unable to get file stat %s: %s", path, strerror(errno));
|
|
return NULL;
|
|
}
|
|
(void)snprintf(path, sizeof(path), "%s/%s%s", sconf->tmpdir, name, sconf->plugins_ext);
|
|
LOG("TMP plugin file: %s", path);
|
|
if ((tofd = open(path, O_WRONLY | O_CREAT, 0600)) < 0)
|
|
{
|
|
close(fromfd);
|
|
ERROR("Unable open file for reading %s: %s", path, strerror(errno));
|
|
return NULL;
|
|
}
|
|
if (sendfile(tofd, fromfd, NULL, st.st_size) != st.st_size)
|
|
{
|
|
close(fromfd);
|
|
close(tofd);
|
|
ERROR("Unable to copy file: %s", strerror(errno));
|
|
return NULL;
|
|
}
|
|
is_tmp = 1;
|
|
}
|
|
|
|
np->pname = strdup(name);
|
|
np->handle = plugin_from_file(name, path, pconf);
|
|
if (is_tmp)
|
|
{
|
|
(void)remove(path);
|
|
}
|
|
if (np->handle == NULL)
|
|
{
|
|
if (np->pname)
|
|
free(np->pname);
|
|
if (np)
|
|
free(np);
|
|
return NULL;
|
|
}
|
|
hashval = hash(name, HASHSIZE);
|
|
np->next = plugin_table[hashval];
|
|
plugin_table[hashval] = np;
|
|
}
|
|
else /* already there */
|
|
{
|
|
LOG("The plugin %s id already loaded", name);
|
|
}
|
|
|
|
// check if plugin is ready
|
|
metafn = (plugin_header_t * (*)()) dlsym(np->handle, "meta");
|
|
if ((error = dlerror()) != NULL)
|
|
{
|
|
ERROR("Unable to fetch plugin meta-data: [%s] %s", name, error);
|
|
unload_plugin_by_name(name);
|
|
free(np);
|
|
return NULL;
|
|
}
|
|
meta = metafn();
|
|
LOG("PLugin status: [%s] %d", name, meta->status);
|
|
if (!meta || meta->status != ANTD_PLUGIN_READY)
|
|
{
|
|
ERROR("Plugin is not ready or error: [%s].", name);
|
|
unload_plugin_by_name(name);
|
|
free(np);
|
|
return NULL;
|
|
}
|
|
return np;
|
|
}
|
|
/**
|
|
* Find a plugin in a file, and load it in to the plugin table
|
|
* @param name Name of the plugin
|
|
* @return
|
|
*/
|
|
static void *plugin_from_file(char *name, char *path, dictionary_t conf)
|
|
{
|
|
void *lib_handle;
|
|
char *error;
|
|
config_t *cnf = config();
|
|
void (*fn)(plugin_header_t *, dictionary_t);
|
|
lib_handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL /*| RTLD_NODELETE*/);
|
|
if (!lib_handle)
|
|
{
|
|
ERROR("Cannot load plugin '%s' : '%s'", name, dlerror());
|
|
return NULL;
|
|
}
|
|
// set database path
|
|
fn = (void (*)(plugin_header_t *, dictionary_t))dlsym(lib_handle, "__init_plugin__");
|
|
if ((error = dlerror()) != NULL)
|
|
ERROR("Problem when finding plugin init function for %s : %s", name, error);
|
|
else
|
|
{
|
|
plugin_header_t header;
|
|
strncpy(header.name, name, MAX_PATH_LEN - 1);
|
|
strncpy(header.dbpath, cnf->db_path, MAX_PATH_LEN - 1);
|
|
strncpy(header.tmpdir, cnf->tmpdir, MAX_PATH_LEN - 1);
|
|
strncpy(header.pdir, cnf->plugins_dir, MAX_PATH_LEN - 1);
|
|
header.config = conf;
|
|
header.raw_body = 0;
|
|
header.status = ANTD_PLUGIN_INIT;
|
|
(*fn)(&header, conf);
|
|
}
|
|
// trick libc that we close this lib, but it is not realy deleted
|
|
return lib_handle;
|
|
}
|
|
|
|
void unload_plugin(struct plugin_entry *np)
|
|
{
|
|
char *error;
|
|
void (*fn)() = NULL;
|
|
// find and execute the exit function
|
|
fn = (void (*)())dlsym(np->handle, "__release__");
|
|
if ((error = dlerror()) != NULL)
|
|
{
|
|
ERROR("Cant not release plugin %s : %s", np->pname, error);
|
|
}
|
|
if (fn)
|
|
{
|
|
(*fn)();
|
|
}
|
|
dlclose(np->handle);
|
|
LOG("Unloaded %s", np->pname);
|
|
// free((void *) np->handle);
|
|
if (np->pname)
|
|
free((void *)np->pname);
|
|
}
|
|
/*
|
|
Unload a plugin by its name
|
|
*/
|
|
void unload_plugin_by_name(const char *name)
|
|
{
|
|
struct plugin_entry *np;
|
|
int hasval = hash(name, HASHSIZE);
|
|
np = plugin_table[hasval];
|
|
if (strcmp(np->pname, name) == 0)
|
|
{
|
|
unload_plugin(np);
|
|
plugin_table[hasval] = np->next;
|
|
}
|
|
else
|
|
{
|
|
for (np = plugin_table[hasval]; np != NULL; np = np->next)
|
|
{
|
|
if (np->next != NULL && strcmp(name, np->next->pname) == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (np == NULL)
|
|
return; // the plugin is is not loaded
|
|
unload_plugin(np->next);
|
|
np->next = np->next->next;
|
|
}
|
|
}
|
|
/**
|
|
* Unload all the plugin loaded on the plugin table
|
|
*/
|
|
void unload_all_plugin()
|
|
{
|
|
LOG("Unload all plugins");
|
|
for (int i = 0; i < HASHSIZE; i++)
|
|
{
|
|
struct plugin_entry **np, *curr;
|
|
np = &plugin_table[i];
|
|
|
|
while ((curr = *np) != NULL)
|
|
{
|
|
(*np) = (*np)->next;
|
|
unload_plugin(curr);
|
|
free(curr);
|
|
}
|
|
plugin_table[i] = NULL;
|
|
}
|
|
exit(0);
|
|
}
|