1 /* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. 2 3 This program is free software; you can redistribute it and/or modify 4 it under the terms of the GNU General Public License, version 2.0, 5 as published by the Free Software Foundation. 6 7 This program is also distributed with certain software (including 8 but not limited to OpenSSL) that is licensed under separate terms, 9 as designated in a particular file or component or in included license 10 documentation. The authors of MySQL hereby grant you an additional 11 permission to link the program and your derivative works with the 12 separately licensed software that they have included with MySQL. 13 14 This program is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License, version 2.0, for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with this program; if not, write to the Free Software 21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ 22 23 #ifndef MYSQL_SERVER_DYNAMIC_LOADER_H 24 #define MYSQL_SERVER_DYNAMIC_LOADER_H 25 26 #include <mysql/components/my_service.h> 27 #include <mysql/components/service_implementation.h> 28 29 #include <mysql/components/services/mysql_runtime_error_service.h> 30 31 #include <mysql/components/services/dynamic_loader.h> 32 #include <forward_list> 33 #include <map> 34 #include <memory> 35 #include <set> 36 #include <utility> 37 #include <vector> 38 39 #include "c_string_less.h" 40 #include "mysql_component_imp.h" 41 #include "rwlock_scoped_lock.h" 42 43 /** 44 Making component object and the generation ID as a pair. Here generation ID 45 represents the group ID maintained at the time of components insertion. 46 The component deinitialization is going to be done as a groups based on the 47 genration ID. This pair is assigned as a value to the my_component_registry 48 map. 49 */ 50 typedef std::map<const char *, std::unique_ptr<mysql_component>, c_string_less> 51 my_component_registry; 52 53 using components_vector = std::vector<mysql_component *>; 54 using generation_urns_list = std::forward_list<components_vector>; 55 56 /** 57 A class with an implementation of the Dynamic Loader Service. 58 */ 59 class mysql_dynamic_loader_imp { 60 typedef std::unordered_map<my_string, 61 my_service<SERVICE_TYPE(dynamic_loader_scheme)>> 62 scheme_service_map; 63 64 /* contain the actual fields definitions */ 65 static my_component_registry components_list; 66 static mysql_rwlock_t LOCK_dynamic_loader; 67 static generation_urns_list urns_with_gen_list; 68 69 public: 70 /** 71 Initializes loader for usage. Initializes RW lock, all other structures 72 should be empty. Shouldn't be called multiple times. 73 */ 74 static void init(); 75 /** 76 De-initializes loader. De-initializes RW lock, all other structures 77 doesn't require any action. 78 */ 79 static void deinit(); 80 81 /** 82 De-initializes RW lock 83 */ 84 static void rw_lock_deinit(); 85 86 public: /* Service Implementations */ 87 /** 88 Loads specified group of Components by URN, initializes them and 89 registers all Service Implementations present in these Components. 90 Assures all dependencies will be met after loading specified Components. 91 The dependencies may be circular, in such case it's necessary to specify 92 all Components on cycle to load in one batch. From URNs specified the 93 scheme part of URN (part before "://") is extracted and used to acquire 94 Service Implementation of scheme Component loader Service for specified 95 scheme. 96 97 @param urns List of URNs of Components to load. 98 @param component_count Number of Components on list to load. 99 @return Status of performed operation 100 @retval false success 101 @retval true failure 102 */ 103 104 static DEFINE_BOOL_METHOD(load, (const char *urns[], int component_count)); 105 /** 106 Unloads specified group of Components by URN, deinitializes them and 107 unregisters all Service Implementations present in these Components. 108 Assumes, thous does not check it, all dependencies of not unloaded 109 Components will still be met after unloading specified Components. 110 The dependencies may be circular, in such case it's necessary to specify 111 all Components on cycle to unload in one batch. From URNs specified the 112 scheme part of URN (part before "://") is extracted and used to acquire 113 Service Implementation of scheme Component loader Service for specified 114 scheme. URN specified should be identical to ones specified in load() 115 method, i.e. all letters must have the same case. 116 117 @param urns List of URNs of Components to unload. 118 @param component_count Number of Components on list to unload. 119 @return Status of performed operation 120 @retval false success 121 @retval true failure 122 */ 123 124 static DEFINE_BOOL_METHOD(unload, (const char *urns[], int component_count)); 125 126 typedef std::pair<my_component_registry::const_iterator, 127 minimal_chassis::rwlock_scoped_lock> 128 component_iterator; 129 130 /** 131 Creates iterator that iterates through all loaded Components. 132 If successful it leaves read lock on dynamic loader until iterator is 133 released. 134 135 @param [out] out_iterator Pointer to Component iterator handle. 136 @return Status of performed operation 137 @retval false success 138 @retval true failure 139 */ 140 static DEFINE_BOOL_METHOD(iterator_create, 141 (my_h_component_iterator * out_iterator)); 142 143 /** 144 Releases Component iterator. Releases read lock on dynamic loader. 145 146 @param iterator Component iterator handle. 147 @return Status of performed operation 148 @retval false success 149 @retval true failure 150 */ 151 static DEFINE_METHOD(void, iterator_release, 152 (my_h_component_iterator iterator)); 153 154 /** 155 Gets name and URN of Service pointed to by iterator. 156 157 @param iterator Component iterator handle. 158 @param [out] out_name Pointer to string with Component name to set result 159 pointer to. 160 @param [out] out_urn Pointer to string with URN from which the Component was 161 loaded from, to set result pointer to. 162 @return Status of performed operation 163 @retval false success 164 @retval true Failure, may be caused when called on iterator that went 165 through all values already. 166 */ 167 static DEFINE_BOOL_METHOD(iterator_get, 168 (my_h_component_iterator iterator, 169 const char **out_name, const char **out_urn)); 170 171 /** 172 Advances specified iterator to next element. Will succeed but return true if 173 it reaches one-past-last element. 174 175 @param iterator Component iterator handle. 176 @return Status of performed operation and validity of iterator after 177 operation. 178 @retval false success 179 @retval true Failure or called on iterator that was on last element. 180 */ 181 static DEFINE_BOOL_METHOD(iterator_next, (my_h_component_iterator iterator)); 182 183 /** 184 Checks if specified iterator is valid, i.e. have not reached one-past-last 185 element. 186 187 @param iterator Component iterator handle. 188 @return Validity of iterator 189 @retval false Valid 190 @retval true Invalid or reached one-past-last element. 191 */ 192 static DEFINE_BOOL_METHOD(iterator_is_valid, 193 (my_h_component_iterator iterator)); 194 195 /* This includes metadata-related method implementations that are shared 196 by registry and dynamic_loader, so we don't duplicate the code. Following 197 defines set up all required symbols. Unfortunately they are not only the 198 types, but also static members with different name, so usage of templates 199 is not enough to reuse that part of code. */ 200 201 #define OBJECT_ITERATOR my_h_component_iterator 202 #define METADATA_ITERATOR my_h_component_metadata_iterator 203 204 #include "registry_metadata.h.inc" 205 206 private: 207 /** 208 Loads specified group of Components by URN. From URNs specified the 209 scheme part of URN (part before "://") is extracted and used to acquire 210 Service Implementation of scheme Component loader Service for specified 211 scheme. In case of failure rollbacks all changes, i.e. unloads loaded 212 Components. 213 214 @param urns List of URNs of Components to load. 215 @param component_count Number of Components on list to load. 216 @return Status of performed operation 217 @retval false success 218 @retval true failure 219 */ 220 static bool load_do_load_component_by_scheme(const char *urns[], 221 int component_count); 222 223 /** 224 Prepares a list of all Services that are provided by specified Components. 225 This will enable us in next step to check if these may be used to satisfy 226 other Components dependencies. 227 228 @param loaded_components List of Components to continue load of. 229 @return Status of performed operation 230 @retval false success 231 @retval true failure 232 */ 233 static bool load_do_collect_services_provided( 234 std::vector<std::unique_ptr<mysql_component>> &loaded_components); 235 236 /** 237 Checks if all dependencies can be satisfied with existing or to be added 238 Services. 239 240 @param loaded_components List of Components to continue load of. 241 @param services_provided List of Services that are being provided by 242 Components to be loaded. 243 @return Status of performed operation 244 @retval false success 245 @retval true failure 246 */ 247 static bool load_do_check_dependencies( 248 std::vector<std::unique_ptr<mysql_component>> &loaded_components, 249 const std::set<my_string> &services_provided); 250 251 /** 252 Registers all Services that are provided by specified Components. 253 In case of failure rollbacks all changes, i.e. unregister registered Service 254 Implementations. 255 256 @param loaded_components List of Components to continue load of. 257 @return Status of performed operation 258 @retval false success 259 @retval true failure 260 */ 261 static bool load_do_register_services( 262 std::vector<std::unique_ptr<mysql_component>> &loaded_components); 263 264 /** 265 Acquires Service Implementations for all dependencies of Components. 266 In case of failure rollbacks all changes, i.e. release Services that were 267 acquired. 268 269 @param loaded_components List of Components to continue load of. 270 @return Status of performed operation 271 @retval false success 272 @retval true failure 273 */ 274 static bool load_do_resolve_dependencies( 275 std::vector<std::unique_ptr<mysql_component>> &loaded_components); 276 277 /** 278 Calls Components initialization method to make Components ready to function. 279 In case of failure rollbacks all changes, i.e. calls deinitialization 280 methods on initialized Components. 281 282 @param loaded_components List of Components to continue load of. 283 @return Status of performed operation 284 @retval false success 285 @retval true failure 286 */ 287 static bool load_do_initialize_components( 288 std::vector<std::unique_ptr<mysql_component>> &loaded_components); 289 290 /** 291 Adds all Components to main list of loaded Components. Marks changes done by 292 all previous steps as not to be rolled back. 293 294 @param loaded_components List of Components to continue load of. 295 @return Status of performed operation 296 @retval false success 297 @retval true failure 298 */ 299 static bool load_do_commit( 300 std::vector<std::unique_ptr<mysql_component>> &loaded_components); 301 302 /** 303 Unloads all Components specified in list. It does not acquire a write lock 304 on dynamic loader, but requires it to be acquired by caller. 305 306 @param urns List of URNs of Components to unload. 307 @param component_count Number of Components on list to unload. 308 @return Status of performed operation 309 @retval false success 310 @retval true failure 311 */ 312 static bool unload_do_list_components(const char *urns[], 313 int component_count); 314 315 /** 316 Orders components in a order that would allow allow deinitialization to be 317 done always for components that have all their dependencies still not 318 deinitialized. It also creates a graph of dependencies between the Service 319 Implementations provided by the Components to be unloaded and Components 320 that use this Service Implementation. 321 322 @param components_to_unload List of Components to continue unload of. 323 @return Status of performed operation 324 @retval false success 325 @retval true failure 326 */ 327 static bool unload_do_topological_order( 328 const std::vector<mysql_component *> &components_to_unload); 329 330 /** 331 Prefetch all scheme loading Services before we get a lock on a Registry. 332 333 @param components_to_unload List of Components to continue unload of. 334 @param dependency_graph A graph of dependencies between the Components 335 to be unloaded. 336 @return Status of performed operation 337 @retval false success 338 @retval true failure 339 */ 340 static bool unload_do_get_scheme_services( 341 const std::vector<mysql_component *> &components_to_unload, 342 const std::map<const void *, std::vector<mysql_component *>> 343 &dependency_graph); 344 345 /** 346 Takes a lock on all services that are provided by the Components to be 347 unloaded, to prevent reference count from being changed. 348 349 @param components_to_unload List of Components to continue unload of. 350 @param dependency_graph A graph of dependencies between the Components 351 to be unloaded. 352 @param scheme_services Map of scheme loading Services prefetched with 353 Service Implementations required to unload all Components to unload. 354 @return Status of performed operation 355 @retval false success 356 @retval true failure 357 */ 358 static bool unload_do_lock_provided_services( 359 const std::vector<mysql_component *> &components_to_unload, 360 const std::map<const void *, std::vector<mysql_component *>> 361 &dependency_graph, 362 scheme_service_map &scheme_services); 363 364 /** 365 Checks if all Service Implementations provided by the Components to be 366 unloaded have no references outside the group of Components to be unloaded. 367 This assures that continuing deinitialization of these Components in 368 topological order, and by this also unregistration of all provided Service 369 Implementations will succeed. 370 371 @param components_to_unload List of Components to continue unload of. 372 @param dependency_graph A graph of dependencies between the Components 373 to be unloaded. 374 @param scheme_services Map of scheme loading Services prefetched with 375 Service Implementations required to unload all Components to unload. 376 @return Status of performed operation 377 @retval false success 378 @retval true failure 379 */ 380 static bool unload_do_check_provided_services_reference_count( 381 const std::vector<mysql_component *> &components_to_unload, 382 const std::map<const void *, std::vector<mysql_component *>> 383 &dependency_graph, 384 scheme_service_map &scheme_services); 385 386 /** 387 Deinitialize Components using their deinitialization method. 388 In case of failure rollbacks all changes, i.e. calls initialization 389 method again on deinitialized Components. 390 391 @param components_to_unload List of Components to continue unload of. 392 @param scheme_services Map of scheme loading Services prefetched with 393 Service Implementations required to unload all Components to unload. 394 @return Status of performed operation 395 @retval false success 396 @retval true failure 397 */ 398 static bool unload_do_deinitialize_components( 399 const std::vector<mysql_component *> &components_to_unload, 400 scheme_service_map &scheme_services); 401 402 /** 403 Releases Service Implementations acquired to satisfy dependencies. 404 In case of failure rollbacks all changes, i.e. acquires Services for 405 released dependencies again. 406 407 @param components_to_unload List of Components to continue unload of. 408 @param scheme_services Map of scheme loading Services prefetched with 409 Service Implementations required to unload all Components to unload. 410 @return Status of performed operation 411 @retval false success 412 @retval true failure 413 */ 414 static bool unload_do_unload_dependencies( 415 const std::vector<mysql_component *> &components_to_unload, 416 scheme_service_map &scheme_services); 417 418 /** 419 Unregisters all Service Implementations of specified Components. 420 In case of failure rollbacks all changes, i.e. registers unregistered 421 Service Implementations again. 422 423 @param components_to_unload List of Components to continue unload of. 424 @param scheme_services Map of scheme loading Services prefetched with 425 Service Implementations required to unload all Components to unload. 426 @return Status of performed operation 427 @retval false success 428 @retval true failure 429 */ 430 static bool unload_do_unregister_services( 431 const std::vector<mysql_component *> &components_to_unload, 432 scheme_service_map &scheme_services); 433 434 /** 435 Uses Component URN to extract the scheme part of URN (part before "://") and 436 use it to acquire Service Implementation of scheme Component loader Service 437 for specified scheme, used then to unload specified Components. The unloaded 438 Components are removed from the main list of all loaded Components. 439 In case of failure rollbacks all changes, i.e. loads unloaded Components 440 by their URN and add them to the main list of loaded Components again. 441 442 @param components_to_unload List of Components to continue unload of. 443 @param scheme_services Map of scheme loading Services prefetched with 444 Service Implementations required to unload all Components to unload. 445 @return Status of performed operation 446 @retval false success 447 @retval true failure 448 */ 449 static bool unload_do_unload_components( 450 const std::vector<mysql_component *> &components_to_unload, 451 scheme_service_map &scheme_services); 452 453 /** 454 Finishes unloading process by marking changes to not be rolled back. 455 456 @return Status of performed operation 457 @retval false success 458 @retval true failure 459 */ 460 static bool unload_do_commit(); 461 462 /** 463 Returns scheme loading Service based on given URN. It uses supplied cache to 464 reuse Services. If Service is not present in cache, it will be acquired from 465 registry. 466 467 @param urn URN of Components to get scheme loader Service for. 468 @param [out] out_scheme_service Pointer to store result scheme loader 469 Service. 470 @param [in,out] scheme_services Map of scheme loader services already 471 acquired. 472 @return Status of performed operation 473 @retval false success 474 @retval true failure 475 */ 476 static bool get_scheme_service_from_urn(const my_string &urn, 477 SERVICE_TYPE(dynamic_loader_scheme) * 478 *out_scheme_service, 479 scheme_service_map &scheme_services); 480 }; 481 482 extern REQUIRES_SERVICE_PLACEHOLDER(mysql_runtime_error); 483 extern my_h_service h_err_service; 484 485 #endif /* MYSQL_SERVER_DYNAMIC_LOADER_H */ 486