From bc6575839f8f826b6e3537f5f88970f4444a06c5 Mon Sep 17 00:00:00 2001 From: bigfoot547 Date: Sun, 23 Nov 2025 01:12:43 -0600 Subject: more work on plugins --- include/macros.h | 18 ++---- include/plugins.h | 76 +++++++++++++++++++++++-- meson.build | 4 +- plugins/epoll.c | 21 +++++++ plugins/meson.build | 4 +- plugins/skibidi.c | 6 -- src/main.c | 11 ++++ src/plugins.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++++---- 8 files changed, 257 insertions(+), 40 deletions(-) delete mode 100644 plugins/skibidi.c diff --git a/include/macros.h b/include/macros.h index b7a1bd2..6d093d7 100644 --- a/include/macros.h +++ b/include/macros.h @@ -6,25 +6,17 @@ #define UNUSED(...) ((void)((void) __VA_ARGS__)) #define NO_OP_DECL _Static_assert(1, "") -#ifdef __has_attribute -# if __has_attribute(unavailable) -# define PTX_UNAVAILABLE(_x) __attribute__((unavailable(_x))) -# define PTX_UNAVAILABLE_DEF(_x, _def) _def PTX_UNAVAILABLE(_x) -# else -# define PTX_UNAVAILABLE(_x) -# define PTX_UNAVAILABLE_DEF(_x, _def) /* definition hidden */ NO_OP_DECL -# endif -#else -# define PTX_UNAVAILABLE(_x) -# define PTX_UNAVAILABLE_DEF(_x, _def) /* definition hidden */ NO_OP_DECL -#endif +#define PTX_UNAVAILABLE_DEF(_def) /* definition hidden */ NO_OP_DECL #ifdef PTX_PLUGIN #define PTX_API PTX_IMPORT -#define PTX_INTERNAL(_def) PTX_UNAVAILABLE_DEF("internal API", _def) +#define PTX_INTERNAL(_def) NO_OP_DECL #else #define PTX_API PTX_EXPORT #define PTX_INTERNAL(_def) _def #endif +#define PTX_STR(_x) PTX_STR2(_x) +#define PTX_STR2(_x) #_x + #endif /* include guard */ diff --git a/include/plugins.h b/include/plugins.h index f6865e5..cbd841d 100644 --- a/include/plugins.h +++ b/include/plugins.h @@ -1,24 +1,92 @@ #ifndef PTX_PLUGINS_H_INCLUDED #define PTX_PLUGINS_H_INCLUDED +#include #include "macros.h" typedef struct ptx__plugin_manager_tag ptx_plugin_manager_t; typedef struct ptx__plugin_handle_tag ptx_plugin_t; +/* returns: + * - < 0 for plugin loading error (plugin will not be loaded) + * - 0 for successful plugin startup + * - (don't return values greater than 0 at this time) + * usage: + * Function will be called with this plugin instance and the "resume" pointer. + * + * The "resume" pointer will be NULL if the plugin is not being resumed. For this reason, it is not advised + * to use NULL as a flag value in the restart function itself. + */ +typedef int (ptx_plugin_init_proc_t)(ptx_plugin_t * /*plugin*/, void * /*resume*/); + +enum { + /* This plugin cannot be resumed at this time. The plugin manager will not unload it if it expects a "seamless" reload. + * The plugin is expected to act as if this function was not called. */ + PTX_PLUGIN_CANNOT_RESUME = -1, + + /* The plugin supports being resumed, but due to a transient error condition (malloc failure?) the plugin could not set itself up for + * resuming. If a plugin returns this value to the plugin manager, it is expected to act as if the function was not called. */ + PTX_PLUGIN_RESUME_ERR = -2, + + /* The plugin supports being resumed, but due to a transient error condition, the plugin could not set itself up for resuming. + * Additionally, as a part of preparing to be reloaded, the plugin performed operations it could not roll back. Likely, an error + * will be reported to the user, and the plugin will be unloaded. The plugin manager makes no assumptions about the behavior of the + * plugin at this time. */ + PTX_PLUGIN_RESUME_ERR_UNRECOVERABLE = -3, + + /* The plugin is ready to be resumed. */ + PTX_PLUGIN_RESUME_OK = 0 +}; + +/* returns: + * - one of the PTX_PLUGIN_* values in the above enum. if the return value is anything else, the plugin manager's behavior is not defined. + */ +typedef int (ptx_plugin_restart_proc_t)(ptx_plugin_t * /*plugin*/, void ** /*presume*/); + +#define PTX_MOD_VERSION (1u) + +/* the fields of this struct are public. ABI changes should be marked by incrementing PTX_MOD_VERSION */ +struct ptx_plugin_desc +{ + size_t desc_len; + unsigned desc_ver; + unsigned reserved; + + ptx_plugin_init_proc_t *init_proc; + ptx_plugin_restart_proc_t *restart_proc; + + const char *name; + const char *description; + const char *version; +}; + /* for testing */ #ifdef _CLANGD #undef PTX_PLUGIN #define PTX_PLUGIN 1 #endif -PTX_INTERNAL(ptx_plugin_manager_t *ptx_plugin_manager_new(void) ATTR_MALLOC() ATTR_MALLOC((ptx_plugin_manager_free, 1)) ATTR_WUR); -PTX_INTERNAL(void ptx_plugin_manager_free(ptx_plugin_manager_t *mgr)); - -PTX_INTERNAL(int ptx_plugin_manager_load_dir(ptx_plugin_manager_t *mgr, const char *dir)); +#define PTX__INTERNAL_PLUGIN_DESC_SYM ptx__plugin_spec #ifdef PTX_PLUGIN +#define PTX_PLUGIN_DESC_START PTX_EXPORT \ + const struct ptx_plugin_desc PTX__INTERNAL_PLUGIN_DESC_SYM = { \ + .desc_len = sizeof(struct ptx_plugin_desc), \ + .desc_ver = PTX_MOD_VERSION, + +#define PTX_PLUGIN_NAME(_x) .name = _x, +#define PTX_PLUGIN_DESCRIPTION(_x) .description = _x, +#define PTX_PLUGIN_VERSION(_x) .version = _x, + +#define PTX_PLUGIN_INIT(_x) .init_proc = _x, +#define PTX_PLUGIN_RESTART(_x) .restart_proc = _x, +#define PTX_PLUGIN_DESC_END }; #endif +PTX_INTERNAL(ptx_plugin_manager_t *ptx_plugin_manager_new(void) ATTR_MALLOC() ATTR_MALLOC((ptx_plugin_manager_free, 1)) ATTR_WUR); +PTX_INTERNAL(void ptx_plugin_manager_free(ptx_plugin_manager_t *mgr)); + +PTX_INTERNAL(int ptx_plugin_manager_load_dir(ptx_plugin_manager_t *mgr, const char *dir)); + #endif diff --git a/meson.build b/meson.build index f6b16e7..b5be67a 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('ptxmc', 'c', default_options : ['c_std=c11']) -add_project_arguments('-D_XOPEN_SOURCE=700', language : [ 'c' ]) +add_project_arguments('-D_GNU_SOURCE=1', language : [ 'c' ]) c_comp = meson.get_compiler('c') @@ -44,7 +44,7 @@ executable('ptxmc', prog_sources, plugin_kwargs = { 'gnu_symbol_visibility' : 'hidden', 'c_args' : ['-DPTX_PLUGIN=1'], - 'name_prefix' : '', + 'name_prefix' : 'm_', 'name_suffix' : 'ptxplugin' } diff --git a/plugins/epoll.c b/plugins/epoll.c index 5c1975a..f0a3970 100644 --- a/plugins/epoll.c +++ b/plugins/epoll.c @@ -1,2 +1,23 @@ #include +#include "plugins.h" +static int plugin_init(ptx_plugin_t *plugin, void *resume) +{ + UNUSED(plugin, resume); + return 0; +} + +static int plugin_restart(ptx_plugin_t *plugin, void **presume) +{ + UNUSED(plugin, presume); + return PTX_PLUGIN_CANNOT_RESUME; +} + +PTX_PLUGIN_DESC_START + PTX_PLUGIN_NAME("io_epoll") + PTX_PLUGIN_DESCRIPTION("provides epoll-based i/o") + PTX_PLUGIN_VERSION("0.1.0") + + PTX_PLUGIN_INIT(plugin_init) + PTX_PLUGIN_RESTART(plugin_restart) +PTX_PLUGIN_DESC_END diff --git a/plugins/meson.build b/plugins/meson.build index c14f536..84243c1 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -1,6 +1,4 @@ -shared_module('m_skibidi', 'skibidi.c', kwargs : plugin_kwargs) - if c_comp.check_header('sys/epoll.h') \ and c_comp.has_header_symbol('sys/epoll.h', 'epoll_create1') - shared_module('m_epoll', 'epoll.c', kwargs : plugin_kwargs) + shared_module('epoll', 'epoll.c', kwargs : plugin_kwargs) endif diff --git a/plugins/skibidi.c b/plugins/skibidi.c deleted file mode 100644 index d763058..0000000 --- a/plugins/skibidi.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "pipeline.h" - -PTX_EXPORT int do_something(void) -{ - return !!ptx_pipeline_new(4); -} diff --git a/src/main.c b/src/main.c index 50baf66..36eeaf7 100644 --- a/src/main.c +++ b/src/main.c @@ -4,6 +4,7 @@ #include #include "pipeline.h" +#include "plugins.h" PIPELINE_STAGE static int identity_stage_handle(const ptx_pipeline_ctx_t *ctx, const void *data, size_t sz) { @@ -119,5 +120,15 @@ int main(void) { ptx_pipeline_free(pl); + ptx_plugin_manager_t *mgr = ptx_plugin_manager_new(); + if (!mgr) { + fputs("Failed to allocate plugin manager\n", stderr); + return 1; + } + + ptx_plugin_manager_load_dir(mgr, "plugins"); + + ptx_plugin_manager_free(mgr); + return 0; } 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 #include #include - +#include +#include #include #include +#include +#include +#include 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); +} -- cgit v1.2.3-70-g09d2