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