diff options
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | include/plugins.h | 30 | ||||
| -rw-r--r-- | src/plugins.c | 91 |
3 files changed, 108 insertions, 17 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..a3c50fc --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# **P**rotocol **T**ool**b**ox for **M**ine**c**raft (PTXMC) +PTXMC is a modular tool for dissecting and manipulating the Minecraft Java Edition multiplayer protocol. + +The tool is developed on and for Linux at this time (ports may come later). diff --git a/include/plugins.h b/include/plugins.h index cbd841d..6d4a87d 100644 --- a/include/plugins.h +++ b/include/plugins.h @@ -8,7 +8,7 @@ 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 plugin loading error (plugin will be unloaded) * - 0 for successful plugin startup * - (don't return values greater than 0 at this time) * usage: @@ -22,30 +22,30 @@ 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, + DEINIT_ERR_RESUME_UNSUPPORTED = -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, + DEINIT_ERR_RESUME_FAILURE = -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, + DEINIT_ERR_RESUME_UNRECOVERABLE = -3, - /* The plugin is ready to be resumed. */ - PTX_PLUGIN_RESUME_OK = 0 + /* The plugin is ready to be resumed/unloaded. */ + DEINIT_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. + * - one of the 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*/); +typedef int (ptx_plugin_deinit_proc_t)(ptx_plugin_t * /*plugin*/, void ** /*presume*/); -#define PTX_MOD_VERSION (1u) +#define PTX_CUR_PLUGIN_VERSION (1u) -/* the fields of this struct are public. ABI changes should be marked by incrementing PTX_MOD_VERSION */ +/* the fields of this struct are public. ABI changes should be marked by incrementing PTX_CUR_PLUGIN_VERSION */ struct ptx_plugin_desc { size_t desc_len; @@ -53,7 +53,7 @@ struct ptx_plugin_desc unsigned reserved; ptx_plugin_init_proc_t *init_proc; - ptx_plugin_restart_proc_t *restart_proc; + ptx_plugin_deinit_proc_t *deinit_proc; const char *name; const char *description; @@ -72,7 +72,7 @@ struct ptx_plugin_desc #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, + .desc_ver = PTX_CUR_PLUGIN_VERSION, #define PTX_PLUGIN_NAME(_x) .name = _x, #define PTX_PLUGIN_DESCRIPTION(_x) .description = _x, @@ -89,4 +89,10 @@ 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)); +/* initializes plugins which have not yet been initialized. returns the number of plugins which have been initialized and are still loaded + * (plugins can return a failure from their initialization function, at which point the plugin will be unloaded.) */ +PTX_INTERNAL(int ptx_plugin_manager_init_plugins(ptx_plugin_manager_t *mgr)); + +PTX_API void ptx_plugin_set_mainloop_func(ptx_plugin_t *plugin, void *func); + #endif diff --git a/src/plugins.c b/src/plugins.c index b96aade..8ccde06 100644 --- a/src/plugins.c +++ b/src/plugins.c @@ -30,10 +30,22 @@ struct ptx__plugin_handle_tag ptx_plugin_t *next; int path_fd; + int pl_state; /* used to assert that the application is following its contract with plugins */ + /* plugin filename */ char fname[1]; }; +enum { + PL_STATE_NEW, /* plugin struct just initialized */ + PL_STATE_LOADED, /* plugin module loaded */ + PL_STATE_INITIALIZING, /* plugin initializer call started */ + PL_STATE_INITIALIZED, /* plugin initializer completed */ + PL_STATE_DEINITIALIZED, /* plugin deinitializer called */ + PL_STATE_ERROR, /* error in initializer, only valid operation is free */ + PL_STATE_UNLOADED /* plugin unloaded */ +}; + ptx_plugin_manager_t *ptx_plugin_manager_new(void) { ptx_plugin_manager_t *mgr = malloc(sizeof(ptx_plugin_manager_t)); @@ -46,7 +58,7 @@ ptx_plugin_manager_t *ptx_plugin_manager_new(void) } /* inform the plugin that it is about to be unloaded. It should clean up its resources. */ -static void deinit_plugin(ptx_plugin_t *plugin); +static int deinit_plugin(ptx_plugin_t *plugin, void **presume); /* unload/cleanup/free the plugin. */ static void free_plugin(ptx_plugin_t *plugin); @@ -58,7 +70,7 @@ void ptx_plugin_manager_free(ptx_plugin_manager_t *mgr) /* do cleanup */ for (ptx_plugin_t *plugin = mgr->pl_head, *next; plugin; plugin = next) { next = plugin->next; - deinit_plugin(plugin); + deinit_plugin(plugin, NULL); /* result ignored (not resuming) */ free_plugin(plugin); } @@ -82,6 +94,7 @@ static ptx_plugin_t *load_plugin(ptx_plugin_manager_t *manager, const char *plug goto cleanup; } + plugin->pl_state = PL_STATE_NEW; plugin->manager = manager; memcpy(plugin->fname, plugin_fname, fnamelen); /* NUL-termination achieved thanks to calloc */ @@ -99,8 +112,19 @@ static ptx_plugin_t *load_plugin(ptx_plugin_manager_t *manager, const char *plug goto cleanup; } + if (pl_desc->desc_len != sizeof(struct ptx_plugin_desc)) { + fprintf(stderr, "(plugin) %s: descriptor struct size mismatch: %zu != %zu (expected)\n", plugin_fname, pl_desc->desc_len, sizeof(struct ptx_plugin_desc)); + goto cleanup; + } + + if (pl_desc->desc_ver != PTX_CUR_PLUGIN_VERSION) { + fprintf(stderr, "(plugin) %s: is for a different version of ptxmc! %u != %u (expected)\n", plugin_fname, pl_desc->desc_ver, PTX_CUR_PLUGIN_VERSION); + goto cleanup; + } + plugin->desc = pl_desc; + plugin->pl_state = PL_STATE_LOADED; return plugin; cleanup: @@ -182,15 +206,72 @@ cleanup_iter: return 0; } -static void deinit_plugin(ptx_plugin_t *plugin) +static int init_plugin(ptx_plugin_t *plugin, void *resume) { - /* TODO */ - UNUSED(plugin); + assert(plugin); + assert(plugin->pl_state == plugin->pl_state == PL_STATE_LOADED); + + plugin->pl_state = PL_STATE_INITIALIZING; + int result = 0; + + if (plugin->desc->init_proc) { + result = (plugin->desc->init_proc)(plugin, resume); + } else { + fprintf(stderr, "(plugin) warning: plugin %s/%s has no initialization function! (does it do anything?)\n", plugin->desc->name, plugin->fname); + } + + if (result < 0) { + plugin->pl_state = PL_STATE_ERROR; + fprintf(stderr, "(plugin) init_plugin: %s/%s: initializer failed (%d)\n", plugin->desc->name, plugin->fname, result); + } else { + plugin->pl_state = PL_STATE_INITIALIZED; + } + + return result; +} + +int ptx_plugin_manager_init_plugins(ptx_plugin_manager_t *mgr) +{ + for (ptx_plugin_t *plugin = mgr->pl_head; plugin; plugin = plugin->next) + { + if (plugin->pl_state >= PL_STATE_INITIALIZED) continue; + assert(plugin->pl_state != PL_STATE_INITIALIZING); /* function should never be called from a plugin initializer */ + + init_plugin(plugin, NULL); + } +} + +static int deinit_plugin(ptx_plugin_t *plugin, void **presume) +{ + assert(plugin); + assert(plugin->pl_state >= PL_STATE_INITIALIZED && plugin->pl_state < PL_STATE_DEINITIALIZED); + + int result = DEINIT_OK; + if (plugin->desc->deinit_proc) { + result = (plugin->desc->deinit_proc)(plugin, presume); + } else { + result = presume ? DEINIT_ERR_RESUME_UNSUPPORTED : DEINIT_OK; + } + + plugin->pl_state = PL_STATE_DEINITIALIZED; + return result; } static void free_plugin(ptx_plugin_t *plugin) { + assert(plugin->pl_state < PL_STATE_INITIALIZED || plugin->pl_state >= PL_STATE_DEINITIALIZED); + dlclose(plugin->modp); close(plugin->path_fd); + + plugin->pl_state = PL_STATE_UNLOADED; /* state never observed by the software */ + free(plugin); } + +PTX_API void ptx_plugin_set_mainloop_func(ptx_plugin_t *plugin, void *func) +{ + assert(plugin); + assert(plugin->pl_state == PL_STATE_INITIALIZING || plugin->pl_state == PL_STATE_INITIALIZED); + UNUSED(plugin, func); +} |
