#include "plugins.h" #include #include #include #include #include #include #include #include #include #include #include struct ptx__plugin_manager_tag { ptx_plugin_t *pl_head, *pl_tail; }; struct ptx__plugin_handle_tag { /* 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) { ptx_plugin_manager_t *mgr = malloc(sizeof(ptx_plugin_manager_t)); if (!mgr) return NULL; /* do init */ 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; } 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; 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", 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); } 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); }