xref: /lighttpd1.4/src/plugin.c (revision 3d04bc45)
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_RESPONSE_START,
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,
50 
51 	PLUGIN_FUNC_SIZEOF
52 } plugin_t;
53 
54 static plugin *plugin_init(void) {
55 	plugin *p;
56 
57 	p = calloc(1, sizeof(*p));
58 	force_assert(NULL != p);
59 
60 	return p;
61 }
62 
63 static void plugin_free(plugin *p) {
64 #if !defined(LIGHTTPD_STATIC)
65 	int use_dlclose = 1;
66 #endif
67 
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
72 
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
80 	}
81 #endif
82 
83 	free(p);
84 }
85 
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);
97 	}
98 
99 	ps = srv->plugins.ptr;
100 	ps[srv->plugins.used++] = p;
101 
102 	return 0;
103 }
104 
105 /**
106  *
107  *
108  *
109  */
110 
111 #if defined(LIGHTTPD_STATIC)
112 
113 /* pre-declare functions, as there is no header for them */
114 #define PLUGIN_INIT(x)\
115 	int x ## _plugin_init(plugin *p);
116 
117 #include "plugin-static.h"
118 
119 #undef PLUGIN_INIT
120 
121 /* build NULL-terminated table of name + init-function */
122 
123 typedef struct {
124 	const char* name;
125 	int (*plugin_init)(plugin *p);
126 } plugin_load_functions;
127 
128 static const plugin_load_functions load_functions[] = {
129 #define PLUGIN_INIT(x) \
130 	{ #x, &x ## _plugin_init },
131 
132 #include "plugin-static.h"
133 
134 	{ NULL, NULL }
135 #undef PLUGIN_INIT
136 };
137 
138 int plugins_load(server *srv) {
139 	plugin *p;
140 	size_t i, j;
141 
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;
145 
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;
152 			}
153 		}
154 
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;
162 				}
163 				plugins_register(srv, p);
164 				break;
165 			}
166 		}
167 		if (!load_functions[j].name) {
168 			log_error_write(srv, __FILE__, __LINE__, "ss", module, " plugin not found" );
169 			return -1;
170 		}
171 	}
172 
173 	return 0;
174 }
175 #else /* defined(LIGHTTPD_STATIC) */
176 int plugins_load(server *srv) {
177 	plugin *p;
178 	int (*init)(plugin *pl);
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 (NULL == init) {
267 			const char *error = dlerror();
268 			if (error != NULL) {
269 				log_error_write(srv, __FILE__, __LINE__, "ss", "dlsym:", error);
270 			} else {
271 				log_error_write(srv, __FILE__, __LINE__, "ss", "dlsym symbol not found:", srv->tmp_buf->ptr);
272 			}
273 
274 			plugin_free(p);
275 			return -1;
276 		}
277 
278 #endif
279 		if ((*init)(p)) {
280 			log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin init failed" );
281 
282 			plugin_free(p);
283 			return -1;
284 		}
285 #if 0
286 		log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin loaded" );
287 #endif
288 		plugins_register(srv, p);
289 	}
290 
291 	return 0;
292 }
293 #endif /* defined(LIGHTTPD_STATIC) */
294 
295 #define PLUGIN_TO_SLOT(x, y) \
296 	handler_t plugins_call_##y(server *srv, connection *con) {\
297 		plugin **slot;\
298 		size_t j;\
299 		if (!srv->plugin_slots) return HANDLER_GO_ON;\
300 		slot = ((plugin ***)(srv->plugin_slots))[x];\
301 		if (!slot) return HANDLER_GO_ON;\
302 		for (j = 0; j < srv->plugins.used && slot[j]; j++) { \
303 			plugin *p = slot[j];\
304 			handler_t r;\
305 			switch(r = p->y(srv, con, p->data)) {\
306 			case HANDLER_GO_ON:\
307 				break;\
308 			case HANDLER_FINISHED:\
309 			case HANDLER_COMEBACK:\
310 			case HANDLER_WAIT_FOR_EVENT:\
311 			case HANDLER_WAIT_FOR_FD:\
312 			case HANDLER_ERROR:\
313 				return r;\
314 			default:\
315 				log_error_write(srv, __FILE__, __LINE__, "sbs", #x, p->name, "unknown state");\
316 				return HANDLER_ERROR;\
317 			}\
318 		}\
319 		return HANDLER_GO_ON;\
320 	}
321 
322 /**
323  * plugins that use
324  *
325  * - server *srv
326  * - connection *con
327  * - void *p_d (plugin_data *)
328  */
329 
330 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean)
331 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw)
332 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done)
333 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close)
334 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest)
335 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start)
336 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_START, handle_response_start)
337 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot)
338 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical)
339 PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset)
340 
341 #undef PLUGIN_TO_SLOT
342 
343 #define PLUGIN_TO_SLOT(x, y) \
344 	handler_t plugins_call_##y(server *srv) {\
345 		plugin **slot;\
346 		size_t j;\
347 		if (!srv->plugin_slots) return HANDLER_GO_ON;\
348 		slot = ((plugin ***)(srv->plugin_slots))[x];\
349 		if (!slot) return HANDLER_GO_ON;\
350 		for (j = 0; j < srv->plugins.used && slot[j]; j++) { \
351 			plugin *p = slot[j];\
352 			handler_t r;\
353 			switch(r = p->y(srv, p->data)) {\
354 			case HANDLER_GO_ON:\
355 				break;\
356 			case HANDLER_FINISHED:\
357 			case HANDLER_COMEBACK:\
358 			case HANDLER_WAIT_FOR_EVENT:\
359 			case HANDLER_WAIT_FOR_FD:\
360 			case HANDLER_ERROR:\
361 				return r;\
362 			default:\
363 				log_error_write(srv, __FILE__, __LINE__, "sbsd", #x, p->name, "unknown state:", r);\
364 				return HANDLER_ERROR;\
365 			}\
366 		}\
367 		return HANDLER_GO_ON;\
368 	}
369 
370 /**
371  * plugins that use
372  *
373  * - server *srv
374  * - void *p_d (plugin_data *)
375  */
376 
377 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger)
378 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup)
379 PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup)
380 PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults)
381 
382 #undef PLUGIN_TO_SLOT
383 
384 #if 0
385 /**
386  *
387  * special handler
388  *
389  */
390 handler_t plugins_call_handle_fdevent(server *srv, const fd_conn *fdc) {
391 	size_t i;
392 	plugin **ps;
393 
394 	ps = srv->plugins.ptr;
395 
396 	for (i = 0; i < srv->plugins.used; i++) {
397 		plugin *p = ps[i];
398 		if (p->handle_fdevent) {
399 			handler_t r;
400 			switch(r = p->handle_fdevent(srv, fdc, p->data)) {
401 			case HANDLER_GO_ON:
402 				break;
403 			case HANDLER_FINISHED:
404 			case HANDLER_COMEBACK:
405 			case HANDLER_WAIT_FOR_EVENT:
406 			case HANDLER_ERROR:
407 				return r;
408 			default:
409 				log_error_write(srv, __FILE__, __LINE__, "d", r);
410 				break;
411 			}
412 		}
413 	}
414 
415 	return HANDLER_GO_ON;
416 }
417 #endif
418 /**
419  *
420  * - call init function of all plugins to init the plugin-internals
421  * - added each plugin that supports has callback to the corresponding slot
422  *
423  * - is only called once.
424  */
425 
426 handler_t plugins_call_init(server *srv) {
427 	size_t i;
428 	plugin **ps;
429 
430 	ps = srv->plugins.ptr;
431 
432 	/* fill slots */
433 
434 	srv->plugin_slots = calloc(PLUGIN_FUNC_SIZEOF, sizeof(ps));
435 	force_assert(NULL != srv->plugin_slots);
436 
437 	for (i = 0; i < srv->plugins.used; i++) {
438 		size_t j;
439 		/* check which calls are supported */
440 
441 		plugin *p = ps[i];
442 
443 #define PLUGIN_TO_SLOT(x, y) \
444 	if (p->y) { \
445 		plugin **slot = ((plugin ***)(srv->plugin_slots))[x]; \
446 		if (!slot) { \
447 			slot = calloc(srv->plugins.used, sizeof(*slot));\
448 			force_assert(NULL != slot); \
449 			((plugin ***)(srv->plugin_slots))[x] = slot; \
450 		} \
451 		for (j = 0; j < srv->plugins.used; j++) { \
452 			if (slot[j]) continue;\
453 			slot[j] = p;\
454 			break;\
455 		}\
456 	}
457 
458 
459 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean);
460 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw);
461 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done);
462 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close);
463 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger);
464 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup);
465 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest);
466 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start);
467 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_START, handle_response_start);
468 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot);
469 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical);
470 		PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset);
471 		PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup);
472 		PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults);
473 #undef PLUGIN_TO_SLOT
474 
475 		if (p->init) {
476 			if (NULL == (p->data = p->init())) {
477 				log_error_write(srv, __FILE__, __LINE__, "sb",
478 						"plugin-init failed for module", p->name);
479 				return HANDLER_ERROR;
480 			}
481 
482 			/* used for con->mode, DIRECT == 0, plugins above that */
483 			((plugin_data *)(p->data))->id = i + 1;
484 
485 			if (p->version != LIGHTTPD_VERSION_ID) {
486 				log_error_write(srv, __FILE__, __LINE__, "sb",
487 						"plugin-version doesn't match lighttpd-version for", p->name);
488 				return HANDLER_ERROR;
489 			}
490 		} else {
491 			p->data = NULL;
492 		}
493 	}
494 
495 	return HANDLER_GO_ON;
496 }
497 
498 void plugins_free(server *srv) {
499 	size_t i;
500 	plugins_call_cleanup(srv);
501 
502 	for (i = 0; i < srv->plugins.used; i++) {
503 		plugin *p = ((plugin **)srv->plugins.ptr)[i];
504 
505 		plugin_free(p);
506 	}
507 
508 	for (i = 0; srv->plugin_slots && i < PLUGIN_FUNC_SIZEOF; i++) {
509 		plugin **slot = ((plugin ***)(srv->plugin_slots))[i];
510 
511 		if (slot) free(slot);
512 	}
513 
514 	free(srv->plugin_slots);
515 	srv->plugin_slots = NULL;
516 
517 	free(srv->plugins.ptr);
518 	srv->plugins.ptr = NULL;
519 	srv->plugins.used = 0;
520 }
521