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