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