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