untangle overly complex control flow logic
[lighttpd.git] / src / plugin.c
blobad27f58e43afeb060399b6295c69bbfcd4e64dd8
1 #include "first.h"
3 #include "plugin.h"
4 #include "log.h"
6 #include <string.h>
7 #include <stdlib.h>
9 #include <stdio.h>
11 #ifdef HAVE_VALGRIND_VALGRIND_H
12 # include <valgrind/valgrind.h>
13 #endif
15 #if !defined(__WIN32) && !defined(LIGHTTPD_STATIC)
16 # include <dlfcn.h>
17 #endif
20 * if you change this enum to add a new callback, be sure
21 * - that PLUGIN_FUNC_SIZEOF is the last entry
22 * - that you add PLUGIN_TO_SLOT twice:
23 * 1. as callback-dispatcher
24 * 2. in plugins_call_init()
28 typedef struct {
29 PLUGIN_DATA;
30 } plugin_data;
32 typedef enum {
33 PLUGIN_FUNC_UNSET,
35 PLUGIN_FUNC_HANDLE_URI_CLEAN,
36 PLUGIN_FUNC_HANDLE_URI_RAW,
37 PLUGIN_FUNC_HANDLE_REQUEST_DONE,
38 PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE,
39 PLUGIN_FUNC_HANDLE_TRIGGER,
40 PLUGIN_FUNC_HANDLE_SIGHUP,
41 PLUGIN_FUNC_HANDLE_SUBREQUEST,
42 PLUGIN_FUNC_HANDLE_SUBREQUEST_START,
43 PLUGIN_FUNC_HANDLE_JOBLIST,
44 PLUGIN_FUNC_HANDLE_DOCROOT,
45 PLUGIN_FUNC_HANDLE_PHYSICAL,
46 PLUGIN_FUNC_CONNECTION_RESET,
47 PLUGIN_FUNC_INIT,
48 PLUGIN_FUNC_CLEANUP,
49 PLUGIN_FUNC_SET_DEFAULTS,
51 PLUGIN_FUNC_SIZEOF
52 } plugin_t;
54 static plugin *plugin_init(void) {
55 plugin *p;
57 p = calloc(1, sizeof(*p));
58 force_assert(NULL != p);
60 return p;
63 static void plugin_free(plugin *p) {
64 #if !defined(LIGHTTPD_STATIC)
65 int use_dlclose = 1;
66 #endif
68 if (p->name) buffer_free(p->name);
69 #if defined(HAVE_VALGRIND_VALGRIND_H) && !defined(LIGHTTPD_STATIC)
70 /*if (RUNNING_ON_VALGRIND) use_dlclose = 0;*/
71 #endif
73 #if !defined(LIGHTTPD_STATIC)
74 if (use_dlclose && p->lib) {
75 #if defined(__WIN32)
76 ) FreeLibrary(p->lib);
77 #else
78 dlclose(p->lib);
79 #endif
81 #endif
83 free(p);
86 static int plugins_register(server *srv, plugin *p) {
87 plugin **ps;
88 if (0 == srv->plugins.size) {
89 srv->plugins.size = 4;
90 srv->plugins.ptr = malloc(srv->plugins.size * sizeof(*ps));
91 force_assert(NULL != srv->plugins.ptr);
92 srv->plugins.used = 0;
93 } else if (srv->plugins.used == srv->plugins.size) {
94 srv->plugins.size += 4;
95 srv->plugins.ptr = realloc(srv->plugins.ptr, srv->plugins.size * sizeof(*ps));
96 force_assert(NULL != srv->plugins.ptr);
99 ps = srv->plugins.ptr;
100 ps[srv->plugins.used++] = p;
102 return 0;
111 #if defined(LIGHTTPD_STATIC)
113 /* pre-declare functions, as there is no header for them */
114 #define PLUGIN_INIT(x)\
115 int x ## _plugin_init(plugin *p);
117 #include "plugin-static.h"
119 #undef PLUGIN_INIT
121 /* build NULL-terminated table of name + init-function */
123 typedef struct {
124 const char* name;
125 int (*plugin_init)(plugin *p);
126 } plugin_load_functions;
128 static const plugin_load_functions load_functions[] = {
129 #define PLUGIN_INIT(x) \
130 { #x, &x ## _plugin_init },
132 #include "plugin-static.h"
134 { NULL, NULL }
135 #undef PLUGIN_INIT
138 int plugins_load(server *srv) {
139 plugin *p;
140 size_t i, j;
142 for (i = 0; i < srv->srvconf.modules->used; i++) {
143 data_string *d = (data_string *)srv->srvconf.modules->data[i];
144 char *module = d->value->ptr;
146 for (j = 0; j < i; j++) {
147 if (buffer_is_equal(d->value, ((data_string *) srv->srvconf.modules->data[j])->value)) {
148 log_error_write(srv, __FILE__, __LINE__, "sbs",
149 "Cannot load plugin", d->value,
150 "more than once, please fix your config (lighttpd may not accept such configs in future releases)");
151 continue;
155 for (j = 0; load_functions[j].name; ++j) {
156 if (0 == strcmp(load_functions[j].name, module)) {
157 p = plugin_init();
158 if ((*load_functions[j].plugin_init)(p)) {
159 log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin init failed" );
160 plugin_free(p);
161 return -1;
163 plugins_register(srv, p);
164 break;
167 if (!load_functions[j].name) {
168 log_error_write(srv, __FILE__, __LINE__, "ss", module, " plugin not found" );
169 return -1;
173 return 0;
175 #else /* defined(LIGHTTPD_STATIC) */
176 int plugins_load(server *srv) {
177 plugin *p;
178 int (*init)(plugin *pl);
179 const char *error;
180 size_t i, j;
182 for (i = 0; i < srv->srvconf.modules->used; i++) {
183 data_string *d = (data_string *)srv->srvconf.modules->data[i];
184 char *module = d->value->ptr;
186 for (j = 0; j < i; j++) {
187 if (buffer_is_equal(d->value, ((data_string *) srv->srvconf.modules->data[j])->value)) {
188 log_error_write(srv, __FILE__, __LINE__, "sbs",
189 "Cannot load plugin", d->value,
190 "more than once, please fix your config (lighttpd may not accept such configs in future releases)");
191 continue;
195 buffer_copy_buffer(srv->tmp_buf, srv->srvconf.modules_dir);
197 buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("/"));
198 buffer_append_string(srv->tmp_buf, module);
199 #if defined(__WIN32) || defined(__CYGWIN__)
200 buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".dll"));
201 #else
202 buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".so"));
203 #endif
205 p = plugin_init();
206 #ifdef __WIN32
207 if (NULL == (p->lib = LoadLibrary(srv->tmp_buf->ptr))) {
208 LPVOID lpMsgBuf;
209 FormatMessage(
210 FORMAT_MESSAGE_ALLOCATE_BUFFER |
211 FORMAT_MESSAGE_FROM_SYSTEM,
212 NULL,
213 GetLastError(),
214 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
215 (LPTSTR) &lpMsgBuf,
216 0, NULL);
218 log_error_write(srv, __FILE__, __LINE__, "ssb", "LoadLibrary() failed",
219 lpMsgBuf, srv->tmp_buf);
221 plugin_free(p);
223 return -1;
226 #else
227 if (NULL == (p->lib = dlopen(srv->tmp_buf->ptr, RTLD_NOW|RTLD_GLOBAL))) {
228 log_error_write(srv, __FILE__, __LINE__, "sbs", "dlopen() failed for:",
229 srv->tmp_buf, dlerror());
231 plugin_free(p);
233 return -1;
236 #endif
237 buffer_reset(srv->tmp_buf);
238 buffer_copy_string(srv->tmp_buf, module);
239 buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("_plugin_init"));
241 #ifdef __WIN32
242 init = GetProcAddress(p->lib, srv->tmp_buf->ptr);
244 if (init == NULL) {
245 LPVOID lpMsgBuf;
246 FormatMessage(
247 FORMAT_MESSAGE_ALLOCATE_BUFFER |
248 FORMAT_MESSAGE_FROM_SYSTEM,
249 NULL,
250 GetLastError(),
251 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
252 (LPTSTR) &lpMsgBuf,
253 0, NULL);
255 log_error_write(srv, __FILE__, __LINE__, "sbs", "getprocaddress failed:", srv->tmp_buf, lpMsgBuf);
257 plugin_free(p);
258 return -1;
261 #else
262 #if 1
263 init = (int (*)(plugin *))(intptr_t)dlsym(p->lib, srv->tmp_buf->ptr);
264 #else
265 *(void **)(&init) = dlsym(p->lib, srv->tmp_buf->ptr);
266 #endif
267 if ((error = dlerror()) != NULL) {
268 log_error_write(srv, __FILE__, __LINE__, "s", error);
270 plugin_free(p);
271 return -1;
274 #endif
275 if ((*init)(p)) {
276 log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin init failed" );
278 plugin_free(p);
279 return -1;
281 #if 0
282 log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin loaded" );
283 #endif
284 plugins_register(srv, p);
287 return 0;
289 #endif /* defined(LIGHTTPD_STATIC) */
291 #define PLUGIN_TO_SLOT(x, y) \
292 handler_t plugins_call_##y(server *srv, connection *con) {\
293 plugin **slot;\
294 size_t j;\
295 if (!srv->plugin_slots) return HANDLER_GO_ON;\
296 slot = ((plugin ***)(srv->plugin_slots))[x];\
297 if (!slot) return HANDLER_GO_ON;\
298 for (j = 0; j < srv->plugins.used && slot[j]; j++) { \
299 plugin *p = slot[j];\
300 handler_t r;\
301 switch(r = p->y(srv, con, p->data)) {\
302 case HANDLER_GO_ON:\
303 break;\
304 case HANDLER_FINISHED:\
305 case HANDLER_COMEBACK:\
306 case HANDLER_WAIT_FOR_EVENT:\
307 case HANDLER_WAIT_FOR_FD:\
308 case HANDLER_ERROR:\
309 return r;\
310 default:\
311 log_error_write(srv, __FILE__, __LINE__, "sbs", #x, p->name, "unknown state");\
312 return HANDLER_ERROR;\
315 return HANDLER_GO_ON;\
319 * plugins that use
321 * - server *srv
322 * - connection *con
323 * - void *p_d (plugin_data *)
326 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean)
327 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw)
328 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done)
329 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close)
330 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest)
331 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start)
332 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_JOBLIST, handle_joblist)
333 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot)
334 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical)
335 PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset)
337 #undef PLUGIN_TO_SLOT
339 #define PLUGIN_TO_SLOT(x, y) \
340 handler_t plugins_call_##y(server *srv) {\
341 plugin **slot;\
342 size_t j;\
343 if (!srv->plugin_slots) return HANDLER_GO_ON;\
344 slot = ((plugin ***)(srv->plugin_slots))[x];\
345 if (!slot) return HANDLER_GO_ON;\
346 for (j = 0; j < srv->plugins.used && slot[j]; j++) { \
347 plugin *p = slot[j];\
348 handler_t r;\
349 switch(r = p->y(srv, p->data)) {\
350 case HANDLER_GO_ON:\
351 break;\
352 case HANDLER_FINISHED:\
353 case HANDLER_COMEBACK:\
354 case HANDLER_WAIT_FOR_EVENT:\
355 case HANDLER_WAIT_FOR_FD:\
356 case HANDLER_ERROR:\
357 return r;\
358 default:\
359 log_error_write(srv, __FILE__, __LINE__, "sbsd", #x, p->name, "unknown state:", r);\
360 return HANDLER_ERROR;\
363 return HANDLER_GO_ON;\
367 * plugins that use
369 * - server *srv
370 * - void *p_d (plugin_data *)
373 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger)
374 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup)
375 PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup)
376 PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults)
378 #undef PLUGIN_TO_SLOT
380 #if 0
383 * special handler
386 handler_t plugins_call_handle_fdevent(server *srv, const fd_conn *fdc) {
387 size_t i;
388 plugin **ps;
390 ps = srv->plugins.ptr;
392 for (i = 0; i < srv->plugins.used; i++) {
393 plugin *p = ps[i];
394 if (p->handle_fdevent) {
395 handler_t r;
396 switch(r = p->handle_fdevent(srv, fdc, p->data)) {
397 case HANDLER_GO_ON:
398 break;
399 case HANDLER_FINISHED:
400 case HANDLER_COMEBACK:
401 case HANDLER_WAIT_FOR_EVENT:
402 case HANDLER_ERROR:
403 return r;
404 default:
405 log_error_write(srv, __FILE__, __LINE__, "d", r);
406 break;
411 return HANDLER_GO_ON;
413 #endif
416 * - call init function of all plugins to init the plugin-internals
417 * - added each plugin that supports has callback to the corresponding slot
419 * - is only called once.
422 handler_t plugins_call_init(server *srv) {
423 size_t i;
424 plugin **ps;
426 ps = srv->plugins.ptr;
428 /* fill slots */
430 srv->plugin_slots = calloc(PLUGIN_FUNC_SIZEOF, sizeof(ps));
431 force_assert(NULL != srv->plugin_slots);
433 for (i = 0; i < srv->plugins.used; i++) {
434 size_t j;
435 /* check which calls are supported */
437 plugin *p = ps[i];
439 #define PLUGIN_TO_SLOT(x, y) \
440 if (p->y) { \
441 plugin **slot = ((plugin ***)(srv->plugin_slots))[x]; \
442 if (!slot) { \
443 slot = calloc(srv->plugins.used, sizeof(*slot));\
444 force_assert(NULL != slot); \
445 ((plugin ***)(srv->plugin_slots))[x] = slot; \
447 for (j = 0; j < srv->plugins.used; j++) { \
448 if (slot[j]) continue;\
449 slot[j] = p;\
450 break;\
455 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean);
456 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw);
457 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done);
458 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close);
459 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger);
460 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup);
461 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest);
462 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start);
463 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_JOBLIST, handle_joblist);
464 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot);
465 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical);
466 PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset);
467 PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup);
468 PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults);
469 #undef PLUGIN_TO_SLOT
471 if (p->init) {
472 if (NULL == (p->data = p->init())) {
473 log_error_write(srv, __FILE__, __LINE__, "sb",
474 "plugin-init failed for module", p->name);
475 return HANDLER_ERROR;
478 /* used for con->mode, DIRECT == 0, plugins above that */
479 ((plugin_data *)(p->data))->id = i + 1;
481 if (p->version != LIGHTTPD_VERSION_ID) {
482 log_error_write(srv, __FILE__, __LINE__, "sb",
483 "plugin-version doesn't match lighttpd-version for", p->name);
484 return HANDLER_ERROR;
486 } else {
487 p->data = NULL;
491 return HANDLER_GO_ON;
494 void plugins_free(server *srv) {
495 size_t i;
496 plugins_call_cleanup(srv);
498 for (i = 0; i < srv->plugins.used; i++) {
499 plugin *p = ((plugin **)srv->plugins.ptr)[i];
501 plugin_free(p);
504 for (i = 0; srv->plugin_slots && i < PLUGIN_FUNC_SIZEOF; i++) {
505 plugin **slot = ((plugin ***)(srv->plugin_slots))[i];
507 if (slot) free(slot);
510 free(srv->plugin_slots);
511 srv->plugin_slots = NULL;
513 free(srv->plugins.ptr);
514 srv->plugins.ptr = NULL;
515 srv->plugins.used = 0;