518 lines
12 KiB
C
518 lines
12 KiB
C
#include "first.h"
|
|
|
|
#include "plugin.h"
|
|
#include "log.h"
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#ifdef HAVE_VALGRIND_VALGRIND_H
|
|
# include <valgrind/valgrind.h>
|
|
#endif
|
|
|
|
#if !defined(__WIN32) && !defined(LIGHTTPD_STATIC)
|
|
# 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_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));
|
|
force_assert(NULL != p);
|
|
|
|
return p;
|
|
}
|
|
|
|
static void plugin_free(plugin *p) {
|
|
#if !defined(LIGHTTPD_STATIC)
|
|
int use_dlclose = 1;
|
|
#endif
|
|
|
|
if (p->name) buffer_free(p->name);
|
|
#if defined(HAVE_VALGRIND_VALGRIND_H) && !defined(LIGHTTPD_STATIC)
|
|
/*if (RUNNING_ON_VALGRIND) use_dlclose = 0;*/
|
|
#endif
|
|
|
|
#if !defined(LIGHTTPD_STATIC)
|
|
if (use_dlclose && p->lib) {
|
|
#if defined(__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));
|
|
force_assert(NULL != srv->plugins.ptr);
|
|
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));
|
|
force_assert(NULL != srv->plugins.ptr);
|
|
}
|
|
|
|
ps = srv->plugins.ptr;
|
|
ps[srv->plugins.used++] = p;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
#if defined(LIGHTTPD_STATIC)
|
|
|
|
/* pre-declare functions, as there is no header for them */
|
|
#define PLUGIN_INIT(x)\
|
|
int x ## _plugin_init(plugin *p);
|
|
|
|
#include "plugin-static.h"
|
|
|
|
#undef PLUGIN_INIT
|
|
|
|
/* build NULL-terminated table of name + init-function */
|
|
|
|
typedef struct {
|
|
const char* name;
|
|
int (*plugin_init)(plugin *p);
|
|
} plugin_load_functions;
|
|
|
|
static const plugin_load_functions load_functions[] = {
|
|
#define PLUGIN_INIT(x) \
|
|
{ #x, &x ## _plugin_init },
|
|
|
|
#include "plugin-static.h"
|
|
|
|
{ NULL, NULL }
|
|
#undef PLUGIN_INIT
|
|
};
|
|
|
|
int plugins_load(server *srv) {
|
|
plugin *p;
|
|
size_t i, j;
|
|
|
|
for (i = 0; i < srv->srvconf.modules->used; i++) {
|
|
data_string *d = (data_string *)srv->srvconf.modules->data[i];
|
|
char *module = d->value->ptr;
|
|
|
|
for (j = 0; j < i; j++) {
|
|
if (buffer_is_equal(d->value, ((data_string *) srv->srvconf.modules->data[j])->value)) {
|
|
log_error_write(srv, __FILE__, __LINE__, "sbs",
|
|
"Cannot load plugin", d->value,
|
|
"more than once, please fix your config (lighttpd may not accept such configs in future releases)");
|
|
continue;
|
|
}
|
|
}
|
|
|
|
for (j = 0; load_functions[j].name; ++j) {
|
|
if (0 == strcmp(load_functions[j].name, module)) {
|
|
p = plugin_init();
|
|
if ((*load_functions[j].plugin_init)(p)) {
|
|
log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin init failed" );
|
|
plugin_free(p);
|
|
return -1;
|
|
}
|
|
plugins_register(srv, p);
|
|
break;
|
|
}
|
|
}
|
|
if (!load_functions[j].name) {
|
|
log_error_write(srv, __FILE__, __LINE__, "ss", module, " plugin not found" );
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else /* defined(LIGHTTPD_STATIC) */
|
|
int plugins_load(server *srv) {
|
|
plugin *p;
|
|
int (*init)(plugin *pl);
|
|
size_t i, j;
|
|
|
|
for (i = 0; i < srv->srvconf.modules->used; i++) {
|
|
data_string *d = (data_string *)srv->srvconf.modules->data[i];
|
|
char *module = d->value->ptr;
|
|
|
|
for (j = 0; j < i; j++) {
|
|
if (buffer_is_equal(d->value, ((data_string *) srv->srvconf.modules->data[j])->value)) {
|
|
log_error_write(srv, __FILE__, __LINE__, "sbs",
|
|
"Cannot load plugin", d->value,
|
|
"more than once, please fix your config (lighttpd may not accept such configs in future releases)");
|
|
continue;
|
|
}
|
|
}
|
|
|
|
buffer_copy_buffer(srv->tmp_buf, srv->srvconf.modules_dir);
|
|
|
|
buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("/"));
|
|
buffer_append_string(srv->tmp_buf, module);
|
|
#if defined(__WIN32) || defined(__CYGWIN__)
|
|
buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".dll"));
|
|
#else
|
|
buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".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, module);
|
|
buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("_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 *))(intptr_t)dlsym(p->lib, srv->tmp_buf->ptr);
|
|
#else
|
|
*(void **)(&init) = dlsym(p->lib, srv->tmp_buf->ptr);
|
|
#endif
|
|
if (NULL == init) {
|
|
const char *error = dlerror();
|
|
if (error != NULL) {
|
|
log_error_write(srv, __FILE__, __LINE__, "ss", "dlsym:", error);
|
|
} else {
|
|
log_error_write(srv, __FILE__, __LINE__, "ss", "dlsym symbol not found:", srv->tmp_buf->ptr);
|
|
}
|
|
|
|
plugin_free(p);
|
|
return -1;
|
|
}
|
|
|
|
#endif
|
|
if ((*init)(p)) {
|
|
log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin init failed" );
|
|
|
|
plugin_free(p);
|
|
return -1;
|
|
}
|
|
#if 0
|
|
log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin loaded" );
|
|
#endif
|
|
plugins_register(srv, p);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* defined(LIGHTTPD_STATIC) */
|
|
|
|
#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_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));
|
|
force_assert(NULL != srv->plugin_slots);
|
|
|
|
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));\
|
|
force_assert(NULL != 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_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;
|
|
}
|