summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar bigfoot547 <[email protected]>2025-11-23 01:12:43 -0600
committerLibravatar bigfoot547 <[email protected]>2025-11-23 01:12:43 -0600
commitbc6575839f8f826b6e3537f5f88970f4444a06c5 (patch)
treea1f9973beda654b77690bcf5672335e179f0a7c3
parentignore this commit (diff)
more work on plugins
-rw-r--r--include/macros.h18
-rw-r--r--include/plugins.h76
-rw-r--r--meson.build4
-rw-r--r--plugins/epoll.c21
-rw-r--r--plugins/meson.build4
-rw-r--r--plugins/skibidi.c6
-rw-r--r--src/main.c11
-rw-r--r--src/plugins.c157
8 files changed, 257 insertions, 40 deletions
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 <stddef.h>
#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 <sys/epoll.h>
+#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 <stdarg.h>
#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 <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);
+}