diff --git a/Makefile b/Makefile index 01493104..f196547a 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/common.mk b/common.mk index 678e1123..3701a49c 100644 --- a/common.mk +++ b/common.mk @@ -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) diff --git a/plugin.c b/plugin.c new file mode 100644 index 00000000..714ddc64 --- /dev/null +++ b/plugin.c @@ -0,0 +1,120 @@ +/* Test of dlopen() for a plugin based services architecture for finit + * + * Copyright (c) 2012 Joachim Nilsson + * + * 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 +#include /* dlopen() et al */ +#include /* readdir() et al */ +#include +#include +#include /* 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: + */ diff --git a/plugin.h b/plugin.h new file mode 100644 index 00000000..77f66028 --- /dev/null +++ b/plugin.h @@ -0,0 +1,77 @@ +/* Plugin framework + * + * Copyright (c) 2012 Joachim Nilsson + * + * 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 /* 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: + */ diff --git a/plugins/Makefile b/plugins/Makefile new file mode 100644 index 00000000..2a931cb6 --- /dev/null +++ b/plugins/Makefile @@ -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 diff --git a/plugins/foo.c b/plugins/foo.c new file mode 100644 index 00000000..8c05d276 --- /dev/null +++ b/plugins/foo.c @@ -0,0 +1,54 @@ +/* Test of plugin to dynamic-linking daemon. + * + * Copyright (c) 2012 Joachim Nilsson + * + * 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 +#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: + */