1 /* Copyright (c) 2001, 2011, 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 as published by
5    the Free Software Foundation; version 2 of the License.
6 
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11 
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
15 
16 /**
17   @file
18 
19   All of the functions defined in this file which are not used (the ones to
20   handle failsafe) are not used; their code has not been updated for more
21   than one year now so should be considered as BADLY BROKEN. Do not enable
22   it. The used functions (to handle LOAD DATA FROM MASTER, plus some small
23   functions like register_slave()) are working.
24 */
25 
26 #include "sql_priv.h"
27 #include "sql_parse.h"                          // check_access
28 #ifdef HAVE_REPLICATION
29 
30 #include "repl_failsafe.h"
31 #include "sql_acl.h"                            // REPL_SLAVE_ACL
32 #include "sql_repl.h"
33 #include "slave.h"
34 #include "rpl_mi.h"
35 #include "rpl_filter.h"
36 #include "log_event.h"
37 #include "sql_db.h"                             // mysql_create_db
38 #include <mysql.h>
39 
40 #define SLAVE_LIST_CHUNK 128
41 #define SLAVE_ERRMSG_SIZE (FN_REFLEN+64)
42 
43 
44 ulong rpl_status=RPL_NULL;
45 mysql_mutex_t LOCK_rpl_status;
46 mysql_cond_t COND_rpl_status;
47 HASH slave_list;
48 
49 const char *rpl_role_type[] = {"MASTER","SLAVE",NullS};
50 TYPELIB rpl_role_typelib = {array_elements(rpl_role_type)-1,"",
51 			    rpl_role_type, NULL};
52 
53 const char* rpl_status_type[]=
54 {
55   "AUTH_MASTER","IDLE_SLAVE","ACTIVE_SLAVE","LOST_SOLDIER","TROOP_SOLDIER",
56   "RECOVERY_CAPTAIN","NULL",NullS
57 };
58 
59 /*
60   All of the functions defined in this file which are not used (the ones to
61   handle failsafe) are not used; their code has not been updated for more than
62   one year now so should be considered as BADLY BROKEN. Do not enable it.
63   The used functions (to handle LOAD DATA FROM MASTER, plus some small
64   functions like register_slave()) are working.
65 */
66 
change_rpl_status(ulong from_status,ulong to_status)67 void change_rpl_status(ulong from_status, ulong to_status)
68 {
69   mysql_mutex_lock(&LOCK_rpl_status);
70   if (rpl_status == from_status || rpl_status == RPL_ANY)
71     rpl_status = to_status;
72   mysql_cond_signal(&COND_rpl_status);
73   mysql_mutex_unlock(&LOCK_rpl_status);
74 }
75 
76 
77 #define get_object(p, obj, msg) \
78 {\
79   uint len = (uint)*p++;  \
80   if (p + len > p_end || len >= sizeof(obj)) \
81   {\
82     errmsg= msg;\
83     goto err; \
84   }\
85   strmake(obj,(char*) p,len); \
86   p+= len; \
87 }\
88 
89 
unregister_slave(THD * thd,bool only_mine,bool need_mutex)90 void unregister_slave(THD* thd, bool only_mine, bool need_mutex)
91 {
92   if (thd->server_id)
93   {
94     if (need_mutex)
95       mysql_mutex_lock(&LOCK_slave_list);
96 
97     SLAVE_INFO* old_si;
98     if ((old_si = (SLAVE_INFO*)my_hash_search(&slave_list,
99                                               (uchar*)&thd->server_id, 4)) &&
100 	(!only_mine || old_si->thd == thd))
101     my_hash_delete(&slave_list, (uchar*)old_si);
102 
103     if (need_mutex)
104       mysql_mutex_unlock(&LOCK_slave_list);
105   }
106 }
107 
108 
109 /**
110   Register slave in 'slave_list' hash table.
111 
112   @return
113     0	ok
114   @return
115     1	Error.   Error message sent to client
116 */
117 
register_slave(THD * thd,uchar * packet,uint packet_length)118 int register_slave(THD* thd, uchar* packet, uint packet_length)
119 {
120   int res;
121   SLAVE_INFO *si;
122   uchar *p= packet, *p_end= packet + packet_length;
123   const char *errmsg= "Wrong parameters to function register_slave";
124 
125   if (check_access(thd, REPL_SLAVE_ACL, any_db, NULL, NULL, 0, 0))
126     return 1;
127   if (!(si = (SLAVE_INFO*)my_malloc(sizeof(SLAVE_INFO), MYF(MY_WME))))
128     goto err2;
129 
130   thd->server_id= si->server_id= uint4korr(p);
131   p+= 4;
132   get_object(p,si->host, "Failed to register slave: too long 'report-host'");
133   get_object(p,si->user, "Failed to register slave: too long 'report-user'");
134   get_object(p,si->password, "Failed to register slave; too long 'report-password'");
135   if (p+10 > p_end)
136     goto err;
137   si->port= uint2korr(p);
138   p += 2;
139   /*
140      We need to by pass the bytes used in the fake rpl_recovery_rank
141      variable. It was removed in patch for BUG#13963. But this would
142      make a server with that patch unable to connect to an old master.
143      See: BUG#49259
144   */
145   // si->rpl_recovery_rank= uint4korr(p);
146   p += 4;
147   if (!(si->master_id= uint4korr(p)))
148     si->master_id= server_id;
149   si->thd= thd;
150 
151   mysql_mutex_lock(&LOCK_slave_list);
152   unregister_slave(thd,0,0);
153   res= my_hash_insert(&slave_list, (uchar*) si);
154   mysql_mutex_unlock(&LOCK_slave_list);
155   return res;
156 
157 err:
158   my_free(si);
159   my_message(ER_UNKNOWN_ERROR, errmsg, MYF(0)); /* purecov: inspected */
160 err2:
161   return 1;
162 }
163 
164 extern "C" uint32
slave_list_key(SLAVE_INFO * si,size_t * len,my_bool not_used)165 *slave_list_key(SLAVE_INFO* si, size_t *len,
166 		my_bool not_used __attribute__((unused)))
167 {
168   *len = 4;
169   return &si->server_id;
170 }
171 
slave_info_free(void * s)172 extern "C" void slave_info_free(void *s)
173 {
174   my_free(s);
175 }
176 
177 #ifdef HAVE_PSI_INTERFACE
178 static PSI_mutex_key key_LOCK_slave_list;
179 
180 static PSI_mutex_info all_slave_list_mutexes[]=
181 {
182   { &key_LOCK_slave_list, "LOCK_slave_list", PSI_FLAG_GLOBAL}
183 };
184 
init_all_slave_list_mutexes(void)185 static void init_all_slave_list_mutexes(void)
186 {
187   const char* category= "sql";
188   int count;
189 
190   if (PSI_server == NULL)
191     return;
192 
193   count= array_elements(all_slave_list_mutexes);
194   PSI_server->register_mutex(category, all_slave_list_mutexes, count);
195 }
196 #endif /* HAVE_PSI_INTERFACE */
197 
init_slave_list()198 void init_slave_list()
199 {
200 #ifdef HAVE_PSI_INTERFACE
201   init_all_slave_list_mutexes();
202 #endif
203 
204   my_hash_init(&slave_list, system_charset_info, SLAVE_LIST_CHUNK, 0, 0,
205                (my_hash_get_key) slave_list_key,
206                (my_hash_free_key) slave_info_free, 0);
207   mysql_mutex_init(key_LOCK_slave_list, &LOCK_slave_list, MY_MUTEX_INIT_FAST);
208 }
209 
end_slave_list()210 void end_slave_list()
211 {
212   /* No protection by a mutex needed as we are only called at shutdown */
213   if (my_hash_inited(&slave_list))
214   {
215     my_hash_free(&slave_list);
216     mysql_mutex_destroy(&LOCK_slave_list);
217   }
218 }
219 
220 
221 /**
222   Execute a SHOW SLAVE HOSTS statement.
223 
224   @param thd Pointer to THD object for the client thread executing the
225   statement.
226 
227   @retval FALSE success
228   @retval TRUE failure
229 */
show_slave_hosts(THD * thd)230 bool show_slave_hosts(THD* thd)
231 {
232   List<Item> field_list;
233   Protocol *protocol= thd->protocol;
234   DBUG_ENTER("show_slave_hosts");
235 
236   field_list.push_back(new Item_return_int("Server_id", 10,
237 					   MYSQL_TYPE_LONG));
238   field_list.push_back(new Item_empty_string("Host", 20));
239   if (opt_show_slave_auth_info)
240   {
241     field_list.push_back(new Item_empty_string("User",20));
242     field_list.push_back(new Item_empty_string("Password",20));
243   }
244   field_list.push_back(new Item_return_int("Port", 7, MYSQL_TYPE_LONG));
245   field_list.push_back(new Item_return_int("Master_id", 10,
246 					   MYSQL_TYPE_LONG));
247 
248   if (protocol->send_result_set_metadata(&field_list,
249                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
250     DBUG_RETURN(TRUE);
251 
252   mysql_mutex_lock(&LOCK_slave_list);
253 
254   for (uint i = 0; i < slave_list.records; ++i)
255   {
256     SLAVE_INFO* si = (SLAVE_INFO*) my_hash_element(&slave_list, i);
257     protocol->prepare_for_resend();
258     protocol->store((uint32) si->server_id);
259     protocol->store(si->host, &my_charset_bin);
260     if (opt_show_slave_auth_info)
261     {
262       protocol->store(si->user, &my_charset_bin);
263       protocol->store(si->password, &my_charset_bin);
264     }
265     protocol->store((uint32) si->port);
266     protocol->store((uint32) si->master_id);
267     if (protocol->write())
268     {
269       mysql_mutex_unlock(&LOCK_slave_list);
270       DBUG_RETURN(TRUE);
271     }
272   }
273   mysql_mutex_unlock(&LOCK_slave_list);
274   my_eof(thd);
275   DBUG_RETURN(FALSE);
276 }
277 
278 #endif /* HAVE_REPLICATION */
279 
280