1 /*
2    Copyright (c) 2001, 2011, Oracle and/or its affiliates.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; version 2 of the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
16 
17 /**
18   @file
19 
20   All of the functions defined in this file which are not used (the ones to
21   handle failsafe) are not used; their code has not been updated for more
22   than one year now so should be considered as BADLY BROKEN. Do not enable
23   it. The used functions (to handle LOAD DATA FROM MASTER, plus some small
24   functions like register_slave()) are working.
25 */
26 
27 #include "mariadb.h"
28 #include "sql_priv.h"
29 #include "sql_parse.h"                          // check_access
30 #ifdef HAVE_REPLICATION
31 
32 #include "repl_failsafe.h"
33 #include "sql_acl.h"                            // REPL_SLAVE_ACL
34 #include "sql_repl.h"
35 #include "slave.h"
36 #include "rpl_mi.h"
37 #include "rpl_filter.h"
38 #include "log_event.h"
39 #include <mysql.h>
40 
41 #define SLAVE_LIST_CHUNK 128
42 #define SLAVE_ERRMSG_SIZE (FN_REFLEN+64)
43 
44 
45 ulong rpl_status=RPL_NULL;
46 mysql_mutex_t LOCK_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_mutex_unlock(&LOCK_rpl_status);
73 }
74 
75 
76 #define get_object(p, obj, msg) \
77 {\
78   uint len = (uint)*p++;  \
79   if (p + len > p_end || len >= sizeof(obj)) \
80   {\
81     errmsg= msg;\
82     goto err; \
83   }\
84   strmake(obj,(char*) p,len); \
85   p+= len; \
86 }\
87 
88 
unregister_slave(THD * thd,bool only_mine,bool need_mutex)89 void unregister_slave(THD* thd, bool only_mine, bool need_mutex)
90 {
91   uint32 thd_server_id= thd->variables.server_id;
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,size_t packet_length)118 int register_slave(THD* thd, uchar* packet, size_t 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->variables.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= global_system_variables.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   Execute a SHOW SLAVE HOSTS statement.
222 
223   @param thd Pointer to THD object for the client thread executing the
224   statement.
225 
226   @retval FALSE success
227   @retval TRUE failure
228 */
show_slave_hosts(THD * thd)229 bool show_slave_hosts(THD* thd)
230 {
231   List<Item> field_list;
232   Protocol *protocol= thd->protocol;
233   MEM_ROOT *mem_root= thd->mem_root;
234   DBUG_ENTER("show_slave_hosts");
235 
236   field_list.push_back(new (mem_root)
237                        Item_return_int(thd, "Server_id", 10,
238                                        MYSQL_TYPE_LONG),
239                        thd->mem_root);
240   field_list.push_back(new (mem_root)
241                        Item_empty_string(thd, "Host", 20),
242                        thd->mem_root);
243   if (opt_show_slave_auth_info)
244   {
245     field_list.push_back(new (mem_root) Item_empty_string(thd, "User", 20),
246                          thd->mem_root);
247     field_list.push_back(new (mem_root) Item_empty_string(thd, "Password", 20),
248                          thd->mem_root);
249   }
250   field_list.push_back(new (mem_root)
251                        Item_return_int(thd, "Port", 7, MYSQL_TYPE_LONG),
252                        thd->mem_root);
253   field_list.push_back(new (mem_root)
254                        Item_return_int(thd, "Master_id", 10, MYSQL_TYPE_LONG),
255                        thd->mem_root);
256 
257   if (protocol->send_result_set_metadata(&field_list,
258                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
259     DBUG_RETURN(TRUE);
260 
261   mysql_mutex_lock(&LOCK_slave_list);
262 
263   for (uint i = 0; i < slave_list.records; ++i)
264   {
265     SLAVE_INFO* si = (SLAVE_INFO*) my_hash_element(&slave_list, i);
266     protocol->prepare_for_resend();
267     protocol->store((uint32) si->server_id);
268     protocol->store(si->host, &my_charset_bin);
269     if (opt_show_slave_auth_info)
270     {
271       protocol->store(si->user, &my_charset_bin);
272       protocol->store(si->password, &my_charset_bin);
273     }
274     protocol->store((uint32) si->port);
275     protocol->store((uint32) si->master_id);
276     if (protocol->write())
277     {
278       mysql_mutex_unlock(&LOCK_slave_list);
279       DBUG_RETURN(TRUE);
280     }
281   }
282   mysql_mutex_unlock(&LOCK_slave_list);
283   my_eof(thd);
284   DBUG_RETURN(FALSE);
285 }
286 
287 #endif /* HAVE_REPLICATION */
288 
289