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