blob: faf5520a8276c520e95986fd1d0dacf86c70b7ad [file] [log] [blame]
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "plugin.h"
#include "log.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_VALGRIND_VALGRIND_H
#include <valgrind/valgrind.h>
#endif
#ifndef __WIN32
#include <dlfcn.h>
#endif
/*
*
* if you change this enum to add a new callback, be sure
* - that PLUGIN_FUNC_SIZEOF is the last entry
* - that you add PLUGIN_TO_SLOT twice:
* 1. as callback-dispatcher
* 2. in plugins_call_init()
*
*/
typedef struct {
PLUGIN_DATA;
} plugin_data;
typedef enum {
PLUGIN_FUNC_UNSET,
PLUGIN_FUNC_HANDLE_URI_CLEAN,
PLUGIN_FUNC_HANDLE_URI_RAW,
PLUGIN_FUNC_HANDLE_REQUEST_DONE,
PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE,
PLUGIN_FUNC_HANDLE_TRIGGER,
PLUGIN_FUNC_HANDLE_SIGHUP,
PLUGIN_FUNC_HANDLE_SUBREQUEST,
PLUGIN_FUNC_HANDLE_SUBREQUEST_START,
PLUGIN_FUNC_HANDLE_JOBLIST,
PLUGIN_FUNC_HANDLE_DOCROOT,
PLUGIN_FUNC_HANDLE_PHYSICAL,
PLUGIN_FUNC_CONNECTION_RESET,
PLUGIN_FUNC_INIT,
PLUGIN_FUNC_CLEANUP,
PLUGIN_FUNC_SET_DEFAULTS,
PLUGIN_FUNC_SIZEOF
} plugin_t;
static plugin *plugin_init(void) {
plugin *p;
p = calloc(1, sizeof(*p));
return p;
}
static void plugin_free(plugin *p) {
int use_dlclose = 1;
if (p->name) buffer_free(p->name);
#ifdef HAVE_VALGRIND_VALGRIND_H
/*if (RUNNING_ON_VALGRIND) use_dlclose = 0;*/
#endif
#ifndef LIGHTTPD_STATIC
if (use_dlclose && p->lib) {
#ifdef __WIN32
FreeLibrary(p->lib);
#else
dlclose(p->lib);
#endif
}
#endif
free(p);
}
static int plugins_register(server *srv, plugin *p) {
plugin **ps;
if (0 == srv->plugins.size) {
srv->plugins.size = 4;
srv->plugins.ptr = malloc(srv->plugins.size * sizeof(*ps));
srv->plugins.used = 0;
} else if (srv->plugins.used == srv->plugins.size) {
srv->plugins.size += 4;
srv->plugins.ptr = realloc(srv->plugins.ptr, srv->plugins.size * sizeof(*ps));
}
ps = srv->plugins.ptr;
ps[srv->plugins.used++] = p;
return 0;
}
/**
*
*
*
*/
#ifdef LIGHTTPD_STATIC
int plugins_load(server *srv) {
plugin *p;
#define PLUGIN_INIT(x)\
p = plugin_init(); \
if (x ## _plugin_init(p)) { \
log_error_write(srv, __FILE__, __LINE__, "ss", #x, "plugin init failed" ); \
plugin_free(p); \
return -1;\
}\
plugins_register(srv, p);
#include "plugin-static.h"
return 0;
}
#else
int plugins_load(server *srv) {
plugin *p;
int (*init)(plugin *pl);
const char *error;
size_t i;
for (i = 0; i < srv->srvconf.modules->used; i++) {
data_string *d = (data_string *)srv->srvconf.modules->data[i];
char *modules = d->value->ptr;
buffer_copy_string_buffer(srv->tmp_buf, srv->srvconf.modules_dir);
buffer_append_string(srv->tmp_buf, "/");
buffer_append_string(srv->tmp_buf, modules);
#if defined(__WIN32) || defined(__CYGWIN__)
buffer_append_string(srv->tmp_buf, ".dll");
#else
buffer_append_string(srv->tmp_buf, ".so");
#endif
p = plugin_init();
#ifdef __WIN32
if (NULL == (p->lib = LoadLibrary(srv->tmp_buf->ptr))) {
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
log_error_write(srv, __FILE__, __LINE__, "ssb", "LoadLibrary() failed",
lpMsgBuf, srv->tmp_buf);
plugin_free(p);
return -1;
}
#else
if (NULL == (p->lib = dlopen(srv->tmp_buf->ptr, RTLD_NOW|RTLD_GLOBAL))) {
log_error_write(srv, __FILE__, __LINE__, "sbs", "dlopen() failed for:",
srv->tmp_buf, dlerror());
plugin_free(p);
return -1;
}
#endif
buffer_reset(srv->tmp_buf);
buffer_copy_string(srv->tmp_buf, modules);
buffer_append_string(srv->tmp_buf, "_plugin_init");
#ifdef __WIN32
init = GetProcAddress(p->lib, srv->tmp_buf->ptr);
if (init == NULL) {
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
log_error_write(srv, __FILE__, __LINE__, "sbs", "getprocaddress failed:", srv->tmp_buf, lpMsgBuf);
plugin_free(p);
return -1;
}
#else
#if 1
init = (int (*)(plugin *))dlsym(p->lib, srv->tmp_buf->ptr);
#else
*(void **)(&init) = dlsym(p->lib, srv->tmp_buf->ptr);
#endif
if ((error = dlerror()) != NULL) {
log_error_write(srv, __FILE__, __LINE__, "s", error);
plugin_free(p);
return -1;
}
#endif
if ((*init)(p)) {
log_error_write(srv, __FILE__, __LINE__, "ss", modules, "plugin init failed" );
plugin_free(p);
return -1;
}
#if 0
log_error_write(srv, __FILE__, __LINE__, "ss", modules, "plugin loaded" );
#endif
plugins_register(srv, p);
}
return 0;
}
#endif
#define PLUGIN_TO_SLOT(x, y) \
handler_t plugins_call_##y(server *srv, connection *con) {\
plugin **slot;\
size_t j;\
if (!srv->plugin_slots) return HANDLER_GO_ON;\
slot = ((plugin ***)(srv->plugin_slots))[x];\
if (!slot) return HANDLER_GO_ON;\
for (j = 0; j < srv->plugins.used && slot[j]; j++) { \
plugin *p = slot[j];\
handler_t r;\
switch(r = p->y(srv, con, p->data)) {\
case HANDLER_GO_ON:\
break;\
case HANDLER_FINISHED:\
case HANDLER_COMEBACK:\
case HANDLER_WAIT_FOR_EVENT:\
case HANDLER_WAIT_FOR_FD:\
case HANDLER_ERROR:\
return r;\
default:\
log_error_write(srv, __FILE__, __LINE__, "sbs", #x, p->name, "unknown state");\
return HANDLER_ERROR;\
}\
}\
return HANDLER_GO_ON;\
}
/**
* plugins that use
*
* - server *srv
* - connection *con
* - void *p_d (plugin_data *)
*/
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean)
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw)
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done)
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close)
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest)
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start)
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_JOBLIST, handle_joblist)
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot)
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical)
PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset)
#undef PLUGIN_TO_SLOT
#define PLUGIN_TO_SLOT(x, y) \
handler_t plugins_call_##y(server *srv) {\
plugin **slot;\
size_t j;\
if (!srv->plugin_slots) return HANDLER_GO_ON;\
slot = ((plugin ***)(srv->plugin_slots))[x];\
if (!slot) return HANDLER_GO_ON;\
for (j = 0; j < srv->plugins.used && slot[j]; j++) { \
plugin *p = slot[j];\
handler_t r;\
switch(r = p->y(srv, p->data)) {\
case HANDLER_GO_ON:\
break;\
case HANDLER_FINISHED:\
case HANDLER_COMEBACK:\
case HANDLER_WAIT_FOR_EVENT:\
case HANDLER_WAIT_FOR_FD:\
case HANDLER_ERROR:\
return r;\
default:\
log_error_write(srv, __FILE__, __LINE__, "sbsd", #x, p->name, "unknown state:", r);\
return HANDLER_ERROR;\
}\
}\
return HANDLER_GO_ON;\
}
/**
* plugins that use
*
* - server *srv
* - void *p_d (plugin_data *)
*/
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger)
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup)
PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup)
PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults)
#undef PLUGIN_TO_SLOT
#if 0
/**
*
* special handler
*
*/
handler_t plugins_call_handle_fdevent(server *srv, const fd_conn *fdc) {
size_t i;
plugin **ps;
ps = srv->plugins.ptr;
for (i = 0; i < srv->plugins.used; i++) {
plugin *p = ps[i];
if (p->handle_fdevent) {
handler_t r;
switch(r = p->handle_fdevent(srv, fdc, p->data)) {
case HANDLER_GO_ON:
break;
case HANDLER_FINISHED:
case HANDLER_COMEBACK:
case HANDLER_WAIT_FOR_EVENT:
case HANDLER_ERROR:
return r;
default:
log_error_write(srv, __FILE__, __LINE__, "d", r);
break;
}
}
}
return HANDLER_GO_ON;
}
#endif
/**
*
* - call init function of all plugins to init the plugin-internals
* - added each plugin that supports has callback to the corresponding slot
*
* - is only called once.
*/
handler_t plugins_call_init(server *srv) {
size_t i;
plugin **ps;
ps = srv->plugins.ptr;
/* fill slots */
srv->plugin_slots = calloc(PLUGIN_FUNC_SIZEOF, sizeof(ps));
for (i = 0; i < srv->plugins.used; i++) {
size_t j;
/* check which calls are supported */
plugin *p = ps[i];
#define PLUGIN_TO_SLOT(x, y) \
if (p->y) { \
plugin **slot = ((plugin ***)(srv->plugin_slots))[x]; \
if (!slot) { \
slot = calloc(srv->plugins.used, sizeof(*slot));\
((plugin ***)(srv->plugin_slots))[x] = slot; \
} \
for (j = 0; j < srv->plugins.used; j++) { \
if (slot[j]) continue;\
slot[j] = p;\
break;\
}\
}
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean);
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw);
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done);
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close);
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger);
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup);
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest);
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start);
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_JOBLIST, handle_joblist);
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot);
PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical);
PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset);
PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup);
PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults);
#undef PLUGIN_TO_SLOT
if (p->init) {
if (NULL == (p->data = p->init())) {
log_error_write(srv, __FILE__, __LINE__, "sb",
"plugin-init failed for module", p->name);
return HANDLER_ERROR;
}
/* used for con->mode, DIRECT == 0, plugins above that */
((plugin_data *)(p->data))->id = i + 1;
if (p->version != LIGHTTPD_VERSION_ID) {
log_error_write(srv, __FILE__, __LINE__, "sb",
"plugin-version doesn't match lighttpd-version for", p->name);
return HANDLER_ERROR;
}
} else {
p->data = NULL;
}
}
return HANDLER_GO_ON;
}
void plugins_free(server *srv) {
size_t i;
plugins_call_cleanup(srv);
for (i = 0; i < srv->plugins.used; i++) {
plugin *p = ((plugin **)srv->plugins.ptr)[i];
plugin_free(p);
}
for (i = 0; srv->plugin_slots && i < PLUGIN_FUNC_SIZEOF; i++) {
plugin **slot = ((plugin ***)(srv->plugin_slots))[i];
if (slot) free(slot);
}
free(srv->plugin_slots);
srv->plugin_slots = NULL;
free(srv->plugins.ptr);
srv->plugins.ptr = NULL;
srv->plugins.used = 0;
}