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