Add basic support for dynamically loadable plugins

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson
2012-09-21 00:33:46 +02:00
parent a0fa877520
commit 239772d667
6 changed files with 300 additions and 3 deletions
+12 -3
View File
@@ -24,20 +24,25 @@
# Top directory for building complete system, fall back to this directory
ROOTDIR ?= $(shell pwd)
# Plugin directory, fall back to this directory if unset in environment
PLUGIN_DIR ?= /lib/finit/plugins
VERSION = 1.0-pre
PKG = finit-$(VERSION)
EXEC = finit
OBJS = finit.o helpers.o initctl.o signal.o svc.o ipc.o service.o \
strlcpy.o
strlcpy.o plugin.o
SRCS = $(OBJS:.o=.c)
DEPS = $(addprefix .,$(SRCS:.c=.d))
CFLAGS += -W -Wall -Werror -Os
# Disable annoying gcc warning for "warn_unused_result", see GIT 37af997
CPPFLAGS += -U_FORTIFY_SOURCE
CPPFLAGS += -I$(ROOTDIR)/$(CONFIG_LINUXDIR)/include/
CPPFLAGS += -DVERSION=\"$(VERSION)\" -DWHOAMI=\"`whoami`@`hostname`\"
CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE
CPPFLAGS += -DVERSION=\"$(VERSION)\" -DWHOAMI=\"`whoami`@`hostname`\"
CPPFLAGS += -DPLUGIN_PATH=\"$(PLUGIN_DIR)\"
LDFLAGS += -rdynamic
LDLIBS += -ldl
prefix ?= /usr/local
sysconfdir ?= /etc
@@ -45,12 +50,14 @@ datadir = $(prefix)/share/doc/finit
mandir = $(prefix)/share/man/man8
include common.mk
export PLUGIN_DIR ROOTDIR
all: Makefile $(EXEC)
$(MAKE) -C plugins $@
$(OBJS): Makefile
finit: $(OBJS)
$(EXEC): $(OBJS)
# @ln -sf /sbin/finit /sbin/init
install: all
@@ -71,9 +78,11 @@ uninstall:
clean:
-@$(RM) $(OBJS) $(DEPS) $(EXEC)
$(MAKE) -C plugins $@
distclean: clean
-@$(RM) $(JUNK) unittest *.o
$(MAKE) -C plugins $@
# Include automatically generated rules, such as:
# uncgi.o: .../some/dir/uncgi.c /usr/include/stdio.h
+5
View File
@@ -1,5 +1,6 @@
# Some junk files we always want to be removed when doing a make clean.
JUNK = *~ *.bak *.aux *.dvi *.idx *.ind *.log *.ps *.map .*.d DEADJOE semantic.cache *.gdb *.elf core core.*
RM := -@$(RM)
MAKE := @$(MAKE)
MAKEFLAGS = --no-print-directory
@@ -17,3 +18,7 @@ MAKEFLAGS = --no-print-directory
%: %.o
@printf " LINK $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n"
@$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-Map,$@.map -o $@ $^ $(LDLIBS$(LDLIBS-$(@)))
%.so: %.o
@printf " PLUGIN $(subst $(ROOTDIR)/,,$(shell pwd)/$@)\n"
@$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+120
View File
@@ -0,0 +1,120 @@
/* Test of dlopen() for a plugin based services architecture for finit
*
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <errno.h>
#include <dlfcn.h> /* dlopen() et al */
#include <dirent.h> /* readdir() et al */
#include <stdio.h>
#include <string.h>
#include <sys/queue.h> /* BSD sys/queue.h API */
#include "plugin.h"
static LIST_HEAD(, plugin) plugins = LIST_HEAD_INITIALIZER();
int plugin_register(plugin_t *plugin)
{
if (!plugin) {
errno = EINVAL;
return 1;
}
if (!plugin->name) {
Dl_info info;
if (!dladdr(plugin, &info) || !info.dli_fname)
plugin->name = "unknown";
else
plugin->name = info.dli_fname;
}
LIST_INSERT_HEAD(&plugins, plugin, link);
return 0;
}
int plugin_unregister(plugin_t *plugin)
{
LIST_REMOVE(plugin, link);
return 0;
}
void run_hooks(hook_point_t no)
{
plugin_t *p;
PLUGIN_ITERATOR(p) {
if (p->hook[no].cb) {
printf("Calling %s hook n:o %d from runloop...\n", p->name, no);
p->hook[no].cb(p->hook[no].arg);
}
}
}
void run_services(void)
{
plugin_t *p;
PLUGIN_ITERATOR(p) {
if (p->svc.cb) {
printf("Calling svc %s from runloop...\n", p->name);
p->svc.cb(p->svc.arg);
}
}
}
int load_plugins(void)
{
DIR *dp = opendir(PLUGIN_PATH);
struct dirent *entry;
if (!dp) {
fprintf(stderr, "Failed, cannot open plugin directory %s: %s\n",
PLUGIN_PATH, strerror(errno));
return 1;
}
while ((entry = readdir(dp))) {
char *ext = entry->d_name + strlen(entry->d_name) - 3;
if (!strcmp(ext, ".so")) {
void *handle;
char filename[1024];
snprintf(filename, sizeof(filename), "%s/%s", PLUGIN_PATH, entry->d_name);
printf("Loading plugin %s ...\n", filename);
handle = dlopen(filename, RTLD_LAZY);
if (!handle) {
fprintf(stderr,
"Failed loading plugin %s: %s\n", filename, dlerror());
return 1;
}
}
}
closedir(dp);
return 0;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+77
View File
@@ -0,0 +1,77 @@
/* Plugin framework
*
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef PLUGIN_H_
#define PLUGIN_H_
#include <sys/queue.h> /* BSD sys/queue.h API */
#define __unused __attribute__ ((unused))
#define PLUGIN_INIT(x) void x(void) __attribute__ ((constructor));
#define PLUGIN_EXIT(x) void x(void) __attribute__ ((destructor));
#define PLUGIN_ITERATOR(x) LIST_FOREACH(x, &plugins, link)
/*
* Predefined hook points for easier plugin debugging
*/
typedef enum {
HOOK_POST_LOAD = 0,
HOOK_PRE_SETUP,
HOOK_PRE_RUNLOOP,
HOOK_EXIT_CLEANUP,
HOOK_MAX_NUM
} hook_point_t;
typedef struct plugin {
/* BSD sys/queue.h linked list node. */
LIST_ENTRY(plugin) link;
/* Optional plugin name, defaults to plugin path if unset. */
const char *name;
/* Service callback to be called once per lap of runloop. */
struct {
void *arg; /* Optional argument to callback func. */
void (*cb) (void *);
} svc;
/* List of hook callbacks. */
struct {
void *arg; /* Optional argument to callback func. */
void (*cb) (void *);
} hook[HOOK_MAX_NUM];
} plugin_t;
/* Public plugin API */
int plugin_register (plugin_t *plugin);
int plugin_unregister (plugin_t *plugin);
/* Private daemon API */
void run_hooks (hook_point_t no);
void run_services (void);
int load_plugins (void);
#endif /* PLUGIN_H_ */
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+32
View File
@@ -0,0 +1,32 @@
# Plugin based services architecture for finit -*-Makefile-*-
CFLAGS = -fPIC -g -Os -I..
CPPFLAGS = -W -Wall
LDFLAGS = -shared
PLUGINS = foo.so
DEPS = $(addprefix .,$(PLUGINS:.so=.d))
include ../common.mk
all: $(PLUGINS)
clean:
$(RM) $(PLUGINS) *.o
distclean: clean
$(RM) $(JUNK) $(DEPS)
install: all
@for plugin in $(PLUGINS); do \
echo " INSTALL $(PLUGIN_DIR)/$$plugin"; \
cp $$plugin $(PLUGIN_DIR); \
done
uninstall:
@for plugin in $(PLUGINS); do \
echo " RM $(PLUGIN_DIR)/$$plugin"; \
rm $(PLUGIN_DIR)/$$plugin; \
done
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif
+54
View File
@@ -0,0 +1,54 @@
/* Test of plugin to dynamic-linking daemon.
*
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include "plugin.h"
#define PLUGIN_NAME "foo"
static void service(void *arg __unused)
{
printf("Service %s\n", PLUGIN_NAME);
}
static plugin_t plugin = {
.name = PLUGIN_NAME,
.svc = {
.cb = service,
},
};
static void init_plugin(void)
{
plugin_register(&plugin);
}
static void exit_plugin(void)
{
plugin_unregister(&plugin);
}
PLUGIN_INIT(init_plugin)
PLUGIN_EXIT(exit_plugin)
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/