1 /* Copyright (c) 2017, 2019, 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 #include <mysql/components/services/group_member_status_listener.h>
24 #include <mysql/components/services/group_membership_listener.h>
25 #include <mysql/components/services/log_builtins.h>
26 
27 #include "plugin/group_replication/include/plugin.h"
28 #include "plugin/group_replication/include/services/notification/notification.h"
29 
30 enum SvcTypes { kGroupMembership = 0, kGroupMemberStatus };
31 
32 typedef int (*svc_notify_func)(Notification_context &, my_h_service);
33 
notify_group_membership(Notification_context & ctx,my_h_service svc)34 static int notify_group_membership(Notification_context &ctx,
35                                    my_h_service svc) {
36   int svc_ko = 0;
37   const char *view_id = ctx.get_view_id().c_str();
38   SERVICE_TYPE(group_membership_listener) *listener = nullptr;
39 
40   /* now that we have the handler for it, notify */
41   listener = reinterpret_cast<SERVICE_TYPE(group_membership_listener) *>(svc);
42 
43   if (ctx.get_view_changed()) {
44     svc_ko = svc_ko + listener->notify_view_change(view_id);
45   }
46 
47   if (ctx.get_quorum_lost()) {
48     svc_ko = svc_ko + listener->notify_quorum_loss(view_id);
49   }
50 
51   return svc_ko;
52 }
53 
notify_group_member_status(Notification_context & ctx,my_h_service svc)54 static int notify_group_member_status(Notification_context &ctx,
55                                       my_h_service svc) {
56   int svc_ko = 0;
57   const char *view_id = ctx.get_view_id().c_str();
58   SERVICE_TYPE(group_member_status_listener) *listener = nullptr;
59 
60   /* now that we have the handler for it, notify */
61   listener =
62       reinterpret_cast<SERVICE_TYPE(group_member_status_listener) *>(svc);
63 
64   if (ctx.get_member_state_changed()) {
65     svc_ko = svc_ko + listener->notify_member_state_change(view_id);
66   }
67 
68   if (ctx.get_member_role_changed()) {
69     svc_ko = svc_ko + listener->notify_member_role_change(view_id);
70   }
71 
72   return svc_ko;
73 }
74 
75 /**
76   Auxiliary function to engage the service registry to
77   notify a set of listeners.
78 
79   @param svc_type The service name.
80   @param ctx The events context
81 
82   @return false on success, true otherwise.
83  */
notify(SvcTypes svc_type,Notification_context & ctx)84 static bool notify(SvcTypes svc_type, Notification_context &ctx) {
85   SERVICE_TYPE(registry) *r = nullptr;
86   SERVICE_TYPE(registry_query) *rq = nullptr;
87   my_h_service_iterator h_ret_it = nullptr;
88   my_h_service h_listener_svc = nullptr;
89   my_h_service h_listener_default_svc = nullptr;
90   bool res = false;
91   bool default_notified = false;
92   std::string svc_name;
93   svc_notify_func notify_func_ptr;
94 
95   if (!registry_module || !(r = registry_module->get_registry_handle()) ||
96       !(rq = registry_module->get_registry_query_handle()))
97     goto err; /* purecov: inspected */
98 
99   /*
100     Decides which listener service to notify, based on the
101     service type. It also checks whether the service should
102     be notified indeed, based on the event context.
103 
104     If the event is not to be notified, the function returns
105     immediately.
106    */
107   switch (svc_type) {
108     case kGroupMembership:
109       notify_func_ptr = notify_group_membership;
110       svc_name = Registry_module_interface::SVC_NAME_MEMBERSHIP;
111       break;
112     case kGroupMemberStatus:
113       notify_func_ptr = notify_group_member_status;
114       svc_name = Registry_module_interface::SVC_NAME_STATUS;
115       break;
116     default:
117       DBUG_ASSERT(false); /* purecov: inspected */
118       /* production builds default to membership */
119       svc_name = Registry_module_interface::SVC_NAME_MEMBERSHIP;
120       notify_func_ptr = notify_group_membership;
121       break;
122   }
123 
124   /* acquire the default service */
125   if (r->acquire(svc_name.c_str(), &h_listener_default_svc) ||
126       !h_listener_default_svc)
127     /* no listener registered, skip */
128     goto end;
129 
130   /*
131     create iterator to navigate notification GMS change
132     notification listeners
133   */
134   if (rq->create(svc_name.c_str(), &h_ret_it)) {
135     goto err; /* purecov: inspected */
136   }
137 
138   /* notify all listeners */
139   while (h_ret_it != nullptr &&
140          /* is_valid returns false on success */
141          rq->is_valid(h_ret_it) == false) {
142     int svc_ko = 0;
143     const char *next_svc_name = nullptr;
144 
145     /* get next registered listener */
146     if (rq->get(h_ret_it, &next_svc_name)) goto err; /* purecov: inspected */
147 
148     /*
149       The iterator currently contains more service implementations than
150       those named after the given service name. The spec says that the
151       name given is used to position the iterator start on the first
152       registered service implementation prefixed with that name. We need
153       to iterate until the next element in the iterator (service implementation)
154       has a different service name.
155     */
156     std::string s(next_svc_name);
157     if (s.find(svc_name, 0) == std::string::npos) break;
158 
159     /* acquire next listener */
160     if (r->acquire(next_svc_name, &h_listener_svc))
161       goto err; /* purecov: inspected */
162 
163     /* don't notify the default service twice */
164     if (h_listener_svc != h_listener_default_svc || !default_notified) {
165       if (notify_func_ptr(ctx, h_listener_svc))
166         LogPluginErr(WARNING_LEVEL,
167                      ER_GRP_RPL_FAILED_TO_NOTIFY_GRP_MEMBERSHIP_EVENT,
168                      next_svc_name); /* purecov: inspected */
169 
170       default_notified =
171           default_notified || (h_listener_svc == h_listener_default_svc);
172     }
173 
174     /* release the listener service */
175     if (r->release(h_listener_svc) || svc_ko) goto err; /* purecov: inspected */
176 
177     /* update iterator */
178     if (rq->next(h_ret_it)) goto err; /* purecov: inspected */
179   }
180 
181 end:
182   /* release the iterator */
183   if (h_ret_it) rq->release(h_ret_it);
184 
185   /* release the default service */
186   if (h_listener_default_svc)
187     if (r->release(h_listener_default_svc)) res = true; /* purecov: inspected */
188 
189   return res;
190 
191 err:
192   res = true; /* purecov: inspected */
193   goto end;
194 }
195 
196 /* Public Functions */
197 
notify_and_reset_ctx(Notification_context & ctx)198 bool notify_and_reset_ctx(Notification_context &ctx) {
199   bool res = false;
200 
201   if (ctx.get_view_changed() || ctx.get_quorum_lost()) {
202     /* notify membership events listeners. */
203     if (notify(kGroupMembership, ctx)) {
204       /* purecov: begin inspected */
205       LogPluginErr(ERROR_LEVEL,
206                    ER_GRP_RPL_FAILED_TO_BROADCAST_GRP_MEMBERSHIP_NOTIFICATION);
207       /* purecov: end */
208       res = true; /* purecov: inspected */
209     }
210   }
211 
212   if (ctx.get_member_state_changed() || ctx.get_member_role_changed()) {
213     /* notify member status events listeners. */
214     if (notify(kGroupMemberStatus, ctx)) {
215       /* purecov: begin inspected */
216       LogPluginErr(ERROR_LEVEL,
217                    ER_GRP_RPL_FAILED_TO_BROADCAST_MEMBER_STATUS_NOTIFICATION);
218       /* purecov: end */
219       res = true; /* purecov: inspected */
220     }
221   }
222 
223   ctx.reset();
224   return res;
225 }
226