diff options
Diffstat (limited to 'src/plugins.c')
| -rw-r--r-- | src/plugins.c | 157 |
1 files changed, 145 insertions, 12 deletions
diff --git a/src/plugins.c b/src/plugins.c index f2f8d14..b96aade 100644 --- a/src/plugins.c +++ b/src/plugins.c @@ -3,19 +3,35 @@ #include <stdio.h> #include <errno.h> #include <string.h> - +#include <assert.h> +#include <dlfcn.h> #include <sys/types.h> #include <dirent.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> struct ptx__plugin_manager_tag { - size_t sz_plugins; - ptx_plugin_t *plugins; + ptx_plugin_t *pl_head, *pl_tail; }; struct ptx__plugin_handle_tag { - char *path; + /* what plugin manager is this plugin known to? */ + ptx_plugin_manager_t *manager; + + /* handle to library */ + void *modp; + const struct ptx_plugin_desc *desc; + + /* plugin list entries */ + ptx_plugin_t *prev; + ptx_plugin_t *next; + + int path_fd; + /* plugin filename */ + char fname[1]; }; ptx_plugin_manager_t *ptx_plugin_manager_new(void) @@ -24,40 +40,157 @@ ptx_plugin_manager_t *ptx_plugin_manager_new(void) if (!mgr) return NULL; /* do init */ - mgr->sz_plugins = 0; - mgr->plugins = NULL; + mgr->pl_head = mgr->pl_tail = NULL; return mgr; } +/* inform the plugin that it is about to be unloaded. It should clean up its resources. */ +static void deinit_plugin(ptx_plugin_t *plugin); + +/* unload/cleanup/free the plugin. */ +static void free_plugin(ptx_plugin_t *plugin); + void ptx_plugin_manager_free(ptx_plugin_manager_t *mgr) { if (!mgr) return; /* do cleanup */ + for (ptx_plugin_t *plugin = mgr->pl_head, *next; plugin; plugin = next) { + next = plugin->next; + deinit_plugin(plugin); + free_plugin(plugin); + } free(mgr); return; } -int ptx_plugin_manager_load_dir(ptx_plugin_manager_t *mgr, const char *dirnam) +static ptx_plugin_t *load_plugin(ptx_plugin_manager_t *manager, const char *plugin_file, const char *plugin_fname) +{ + /* define and initialize all variables to be cleaned up here! */ + size_t fnamelen = strlen(plugin_fname); + + ptx_plugin_t *plugin = NULL; + void *mod_dl = NULL; + const struct ptx_plugin_desc *pl_desc; + + plugin = calloc(1, sizeof(ptx_plugin_t) + fnamelen); + if (!plugin) { + fprintf(stderr, "(plugin) malloc (loading %s): %s\n", plugin_file, strerror(errno)); + /* FIXME: maybe abort() for these sorts of issues? */ + goto cleanup; + } + + plugin->manager = manager; + memcpy(plugin->fname, plugin_fname, fnamelen); /* NUL-termination achieved thanks to calloc */ + + mod_dl = dlopen(plugin_file, RTLD_NOW | RTLD_LOCAL); + if (!mod_dl) { + fprintf(stderr, "(plugin) dlopen: failed to load %s: %s\n", plugin_fname, dlerror()); + goto cleanup; + } + + plugin->modp = mod_dl; + + pl_desc = dlsym(mod_dl, PTX_STR(PTX__INTERNAL_PLUGIN_DESC_SYM)); + if (!pl_desc) { + fprintf(stderr, "(plugin) dlsym(descriptor): failed to load %s: %s\n", plugin_fname, dlerror()); + goto cleanup; + } + + plugin->desc = pl_desc; + + return plugin; + +cleanup: + if (mod_dl) dlclose(mod_dl); + free(plugin); + + return NULL; +} + +#define PATHBUF_LEN (32) +int ptx_plugin_manager_load_dir(ptx_plugin_manager_t *mgr, const char *dirname) { struct dirent *ent; - DIR *dir = opendir(dirnam); + char pl_pathbuf[PATHBUF_LEN] = { 0 }; + struct stat st; + + DIR *dir = opendir(dirname); if (!dir) { - fprintf(stderr, "Failed to load plugins from %s (opendir): %s\n", dirnam, strerror(errno)); + fprintf(stderr, "Failed to load plugins from %s (opendir): %s\n", dirname, strerror(errno)); return -1; } + int pl_dir_fd = dirfd(dir); + assert(pl_dir_fd >= 0); + while ((errno = 0, ent = readdir(dir))) { - + int pl_fd = openat(pl_dir_fd, ent->d_name, O_PATH); + if (pl_fd < 0) { + fprintf(stderr, "(plugin) openat: failed to open %s/%s: %s\n", dirname, ent->d_name, strerror(errno)); + continue; + } + + if (fstat(pl_fd, &st) < 0) { + fprintf(stderr, "(plugin) fstat: failed to stat %s/%s: %s\n", dirname, ent->d_name, strerror(errno)); + goto cleanup_iter; + } + + if (!S_ISREG(st.st_mode)) { + goto cleanup_iter; /* not a file. skip silently */ + } + + int rlen = snprintf(pl_pathbuf, PATHBUF_LEN, "/proc/self/fd/%d", pl_fd); + assert(rlen < PATHBUF_LEN); + assert(pl_pathbuf[PATHBUF_LEN-1] == '\0'); + + ptx_plugin_t *plugin = load_plugin(mgr, pl_pathbuf, ent->d_name); + if (!plugin) goto cleanup_iter; /* error has been reported by load_plugin already. */ + + plugin->path_fd = pl_fd; + + if (mgr->pl_head) { + assert(mgr->pl_tail); + + mgr->pl_tail->next = plugin; + plugin->prev = mgr->pl_tail; + plugin->next = NULL; + mgr->pl_tail = plugin; + } else { + assert(!mgr->pl_tail); + plugin->prev = plugin->next = NULL; + mgr->pl_head = mgr->pl_tail = plugin; + } + + continue; + +cleanup_iter: + close(pl_fd); } - if (errno != 0) { - fprintf(stderr, "Error reading entries from %s (readdir): %s\n", dirnam, strerror(errno)); + int errno_save = errno; + closedir(dir); + + if (errno_save != 0) { + fprintf(stderr, "Error reading entries from %s (readdir): %s\n", dirname, strerror(errno)); return -1; } return 0; } + +static void deinit_plugin(ptx_plugin_t *plugin) +{ + /* TODO */ + UNUSED(plugin); +} + +static void free_plugin(ptx_plugin_t *plugin) +{ + dlclose(plugin->modp); + close(plugin->path_fd); + free(plugin); +} |
