mirror of
https://github.com/troglobit/finit.git
synced 2026-10-01 05:22:48 +07:00
Add basic support for dynamically loadable plugins
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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:
|
||||
*/
|
||||
Reference in New Issue
Block a user