mhd_lib_init.c (13831B)
1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ 2 /* 3 This file is part of GNU libmicrohttpd. 4 Copyright (C) 2024 Evgeny Grin (Karlson2k) 5 6 GNU libmicrohttpd is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 GNU libmicrohttpd is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 Alternatively, you can redistribute GNU libmicrohttpd and/or 17 modify it under the terms of the GNU General Public License as 18 published by the Free Software Foundation; either version 2 of 19 the License, or (at your option) any later version, together 20 with the eCos exception, as follows: 21 22 As a special exception, if other files instantiate templates or 23 use macros or inline functions from this file, or you compile this 24 file and link it with other works to produce a work based on this 25 file, this file does not by itself cause the resulting work to be 26 covered by the GNU General Public License. However the source code 27 for this file must still be made available in accordance with 28 section (3) of the GNU General Public License v2. 29 30 This exception does not invalidate any other reasons why a work 31 based on this file might be covered by the GNU General Public 32 License. 33 34 You should have received copies of the GNU Lesser General Public 35 License and the GNU General Public License along with this library; 36 if not, see <https://www.gnu.org/licenses/>. 37 */ 38 39 /** 40 * @file src/mhd2/mhd_lib_init.c 41 * @brief Library global initialisers and de-initialisers 42 * @author Karlson2k (Evgeny Grin) 43 */ 44 #include "mhd_sys_options.h" 45 46 #include "mhd_panic.h" 47 48 #include "sys_base_types.h" 49 #include "sys_bool_type.h" 50 51 #include "mhd_locks.h" 52 53 #include "mhd_socket_type.h" 54 #ifdef MHD_SOCKETS_KIND_WINSOCK 55 # include <winsock2.h> 56 #endif 57 58 #include "mhd_assert.h" 59 60 #ifndef NDEBUG 61 # include <stdio.h> /* For debug error reporting */ 62 # include <stdlib.h> /* For debug error exit */ 63 #endif 64 65 #include "mhd_mono_clock.h" 66 #include "mhd_send.h" 67 #ifdef MHD_SUPPORT_HTTPS 68 # include "mhd_tls_funcs.h" 69 #endif 70 #ifdef MHD_SUPPORT_HTTP2 71 # include "h2/hpack/h2_huffman_codec.h" 72 #endif 73 74 #include "mhd_lib_init.h" 75 #include "mhd_lib_init_auto.h" 76 77 #ifdef AIF_W32_USR_DLLMAIN_NAME 78 # include <windows.h> /* For DisableThreadLibraryCalls() */ 79 #endif 80 81 #if defined(mhd_AUTOINIT_FUNCS_USE) 82 /** 83 * The function is automatically called to perform global lazy initialisation 84 */ 85 # define mhd_INIT_LAZY_BY_FUNC 1 86 #elif defined(mhd_MUTEX_INITIALISER_STAT) 87 /** 88 * Global lazy initialisation is performed by defined variables with static 89 * initialisation values 90 */ 91 # define mhd_INIT_LAZY_BY_STATIC 1 92 #endif 93 94 #if defined(mhd_INIT_LAZY_BY_FUNC) || defined(mhd_INIT_LAZY_BY_STATIC) 95 /** 96 * Global lazy initialisation is automatic (either by automatic functions or 97 * by static initialisation) 98 */ 99 # define mhd_INIT_LAZY_AUTOMATIC 1 100 #endif 101 102 /* 103 * *** Minimal (lazy) initialisation section *** 104 */ 105 106 /** 107 * The magic value to determine the library initialisation status 108 */ 109 #define mhd_LIB_INIT_MARKER_VALUE 0xB167A105 /* "Big Talos" */ 110 111 112 #ifndef mhd_INIT_LAZY_BY_STATIC 113 # ifdef mhd_INIT_LAZY_BY_FUNC 114 /* Markers of performed lazy initialisation */ 115 /* Do not initialise statically to avoid breaking by too early automatic 116 initialisation in function, which is then overwritten by library or 117 application initialisation. */ 118 /** 119 * The indicator of performed global lazy initialisation. 120 * Have #mhd_LIB_INIT_MARKER_VALUE value when initialised. 121 */ 122 static volatile uint_fast32_t mhd_lib_global_init_marker; 123 /** 124 * The indicator of performed global lazy initialisation. 125 * Have (~ #mhd_LIB_INIT_MARKER_VALUE) value when initialised. 126 */ 127 static volatile uint_fast32_t mhd_lib_global_init_Nmarker; 128 # else /* ! mhd_INIT_LAZY_BY_FUNC */ 129 /* Markers of performed lazy initialisation */ 130 /** 131 * The indicator of performed global lazy initialisation. 132 * Have #mhd_LIB_INIT_MARKER_VALUE value when initialised. 133 */ 134 static volatile uint_fast32_t mhd_lib_global_init_marker = 0; 135 /** 136 * The indicator of performed global lazy initialisation. 137 * Have (~ #mhd_LIB_INIT_MARKER_VALUE) value when initialised. 138 */ 139 static volatile uint_fast32_t mhd_lib_global_init_Nmarker = 0; 140 # endif 141 142 /* Variables used for full initialisation */ 143 /** 144 * The number of user of library global resources. 145 * In practice the value should correspond to number of running daemons plus 146 * number of any possible executed functions with one use of global resources. 147 */ 148 static volatile size_t mhd_lib_use_counter; 149 /** 150 * Indicates that library was already fully initialised at least one time. 151 * Some resources that do not require re-initialisation, skipped from repeated 152 * global initialisation (after deinitialisation). 153 */ 154 static volatile bool mhd_lib_fully_inited_once; 155 # ifdef MHD_SUPPORT_THREADS 156 157 /** 158 * The mutex to control access to full global initialisers and deinitialisers 159 */ 160 static mhd_mutex mhd_init_mutex; 161 # endif /* MHD_SUPPORT_THREADS */ 162 #else /* mhd_INIT_LAZY_BY_STATIC */ 163 /* Markers of performed lazy initialisation */ 164 /** 165 * The indicator of performed global lazy initialisation. 166 * Have #mhd_LIB_INIT_MARKER_VALUE value when initialised. 167 */ 168 static volatile uint_fast32_t mhd_lib_global_init_marker = 169 (uint_fast32_t) mhd_LIB_INIT_MARKER_VALUE; 170 /** 171 * The indicator of performed global lazy initialisation. 172 * Have (~ #mhd_LIB_INIT_MARKER_VALUE) value when initialised. 173 */ 174 static volatile uint_fast32_t mhd_lib_global_init_Nmarker = 175 (uint_fast32_t) ~((uint_fast32_t) mhd_LIB_INIT_MARKER_VALUE); 176 /* Variables used for full initialisation */ 177 /** 178 * The number of user of library global resources. 179 * In practice the value should correspond to number of running daemons plus 180 * number of any possible executed functions with one use of global resources. 181 */ 182 static volatile size_t mhd_lib_use_counter = 0; 183 /** 184 * Indicates that library was already fully initialised at least one time. 185 * Some resources that do not require re-initialisation, skipped from repeated 186 * global initialisation (after deinitialisation). 187 */ 188 static volatile bool mhd_lib_fully_inited_once = false; 189 /** 190 * The mutex to control access to full global initialisers and deinitialisers 191 */ 192 mhd_MUTEX_STATIC_DEFN_INIT (mhd_init_mutex); 193 #endif /* mhd_INIT_LAZY_BY_STATIC */ 194 195 196 /** 197 * Check whether the markers of initialisation set to "initialised" values. 198 */ 199 #define mhd_LIB_INIT_LAZY_IS_PERFORMED() \ 200 ((mhd_lib_global_init_marker == \ 201 ((uint_fast32_t) mhd_LIB_INIT_MARKER_VALUE)) \ 202 && (mhd_lib_global_init_marker == ~mhd_lib_global_init_Nmarker)) 203 204 /** 205 * Perform global lazy initialisation. 206 * If library is initialised statically, this function must never be called 207 * unless automatic initialisation has failed. 208 * This function does not perform any checking whether the library has been 209 * initialised before. 210 * @return 'true' if succeed, 211 * 'false' if failed 212 */ 213 static bool 214 mhd_lib_global_lazy_init (void) 215 { 216 mhd_panic_init_default (); /* Just set a few variables to NULL */ 217 if (! mhd_mutex_init (&mhd_init_mutex)) 218 return false; 219 mhd_lib_fully_inited_once = false; 220 mhd_lib_use_counter = 0; 221 mhd_lib_global_init_marker = (uint_fast32_t) mhd_LIB_INIT_MARKER_VALUE; 222 mhd_lib_global_init_Nmarker = (uint_fast32_t) ~mhd_lib_global_init_marker; 223 return true; 224 } 225 226 227 #ifdef mhd_AUTOINIT_FUNCS_USE 228 229 /** 230 * Perform de-initialisation of the resources previously initialised by 231 * #mhd_lib_global_lazy_init(). 232 * This function does not perform any checking whether the library has been 233 * initialised or de-initialised before. 234 */ 235 static void 236 mhd_lib_global_lazy_deinit (void) 237 { 238 mhd_lib_global_init_Nmarker = 0u; 239 mhd_lib_global_init_marker = 0u; 240 (void) mhd_mutex_destroy (&mhd_init_mutex); 241 } 242 243 244 #endif /* mhd_AUTOINIT_FUNCS_USE */ 245 246 247 /* 248 * *** The automatically called functions section *** 249 */ 250 251 #ifdef mhd_AUTOINIT_FUNCS_USE 252 253 void 254 mhd_lib_global_init_auto (void) 255 { 256 if (! mhd_lib_global_lazy_init ()) 257 { 258 (void) 0; 259 /* Do not abort in non-debug builds, weak workarounds will be used */ 260 #ifndef NDEBUG 261 MHD_PANIC ("Failed to initialise the MHD library"); 262 #endif /* ! NDEBUG */ 263 } 264 } 265 266 267 void 268 mhd_lib_global_deinit_auto (void) 269 { 270 #ifndef NDEBUG 271 if (! mhd_LIB_INIT_LAZY_IS_PERFORMED ()) 272 { 273 fprintf (stderr, "Automatic MHD library initialisation has not been " 274 "performed, but the library de-initialisation is called.\n"); 275 fflush (stderr); 276 abort (); 277 } 278 if (0 != mhd_lib_use_counter) 279 { 280 fprintf (stderr, "Automatic MHD library de-initialisation started, but " 281 "some MHD resources are still in use by the application.\n"); 282 fflush (stderr); 283 } 284 #endif /* ! NDEBUG */ 285 mhd_lib_global_lazy_deinit (); 286 } 287 288 289 # ifdef AIF_W32_USR_DLLMAIN_NAME 290 291 AIF_DECL_USR_DLLMAIN /* Declare the function */ 292 293 /* MHD is used on W32 as DLL library with DLL runtime lib */ 294 /** 295 * Special automatically called function for DLL initialisation on W32. 296 * @param hinst the DLL module handle 297 * @param reason the code of the call reason 298 * @param pReserved NULL is statically loaded, non-NULL if loaded dynamically 299 * @return TRUE if succeed (always), 300 * FALSE if failed 301 */ 302 BOOL WINAPI 303 AUTOINIT_FUNCS_USR_DLLMAIN_NAME (HINSTANCE hinst, 304 DWORD reason, 305 LPVOID pReserved) 306 { 307 (void) pReserved; /* Not used */ 308 309 /* Disable calls with DLL_THREAD_ATTACH and DLL_THREAD_DETACH messages */ 310 if (AIF_W32_DLL_PROCESS_ATTACH == reason) 311 (void) DisableThreadLibraryCalls ((HMODULE) hinst); 312 313 return TRUE; 314 } 315 316 317 # endif /* AIF_W32_USR_DLLMAIN_NAME */ 318 319 #endif /* mhd_AUTOINIT_FUNCS_USE */ 320 321 322 /* 323 * *** Full global initialisation, deinitialisation and re-initialisaion *** 324 */ 325 326 #if defined(MHD_SOCKETS_KIND_WINSOCK) 327 /** 328 * Initialise W32 sockets 329 * @return 'true' if succeed, 330 * 'false' if failed 331 */ 332 mhd_static_inline bool 333 mhd_lib_sockets_init_w32 (void) 334 { 335 WSADATA wsd; 336 if (0 != WSAStartup (MAKEWORD (2, 2), &wsd)) 337 return false; 338 if (MAKEWORD (2, 2) != wsd.wVersion) 339 { 340 WSACleanup (); 341 return false; 342 } 343 return true; 344 } 345 346 347 /** 348 * De-initialise W32 sockets 349 */ 350 mhd_static_inline void 351 mhd_lib_sockets_deinit_w32 (void) 352 { 353 (void) WSACleanup (); 354 } 355 356 357 #else /* ! MHD_SOCKETS_KIND_WINSOCK */ 358 /* No-op implementations */ 359 # define mhd_lib_sockets_init_w32() (true) 360 # define mhd_lib_sockets_deinit_w32() ((void) 0) 361 #endif /* ! MHD_SOCKETS_KIND_WINSOCK */ 362 363 /** 364 * Perform full initialisation of MHD library global resources. 365 * Must be called only with initialisation lock held. 366 * @return 'true' if succeed, 367 * 'false' if failed 368 */ 369 static bool 370 mhd_lib_global_full_init_once (void) 371 { 372 mhd_assert (mhd_LIB_INIT_LAZY_IS_PERFORMED ()); 373 mhd_assert (! mhd_lib_fully_inited_once); 374 mhd_assert (0 == mhd_lib_use_counter); 375 376 if (! mhd_lib_sockets_init_w32 ()) 377 return false; 378 mhd_mclock_init_once (); 379 mhd_send_init_once (); 380 #ifdef MHD_SUPPORT_HTTPS 381 mhd_tls_global_init_once (); 382 #endif /* MHD_SUPPORT_HTTPS */ 383 #ifdef MHD_SUPPORT_HTTP2 384 mhd_h2_huffman_init (); 385 #endif /* MHD_SUPPORT_HTTP2 */ 386 387 mhd_lib_fully_inited_once = true; 388 389 return true; 390 } 391 392 393 /** 394 * Release library global resources allocated 395 * by #mhd_lib_global_full_init_once() 396 */ 397 static void 398 mhd_lib_global_full_deinit (void) 399 { 400 #ifdef MHD_SUPPORT_HTTPS 401 mhd_tls_global_deinit (); 402 #endif /* MHD_SUPPORT_HTTPS */ 403 mhd_mclock_deinit (); 404 mhd_lib_sockets_deinit_w32 (); 405 } 406 407 408 /** 409 * Re-initialise library global resources after 410 * de-initialistion by #mhd_lib_global_full_deinit(). 411 * This function can be called many times. 412 * @return 'true' if succeed, 413 * 'false' if failed 414 */ 415 static bool 416 mhd_lib_global_full_re_init (void) 417 { 418 mhd_assert (mhd_lib_fully_inited_once); 419 if (! mhd_lib_sockets_init_w32 ()) 420 return false; 421 mhd_mclock_re_init (); 422 #ifdef MHD_SUPPORT_HTTPS 423 mhd_tls_global_re_init (); 424 #endif /* MHD_SUPPORT_HTTPS */ 425 426 return true; 427 } 428 429 430 /* 431 * *** Automatic global initialisation and deinitialisation for daemons *** 432 */ 433 434 MHD_INTERNAL bool 435 mhd_lib_init_global_if_needed (void) 436 { 437 bool ret; 438 if (! mhd_LIB_INIT_LAZY_IS_PERFORMED ()) 439 { 440 #if defined (mhd_INIT_LAZY_AUTOMATIC) && ! defined(NDEBUG) 441 /* Problem detected: the library must be already initialised 442 automatically, but it is not. */ 443 abort (); /* abort if this is a debug build */ 444 #else /* !mhd_INIT_LAZY_AUTOMATIC || NDEBUG */ 445 if (! mhd_lib_global_lazy_init ()) /* Not thread safe, but no choice here */ 446 return false; 447 #endif /* !mhd_INIT_LAZY_AUTOMATIC || NDEBUG */ 448 } 449 450 if (! mhd_mutex_lock (&mhd_init_mutex)) 451 return false; 452 if (0 == mhd_lib_use_counter) 453 { 454 if (! mhd_lib_fully_inited_once) 455 ret = mhd_lib_global_full_init_once (); 456 else 457 ret = mhd_lib_global_full_re_init (); 458 } 459 else 460 { 461 mhd_assert (mhd_lib_fully_inited_once); 462 ret = true; 463 } 464 if (ret) 465 ++mhd_lib_use_counter; 466 mhd_mutex_unlock_chk (&mhd_init_mutex); 467 468 return ret; 469 } 470 471 472 MHD_INTERNAL void 473 mhd_lib_deinit_global_if_needed (void) 474 { 475 mhd_assert (0 != mhd_lib_use_counter); 476 477 mhd_mutex_lock_chk (&mhd_init_mutex); 478 if (0 == --mhd_lib_use_counter) 479 mhd_lib_global_full_deinit (); 480 mhd_mutex_unlock_chk (&mhd_init_mutex); 481 } 482 483 484 MHD_INTERNAL bool 485 mhd_lib_is_fully_initialised_once (void) 486 { 487 return mhd_LIB_INIT_LAZY_IS_PERFORMED () && mhd_lib_fully_inited_once; 488 } 489 490 491 MHD_INTERNAL bool 492 mhd_lib_is_fully_initialised_now (void) 493 { 494 return mhd_lib_is_fully_initialised_once () && (0 != mhd_lib_use_counter); 495 }