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