xref: /lighttpd1.4/src/plugin.c (revision 4f0261e5)
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 	PLUGIN_FUNC_WORKER_INIT,
54 
55 	PLUGIN_FUNC_SIZEOF
56 } plugin_t;
57 
58 static plugin *plugin_init(void) {
59 	plugin *p;
60 
61 	p = calloc(1, sizeof(*p));
62 	force_assert(NULL != p);
63 
64 	return p;
65 }
66 
67 static void plugin_free(plugin *p) {
68 #if !defined(LIGHTTPD_STATIC)
69 	int use_dlclose = 1;
70 #endif
71 
72 	if (p->name) buffer_free(p->name);
73 #if defined(HAVE_VALGRIND_VALGRIND_H) && !defined(LIGHTTPD_STATIC)
74 	/*if (RUNNING_ON_VALGRIND) use_dlclose = 0;*/
75 #endif
76 
77 #if !defined(LIGHTTPD_STATIC)
78 	if (use_dlclose && p->lib) {
79 #if defined(__WIN32)
80 )		FreeLibrary(p->lib);
81 #else
82 		dlclose(p->lib);
83 #endif
84 	}
85 #endif
86 
87 	free(p);
88 }
89 
90 static int plugins_register(server *srv, plugin *p) {
91 	plugin **ps;
92 	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 	size_t i, j;
179 
180 	for (i = 0; i < srv->srvconf.modules->used; i++) {
181 		data_string *d = (data_string *)srv->srvconf.modules->data[i];
182 		char *module = d->value->ptr;
183 
184 		for (j = 0; j < i; j++) {
185 			if (buffer_is_equal(d->value, ((data_string *) srv->srvconf.modules->data[j])->value)) {
186 				log_error_write(srv, __FILE__, __LINE__, "sbs",
187 					"Cannot load plugin", d->value,
188 					"more than once, please fix your config (lighttpd may not accept such configs in future releases)");
189 				continue;
190 			}
191 		}
192 
193 		buffer_copy_buffer(srv->tmp_buf, srv->srvconf.modules_dir);
194 
195 		buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("/"));
196 		buffer_append_string(srv->tmp_buf, module);
197 #if defined(__WIN32) || defined(__CYGWIN__)
198 		buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".dll"));
199 #else
200 		buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".so"));
201 #endif
202 
203 		p = plugin_init();
204 #ifdef __WIN32
205 		if (NULL == (p->lib = LoadLibrary(srv->tmp_buf->ptr))) {
206 			LPVOID lpMsgBuf;
207 			FormatMessage(
208 				FORMAT_MESSAGE_ALLOCATE_BUFFER |
209 					FORMAT_MESSAGE_FROM_SYSTEM,
210 				NULL,
211 				GetLastError(),
212 				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
213 				(LPTSTR) &lpMsgBuf,
214 				0, NULL);
215 
216 			log_error_write(srv, __FILE__, __LINE__, "ssb", "LoadLibrary() failed",
217 				lpMsgBuf, srv->tmp_buf);
218 
219 			plugin_free(p);
220 
221 			return -1;
222 
223 		}
224 #else
225 		if (NULL == (p->lib = dlopen(srv->tmp_buf->ptr, RTLD_NOW|RTLD_GLOBAL))) {
226 			log_error_write(srv, __FILE__, __LINE__, "sbs", "dlopen() failed for:",
227 				srv->tmp_buf, dlerror());
228 
229 			plugin_free(p);
230 
231 			return -1;
232 		}
233 
234 #endif
235 		buffer_copy_string(srv->tmp_buf, module);
236 		buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("_plugin_init"));
237 
238 #ifdef __WIN32
239 		init = GetProcAddress(p->lib, srv->tmp_buf->ptr);
240 
241 		if (init == NULL) {
242 			LPVOID lpMsgBuf;
243 			FormatMessage(
244 				FORMAT_MESSAGE_ALLOCATE_BUFFER |
245 					FORMAT_MESSAGE_FROM_SYSTEM,
246 				NULL,
247 				GetLastError(),
248 				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
249 				(LPTSTR) &lpMsgBuf,
250 				0, NULL);
251 
252 			log_error_write(srv, __FILE__, __LINE__, "sbs", "getprocaddress failed:", srv->tmp_buf, lpMsgBuf);
253 
254 			plugin_free(p);
255 			return -1;
256 		}
257 
258 #else
259 #if 1
260 		init = (int (*)(plugin *))(intptr_t)dlsym(p->lib, srv->tmp_buf->ptr);
261 #else
262 		*(void **)(&init) = dlsym(p->lib, srv->tmp_buf->ptr);
263 #endif
264 		if (NULL == init) {
265 			const char *error = dlerror();
266 			if (error != NULL) {
267 				log_error_write(srv, __FILE__, __LINE__, "ss", "dlsym:", error);
268 			} else {
269 				log_error_write(srv, __FILE__, __LINE__, "ss", "dlsym symbol not found:", srv->tmp_buf->ptr);
270 			}
271 
272 			plugin_free(p);
273 			return -1;
274 		}
275 
276 #endif
277 		if ((*init)(p)) {
278 			log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin init failed" );
279 
280 			plugin_free(p);
281 			return -1;
282 		}
283 #if 0
284 		log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin loaded" );
285 #endif
286 		plugins_register(srv, p);
287 	}
288 
289 	return 0;
290 }
291 #endif /* defined(LIGHTTPD_STATIC) */
292 
293 #define PLUGIN_TO_SLOT(x, y) \
294 	handler_t plugins_call_##y(server *srv, connection *con) {\
295 		plugin ** const slot = ((plugin ***)(srv->plugin_slots))[x];\
296 		const size_t used = srv->plugins.used;\
297 		handler_t rc = HANDLER_GO_ON;\
298 		if (slot) {\
299 			const plugin *p;\
300 			for (size_t i = 0; i < used && (p = slot[i]) && (rc = p->y(srv, con, p->data)) == HANDLER_GO_ON; ++i) ;\
301 		}\
302 		return rc;\
303 	}
304 
305 /**
306  * plugins that use
307  *
308  * - server *srv
309  * - connection *con
310  * - void *p_d (plugin_data *)
311  */
312 
313 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean)
314 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw)
315 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_ENV, handle_request_env)
316 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done)
317 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_ACCEPT, handle_connection_accept)
318 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_SHUT_WR, handle_connection_shut_wr)
319 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close)
320 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest)
321 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start)
322 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_START, handle_response_start)
323 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot)
324 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical)
325 PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset)
326 
327 #undef PLUGIN_TO_SLOT
328 
329 #define PLUGIN_TO_SLOT(x, y) \
330 	handler_t plugins_call_##y(server *srv) {\
331 		plugin ** const slot = ((plugin ***)(srv->plugin_slots))[x];\
332 		const size_t used = srv->plugins.used; \
333 		handler_t rc = HANDLER_GO_ON;\
334 		if (slot) {\
335 			const plugin *p;\
336 			for (size_t i = 0; i < used && (p = slot[i]) && (rc = p->y(srv, p->data)) == HANDLER_GO_ON; ++i) ;\
337 		}\
338 		return rc;\
339 	}
340 
341 /**
342  * plugins that use
343  *
344  * - server *srv
345  * - void *p_d (plugin_data *)
346  */
347 
348 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger)
349 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup)
350 PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup)
351 PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults)
352 PLUGIN_TO_SLOT(PLUGIN_FUNC_WORKER_INIT, worker_init)
353 
354 #undef PLUGIN_TO_SLOT
355 
356 handler_t plugins_call_handle_waitpid(server *srv, pid_t pid, int status) {
357 	plugin ** const slot =
358 	  ((plugin ***)(srv->plugin_slots))[PLUGIN_FUNC_HANDLE_WAITPID];
359 	if (!slot) return HANDLER_GO_ON;
360 	for (size_t i = 0; i < srv->plugins.used && slot[i]; ++i) {
361 		plugin *p = slot[i];
362 		handler_t r = p->handle_waitpid(srv, p->data, pid, status);
363 		if (r != HANDLER_GO_ON) return r;
364 	}
365 	return HANDLER_GO_ON;
366 }
367 
368 #if 0
369 /**
370  *
371  * special handler
372  *
373  */
374 handler_t plugins_call_handle_fdevent(server *srv, const fd_conn *fdc) {
375 	size_t i;
376 	plugin **ps;
377 
378 	ps = srv->plugins.ptr;
379 
380 	for (i = 0; i < srv->plugins.used; i++) {
381 		plugin *p = ps[i];
382 		if (p->handle_fdevent) {
383 			handler_t r;
384 			switch(r = p->handle_fdevent(srv, fdc, p->data)) {
385 			case HANDLER_GO_ON:
386 				break;
387 			case HANDLER_FINISHED:
388 			case HANDLER_COMEBACK:
389 			case HANDLER_WAIT_FOR_EVENT:
390 			case HANDLER_ERROR:
391 				return r;
392 			default:
393 				log_error_write(srv, __FILE__, __LINE__, "d", r);
394 				break;
395 			}
396 		}
397 	}
398 
399 	return HANDLER_GO_ON;
400 }
401 #endif
402 /**
403  *
404  * - call init function of all plugins to init the plugin-internals
405  * - added each plugin that supports has callback to the corresponding slot
406  *
407  * - is only called once.
408  */
409 
410 handler_t plugins_call_init(server *srv) {
411 	size_t i;
412 	plugin **ps;
413 
414 	ps = srv->plugins.ptr;
415 
416 	/* fill slots */
417 
418 	srv->plugin_slots = calloc(PLUGIN_FUNC_SIZEOF, sizeof(ps));
419 	force_assert(NULL != srv->plugin_slots);
420 
421 	for (i = 0; i < srv->plugins.used; i++) {
422 		size_t j;
423 		/* check which calls are supported */
424 
425 		plugin *p = ps[i];
426 
427 #define PLUGIN_TO_SLOT(x, y) \
428 	if (p->y) { \
429 		plugin **slot = ((plugin ***)(srv->plugin_slots))[x]; \
430 		if (!slot) { \
431 			slot = calloc(srv->plugins.used, sizeof(*slot));\
432 			force_assert(NULL != slot); \
433 			((plugin ***)(srv->plugin_slots))[x] = slot; \
434 		} \
435 		for (j = 0; j < srv->plugins.used; j++) { \
436 			if (slot[j]) continue;\
437 			slot[j] = p;\
438 			break;\
439 		}\
440 	}
441 
442 
443 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean);
444 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw);
445 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_ENV, handle_request_env);
446 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done);
447 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_ACCEPT, handle_connection_accept);
448 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_SHUT_WR, handle_connection_shut_wr);
449 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close);
450 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger);
451 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup);
452 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_WAITPID, handle_waitpid);
453 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest);
454 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start);
455 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_START, handle_response_start);
456 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot);
457 		PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical);
458 		PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset);
459 		PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup);
460 		PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults);
461 		PLUGIN_TO_SLOT(PLUGIN_FUNC_WORKER_INIT, worker_init);
462 #undef PLUGIN_TO_SLOT
463 
464 		if (p->init) {
465 			if (NULL == (p->data = p->init())) {
466 				log_error_write(srv, __FILE__, __LINE__, "sb",
467 						"plugin-init failed for module", p->name);
468 				return HANDLER_ERROR;
469 			}
470 
471 			/* used for con->mode, DIRECT == 0, plugins above that */
472 			((plugin_data *)(p->data))->id = i + 1;
473 
474 			if (p->version != LIGHTTPD_VERSION_ID) {
475 				log_error_write(srv, __FILE__, __LINE__, "sb",
476 						"plugin-version doesn't match lighttpd-version for", p->name);
477 				return HANDLER_ERROR;
478 			}
479 		} else {
480 			p->data = NULL;
481 		}
482 
483 		if (p->priv_defaults && HANDLER_ERROR==p->priv_defaults(srv, p->data)) {
484 			return HANDLER_ERROR;
485 		}
486 	}
487 
488 	return HANDLER_GO_ON;
489 }
490 
491 void plugins_free(server *srv) {
492 	size_t i;
493 	if (srv->plugin_slots) 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