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