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