From 705a946882a51c8d2a7f31f8c4cbac53d8d14d98 Mon Sep 17 00:00:00 2001 From: Artem Shinkarov Date: Sun, 18 Aug 2013 03:55:20 +0100 Subject: [PATCH] Adding plugin interface for C files. Implementation of hooks using C interface basedon dlfcn. Added test plugin and makefile to build it. In order to test it add the followin into your profrc: [plugins] load=test-c-plugin.so and execute profanity piping stderr to some file. The file should contain all entries whenever the plugin function is triggered. It seem to be workin but some parts are missing. --- Makefile.am | 2 + configure.ac | 2 +- plugins/test-c-plugin/Makefile | 12 +++ plugins/test-c-plugin/test-c-plugin.c | 31 +++++++ src/plugins/c_plugins.c | 123 ++++++++++++++++++++++++++ src/plugins/c_plugins.h | 14 +++ src/plugins/plugins.c | 30 ++++++- src/plugins/plugins.h | 3 +- 8 files changed, 214 insertions(+), 3 deletions(-) create mode 100644 plugins/test-c-plugin/Makefile create mode 100644 plugins/test-c-plugin/test-c-plugin.c create mode 100644 src/plugins/c_plugins.c create mode 100644 src/plugins/c_plugins.h diff --git a/Makefile.am b/Makefile.am index 5afdf58d..92dc7a94 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,6 +26,7 @@ profanity_SOURCES = \ src/plugins/api.h src/plugins/api.c \ src/plugins/callbacks.h src/plugins/callbacks.c \ src/plugins/python_plugins.h src/plugins/python_plugins.c \ + src/plugins/c_plugins.h src/plugins/c_plugins.c \ src/plugins/python_api.h src/plugins/python_api.c TESTS = tests/testsuite @@ -57,6 +58,7 @@ tests_testsuite_SOURCES = \ src/plugins/api.h src/plugins/api.c \ src/plugins/callbacks.h src/plugins/callbacks.c \ src/plugins/python_plugins.h src/plugins/python_plugins.c \ + src/plugins/c_plugins.h src/plugins/c_plugins.c \ src/plugins/python_api.h src/plugins/python_api.c \ tests/test_roster.c tests/test_common.c tests/test_history.c \ tests/test_autocomplete.c tests/testsuite.c tests/test_parser.c \ diff --git a/configure.ac b/configure.ac index a1b462de..c277c105 100644 --- a/configure.ac +++ b/configure.ac @@ -88,7 +88,7 @@ if test "x$enable_notifications" != xno; then fi # Default parameters -AM_CFLAGS="-Wall -I/usr/include/python2.7 -I/usr/include/python2.7 -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -g -fstack-protector --param=ssp-buffer-size=4 -Wformat -Wformat-security -Werror=format-security -L/usr/lib/python2.7/config -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions" +AM_CFLAGS="-Wall -I/usr/include/python2.7 -I/usr/include/python2.7 -fno-strict-aliasing -DNDEBUG -g -fwrapv -O0 -Wall -Wstrict-prototypes -fstack-protector --param=ssp-buffer-size=4 -Wformat -Wformat-security -Werror=format-security -L/usr/lib/python2.7/config -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions" if test "x$PACKAGE_STATUS" = xdevelopment; then # AM_CFLAGS="$AM_CFLAGS -Wunused -Werror" AM_CFLAGS="$AM_CFLAGS -Wunused" diff --git a/plugins/test-c-plugin/Makefile b/plugins/test-c-plugin/Makefile new file mode 100644 index 00000000..96779ecb --- /dev/null +++ b/plugins/test-c-plugin/Makefile @@ -0,0 +1,12 @@ + +all: test-c-plugin.so + +%.so:%.o + $(CC) -shared -fpic -o $@ $^ + +%.o:%.c + $(CC) $(INCLUDE) -D_GNU_SOURCE -D_BSD_SOURCE -fpic -O3 -std=c99 \ + -Wall -Wextra -pedantic -c -o $@ $< + +clean: + $(RM) test-c-plugin.so diff --git a/plugins/test-c-plugin/test-c-plugin.c b/plugins/test-c-plugin/test-c-plugin.c new file mode 100644 index 00000000..e0014e9f --- /dev/null +++ b/plugins/test-c-plugin/test-c-plugin.c @@ -0,0 +1,31 @@ +#include +#include +#include + +static FILE * f = NULL; + +void +prof_init (const char * const version, const char * const status) +{ + fprintf (stderr, "called %s with args=<%s, %s>\n", __func__, version, status); +} + +void +prof_on_start (void) +{ + fprintf (stderr, "called %s with no args\n", __func__); +} + +void +prof_on_connect (void) +{ + fprintf (stderr, "called %s with no args\n", __func__); +} + +void +c_on_message_received (const char * const jid, const char * const message) +{ + fprintf (stderr, "called %s with args=<%s, %s>\n", __func__, jid, message); +} + + diff --git a/src/plugins/c_plugins.c b/src/plugins/c_plugins.c new file mode 100644 index 00000000..ab1917cd --- /dev/null +++ b/src/plugins/c_plugins.c @@ -0,0 +1,123 @@ + +#include "config/preferences.h" +#include "plugins/api.h" +#include "plugins/callbacks.h" +#include "plugins/plugins.h" +#include "plugins/c_plugins.h" +#include "ui/ui.h" + +// FIXME on windows this might be a different header +#include +#include +#include +#include + +ProfPlugin * +c_plugin_create(const char * const filename) +{ + ProfPlugin *plugin; + void * handle = NULL; + char * path = NULL; + + // FIXME so where is the right path for the plugin dir? + if (-1 == asprintf (&path, "./plugins/%s", filename)) { + log_warning ("asprintf failed, plugin %s not loaded", filename); + return NULL; + } + + handle = dlopen (path, RTLD_NOW | RTLD_GLOBAL); + + if (!handle) { + log_warning ("dlopen failed to open `%s', %s", filename, dlerror ()); + return NULL; + } + + plugin = malloc(sizeof(ProfPlugin)); + plugin->name = g_strdup(filename); + plugin->lang = C; + plugin->module = handle; + plugin->init_func = c_init_hook; + plugin->on_start_func = c_on_start_hook; + plugin->on_connect_func = c_on_connect_hook; + plugin->on_message_received_func = c_on_message_received_hook; + + if (!path) + free (path); + + return plugin; +} + +void +c_init_hook(ProfPlugin *plugin, const char * const version, const char * const status) +{ + void * f = NULL; + void (*func)(const char * const __version, const char * const __status); + + assert (plugin && plugin->module); + + if (NULL == (f = dlsym (plugin->module, "prof_init"))) { + log_warning ("warning: %s does not have init function", plugin->name); + return ; + } + + func = (void (*)(const char * const, const char * const))f; + + // FIXME maybe we want to make it boolean to see if it succeeded or not? + func (version, status); +} + + +void +c_on_start_hook (ProfPlugin *plugin) +{ + void * f = NULL; + void (*func)(void); + assert (plugin && plugin->module); + + if (NULL == (f = dlsym (plugin->module, "prof_on_start"))) + return ; + + func = (void (*)(void)) f; + func (); +} + + +void +c_on_connect_hook (ProfPlugin *plugin) +{ + void * f = NULL; + void (*func)(void); + assert (plugin && plugin->module); + + if (NULL == (f = dlsym (plugin->module, "prof_on_connect"))) + return ; + + func = (void (*)(void)) f; + func (); +} + + +// FIXME wooow, if the message is cosntant, then how could I possibly fiddle around with it? +void +c_on_message_received_hook(ProfPlugin *plugin, const char * const jid, const char * const message) +{ + void * f = NULL; + void (*func)(const char * const __jid, const char * const __message); + assert (plugin && plugin->module); + + if (NULL == (f = dlsym (plugin->module, "prof_on_message_received"))) + return; + + func = (void (*)(const char * const, const char * const)) f; + func (jid, message); + +} + +void +c_close_library (ProfPlugin * plugin) +{ + assert (plugin && plugin->module); + + if (dlclose (plugin->module)) + log_warning ("dlclose failed to close `%s' with `%s'", plugin->name, dlerror ()); +} diff --git a/src/plugins/c_plugins.h b/src/plugins/c_plugins.h new file mode 100644 index 00000000..9f163bf5 --- /dev/null +++ b/src/plugins/c_plugins.h @@ -0,0 +1,14 @@ +#ifndef C_PLUGINS_H +#define C_PLUGINS_H + +#include "plugins/plugins.h" + +ProfPlugin* c_plugin_create(const char * const filename); + +void c_init_hook(ProfPlugin *plugin, const char * const version, const char * const status); +void c_on_start_hook (ProfPlugin *plugin); +void c_on_connect_hook (ProfPlugin *plugin); +void c_on_message_received_hook(ProfPlugin *plugin, const char * const jid, const char * const message); +void c_close_library (ProfPlugin * plugin); + +#endif diff --git a/src/plugins/plugins.c b/src/plugins/plugins.c index 2f77c320..0aa4ad52 100644 --- a/src/plugins/plugins.c +++ b/src/plugins/plugins.c @@ -25,8 +25,11 @@ #include "plugins/plugins.h" #include "plugins/python_api.h" #include "plugins/python_plugins.h" +#include "plugins/c_plugins.h" #include "ui/ui.h" + + static GSList* plugins; void @@ -47,7 +50,16 @@ plugins_init(void) ProfPlugin *plugin = python_plugin_create(filename); if (plugin != NULL) { plugins = g_slist_append(plugins, plugin); - cons_show("Loaded plugin: %s", filename); + cons_show("Loaded python plugin: %s", filename); + } + // TODO include configure option to vary on windows and + // unix i.e. so, dll, or maybe we can come up with unified + // shared library plugin name... dunno... + } else if (g_str_has_suffix(filename, ".so")) { + ProfPlugin *plugin = c_plugin_create(filename); + if (plugin != NULL) { + plugins = g_slist_append(plugins, plugin); + cons_show("Loaded C plugin: %s", filename); } } } @@ -56,7 +68,9 @@ plugins_init(void) GSList *curr = plugins; while (curr != NULL) { ProfPlugin *plugin = curr->data; + plugin->init_func(plugin, PACKAGE_VERSION, PACKAGE_STATUS); + // TODO well, it should be more of a generic check error here python_check_error(); curr = g_slist_next(curr); } @@ -101,5 +115,19 @@ plugins_on_message_received(const char * const jid, const char * const message) void plugins_shutdown(void) { + GSList *curr = plugins; + python_shutdown(); + + //FIXME do we need to clean the plugins list? + //for the time being I'll just call dlclose for + //every C plugin. + + while (curr != NULL) { + ProfPlugin *plugin = curr->data; + if (plugin->lang == C) + c_close_library (plugin); + + curr = g_slist_next(curr); + } } diff --git a/src/plugins/plugins.h b/src/plugins/plugins.h index 62fe6ed0..e6ffdbc1 100644 --- a/src/plugins/plugins.h +++ b/src/plugins/plugins.h @@ -24,7 +24,8 @@ #define PLUGINS_H typedef enum { - PYTHON + PYTHON, + C } lang_t; typedef struct prof_plugin_t {