aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortomsmeding <tom.smeding@gmail.com>2017-05-09 22:39:16 +0200
committertomsmeding <tom.smeding@gmail.com>2017-05-09 22:39:16 +0200
commit24de2cd600a4e108c24f6f77a5a4d30111fd9918 (patch)
treeb27f97f511fb3d6fd5ce7f8a3fad9687867dbfda
parenta7a0ce278e661bf5d3f2b47556d8c30469d2bd6c (diff)
server: Add plugin framework
-rw-r--r--Makefile10
-rw-r--r--main.c26
-rw-r--r--plugin.c79
-rw-r--r--plugin.h7
-rw-r--r--plugin_client_header.h26
-rw-r--r--plugins/Makefile34
-rw-r--r--plugins/log/main.c11
7 files changed, 172 insertions, 21 deletions
diff --git a/Makefile b/Makefile
index 8922662..307fbfa 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,13 @@
CC = gcc
-CFLAGS = -Wall -Wextra -std=c11 -g -fwrapv
-LDFLAGS = -lsqlite3
+CFLAGS = -Wall -Wextra -std=c11 -g -fwrapv -D_BSD_SOURCE
+LDFLAGS = -lsqlite3 -ldl
TARGETS = tomsg_server
+PLUGINDIR = plugins
+
+PLUGINS = $(patsubst $(PLUGINDIR)/%,%,$(shell find $(PLUGINDIR)/ -maxdepth 1 -type d))
+
.PHONY: all clean remake
# Clear all implicit suffix rules
@@ -13,9 +17,11 @@ TARGETS = tomsg_server
.SECONDARY:
all: $(TARGETS)
+ for pl in $(PLUGINS); do make --no-print-directory -C $(PLUGINDIR) $@ PLUGINNAME=$$pl; done
clean:
rm -f $(TARGETS) *.o *.sql.h
+ for pl in $(PLUGINS); do make --no-print-directory -C $(PLUGINDIR) $@ PLUGINNAME=$$pl; done
remake: clean
$(MAKE) all
diff --git a/main.c b/main.c
index a754f02..5028190 100644
--- a/main.c
+++ b/main.c
@@ -13,6 +13,7 @@
#include "db.h"
#include "event.h"
#include "net.h"
+#include "plugin.h"
#include "runloop.h"
#include "user_data.h"
@@ -146,34 +147,21 @@ void srandomdev(void){
}
#endif
-__attribute__((unused))
-static void dummy_event_handler(const struct event_item *event){
- switch(event->type){
- case EVENT_MESSAGE:
- debug("event: <%s>@%s: %s",event->user,event->room,event->message);
- break;
-
- case EVENT_ONLINE:
- debug("event: <%s> online=%" PRIi64,event->user,event->num);
- break;
+int main(int argc,char **argv){
+ srandomdev();
- case EVENT_JOIN:
- debug("event: <%s> joins %s",event->user,event->room);
- break;
+ plugin_init();
+ for(int i=1;i<argc;i++){
+ plugin_register(argv[i]);
}
-}
-
-int main(void){
- srandomdev();
+ if(argc<=1)printf("Loaded no plugins\n");
db_init();
int sock=create_server_socket();
printf("Listening on port %d\n",PORT);
- // event_register(dummy_event_handler);
runloop_set_timeout(60*1000000,timeout_callback);
runloop_add_fd(sock,server_socket_callback,false);
runloop_run();
printf("Runloop empty, shutting down\n");
- // event_unregister(dummy_event_handler);
db_close();
}
diff --git a/plugin.c b/plugin.c
new file mode 100644
index 0000000..14dce01
--- /dev/null
+++ b/plugin.c
@@ -0,0 +1,79 @@
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include "event.h"
+#include "global.h"
+#include "plugin.h"
+#include "plugin_client_header.h"
+
+
+struct plugin_data{
+ void *handle;
+ plugin_event_func_t *callback;
+};
+
+static i64 num_plugins=0;
+static i64 plugins_cap=8;
+static struct plugin_data *plugins_list=NULL;
+
+
+static void consume_event(const struct event_item *event){
+ struct plugin_event pe;
+ switch(event->type){
+ case EVENT_MESSAGE: pe.type=PLUGIN_EVENT_MESSAGE; break;
+ case EVENT_ONLINE: pe.type=PLUGIN_EVENT_ONLINE; break;
+ case EVENT_JOIN: pe.type=PLUGIN_EVENT_JOIN; break;
+ default:
+ die("Unknown event type %d in plugin.c:consume_event",event->type);
+ }
+ pe.timestamp=event->timestamp;
+ pe.message=event->message;
+ pe.user=event->user;
+ pe.room=event->room;
+ pe.num=event->num;
+
+ for(i64 i=0;i<num_plugins;i++){
+ plugins_list[i].callback(&pe);
+ }
+}
+
+
+void plugin_init(void){
+ if(plugins_list!=NULL)return;
+ plugins_list=malloc(plugins_cap,struct plugin_data);
+ memset(plugins_list,0,num_plugins*sizeof(struct plugin_data));
+
+ event_register(consume_event);
+}
+
+void plugin_register(const char *fname){
+ if(num_plugins==plugins_cap){
+ plugins_cap*=2;
+ plugins_list=realloc(plugins_list,plugins_cap,struct plugin_data);
+ }
+
+ struct plugin_data *data=plugins_list+num_plugins;
+ num_plugins++;
+ data->handle=dlopen(fname,RTLD_NOW|RTLD_LOCAL);
+ if(data->handle==NULL){
+ die("Error loading plugin '%s': %s",fname,dlerror());
+ }
+
+ plugin_event_func_t* (*init_func)(void)=NULL;
+ dlerror();
+ *(void**)&init_func=dlsym(data->handle,"plugin_init_func");
+ char *err=dlerror();
+ if(err!=NULL){
+ die("Error reading symbol 'plugin_init_func' from plugin '%s'",fname);
+ }
+ if(init_func==NULL){
+ die("Plugin '%s' exports NULL init function",fname);
+ }
+
+ data->callback=init_func();
+ if(data->callback==NULL){
+ die("Init function of plugin '%s' returned NULL",fname);
+ }
+
+ printf("Loaded plugin '%s'\n",fname);
+}
diff --git a/plugin.h b/plugin.h
new file mode 100644
index 0000000..de235c9
--- /dev/null
+++ b/plugin.h
@@ -0,0 +1,7 @@
+#pragma once
+
+
+void plugin_init(void);
+
+// Pass path to a shared object file / dynamic library
+void plugin_register(const char *fname);
diff --git a/plugin_client_header.h b/plugin_client_header.h
new file mode 100644
index 0000000..4428b3c
--- /dev/null
+++ b/plugin_client_header.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <stdint.h>
+
+
+enum plugin_event_type{
+ PLUGIN_EVENT_MESSAGE, // message, user, room
+ PLUGIN_EVENT_ONLINE, // user, num
+ PLUGIN_EVENT_JOIN, // user, room
+};
+
+struct plugin_event{
+ enum plugin_event_type type;
+ int64_t timestamp; // always set
+ char *message,*user,*room;
+ int64_t num;
+};
+
+
+typedef void plugin_event_func_t(const struct plugin_event *event);
+
+
+// All plugins should export a function of type plugin_init_func_t with
+// name "plugin_init_func" that returns a pointer to a function that
+// receives all events.
+plugin_event_func_t* plugin_init_func(void);
diff --git a/plugins/Makefile b/plugins/Makefile
new file mode 100644
index 0000000..0824f2e
--- /dev/null
+++ b/plugins/Makefile
@@ -0,0 +1,34 @@
+CC = gcc
+CFLAGS = -Wall -Wextra -std=c11 -g -fwrapv -I.. -fPIC
+
+ifeq ($(shell uname),Darwin)
+ SO_EXT = dylib
+ SO_FLAGS = -dynamiclib
+else
+ SO_EXT = so
+ SO_FLAGS = -shared
+endif
+SO_FLAGS += -fPIC
+
+TARGET = $(PLUGINNAME)/$(PLUGINNAME).$(SO_EXT)
+
+
+.PHONY: all clean remake warning
+
+all: warning $(TARGET)
+
+clean: warning
+ rm -rf $(TARGET) $(PLUGINNAME)/*.{o,dSYM}
+
+remake: clean all
+
+warning:
+ifndef PLUGINNAME
+ $(error "PLUGINNAME not set! Please run root Makefile instead.")
+endif
+
+
+.SECONDARY:
+
+$(PLUGINNAME)/%.$(SO_EXT): $(patsubst %.c,%.o,$(wildcard $(PLUGINNAME)/*.c)) $(wildcard ../*.h)
+ $(CC) $(SO_FLAGS) -o $@ $(filter %.o,$^)
diff --git a/plugins/log/main.c b/plugins/log/main.c
new file mode 100644
index 0000000..b931d12
--- /dev/null
+++ b/plugins/log/main.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include "plugin_client_header.h"
+
+
+static void eventfunc(const struct plugin_event *event){
+ printf("type = %d\n",event->type);
+}
+
+plugin_event_func_t* plugin_init_func(void){
+ return eventfunc;
+}