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