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