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