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