1 /* Copyright (c) 2010, 2021, Oracle and/or its affiliates.
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 Foundation,
21 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22
23
24 #ifdef HAVE_REPLICATION
25 #include "rpl_master.h"
26
27 #include "hash.h" // HASH
28 #include "m_string.h" // strmake
29 #include "auth_common.h" // check_global_access
30 #include "binlog.h" // mysql_bin_log
31 #include "debug_sync.h" // DEBUG_SYNC
32 #include "log.h" // sql_print_information
33 #include "mysqld_thd_manager.h" // Global_THD_manager
34 #include "rpl_binlog_sender.h" // Binlog_sender
35 #include "rpl_filter.h" // binlog_filter
36 #include "rpl_handler.h" // RUN_HOOK
37 #include "sql_class.h" // THD
38 #include "rpl_group_replication.h" // is_group_replication_running
39 #include "mutex_lock.h" // Mutex_lock
40 #include "pfs_file_provider.h"
41 #include "mysql/psi/mysql_file.h"
42
43
44 int max_binlog_dump_events = 0; // unlimited
45 my_bool opt_sporadic_binlog_dump_fail = 0;
46
47 #define SLAVE_LIST_CHUNK 128
48 #define SLAVE_ERRMSG_SIZE (FN_REFLEN+64)
49 HASH slave_list;
50 extern TYPELIB binlog_checksum_typelib;
51
52
53 #define get_object(p, obj, msg) \
54 {\
55 uint len; \
56 if (p >= p_end) \
57 { \
58 my_error(ER_MALFORMED_PACKET, MYF(0)); \
59 my_free(si); \
60 return 1; \
61 } \
62 len= (uint)*p++; \
63 if (p + len > p_end || len >= sizeof(obj)) \
64 {\
65 errmsg= msg;\
66 goto err; \
67 }\
68 strmake(obj,(char*) p,len); \
69 p+= len; \
70 }\
71
72 extern "C" uint32
slave_list_key(SLAVE_INFO * si,size_t * len,my_bool not_used MY_ATTRIBUTE ((unused)))73 *slave_list_key(SLAVE_INFO* si, size_t *len,
74 my_bool not_used MY_ATTRIBUTE((unused)))
75 {
76 *len = 4;
77 return &si->server_id;
78 }
79
slave_info_free(void * s)80 extern "C" void slave_info_free(void *s)
81 {
82 my_free(s);
83 }
84
85 #ifdef HAVE_PSI_INTERFACE
86 static PSI_mutex_key key_LOCK_slave_list;
87
88 static PSI_mutex_info all_slave_list_mutexes[]=
89 {
90 { &key_LOCK_slave_list, "LOCK_slave_list", PSI_FLAG_GLOBAL}
91 };
92
init_all_slave_list_mutexes(void)93 static void init_all_slave_list_mutexes(void)
94 {
95 int count;
96
97 count= array_elements(all_slave_list_mutexes);
98 mysql_mutex_register("sql", all_slave_list_mutexes, count);
99 }
100 #endif /* HAVE_PSI_INTERFACE */
101
init_slave_list()102 void init_slave_list()
103 {
104 #ifdef HAVE_PSI_INTERFACE
105 init_all_slave_list_mutexes();
106 #endif
107
108 my_hash_init(&slave_list, system_charset_info, SLAVE_LIST_CHUNK, 0, 0,
109 (my_hash_get_key) slave_list_key,
110 (my_hash_free_key) slave_info_free, 0,
111 key_memory_SLAVE_INFO);
112 mysql_mutex_init(key_LOCK_slave_list, &LOCK_slave_list, MY_MUTEX_INIT_FAST);
113 }
114
end_slave_list()115 void end_slave_list()
116 {
117 /* No protection by a mutex needed as we are only called at shutdown */
118 if (my_hash_inited(&slave_list))
119 {
120 my_hash_free(&slave_list);
121 mysql_mutex_destroy(&LOCK_slave_list);
122 }
123 }
124
125 /**
126 Register slave in 'slave_list' hash table.
127
128 @return
129 0 ok
130 @return
131 1 Error. Error message sent to client
132 */
133
register_slave(THD * thd,uchar * packet,size_t packet_length)134 int register_slave(THD* thd, uchar* packet, size_t packet_length)
135 {
136 int res;
137 SLAVE_INFO *si;
138 uchar *p= packet, *p_end= packet + packet_length;
139 const char *errmsg= "Wrong parameters to function register_slave";
140
141 if (check_access(thd, REPL_SLAVE_ACL, any_db, NULL, NULL, 0, 0))
142 return 1;
143 if (!(si = (SLAVE_INFO*)my_malloc(key_memory_SLAVE_INFO,
144 sizeof(SLAVE_INFO), MYF(MY_WME))))
145 goto err2;
146
147 /* 4 bytes for the server id */
148 if (p + 4 > p_end)
149 {
150 my_error(ER_MALFORMED_PACKET, MYF(0));
151 my_free(si);
152 return 1;
153 }
154
155 thd->server_id= si->server_id= uint4korr(p);
156 p+= 4;
157 get_object(p,si->host, "Failed to register slave: too long 'report-host'");
158 get_object(p,si->user, "Failed to register slave: too long 'report-user'");
159 get_object(p,si->password, "Failed to register slave; too long 'report-password'");
160 if (p+10 > p_end)
161 goto err;
162 si->port= uint2korr(p);
163 p += 2;
164 /*
165 We need to by pass the bytes used in the fake rpl_recovery_rank
166 variable. It was removed in patch for BUG#13963. But this would
167 make a server with that patch unable to connect to an old master.
168 See: BUG#49259
169 */
170 p += 4;
171 if (!(si->master_id= uint4korr(p)))
172 si->master_id= server_id;
173 si->thd= thd;
174
175 mysql_mutex_lock(&LOCK_slave_list);
176 unregister_slave(thd, false, false/*need_lock_slave_list=false*/);
177 res= my_hash_insert(&slave_list, (uchar*) si);
178 mysql_mutex_unlock(&LOCK_slave_list);
179 return res;
180
181 err:
182 my_free(si);
183 my_message(ER_UNKNOWN_ERROR, errmsg, MYF(0)); /* purecov: inspected */
184 err2:
185 return 1;
186 }
187
unregister_slave(THD * thd,bool only_mine,bool need_lock_slave_list)188 void unregister_slave(THD* thd, bool only_mine, bool need_lock_slave_list)
189 {
190 if (thd->server_id && my_hash_inited(&slave_list))
191 {
192 if (need_lock_slave_list)
193 mysql_mutex_lock(&LOCK_slave_list);
194 else
195 mysql_mutex_assert_owner(&LOCK_slave_list);
196
197 SLAVE_INFO* old_si;
198 if ((old_si = (SLAVE_INFO*)my_hash_search(&slave_list,
199 (uchar*)&thd->server_id, 4)) &&
200 (!only_mine || old_si->thd == thd))
201 my_hash_delete(&slave_list, (uchar*)old_si);
202
203 if (need_lock_slave_list)
204 mysql_mutex_unlock(&LOCK_slave_list);
205 }
206 }
207
208
209 /**
210 Execute a SHOW SLAVE HOSTS statement.
211
212 @param thd Pointer to THD object for the client thread executing the
213 statement.
214
215 @retval FALSE success
216 @retval TRUE failure
217 */
show_slave_hosts(THD * thd)218 bool show_slave_hosts(THD* thd)
219 {
220 List<Item> field_list;
221 Protocol *protocol= thd->get_protocol();
222 DBUG_ENTER("show_slave_hosts");
223
224 field_list.push_back(new Item_return_int("Server_id", 10,
225 MYSQL_TYPE_LONG));
226 field_list.push_back(new Item_empty_string("Host", 20));
227 if (opt_show_slave_auth_info)
228 {
229 field_list.push_back(new Item_empty_string("User",20));
230 field_list.push_back(new Item_empty_string("Password",20));
231 }
232 field_list.push_back(new Item_return_int("Port", 7, MYSQL_TYPE_LONG));
233 field_list.push_back(new Item_return_int("Master_id", 10,
234 MYSQL_TYPE_LONG));
235 field_list.push_back(new Item_empty_string("Slave_UUID", UUID_LENGTH));
236
237 if (thd->send_result_metadata(&field_list,
238 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
239 DBUG_RETURN(TRUE);
240
241 mysql_mutex_lock(&LOCK_slave_list);
242
243 for (uint i = 0; i < slave_list.records; ++i)
244 {
245 SLAVE_INFO* si = (SLAVE_INFO*) my_hash_element(&slave_list, i);
246 protocol->start_row();
247 protocol->store((uint32) si->server_id);
248 protocol->store(si->host, &my_charset_bin);
249 if (opt_show_slave_auth_info)
250 {
251 protocol->store(si->user, &my_charset_bin);
252 protocol->store(si->password, &my_charset_bin);
253 }
254 protocol->store((uint32) si->port);
255 protocol->store((uint32) si->master_id);
256
257 /* get slave's UUID */
258 String slave_uuid;
259 if (get_slave_uuid(si->thd, &slave_uuid))
260 protocol->store(slave_uuid.c_ptr_safe(), &my_charset_bin);
261 if (protocol->end_row())
262 {
263 mysql_mutex_unlock(&LOCK_slave_list);
264 DBUG_RETURN(TRUE);
265 }
266 }
267 mysql_mutex_unlock(&LOCK_slave_list);
268 my_eof(thd);
269 DBUG_RETURN(FALSE);
270 }
271
272 /**
273 If there are less than BYTES bytes left to read in the packet,
274 report error.
275 */
276 #define CHECK_PACKET_SIZE(BYTES) \
277 do { \
278 if (packet_bytes_todo < BYTES) \
279 goto error_malformed_packet; \
280 } while (0)
281
282 /**
283 Auxiliary macro used to define READ_INT and READ_STRING.
284
285 Check that there are at least BYTES more bytes to read, then read
286 the bytes using the given DECODER, then advance the reading
287 position.
288 */
289 #define READ(DECODE, BYTES) \
290 do { \
291 CHECK_PACKET_SIZE(BYTES); \
292 DECODE; \
293 packet_position+= BYTES; \
294 packet_bytes_todo-= BYTES; \
295 } while (0)
296
297
298 /**
299 Check that there are at least BYTES more bytes to read, then read
300 the bytes and decode them into the given integer VAR, then advance
301 the reading position.
302 */
303 #define READ_INT(VAR, BYTES) \
304 READ(VAR= uint ## BYTES ## korr(packet_position), BYTES)
305
306 /**
307 Check that there are at least BYTES more bytes to read and that
308 BYTES+1 is not greater than BUFFER_SIZE, then read the bytes into
309 the given variable VAR, then advance the reading position.
310 */
311 #define READ_STRING(VAR, BYTES, BUFFER_SIZE) \
312 do { \
313 if (BUFFER_SIZE <= BYTES) \
314 goto error_malformed_packet; \
315 READ(memcpy(VAR, packet_position, BYTES), BYTES); \
316 VAR[BYTES]= '\0'; \
317 } while (0)
318
319
com_binlog_dump(THD * thd,char * packet,size_t packet_length)320 bool com_binlog_dump(THD *thd, char *packet, size_t packet_length)
321 {
322 DBUG_ENTER("com_binlog_dump");
323 ulong pos;
324 ushort flags= 0;
325 const uchar* packet_position= (uchar *) packet;
326 size_t packet_bytes_todo= packet_length;
327
328 assert(!thd->status_var_aggregated);
329 thd->status_var.com_other++;
330 thd->enable_slow_log= opt_log_slow_admin_statements;
331 if (check_global_access(thd, REPL_SLAVE_ACL))
332 DBUG_RETURN(false);
333
334 /*
335 4 bytes is too little, but changing the protocol would break
336 compatibility. This has been fixed in the new protocol. @see
337 com_binlog_dump_gtid().
338 */
339 READ_INT(pos, 4);
340 READ_INT(flags, 2);
341 READ_INT(thd->server_id, 4);
342
343 DBUG_PRINT("info", ("pos=%lu flags=%d server_id=%d", pos, flags, thd->server_id));
344
345 kill_zombie_dump_threads(thd);
346
347 query_logger.general_log_print(thd, thd->get_command(), "Log: '%s' Pos: %ld",
348 packet + 10, (long) pos);
349 mysql_binlog_send(thd, thd->mem_strdup(packet + 10), (my_off_t) pos, NULL, flags);
350
351 unregister_slave(thd, true, true/*need_lock_slave_list=true*/);
352 /* fake COM_QUIT -- if we get here, the thread needs to terminate */
353 DBUG_RETURN(true);
354
355 error_malformed_packet:
356 my_error(ER_MALFORMED_PACKET, MYF(0));
357 DBUG_RETURN(true);
358 }
359
360
com_binlog_dump_gtid(THD * thd,char * packet,size_t packet_length)361 bool com_binlog_dump_gtid(THD *thd, char *packet, size_t packet_length)
362 {
363 DBUG_ENTER("com_binlog_dump_gtid");
364 /*
365 Before going GA, we need to make this protocol extensible without
366 breaking compatitibilty. /Alfranio.
367 */
368 ushort flags= 0;
369 uint32 data_size= 0;
370 uint64 pos= 0;
371 char name[FN_REFLEN + 1];
372 uint32 name_size= 0;
373 char* gtid_string= NULL;
374 const uchar* packet_position= (uchar *) packet;
375 size_t packet_bytes_todo= packet_length;
376 Sid_map sid_map(NULL/*no sid_lock because this is a completely local object*/);
377 Gtid_set slave_gtid_executed(&sid_map);
378
379 assert(!thd->status_var_aggregated);
380 thd->status_var.com_other++;
381 thd->enable_slow_log= opt_log_slow_admin_statements;
382 if (check_global_access(thd, REPL_SLAVE_ACL))
383 DBUG_RETURN(false);
384
385 READ_INT(flags,2);
386 READ_INT(thd->server_id, 4);
387 READ_INT(name_size, 4);
388 READ_STRING(name, name_size, sizeof(name));
389 READ_INT(pos, 8);
390 DBUG_PRINT("info", ("pos=%llu flags=%d server_id=%d", pos, flags, thd->server_id));
391 READ_INT(data_size, 4);
392 CHECK_PACKET_SIZE(data_size);
393 if (slave_gtid_executed.add_gtid_encoding(packet_position, data_size) !=
394 RETURN_STATUS_OK)
395 DBUG_RETURN(true);
396 slave_gtid_executed.to_string(>id_string);
397 DBUG_PRINT("info", ("Slave %d requested to read %s at position %llu gtid set "
398 "'%s'.", thd->server_id, name, pos, gtid_string));
399
400 kill_zombie_dump_threads(thd);
401 query_logger.general_log_print(thd, thd->get_command(),
402 "Log: '%s' Pos: %llu GTIDs: '%s'",
403 name, pos, gtid_string);
404 my_free(gtid_string);
405 mysql_binlog_send(thd, name, (my_off_t) pos, &slave_gtid_executed, flags);
406
407 unregister_slave(thd, true, true/*need_lock_slave_list=true*/);
408 /* fake COM_QUIT -- if we get here, the thread needs to terminate */
409 DBUG_RETURN(true);
410
411 error_malformed_packet:
412 my_error(ER_MALFORMED_PACKET, MYF(0));
413 DBUG_RETURN(true);
414 }
415
mysql_binlog_send(THD * thd,char * log_ident,my_off_t pos,Gtid_set * slave_gtid_executed,uint32 flags)416 void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
417 Gtid_set* slave_gtid_executed, uint32 flags)
418 {
419 Binlog_sender sender(thd, log_ident, pos, slave_gtid_executed, flags);
420
421 sender.run();
422 }
423
424 /**
425 An auxiliary function extracts slave UUID.
426
427 @param[in] thd THD to access a user variable
428 @param[out] value String to return UUID value.
429
430 @return if success value is returned else NULL is returned.
431 */
get_slave_uuid(THD * thd,String * value)432 String *get_slave_uuid(THD *thd, String *value)
433 {
434 uchar name[]= "slave_uuid";
435
436 if (value == NULL)
437 return NULL;
438
439 /* Protects thd->user_vars. */
440 Mutex_lock lock_guard(&thd->LOCK_thd_data);
441
442 user_var_entry *entry=
443 (user_var_entry*) my_hash_search(&thd->user_vars, name, sizeof(name)-1);
444 if (entry && entry->length() > 0)
445 {
446 value->copy(entry->ptr(), entry->length(), NULL);
447 return value;
448 }
449 return NULL;
450 }
451
452 /**
453 Callback function used by kill_zombie_dump_threads() function to
454 to find zombie dump thread from the thd list.
455
456 @note It acquires LOCK_thd_data mutex when it finds matching thd.
457 It is the responsibility of the caller to release this mutex.
458 */
459 class Find_zombie_dump_thread : public Find_THD_Impl
460 {
461 public:
Find_zombie_dump_thread(String value)462 Find_zombie_dump_thread(String value): m_slave_uuid(value) {}
operator ()(THD * thd)463 virtual bool operator()(THD *thd)
464 {
465 THD *cur_thd= current_thd;
466 if (thd != cur_thd && (thd->get_command() == COM_BINLOG_DUMP ||
467 thd->get_command() == COM_BINLOG_DUMP_GTID))
468 {
469 String tmp_uuid;
470 bool is_zombie_thread= false;
471 get_slave_uuid(thd, &tmp_uuid);
472 if (m_slave_uuid.length())
473 {
474 is_zombie_thread= (tmp_uuid.length() &&
475 !strncmp(m_slave_uuid.c_ptr(),
476 tmp_uuid.c_ptr(), UUID_LENGTH));
477 }
478 else
479 {
480 /*
481 Check if it is a 5.5 slave's dump thread i.e., server_id should be
482 same && dump thread should not contain 'UUID'.
483 */
484 is_zombie_thread= ((thd->server_id == cur_thd->server_id) &&
485 !tmp_uuid.length());
486 }
487 if (is_zombie_thread)
488 {
489 mysql_mutex_lock(&thd->LOCK_thd_data);
490 return true;
491 }
492 }
493 return false;
494 }
495 private:
496 String m_slave_uuid;
497 };
498
499 /*
500
501 Kill all Binlog_dump threads which previously talked to the same slave
502 ("same" means with the same UUID(for slave versions >= 5.6) or same server id
503 (for slave versions < 5.6). Indeed, if the slave stops, if the
504 Binlog_dump thread is waiting (mysql_cond_wait) for binlog update, then it
505 will keep existing until a query is written to the binlog. If the master is
506 idle, then this could last long, and if the slave reconnects, we could have 2
507 Binlog_dump threads in SHOW PROCESSLIST, until a query is written to the
508 binlog. To avoid this, when the slave reconnects and sends COM_BINLOG_DUMP,
509 the master kills any existing thread with the slave's UUID/server id (if this id is
510 not zero; it will be true for real slaves, but false for mysqlbinlog when it
511 sends COM_BINLOG_DUMP to get a remote binlog dump).
512
513 SYNOPSIS
514 kill_zombie_dump_threads()
515 @param thd newly connected dump thread object
516
517 */
518
kill_zombie_dump_threads(THD * thd)519 void kill_zombie_dump_threads(THD *thd)
520 {
521 String slave_uuid;
522 get_slave_uuid(thd, &slave_uuid);
523 if (slave_uuid.length() == 0 && thd->server_id == 0)
524 return;
525
526 Find_zombie_dump_thread find_zombie_dump_thread(slave_uuid);
527 THD *tmp= Global_THD_manager::get_instance()->
528 find_thd(&find_zombie_dump_thread);
529 if (tmp)
530 {
531 /*
532 Here we do not call kill_one_thread() as
533 it will be slow because it will iterate through the list
534 again. We just to do kill the thread ourselves.
535 */
536 if (log_warnings > 1)
537 {
538 if (slave_uuid.length())
539 {
540 sql_print_information("While initializing dump thread for slave with "
541 "UUID <%s>, found a zombie dump thread with the "
542 "same UUID. Master is killing the zombie dump "
543 "thread(%u).", slave_uuid.c_ptr(),
544 tmp->thread_id());
545 }
546 else
547 {
548 sql_print_information("While initializing dump thread for slave with "
549 "server_id <%u>, found a zombie dump thread with the "
550 "same server_id. Master is killing the zombie dump "
551 "thread(%u).", thd->server_id,
552 tmp->thread_id());
553 }
554 }
555 tmp->duplicate_slave_id= true;
556 tmp->awake(THD::KILL_QUERY);
557 mysql_mutex_unlock(&tmp->LOCK_thd_data);
558 }
559 }
560
561 /**
562 Execute a RESET MASTER statement.
563
564 @param thd Pointer to THD object of the client thread executing the
565 statement.
566
567 @retval false success
568 @retval true error
569 */
reset_master(THD * thd)570 bool reset_master(THD* thd)
571 {
572 bool ret= false;
573
574 /*
575 RESET MASTER command should ignore 'read-only' and 'super_read_only'
576 options so that it can update 'mysql.gtid_executed' replication repository
577 table.
578
579 Please note that skip_readonly_check flag should be set even when binary log
580 is not enabled, as RESET MASTER command will clear 'gtid_executed' table.
581 */
582 thd->set_skip_readonly_check();
583 if (is_group_replication_running())
584 {
585 my_error(ER_CANT_RESET_MASTER, MYF(0), "Group Replication is running");
586 return true;
587 }
588
589 if (mysql_bin_log.is_open())
590 {
591 /*
592 mysql_bin_log.reset_logs will delete the binary logs *and* clear
593 gtid_state. It is important to do both these operations from
594 within reset_logs, since the operations can then use the same
595 lock. I.e., if we would remove the call to gtid_state->clear
596 from reset_logs and call gtid_state->clear explicitly from this
597 function instead, it would be possible for a concurrent thread
598 to commit between the point where the binary log was removed and
599 the point where the gtid_executed table is cleared. This would
600 lead to an inconsistent state.
601 */
602 ret= mysql_bin_log.reset_logs(thd);
603 }
604 else
605 {
606 global_sid_lock->wrlock();
607 ret= (gtid_state->clear(thd) != 0);
608 global_sid_lock->unlock();
609 }
610
611 /*
612 Only run after_reset_master hook, when all reset operations preceding this
613 have succeeded.
614 */
615 if (!ret)
616 (void) RUN_HOOK(binlog_transmit, after_reset_master, (thd, 0 /* flags */));
617 return ret;
618 }
619
620
621 /**
622 Execute a SHOW MASTER STATUS statement.
623
624 @param thd Pointer to THD object for the client thread executing the
625 statement.
626
627 @retval false success
628 @retval true failure
629 */
show_master_status(THD * thd)630 bool show_master_status(THD* thd)
631 {
632 Protocol *protocol= thd->get_protocol();
633 char* gtid_set_buffer= NULL;
634 int gtid_set_size= 0;
635 List<Item> field_list;
636
637 DBUG_ENTER("show_binlog_info");
638
639 global_sid_lock->wrlock();
640 const Gtid_set* gtid_set= gtid_state->get_executed_gtids();
641 if ((gtid_set_size= gtid_set->to_string(>id_set_buffer)) < 0)
642 {
643 global_sid_lock->unlock();
644 my_eof(thd);
645 my_free(gtid_set_buffer);
646 DBUG_RETURN(true);
647 }
648 global_sid_lock->unlock();
649
650 field_list.push_back(new Item_empty_string("File", FN_REFLEN));
651 field_list.push_back(new Item_return_int("Position",20,
652 MYSQL_TYPE_LONGLONG));
653 field_list.push_back(new Item_empty_string("Binlog_Do_DB",255));
654 field_list.push_back(new Item_empty_string("Binlog_Ignore_DB",255));
655 field_list.push_back(new Item_empty_string("Executed_Gtid_Set",
656 gtid_set_size));
657
658 if (thd->send_result_metadata(&field_list,
659 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
660 {
661 my_free(gtid_set_buffer);
662 DBUG_RETURN(true);
663 }
664 protocol->start_row();
665
666 if (mysql_bin_log.is_open())
667 {
668 LOG_INFO li;
669 mysql_bin_log.get_current_log(&li);
670 size_t dir_len = dirname_length(li.log_file_name);
671 protocol->store(li.log_file_name + dir_len, &my_charset_bin);
672 protocol->store((ulonglong) li.pos);
673 store(protocol, binlog_filter->get_do_db());
674 store(protocol, binlog_filter->get_ignore_db());
675 protocol->store(gtid_set_buffer, &my_charset_bin);
676 if (protocol->end_row())
677 {
678 my_free(gtid_set_buffer);
679 DBUG_RETURN(true);
680 }
681 }
682 my_eof(thd);
683 my_free(gtid_set_buffer);
684 DBUG_RETURN(false);
685 }
686
687
688 /**
689 Execute a SHOW BINARY LOGS statement.
690
691 @param thd Pointer to THD object for the client thread executing the
692 statement.
693
694 @retval FALSE success
695 @retval TRUE failure
696 */
show_binlogs(THD * thd)697 bool show_binlogs(THD* thd)
698 {
699 IO_CACHE *index_file;
700 LOG_INFO cur;
701 File file;
702 char fname[FN_REFLEN];
703 List<Item> field_list;
704 size_t length;
705 size_t cur_dir_len;
706 Protocol *protocol= thd->get_protocol();
707 DBUG_ENTER("show_binlogs");
708
709 if (!mysql_bin_log.is_open())
710 {
711 my_error(ER_NO_BINARY_LOGGING, MYF(0));
712 DBUG_RETURN(TRUE);
713 }
714
715 field_list.push_back(new Item_empty_string("Log_name", 255));
716 field_list.push_back(new Item_return_int("File_size", 20,
717 MYSQL_TYPE_LONGLONG));
718 if (thd->send_result_metadata(&field_list, Protocol::SEND_NUM_ROWS |
719 Protocol::SEND_EOF))
720 DBUG_RETURN(TRUE);
721
722 mysql_mutex_lock(mysql_bin_log.get_log_lock());
723 DEBUG_SYNC(thd, "show_binlogs_after_lock_log_before_lock_index");
724 mysql_bin_log.lock_index();
725 index_file=mysql_bin_log.get_index_file();
726
727 mysql_bin_log.raw_get_current_log(&cur); // dont take mutex
728 mysql_mutex_unlock(mysql_bin_log.get_log_lock()); // lockdep, OK
729
730 cur_dir_len= dirname_length(cur.log_file_name);
731
732 if (reinit_io_cache(index_file, READ_CACHE, (my_off_t)0, 0, 0))
733 goto err;
734
735 /* The file ends with EOF or empty line */
736 while ((length=my_b_gets(index_file, fname, sizeof(fname))) > 1)
737 {
738 size_t dir_len;
739 ulonglong file_length= 0; // Length if open fails
740 fname[--length] = '\0'; // remove the newline
741
742 protocol->start_row();
743 dir_len= dirname_length(fname);
744 length-= dir_len;
745 protocol->store(fname + dir_len, length, &my_charset_bin);
746
747 if (!(strncmp(fname+dir_len, cur.log_file_name+cur_dir_len, length)))
748 file_length= cur.pos; /* The active log, use the active position */
749 else
750 {
751 /* this is an old log, open it and find the size */
752 if ((file= mysql_file_open(key_file_binlog,
753 fname, O_RDONLY | O_SHARE | O_BINARY,
754 MYF(0))) >= 0)
755 {
756 file_length= (ulonglong) mysql_file_seek(file, 0L, MY_SEEK_END, MYF(0));
757 mysql_file_close(file, MYF(0));
758 }
759 }
760 protocol->store(file_length);
761 if (protocol->end_row())
762 {
763 DBUG_PRINT("info", ("stopping dump thread because protocol->write failed at line %d", __LINE__));
764 goto err;
765 }
766 }
767 if(index_file->error == -1)
768 goto err;
769 mysql_bin_log.unlock_index();
770 my_eof(thd);
771 DBUG_RETURN(FALSE);
772
773 err:
774 mysql_bin_log.unlock_index();
775 DBUG_RETURN(TRUE);
776 }
777
778 #endif /* HAVE_REPLICATION */
779