#include "first.h" #include "plugin.h" #include "log.h" #include #include #include #ifdef HAVE_VALGRIND_VALGRIND_H # include #endif #if !defined(__WIN32) && !defined(LIGHTTPD_STATIC) # include #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_ENV, PLUGIN_FUNC_HANDLE_REQUEST_DONE, PLUGIN_FUNC_HANDLE_CONNECTION_ACCEPT, PLUGIN_FUNC_HANDLE_CONNECTION_SHUT_WR, 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_RESPONSE_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_ENV, handle_request_env) PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done) PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_ACCEPT, handle_connection_accept) PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_SHUT_WR, handle_connection_shut_wr) 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_RESPONSE_START, handle_response_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_ENV, handle_request_env); PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done); PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_ACCEPT, handle_connection_accept); PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_SHUT_WR, handle_connection_shut_wr); 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_RESPONSE_START, handle_response_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; }