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