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