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