xref: /lighttpd1.4/src/plugin.c (revision 14f2d9db)
1 #include "first.h"
2 
3 #include "plugins.h"
4 #include "plugin.h"
5 #include "base.h"
6 #include "array.h"
7 #include "log.h"
8 
9 #include <string.h>
10 #include <stdlib.h>
11 
12 #ifdef HAVE_VALGRIND_VALGRIND_H
13 # include <valgrind/valgrind.h>
14 #endif
15 
16 #ifndef LIGHTTPD_STATIC
17 #ifdef HAVE_DLFCN_H
18 #include <dlfcn.h>
19 #endif
20 #endif
21 /*
22  *
23  * if you change this enum to add a new callback, be sure
24  * - that PLUGIN_FUNC_SIZEOF is the last entry
25  * - that you add:
26  *   1. PLUGIN_CALL_... as callback-dispatcher
27  *   2. count and assignment in plugins_call_init()
28  *
29  */
30 
31 typedef enum {
32 	PLUGIN_FUNC_HANDLE_URI_CLEAN,
33 	PLUGIN_FUNC_HANDLE_DOCROOT,
34 	PLUGIN_FUNC_HANDLE_PHYSICAL,
35 	PLUGIN_FUNC_HANDLE_SUBREQUEST_START,
36 	/* PLUGIN_FUNC_HANDLE_SUBREQUEST, *//* max one handler_module per req */
37 	PLUGIN_FUNC_HANDLE_RESPONSE_START,
38 	PLUGIN_FUNC_HANDLE_REQUEST_DONE,
39 	PLUGIN_FUNC_HANDLE_REQUEST_RESET,
40 	PLUGIN_FUNC_HANDLE_REQUEST_ENV,
41 	PLUGIN_FUNC_HANDLE_CONNECTION_ACCEPT,
42 	PLUGIN_FUNC_HANDLE_CONNECTION_SHUT_WR,
43 	PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE,
44 	PLUGIN_FUNC_HANDLE_TRIGGER,
45 	PLUGIN_FUNC_HANDLE_WAITPID,
46 	PLUGIN_FUNC_HANDLE_SIGHUP,
47 	/* PLUGIN_FUNC_INIT, *//* handled here in plugin.c */
48 	/* PLUGIN_FUNC_CLEANUP, *//* handled here in plugin.c */
49 	PLUGIN_FUNC_SET_DEFAULTS,
50 	PLUGIN_FUNC_WORKER_INIT,
51 
52 	PLUGIN_FUNC_SIZEOF
53 } plugin_t;
54 
55 __attribute_malloc__
56 __attribute_returns_nonnull__
57 static plugin *plugin_init(void) {
58 	return ck_calloc(1, sizeof(plugin));
59 }
60 
61 static void plugin_free(plugin *p) {
62     if (NULL == p) return; /*(should not happen w/ current usage)*/
63   #if !defined(LIGHTTPD_STATIC)
64     if (p->lib) {
65      #if defined(HAVE_VALGRIND_VALGRIND_H)
66      /*if (!RUNNING_ON_VALGRIND) */
67      #endif
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 #ifdef _WIN32
80 __attribute_cold__
81 static void
82 log_w32_syserror_2 (log_error_st *const errh, const char *file, const int line, const char * const str1, const char * const str2)
83 {
84     TCHAR lpMsgBuf[1024];
85     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
86                   0, /* MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) */
87                   (LPTSTR)lpMsgBuf, sizeof(lpMsgBuf)/sizeof(TCHAR), NULL);
88     log_error(errh, file, line, "%s for %s: %s", str1, str2, (char *)lpMsgBuf);
89 }
90 #endif
91 
92 /**
93  *
94  *
95  *
96  */
97 
98 #if defined(LIGHTTPD_STATIC)
99 
100 /* pre-declare functions, as there is no header for them */
101 #define PLUGIN_INIT(x)\
102 	int x ## _plugin_init(plugin *p);
103 
104 #include "plugin-static.h"
105 
106 #undef PLUGIN_INIT
107 
108 /* build NULL-terminated table of name + init-function */
109 
110 typedef struct {
111 	const char* name;
112 	int (*plugin_init)(plugin *p);
113 } plugin_load_functions;
114 
115 static const plugin_load_functions load_functions[] = {
116 #define PLUGIN_INIT(x) \
117 	{ #x, &x ## _plugin_init },
118 
119 #include "plugin-static.h"
120 
121 	{ NULL, NULL }
122 #undef PLUGIN_INIT
123 };
124 
125 int plugins_load(server *srv) {
126 	ck_realloc_u32(&srv->plugins.ptr, 0,
127 	               srv->srvconf.modules->used, sizeof(plugin *));
128 
129 	for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
130 		data_string *ds = (data_string *)srv->srvconf.modules->data[i];
131 		char *module = ds->value.ptr;
132 
133 		uint32_t j;
134 		for (j = 0; load_functions[j].name; ++j) {
135 			if (0 == strcmp(load_functions[j].name, module)) {
136 				plugin * const p = plugin_init();
137 				if ((*load_functions[j].plugin_init)(p)) {
138 					log_error(srv->errh, __FILE__, __LINE__, "%s plugin init failed", module);
139 					plugin_free(p);
140 					return -1;
141 				}
142 				((plugin **)srv->plugins.ptr)[srv->plugins.used++] = p;
143 				break;
144 			}
145 		}
146 		if (!load_functions[j].name) {
147 			log_error(srv->errh, __FILE__, __LINE__, "%s plugin not found", module);
148 			if (srv->srvconf.compat_module_load) {
149 				if (buffer_eq_slen(&ds->value, CONST_STR_LEN("mod_deflate")))
150 					continue;
151 			}
152 			return -1;
153 		}
154 	}
155 
156 	return 0;
157 }
158 
159 #else /* defined(LIGHTTPD_STATIC) */
160 
161 int plugins_load(server *srv) {
162 	ck_realloc_u32(&srv->plugins.ptr, 0,
163 	               srv->srvconf.modules->used, sizeof(plugin *));
164 
165 	buffer * const tb = srv->tmp_buf;
166 	int (*init)(plugin *pl);
167 
168 	for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
169 		const buffer * const module = &((data_string *)srv->srvconf.modules->data[i])->value;
170 		void *lib = NULL;
171 
172 		/* check if module is built-in to main executable */
173 		buffer_clear(tb);
174 		buffer_append_str2(tb, BUF_PTR_LEN(module),
175 		                       CONST_STR_LEN("_plugin_init"));
176 	  #ifdef _WIN32
177 		init = (int(WINAPI *)(plugin *))(intptr_t)
178 		  GetProcAddress(GetModuleHandle(NULL), tb->ptr);
179 	  #else
180 		init = (int (*)(plugin *))(intptr_t)dlsym(RTLD_DEFAULT, tb->ptr);
181 	  #endif
182 
183 	  if (NULL == init) {
184 		buffer_copy_string(tb, srv->srvconf.modules_dir);
185 		buffer_append_path_len(tb, BUF_PTR_LEN(module));
186 
187 	  #ifdef _WIN32
188 		buffer_append_string_len(tb, CONST_STR_LEN(".dll"));
189 		if (NULL == (lib = LoadLibrary(tb->ptr))) {
190 			log_w32_syserror_2(srv->errh, __FILE__, __LINE__,
191 			                   "LoadLibrary()", tb->ptr);
192 			if (srv->srvconf.compat_module_load) {
193 				if (buffer_eq_slen(module, CONST_STR_LEN("mod_deflate")))
194 					continue;
195 			}
196 			return -1;
197 		}
198 		buffer_copy_buffer(tb, module);
199 		buffer_append_string_len(tb, CONST_STR_LEN("_plugin_init"));
200 		init = (int(WINAPI *)(plugin *))(intptr_t)GetProcAddress(lib, tb->ptr);
201 		if (init == NULL) {
202 			log_w32_syserror_2(srv->errh, __FILE__, __LINE__,
203 			                   "GetProcAddress()", tb->ptr);
204 		        FreeLibrary(lib);
205 			return -1;
206 		}
207 	  #else
208 	   #if defined(__CYGWIN__)
209 		buffer_append_string_len(tb, CONST_STR_LEN(".dll"));
210 	   #else
211 		buffer_append_string_len(tb, CONST_STR_LEN(".so"));
212 	   #endif
213 		if (NULL == (lib = dlopen(tb->ptr, RTLD_NOW|RTLD_GLOBAL))) {
214 			log_error(srv->errh, __FILE__, __LINE__,
215 			  "dlopen() failed for: %s %s", tb->ptr, dlerror());
216 			if (srv->srvconf.compat_module_load) {
217 				if (buffer_eq_slen(module, CONST_STR_LEN("mod_deflate")))
218 					continue;
219 			}
220 			return -1;
221 		}
222 		buffer_clear(tb);
223 		buffer_append_str2(tb, BUF_PTR_LEN(module),
224                                        CONST_STR_LEN("_plugin_init"));
225 		init = (int (*)(plugin *))(intptr_t)dlsym(lib, tb->ptr);
226 		if (NULL == init) {
227 			const char *error = dlerror();
228 			if (error != NULL) {
229 				log_error(srv->errh, __FILE__, __LINE__, "dlsym: %s", error);
230 			} else {
231 				log_error(srv->errh, __FILE__, __LINE__, "dlsym symbol not found: %s", tb->ptr);
232 			}
233 		        dlclose(lib);
234 			return -1;
235 		}
236 	  #endif
237 	  }
238 
239 		plugin *p = plugin_init();
240 		p->lib = lib;
241 		if ((*init)(p)) {
242 			log_error(srv->errh, __FILE__, __LINE__, "%s plugin init failed", module->ptr);
243 			plugin_free(p);
244 			return -1;
245 		}
246 		((plugin **)srv->plugins.ptr)[srv->plugins.used++] = p;
247 	}
248 
249 	return 0;
250 }
251 
252 #endif /* defined(LIGHTTPD_STATIC) */
253 
254 typedef struct {
255   handler_t(*fn)();
256   plugin_data_base *data;
257 } plugin_fn_data;
258 
259 __attribute_hot__
260 static handler_t plugins_call_fn_req_data(request_st * const r, const int e) {
261     const void * const plugin_slots = r->con->plugin_slots;
262     const uint32_t offset = ((const uint16_t *)plugin_slots)[e];
263     if (0 == offset) return HANDLER_GO_ON;
264     const plugin_fn_data *plfd = (const plugin_fn_data *)
265       (((uintptr_t)plugin_slots) + offset);
266     handler_t rc = HANDLER_GO_ON;
267     while (plfd->fn && (rc = plfd->fn(r, plfd->data)) == HANDLER_GO_ON)
268         ++plfd;
269     return rc;
270 }
271 
272 __attribute_hot__
273 static handler_t plugins_call_fn_con_data(connection * const con, const int e) {
274     const void * const plugin_slots = con->plugin_slots;
275     const uint32_t offset = ((const uint16_t *)plugin_slots)[e];
276     if (0 == offset) return HANDLER_GO_ON;
277     const plugin_fn_data *plfd = (const plugin_fn_data *)
278       (((uintptr_t)plugin_slots) + offset);
279     handler_t rc = HANDLER_GO_ON;
280     while (plfd->fn && (rc = plfd->fn(con, plfd->data)) == HANDLER_GO_ON)
281         ++plfd;
282     return rc;
283 }
284 
285 static handler_t plugins_call_fn_srv_data(server * const srv, const int e) {
286     const uint32_t offset = ((const uint16_t *)srv->plugin_slots)[e];
287     if (0 == offset) return HANDLER_GO_ON;
288     const plugin_fn_data *plfd = (const plugin_fn_data *)
289       (((uintptr_t)srv->plugin_slots) + offset);
290     handler_t rc = HANDLER_GO_ON;
291     while (plfd->fn && (rc = plfd->fn(srv,plfd->data)) == HANDLER_GO_ON)
292         ++plfd;
293     return rc;
294 }
295 
296 static void plugins_call_fn_srv_data_all(server * const srv, const int e) {
297     const uint32_t offset = ((const uint16_t *)srv->plugin_slots)[e];
298     if (0 == offset) return;
299     const plugin_fn_data *plfd = (const plugin_fn_data *)
300       (((uintptr_t)srv->plugin_slots) + offset);
301     for (; plfd->fn; ++plfd)
302         plfd->fn(srv, plfd->data);
303 }
304 
305 /**
306  * plugins that use
307  *
308  * - request_st *r
309  * - void *p_d (plugin_data *)
310  */
311 
312 #define PLUGIN_CALL_FN_REQ_DATA(x, y) \
313     handler_t plugins_call_##y(request_st * const r) {\
314         return plugins_call_fn_req_data(r, x); \
315     }
316 
317 PLUGIN_CALL_FN_REQ_DATA(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean)
318 PLUGIN_CALL_FN_REQ_DATA(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot)
319 PLUGIN_CALL_FN_REQ_DATA(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical)
320 PLUGIN_CALL_FN_REQ_DATA(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start)
321 PLUGIN_CALL_FN_REQ_DATA(PLUGIN_FUNC_HANDLE_RESPONSE_START, handle_response_start)
322 PLUGIN_CALL_FN_REQ_DATA(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done)
323 PLUGIN_CALL_FN_REQ_DATA(PLUGIN_FUNC_HANDLE_REQUEST_RESET, handle_request_reset)
324 PLUGIN_CALL_FN_REQ_DATA(PLUGIN_FUNC_HANDLE_REQUEST_ENV, handle_request_env)
325 
326 /**
327  * plugins that use
328  *
329  * - connection *con
330  * - void *p_d (plugin_data *)
331  */
332 
333 #define PLUGIN_CALL_FN_CON_DATA(x, y) \
334     handler_t plugins_call_##y(connection *con) {\
335         return plugins_call_fn_con_data(con, x); \
336     }
337 
338 PLUGIN_CALL_FN_CON_DATA(PLUGIN_FUNC_HANDLE_CONNECTION_ACCEPT, handle_connection_accept)
339 PLUGIN_CALL_FN_CON_DATA(PLUGIN_FUNC_HANDLE_CONNECTION_SHUT_WR, handle_connection_shut_wr)
340 PLUGIN_CALL_FN_CON_DATA(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close)
341 
342 #undef PLUGIN_CALL_FN_SRV_CON_DATA
343 
344 /**
345  * plugins that use
346  *
347  * - server *srv
348  * - void *p_d (plugin_data *)
349  */
350 
351 handler_t plugins_call_set_defaults(server *srv) {
352     return plugins_call_fn_srv_data(srv, PLUGIN_FUNC_SET_DEFAULTS);
353 }
354 
355 handler_t plugins_call_worker_init(server *srv) {
356     return plugins_call_fn_srv_data(srv, PLUGIN_FUNC_WORKER_INIT);
357 }
358 
359 void plugins_call_handle_trigger(server *srv) {
360     plugins_call_fn_srv_data_all(srv, PLUGIN_FUNC_HANDLE_TRIGGER);
361 }
362 
363 void plugins_call_handle_sighup(server *srv) {
364     plugins_call_fn_srv_data_all(srv, PLUGIN_FUNC_HANDLE_SIGHUP);
365 }
366 
367 handler_t plugins_call_handle_waitpid(server *srv, pid_t pid, int status) {
368     const uint32_t offset =
369       ((const uint16_t *)srv->plugin_slots)[PLUGIN_FUNC_HANDLE_WAITPID];
370     if (0 == offset) return HANDLER_GO_ON;
371     const plugin_fn_data *plfd = (const plugin_fn_data *)
372       (((uintptr_t)srv->plugin_slots) + offset);
373     handler_t rc = HANDLER_GO_ON;
374     while (plfd->fn&&(rc=plfd->fn(srv,plfd->data,pid,status))==HANDLER_GO_ON)
375         ++plfd;
376     return rc;
377 }
378 
379 static void plugins_call_cleanup(server * const srv) {
380     plugin ** const ps = srv->plugins.ptr;
381     for (uint32_t i = 0; i < srv->plugins.used; ++i) {
382         plugin *p = ps[i];
383         if (NULL == p) continue;
384         if (NULL != p->data) {
385             plugin_data_base *pd = p->data;
386             if (p->cleanup)
387                 p->cleanup(p->data);
388             free(pd->cvlist);
389             free(pd);
390             p->data = NULL;
391         }
392     }
393 }
394 
395 __attribute_cold__
396 static void plugins_call_init_reverse(server *srv, const uint32_t offset) {
397     if (0 == offset) return;
398     plugin_fn_data *a = (plugin_fn_data *)
399       (((uintptr_t)srv->plugin_slots) + offset);
400     plugin_fn_data *b = a;
401     while (b->fn) ++b;
402     for (; a < --b; ++a) { /* swap to reverse list */
403         plugin_fn_data tmp = *a;
404         *a = *b;
405         *b = tmp;
406     }
407 }
408 
409 __attribute_cold__
410 static void plugins_call_init_slot(server *srv, handler_t(*fn)(), void *data, const uint32_t offset) {
411     if (fn) {
412         plugin_fn_data *plfd = (plugin_fn_data *)
413           (((uintptr_t)srv->plugin_slots) + offset);
414         while (plfd->fn) ++plfd;
415         plfd->fn = fn;
416         plfd->data = data;
417     }
418 }
419 
420 handler_t plugins_call_init(server *srv) {
421 	plugin ** const ps = srv->plugins.ptr;
422 	uint16_t offsets[PLUGIN_FUNC_SIZEOF];
423 	memset(offsets, 0, sizeof(offsets));
424 
425 	for (uint32_t i = 0; i < srv->plugins.used; ++i) {
426 		/* check which calls are supported */
427 
428 		plugin *p = ps[i];
429 
430 		if (p->init) {
431 			if (NULL == (p->data = p->init())) {
432 				log_error(srv->errh, __FILE__, __LINE__,
433 				  "plugin-init failed for module %s", p->name);
434 				return HANDLER_ERROR;
435 			}
436 
437 			((plugin_data_base *)(p->data))->self = p;
438 			((plugin_data_base *)(p->data))->id = i + 1;
439 
440 			if (p->version != LIGHTTPD_VERSION_ID) {
441 				log_error(srv->errh, __FILE__, __LINE__,
442 				  "plugin-version doesn't match lighttpd-version for %s", p->name);
443 				return HANDLER_ERROR;
444 			}
445 		}
446 
447 		if (p->priv_defaults && HANDLER_ERROR==p->priv_defaults(srv, p->data)) {
448 			return HANDLER_ERROR;
449 		}
450 
451 		if (p->handle_uri_clean)
452 			++offsets[PLUGIN_FUNC_HANDLE_URI_CLEAN];
453 		if (p->handle_uri_raw && !p->handle_uri_clean)
454 			++offsets[PLUGIN_FUNC_HANDLE_URI_CLEAN]; /*(same as above)*/
455 		if (p->handle_request_env)
456 			++offsets[PLUGIN_FUNC_HANDLE_REQUEST_ENV];
457 		if (p->handle_request_done)
458 			++offsets[PLUGIN_FUNC_HANDLE_REQUEST_DONE];
459 		if (p->handle_connection_accept)
460 			++offsets[PLUGIN_FUNC_HANDLE_CONNECTION_ACCEPT];
461 		if (p->handle_connection_shut_wr)
462 			++offsets[PLUGIN_FUNC_HANDLE_CONNECTION_SHUT_WR];
463 		if (p->handle_connection_close)
464 			++offsets[PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE];
465 		if (p->handle_trigger)
466 			++offsets[PLUGIN_FUNC_HANDLE_TRIGGER];
467 		if (p->handle_sighup)
468 			++offsets[PLUGIN_FUNC_HANDLE_SIGHUP];
469 		if (p->handle_waitpid)
470 			++offsets[PLUGIN_FUNC_HANDLE_WAITPID];
471 		if (p->handle_subrequest_start)
472 			++offsets[PLUGIN_FUNC_HANDLE_SUBREQUEST_START];
473 		if (p->handle_response_start)
474 			++offsets[PLUGIN_FUNC_HANDLE_RESPONSE_START];
475 		if (p->handle_docroot)
476 			++offsets[PLUGIN_FUNC_HANDLE_DOCROOT];
477 		if (p->handle_physical)
478 			++offsets[PLUGIN_FUNC_HANDLE_PHYSICAL];
479 		if (p->handle_request_reset)
480 			++offsets[PLUGIN_FUNC_HANDLE_REQUEST_RESET];
481 		if (p->set_defaults)
482 			++offsets[PLUGIN_FUNC_SET_DEFAULTS];
483 		if (p->worker_init)
484 			++offsets[PLUGIN_FUNC_WORKER_INIT];
485 	}
486 
487 	uint32_t nslots =
488 	  (sizeof(offsets)+sizeof(plugin_fn_data)-1) / sizeof(plugin_fn_data);
489 	for (uint32_t i = 0; i < PLUGIN_FUNC_SIZEOF; ++i) {
490 		if (offsets[i]) {
491 			uint32_t offset = nslots;
492 			nslots += offsets[i]+1; /* +1 to mark end of each list */
493 			force_assert(offset * sizeof(plugin_fn_data) <= USHRT_MAX);
494 			offsets[i] = (uint16_t)(offset * sizeof(plugin_fn_data));
495 		}
496 	}
497 
498 	/* allocate and fill slots of two dimensional array */
499 	srv->plugin_slots = ck_calloc(nslots, sizeof(plugin_fn_data));
500 	memcpy(srv->plugin_slots, offsets, sizeof(offsets));
501 
502 	/* add handle_uri_raw before handle_uri_clean, but in same slot */
503 	for (uint32_t i = 0; i < srv->plugins.used; ++i) {
504 		plugin * const p = ps[i];
505 		plugins_call_init_slot(srv, p->handle_uri_raw, p->data,
506 					offsets[PLUGIN_FUNC_HANDLE_URI_CLEAN]);
507 	}
508 
509 	for (uint32_t i = 0; i < srv->plugins.used; ++i) {
510 		plugin * const p = ps[i];
511 
512 		if (!p->handle_uri_raw)
513 			plugins_call_init_slot(srv, p->handle_uri_clean, p->data,
514 						offsets[PLUGIN_FUNC_HANDLE_URI_CLEAN]);
515 		plugins_call_init_slot(srv, p->handle_request_env, p->data,
516 					offsets[PLUGIN_FUNC_HANDLE_REQUEST_ENV]);
517 		plugins_call_init_slot(srv, p->handle_request_done, p->data,
518 					offsets[PLUGIN_FUNC_HANDLE_REQUEST_DONE]);
519 		plugins_call_init_slot(srv, p->handle_connection_accept, p->data,
520 					offsets[PLUGIN_FUNC_HANDLE_CONNECTION_ACCEPT]);
521 		plugins_call_init_slot(srv, p->handle_connection_shut_wr, p->data,
522 					offsets[PLUGIN_FUNC_HANDLE_CONNECTION_SHUT_WR]);
523 		plugins_call_init_slot(srv, p->handle_connection_close, p->data,
524 					offsets[PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE]);
525 		plugins_call_init_slot(srv, p->handle_trigger, p->data,
526 					offsets[PLUGIN_FUNC_HANDLE_TRIGGER]);
527 		plugins_call_init_slot(srv, p->handle_sighup, p->data,
528 					offsets[PLUGIN_FUNC_HANDLE_SIGHUP]);
529 		plugins_call_init_slot(srv, p->handle_waitpid, p->data,
530 					offsets[PLUGIN_FUNC_HANDLE_WAITPID]);
531 		plugins_call_init_slot(srv, p->handle_subrequest_start, p->data,
532 					offsets[PLUGIN_FUNC_HANDLE_SUBREQUEST_START]);
533 		plugins_call_init_slot(srv, p->handle_response_start, p->data,
534 					offsets[PLUGIN_FUNC_HANDLE_RESPONSE_START]);
535 		plugins_call_init_slot(srv, p->handle_docroot, p->data,
536 					offsets[PLUGIN_FUNC_HANDLE_DOCROOT]);
537 		plugins_call_init_slot(srv, p->handle_physical, p->data,
538 					offsets[PLUGIN_FUNC_HANDLE_PHYSICAL]);
539 		plugins_call_init_slot(srv, p->handle_request_reset, p->data,
540 					offsets[PLUGIN_FUNC_HANDLE_REQUEST_RESET]);
541 		plugins_call_init_slot(srv, p->set_defaults, p->data,
542 					offsets[PLUGIN_FUNC_SET_DEFAULTS]);
543 		plugins_call_init_slot(srv, p->worker_init, p->data,
544 					offsets[PLUGIN_FUNC_WORKER_INIT]);
545 	}
546 
547 	/* reverse cleanup lists to balance ctor/dtor-like plugin behaviors */
548 	plugins_call_init_reverse(srv,offsets[PLUGIN_FUNC_HANDLE_REQUEST_RESET]);
549 	plugins_call_init_reverse(srv,offsets[PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE]);
550 
551 	return HANDLER_GO_ON;
552 }
553 
554 void plugins_free(server *srv) {
555 	if (srv->plugin_slots) {
556 		plugins_call_cleanup(srv);
557 		free(srv->plugin_slots);
558 		srv->plugin_slots = NULL;
559 	}
560 
561 	for (uint32_t i = 0; i < srv->plugins.used; ++i) {
562 		plugin_free(((plugin **)srv->plugins.ptr)[i]);
563 	}
564 	free(srv->plugins.ptr);
565 	srv->plugins.ptr = NULL;
566 	srv->plugins.used = 0;
567 	array_free_data(&plugin_stats);
568 }
569