xref: /lighttpd1.4/src/plugin.c (revision dbdab5db)
1 #include "first.h"
2 
3 #include "plugin.h"
4 #include "log.h"
5 
6 #include <string.h>
7 #include <stdlib.h>
8 
9 #include <stdio.h>
10 
11 #ifdef HAVE_VALGRIND_VALGRIND_H
12 # include <valgrind/valgrind.h>
13 #endif
14 
15 #if !defined(__WIN32) && !defined(LIGHTTPD_STATIC)
16 # include <dlfcn.h>
17 #endif
18 /*
19  *
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()
25  *
26  */
27 
28 typedef struct {
29 	PLUGIN_DATA;
30 } plugin_data;
31 
32 typedef enum {
33 	PLUGIN_FUNC_UNSET,
34 
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_DOCROOT,
44 	PLUGIN_FUNC_HANDLE_PHYSICAL,
45 	PLUGIN_FUNC_CONNECTION_RESET,
46 	PLUGIN_FUNC_INIT,
47 	PLUGIN_FUNC_CLEANUP,
48 	PLUGIN_FUNC_SET_DEFAULTS,
49 
50 	PLUGIN_FUNC_SIZEOF
51 } plugin_t;
52 
53 static plugin *plugin_init(void) {
54 	plugin *p;
55 
56 	p = calloc(1, sizeof(*p));
57 	force_assert(NULL != p);
58 
59 	return p;
60 }
61 
62 static void plugin_free(plugin *p) {
63 #if !defined(LIGHTTPD_STATIC)
64 	int use_dlclose = 1;
65 #endif
66 
67 	if (p->name) buffer_free(p->name);
68 #if defined(HAVE_VALGRIND_VALGRIND_H) && !defined(LIGHTTPD_STATIC)
69 	/*if (RUNNING_ON_VALGRIND) use_dlclose = 0;*/
70 #endif
71 
72 #if !defined(LIGHTTPD_STATIC)
73 	if (use_dlclose && p->lib) {
74 #if defined(__WIN32)
75 )		FreeLibrary(p->lib);
76 #else
77 		dlclose(p->lib);
78 #endif
79 	}
80 #endif
81 
82 	free(p);
83 }
84 
85 static int plugins_register(server *srv, plugin *p) {
86 	plugin **ps;
87 	if (0 == srv->plugins.size) {
88 		srv->plugins.size = 4;
89 		srv->plugins.ptr  = malloc(srv->plugins.size * sizeof(*ps));
90 		force_assert(NULL != srv->plugins.ptr);
91 		srv->plugins.used = 0;
92 	} else if (srv->plugins.used == srv->plugins.size) {
93 		srv->plugins.size += 4;
94 		srv->plugins.ptr   = realloc(srv->plugins.ptr, srv->plugins.size * sizeof(*ps));
95 		force_assert(NULL != srv->plugins.ptr);
96 	}
97 
98 	ps = srv->plugins.ptr;
99 	ps[srv->plugins.used++] = p;
100 
101 	return 0;
102 }
103 
104 /**
105  *
106  *
107  *
108  */
109 
110 #if defined(LIGHTTPD_STATIC)
111 
112 /* pre-declare functions, as there is no header for them */
113 #define PLUGIN_INIT(x)\
114 	int x ## _plugin_init(plugin *p);
115 
116 #include "plugin-static.h"
117 
118 #undef PLUGIN_INIT
119 
120 /* build NULL-terminated table of name + init-function */
121 
122 typedef struct {
123 	const char* name;
124 	int (*plugin_init)(plugin *p);
125 } plugin_load_functions;
126 
127 static const plugin_load_functions load_functions[] = {
128 #define PLUGIN_INIT(x) \
129 	{ #x, &x ## _plugin_init },
130 
131 #include "plugin-static.h"
132 
133 	{ NULL, NULL }
134 #undef PLUGIN_INIT
135 };
136 
137 int plugins_load(server *srv) {
138 	plugin *p;
139 	size_t i, j;
140 
141 	for (i = 0; i < srv->srvconf.modules->used; i++) {
142 		data_string *d = (data_string *)srv->srvconf.modules->data[i];
143 		char *module = d->value->ptr;
144 
145 		for (j = 0; j < i; j++) {
146 			if (buffer_is_equal(d->value, ((data_string *) srv->srvconf.modules->data[j])->value)) {
147 				log_error_write(srv, __FILE__, __LINE__, "sbs",
148 					"Cannot load plugin", d->value,
149 					"more than once, please fix your config (lighttpd may not accept such configs in future releases)");
150 				continue;
151 			}
152 		}
153 
154 		for (j = 0; load_functions[j].name; ++j) {
155 			if (0 == strcmp(load_functions[j].name, module)) {
156 				p = plugin_init();
157 				if ((*load_functions[j].plugin_init)(p)) {
158 					log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin init failed" );
159 					plugin_free(p);
160 					return -1;
161 				}
162 				plugins_register(srv, p);
163 				break;
164 			}
165 		}
166 		if (!load_functions[j].name) {
167 			log_error_write(srv, __FILE__, __LINE__, "ss", module, " plugin not found" );
168 			return -1;
169 		}
170 	}
171 
172 	return 0;
173 }
174 #else /* defined(LIGHTTPD_STATIC) */
175 int plugins_load(server *srv) {
176 	plugin *p;
177 	int (*init)(plugin *pl);
178 	const char *error;
179 	size_t i, j;
180 
181 	for (i = 0; i < srv->srvconf.modules->used; i++) {
182 		data_string *d = (data_string *)srv->srvconf.modules->data[i];
183 		char *module = d->value->ptr;
184 
185 		for (j = 0; j < i; j++) {
186 			if (buffer_is_equal(d->value, ((data_string *) srv->srvconf.modules->data[j])->value)) {
187 				log_error_write(srv, __FILE__, __LINE__, "sbs",
188 					"Cannot load plugin", d->value,
189 					"more than once, please fix your config (lighttpd may not accept such configs in future releases)");
190 				continue;
191 			}
192 		}
193 
194 		buffer_copy_buffer(srv->tmp_buf, srv->srvconf.modules_dir);
195 
196 		buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("/"));
197 		buffer_append_string(srv->tmp_buf, module);
198 #if defined(__WIN32) || defined(__CYGWIN__)
199 		buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".dll"));
200 #else
201 		buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".so"));
202 #endif
203 
204 		p = plugin_init();
205 #ifdef __WIN32
206 		if (NULL == (p->lib = LoadLibrary(srv->tmp_buf->ptr))) {
207 			LPVOID lpMsgBuf;
208 			FormatMessage(
209 				FORMAT_MESSAGE_ALLOCATE_BUFFER |
210 					FORMAT_MESSAGE_FROM_SYSTEM,
211 				NULL,
212 				GetLastError(),
213 				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
214 				(LPTSTR) &lpMsgBuf,
215 				0, NULL);
216 
217 			log_error_write(srv, __FILE__, __LINE__, "ssb", "LoadLibrary() failed",
218 				lpMsgBuf, srv->tmp_buf);
219 
220 			plugin_free(p);
221 
222 			return -1;
223 
224 		}
225 #else
226 		if (NULL == (p->lib = dlopen(srv->tmp_buf->ptr, RTLD_NOW|RTLD_GLOBAL))) {
227 			log_error_write(srv, __FILE__, __LINE__, "sbs", "dlopen() failed for:",
228 				srv->tmp_buf, dlerror());
229 
230 			plugin_free(p);
231 
232 			return -1;
233 		}
234 
235 #endif
236 		buffer_reset(srv->tmp_buf);
237 		buffer_copy_string(srv->tmp_buf, module);
238 		buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("_plugin_init"));
239 
240 #ifdef __WIN32
241 		init = GetProcAddress(p->lib, srv->tmp_buf->ptr);
242 
243 		if (init == NULL) {
244 			LPVOID lpMsgBuf;
245 			FormatMessage(
246 				FORMAT_MESSAGE_ALLOCATE_BUFFER |
247 					FORMAT_MESSAGE_FROM_SYSTEM,
248 				NULL,
249 				GetLastError(),
250 				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
251 				(LPTSTR) &lpMsgBuf,
252 				0, NULL);
253 
254 			log_error_write(srv, __FILE__, __LINE__, "sbs", "getprocaddress failed:", srv->tmp_buf, lpMsgBuf);
255 
256 			plugin_free(p);
257 			return -1;
258 		}
259 
260 #else
261 #if 1
262 		init = (int (*)(plugin *))(intptr_t)dlsym(p->lib, srv->tmp_buf->ptr);
263 #else
264 		*(void **)(&init) = dlsym(p->lib, srv->tmp_buf->ptr);
265 #endif
266 		if ((error = dlerror()) != NULL)  {
267 			log_error_write(srv, __FILE__, __LINE__, "s", error);
268 
269 			plugin_free(p);
270 			return -1;
271 		}
272 
273 #endif
274 		if ((*init)(p)) {
275 			log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin init failed" );
276 
277 			plugin_free(p);
278 			return -1;
279 		}
280 #if 0
281 		log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin loaded" );
282 #endif
283 		plugins_register(srv, p);
284 	}
285 
286 	return 0;
287 }
288 #endif /* defined(LIGHTTPD_STATIC) */
289 
290 #define PLUGIN_TO_SLOT(x, y) \
291 	handler_t plugins_call_##y(server *srv, connection *con) {\
292 		plugin **slot;\
293 		size_t j;\
294 		if (!srv->plugin_slots) return HANDLER_GO_ON;\
295 		slot = ((plugin ***)(srv->plugin_slots))[x];\
296 		if (!slot) return HANDLER_GO_ON;\
297 		for (j = 0; j < srv->plugins.used && slot[j]; j++) { \
298 			plugin *p = slot[j];\
299 			handler_t r;\
300 			switch(r = p->y(srv, con, p->data)) {\
301 			case HANDLER_GO_ON:\
302 				break;\
303 			case HANDLER_FINISHED:\
304 			case HANDLER_COMEBACK:\
305 			case HANDLER_WAIT_FOR_EVENT:\
306 			case HANDLER_WAIT_FOR_FD:\
307 			case HANDLER_ERROR:\
308 				return r;\
309 			default:\
310 				log_error_write(srv, __FILE__, __LINE__, "sbs", #x, p->name, "unknown state");\
311 				return HANDLER_ERROR;\
312 			}\
313 		}\
314 		return HANDLER_GO_ON;\
315 	}
316 
317 /**
318  * plugins that use
319  *
320  * - server *srv
321  * - connection *con
322  * - void *p_d (plugin_data *)
323  */
324 
325 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean)
326 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw)
327 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done)
328 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close)
329 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest)
330 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start)
331 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot)
332 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical)
333 PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset)
334 
335 #undef PLUGIN_TO_SLOT
336 
337 #define PLUGIN_TO_SLOT(x, y) \
338 	handler_t plugins_call_##y(server *srv) {\
339 		plugin **slot;\
340 		size_t j;\
341 		if (!srv->plugin_slots) return HANDLER_GO_ON;\
342 		slot = ((plugin ***)(srv->plugin_slots))[x];\
343 		if (!slot) return HANDLER_GO_ON;\
344 		for (j = 0; j < srv->plugins.used && slot[j]; j++) { \
345 			plugin *p = slot[j];\
346 			handler_t r;\
347 			switch(r = p->y(srv, p->data)) {\
348 			case HANDLER_GO_ON:\
349 				break;\
350 			case HANDLER_FINISHED:\
351 			case HANDLER_COMEBACK:\
352 			case HANDLER_WAIT_FOR_EVENT:\
353 			case HANDLER_WAIT_FOR_FD:\
354 			case HANDLER_ERROR:\
355 				return r;\
356 			default:\
357 				log_error_write(srv, __FILE__, __LINE__, "sbsd", #x, p->name, "unknown state:", r);\
358 				return HANDLER_ERROR;\
359 			}\
360 		}\
361 		return HANDLER_GO_ON;\
362 	}
363 
364 /**
365  * plugins that use
366  *
367  * - server *srv
368  * - void *p_d (plugin_data *)
369  */
370 
371 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger)
372 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup)
373 PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup)
374 PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults)
375 
376 #undef PLUGIN_TO_SLOT
377 
378 #if 0
379 /**
380  *
381  * special handler
382  *
383  */
384 handler_t plugins_call_handle_fdevent(server *srv, const fd_conn *fdc) {
385 	size_t i;
386 	plugin **ps;
387 
388 	ps = srv->plugins.ptr;
389 
390 	for (i = 0; i < srv->plugins.used; i++) {
391 		plugin *p = ps[i];
392 		if (p->handle_fdevent) {
393 			handler_t r;
394 			switch(r = p->handle_fdevent(srv, fdc, p->data)) {
395 			case HANDLER_GO_ON:
396 				break;
397 			case HANDLER_FINISHED:
398 			case HANDLER_COMEBACK:
399 			case HANDLER_WAIT_FOR_EVENT:
400 			case HANDLER_ERROR:
401 				return r;
402 			default:
403 				log_error_write(srv, __FILE__, __LINE__, "d", r);
404 				break;
405 			}
406 		}
407 	}
408 
409 	return HANDLER_GO_ON;
410 }
411 #endif
412 /**
413  *
414  * - call init function of all plugins to init the plugin-internals
415  * - added each plugin that supports has callback to the corresponding slot
416  *
417  * - is only called once.
418  */
419 
420 handler_t plugins_call_init(server *srv) {
421 	size_t i;
422 	plugin **ps;
423 
424 	ps = srv->plugins.ptr;
425 
426 	/* fill slots */
427 
428 	srv->plugin_slots = calloc(PLUGIN_FUNC_SIZEOF, sizeof(ps));
429 	force_assert(NULL != srv->plugin_slots);
430 
431 	for (i = 0; i < srv->plugins.used; i++) {
432 		size_t j;
433 		/* check which calls are supported */
434 
435 		plugin *p = ps[i];
436 
437 #define PLUGIN_TO_SLOT(x, y) \
438 	if (p->y) { \
439 		plugin **slot = ((plugin ***)(srv->plugin_slots))[x]; \
440 		if (!slot) { \
441 			slot = calloc(srv->plugins.used, sizeof(*slot));\
442 			force_assert(NULL != slot); \
443 			((plugin ***)(srv->plugin_slots))[x] = slot; \
444 		} \
445 		for (j = 0; j < srv->plugins.used; j++) { \
446 			if (slot[j]) continue;\
447 			slot[j] = p;\
448 			break;\
449 		}\
450 	}
451 
452 
453 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean);
454 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw);
455 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done);
456 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close);
457 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger);
458 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup);
459 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest);
460 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start);
461 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot);
462 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical);
463 		PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset);
464 		PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup);
465 		PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults);
466 #undef PLUGIN_TO_SLOT
467 
468 		if (p->init) {
469 			if (NULL == (p->data = p->init())) {
470 				log_error_write(srv, __FILE__, __LINE__, "sb",
471 						"plugin-init failed for module", p->name);
472 				return HANDLER_ERROR;
473 			}
474 
475 			/* used for con->mode, DIRECT == 0, plugins above that */
476 			((plugin_data *)(p->data))->id = i + 1;
477 
478 			if (p->version != LIGHTTPD_VERSION_ID) {
479 				log_error_write(srv, __FILE__, __LINE__, "sb",
480 						"plugin-version doesn't match lighttpd-version for", p->name);
481 				return HANDLER_ERROR;
482 			}
483 		} else {
484 			p->data = NULL;
485 		}
486 	}
487 
488 	return HANDLER_GO_ON;
489 }
490 
491 void plugins_free(server *srv) {
492 	size_t i;
493 	plugins_call_cleanup(srv);
494 
495 	for (i = 0; i < srv->plugins.used; i++) {
496 		plugin *p = ((plugin **)srv->plugins.ptr)[i];
497 
498 		plugin_free(p);
499 	}
500 
501 	for (i = 0; srv->plugin_slots && i < PLUGIN_FUNC_SIZEOF; i++) {
502 		plugin **slot = ((plugin ***)(srv->plugin_slots))[i];
503 
504 		if (slot) free(slot);
505 	}
506 
507 	free(srv->plugin_slots);
508 	srv->plugin_slots = NULL;
509 
510 	free(srv->plugins.ptr);
511 	srv->plugins.ptr = NULL;
512 	srv->plugins.used = 0;
513 }
514