1 /* Copyright (c) 2010, 2016, 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 <my_global.h>
24 #include <sql_priv.h>
25 #include <my_dir.h>
26 #include "rpl_info_file.h"
27 #include "mysqld.h"
28 #include "log.h"
29
30 int init_ulongvar_from_file(ulong* var, IO_CACHE* f, ulong default_val);
31 int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
32 const char *default_val);
33 int init_intvar_from_file(int* var, IO_CACHE* f, int default_val);
34 int init_floatvar_from_file(float* var, IO_CACHE* f, float default_val);
35 bool init_dynarray_intvar_from_file(char *buffer, size_t size,
36 char **buffer_act, IO_CACHE* f);
37
Rpl_info_file(const int nparam,const char * param_pattern_fname,const char * param_info_fname,bool indexed_arg)38 Rpl_info_file::Rpl_info_file(const int nparam,
39 const char* param_pattern_fname,
40 const char* param_info_fname,
41 bool indexed_arg)
42 :Rpl_info_handler(nparam), info_fd(-1), name_indexed(indexed_arg)
43 {
44 DBUG_ENTER("Rpl_info_file::Rpl_info_file");
45
46 memset(&info_file, 0, sizeof(info_file));
47 fn_format(pattern_fname, param_pattern_fname, mysql_data_home, "", 4 + 32);
48 fn_format(info_fname, param_info_fname, mysql_data_home, "", 4 + 32);
49
50 DBUG_VOID_RETURN;
51 }
52
~Rpl_info_file()53 Rpl_info_file::~Rpl_info_file()
54 {
55 DBUG_ENTER("Rpl_info_file::~Rpl_info_file");
56
57 do_end_info();
58
59 DBUG_VOID_RETURN;
60 }
61
do_init_info(uint instance)62 int Rpl_info_file::do_init_info(uint instance)
63 {
64 DBUG_ENTER("Rpl_info_file::do_init_info(uint)");
65
66 char fname_local[FN_REFLEN];
67 char *pos= strmov(fname_local, pattern_fname);
68 if (name_indexed)
69 sprintf(pos, "%u", instance);
70
71 fn_format(info_fname, fname_local, mysql_data_home, "", 4 + 32);
72 DBUG_RETURN(do_init_info());
73 }
74
do_init_info()75 int Rpl_info_file::do_init_info()
76 {
77 int error= 0;
78 DBUG_ENTER("Rpl_info_file::do_init_info");
79
80 /* does info file exist ? */
81 enum_return_check ret_check= do_check_info();
82 if (ret_check == REPOSITORY_DOES_NOT_EXIST)
83 {
84 /*
85 If someone removed the file from underneath our feet, just close
86 the old descriptor and re-create the old file
87 */
88 if (info_fd >= 0)
89 {
90 if (my_b_inited(&info_file))
91 end_io_cache(&info_file);
92 my_close(info_fd, MYF(MY_WME));
93 }
94 if ((info_fd = my_open(info_fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0)
95 {
96 sql_print_error("Failed to create a new info file (\
97 file '%s', errno %d)", info_fname, my_errno);
98 error= 1;
99 }
100 else if (init_io_cache(&info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0,
101 MYF(MY_WME)))
102 {
103 sql_print_error("Failed to create a cache on info file (\
104 file '%s')", info_fname);
105 error= 1;
106 }
107 if (error)
108 {
109 if (info_fd >= 0)
110 my_close(info_fd, MYF(0));
111 info_fd= -1;
112 }
113 }
114 /* file exists */
115 else if (ret_check == REPOSITORY_EXISTS)
116 {
117 if (info_fd >= 0)
118 reinit_io_cache(&info_file, READ_CACHE, 0L,0,0);
119 else
120 {
121 if ((info_fd = my_open(info_fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0 )
122 {
123 sql_print_error("Failed to open the existing info file (\
124 file '%s', errno %d)", info_fname, my_errno);
125 error= 1;
126 }
127 else if (init_io_cache(&info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,
128 0, MYF(MY_WME)))
129 {
130 sql_print_error("Failed to create a cache on info file (\
131 file '%s')", info_fname);
132 error= 1;
133 }
134 if (error)
135 {
136 if (info_fd >= 0)
137 my_close(info_fd, MYF(0));
138 info_fd= -1;
139 }
140 }
141 }
142 else
143 error= 1;
144 DBUG_RETURN(error);
145 }
146
do_prepare_info_for_read()147 int Rpl_info_file::do_prepare_info_for_read()
148 {
149 cursor= 0;
150 prv_error= FALSE;
151 return (reinit_io_cache(&info_file, READ_CACHE, 0L, 0, 0));
152 }
153
do_prepare_info_for_write()154 int Rpl_info_file::do_prepare_info_for_write()
155 {
156 cursor= 0;
157 prv_error= FALSE;
158 return (reinit_io_cache(&info_file, WRITE_CACHE, 0L, 0, 1));
159 }
160
do_check_repository_file(const char * fname)161 inline enum_return_check do_check_repository_file(const char *fname)
162 {
163 if (my_access(fname, F_OK))
164 return REPOSITORY_DOES_NOT_EXIST;
165
166 if (my_access(fname, F_OK | R_OK | W_OK))
167 return ERROR_CHECKING_REPOSITORY;
168
169 return REPOSITORY_EXISTS;
170 }
171
172 /*
173 The method verifies existence of an instance of the repository.
174
175 @param instance an index in the repository
176 @retval REPOSITORY_EXISTS when the check is successful
177 @retval REPOSITORY_DOES_NOT_EXIST otherwise
178
179 @note This method also verifies overall integrity
180 of the repositories to make sure they are indexed without any gaps.
181 */
do_check_info(uint instance)182 enum_return_check Rpl_info_file::do_check_info(uint instance)
183 {
184 uint i;
185 enum_return_check last_check= REPOSITORY_EXISTS;
186 char fname_local[FN_REFLEN];
187 char *pos= NULL;
188
189 for (i= 1; i <= instance && last_check == REPOSITORY_EXISTS; i++)
190 {
191 pos= strmov(fname_local, pattern_fname);
192 if (name_indexed)
193 sprintf(pos, "%u", i);
194 fn_format(fname_local, fname_local, mysql_data_home, "", 4 + 32);
195 last_check= do_check_repository_file(fname_local);
196 }
197 return last_check;
198 }
199
do_check_info()200 enum_return_check Rpl_info_file::do_check_info()
201 {
202 return do_check_repository_file(info_fname);
203 }
204
205 /*
206 The function counts number of files in a range starting
207 from one. The range degenerates into one item when @c indexed is false.
208 Scanning ends once the next indexed file is not found.
209
210 @param nparam Number of fields
211 @param param_pattern
212 a string pattern to generate
213 the actual file name
214 @param indexed indicates whether the file is indexed and if so
215 there is a range to count in.
216 @param[out] counter the number of discovered instances before the first
217 unsuccess in locating the next file.
218
219 @retval false All OK
220 @retval true An error
221 */
do_count_info(const int nparam,const char * param_pattern,bool indexed,uint * counter)222 bool Rpl_info_file::do_count_info(const int nparam,
223 const char* param_pattern,
224 bool indexed,
225 uint* counter)
226 {
227 uint i= 0;
228 Rpl_info_file* info= NULL;
229
230 char fname_local[FN_REFLEN];
231 char *pos= NULL;
232 enum_return_check last_check= REPOSITORY_EXISTS;
233
234 DBUG_ENTER("Rpl_info_file::do_count_info");
235
236 if (!(info= new Rpl_info_file(nparam, param_pattern, "", indexed)))
237 DBUG_RETURN(true);
238
239 for (i= 1; last_check == REPOSITORY_EXISTS; i++)
240 {
241 pos= strmov(fname_local, param_pattern);
242 if (indexed)
243 {
244 sprintf(pos, "%u", i);
245 }
246 fn_format(fname_local, fname_local, mysql_data_home, "", 4 + 32);
247 if ((last_check= do_check_repository_file(fname_local)) == REPOSITORY_EXISTS)
248 (*counter)++;
249 // just one loop pass for MI and RLI file
250 if (!indexed)
251 break;
252 }
253 delete info;
254
255 DBUG_RETURN(false);
256 }
257
do_flush_info(const bool force)258 int Rpl_info_file::do_flush_info(const bool force)
259 {
260 int error= 0;
261
262 DBUG_ENTER("Rpl_info_file::do_flush_info");
263
264 if (flush_io_cache(&info_file))
265 error= 1;
266 if (!error && (force ||
267 (sync_period &&
268 ++(sync_counter) >= sync_period)))
269 {
270 if (my_sync(info_fd, MYF(MY_WME)))
271 error= 1;
272 sync_counter= 0;
273 }
274
275 DBUG_RETURN(error);
276 }
277
do_end_info()278 void Rpl_info_file::do_end_info()
279 {
280 DBUG_ENTER("Rpl_info_file::do_end_info");
281
282 if (info_fd >= 0)
283 {
284 if (my_b_inited(&info_file))
285 end_io_cache(&info_file);
286 my_close(info_fd, MYF(MY_WME));
287 info_fd = -1;
288 }
289
290 DBUG_VOID_RETURN;
291 }
292
do_remove_info()293 int Rpl_info_file::do_remove_info()
294 {
295 MY_STAT stat_area;
296 int error= 0;
297
298 DBUG_ENTER("Rpl_info_file::do_remove_info");
299
300 if (my_stat(info_fname, &stat_area, MYF(0)) && my_delete(info_fname, MYF(MY_WME)))
301 error= 1;
302
303 DBUG_RETURN(error);
304 }
305
do_clean_info()306 int Rpl_info_file::do_clean_info()
307 {
308 /*
309 There is nothing to do here. Maybe we can truncate the
310 file in the future. Howerver, for now, there is no need.
311 */
312 return 0;
313 }
314
do_reset_info(const int nparam,const char * param_pattern,bool indexed)315 int Rpl_info_file::do_reset_info(const int nparam,
316 const char* param_pattern,
317 bool indexed)
318 {
319 int error= false;
320 uint i= 0;
321 Rpl_info_file* info= NULL;
322 char fname_local[FN_REFLEN];
323 char *pos= NULL;
324 enum_return_check last_check= REPOSITORY_EXISTS;
325
326 DBUG_ENTER("Rpl_info_file::do_count_info");
327
328 if (!(info= new Rpl_info_file(nparam, param_pattern, "", indexed)))
329 DBUG_RETURN(true);
330
331 for (i= 1; last_check == REPOSITORY_EXISTS; i++)
332 {
333 pos= strmov(fname_local, param_pattern);
334 if (indexed)
335 {
336 sprintf(pos, "%u", i);
337 }
338 fn_format(fname_local, fname_local, mysql_data_home, "", 4 + 32);
339 if ((last_check= do_check_repository_file(fname_local)) == REPOSITORY_EXISTS)
340 if (my_delete(fname_local, MYF(MY_WME)))
341 error= true;
342 // just one loop pass for MI and RLI file
343 if (!indexed)
344 break;
345 }
346 delete info;
347
348 DBUG_RETURN(error);
349 }
350
do_set_info(const int pos,const char * value)351 bool Rpl_info_file::do_set_info(const int pos, const char *value)
352 {
353 return (my_b_printf(&info_file, "%s\n", value) > (size_t) 0 ?
354 FALSE : TRUE);
355 }
356
do_set_info(const int pos,const uchar * value,const size_t size)357 bool Rpl_info_file::do_set_info(const int pos, const uchar *value,
358 const size_t size)
359 {
360 return (my_b_write(&info_file, value, size));
361 }
362
do_set_info(const int pos,const ulong value)363 bool Rpl_info_file::do_set_info(const int pos, const ulong value)
364 {
365 return (my_b_printf(&info_file, "%lu\n", value) > (size_t) 0 ?
366 FALSE : TRUE);
367 }
368
do_set_info(const int pos,const int value)369 bool Rpl_info_file::do_set_info(const int pos, const int value)
370 {
371 return (my_b_printf(&info_file, "%d\n", value) > (size_t) 0 ?
372 FALSE : TRUE);
373 }
374
do_set_info(const int pos,const float value)375 bool Rpl_info_file::do_set_info(const int pos, const float value)
376 {
377 /*
378 64 bytes provide enough space considering that the precision is 3
379 bytes (See the appropriate set funciton):
380
381 FLT_MAX The value of this macro is the maximum number representable
382 in type float. It is supposed to be at least 1E+37.
383 FLT_MIN Similar to the FLT_MAX, we have 1E-37.
384
385 If a file is manually and not properly changed, this function may
386 crash the server.
387 */
388 char buffer[64];
389
390 sprintf(buffer, "%.3f", value);
391
392 return (my_b_printf(&info_file, "%s\n", buffer) > (size_t) 0 ?
393 FALSE : TRUE);
394 }
395
do_set_info(const int pos,const Dynamic_ids * value)396 bool Rpl_info_file::do_set_info(const int pos, const Dynamic_ids *value)
397 {
398 bool error= TRUE;
399 String buffer;
400
401 /*
402 This produces a line listing the total number and all the server_ids.
403 */
404 if (const_cast<Dynamic_ids *>(value)->pack_dynamic_ids(&buffer))
405 goto err;
406
407 error= (my_b_printf(&info_file, "%s\n", buffer.c_ptr_safe()) >
408 (size_t) 0 ? FALSE : TRUE);
409 err:
410 return error;
411 }
412
do_get_info(const int pos,char * value,const size_t size,const char * default_value)413 bool Rpl_info_file::do_get_info(const int pos, char *value, const size_t size,
414 const char *default_value)
415 {
416 return (init_strvar_from_file(value, size, &info_file,
417 default_value));
418 }
419
do_get_info(const int pos,uchar * value,const size_t size,const uchar * default_value)420 bool Rpl_info_file::do_get_info(const int pos, uchar *value, const size_t size,
421 const uchar *default_value)
422 {
423 return(my_b_read(&info_file, value, size));
424 }
425
do_get_info(const int pos,ulong * value,const ulong default_value)426 bool Rpl_info_file::do_get_info(const int pos, ulong *value,
427 const ulong default_value)
428 {
429 return (init_ulongvar_from_file(value, &info_file,
430 default_value));
431 }
432
do_get_info(const int pos,int * value,const int default_value)433 bool Rpl_info_file::do_get_info(const int pos, int *value,
434 const int default_value)
435 {
436 return (init_intvar_from_file((int *) value, &info_file,
437 (int) default_value));
438 }
439
do_get_info(const int pos,float * value,const float default_value)440 bool Rpl_info_file::do_get_info(const int pos, float *value,
441 const float default_value)
442 {
443 return (init_floatvar_from_file(value, &info_file,
444 default_value));
445 }
446
do_get_info(const int pos,Dynamic_ids * value,const Dynamic_ids * default_value MY_ATTRIBUTE ((unused)))447 bool Rpl_info_file::do_get_info(const int pos, Dynamic_ids *value,
448 const Dynamic_ids *default_value MY_ATTRIBUTE((unused)))
449 {
450 /*
451 Static buffer to use most of the times. However, if it is not big
452 enough to accommodate the server ids, a new buffer is allocated.
453 */
454 const int array_size= 16 * (sizeof(long) * 3 + 1);
455 char buffer[array_size];
456 char *buffer_act= buffer;
457
458 bool error= init_dynarray_intvar_from_file(buffer, sizeof(buffer),
459 &buffer_act,
460 &info_file);
461 if (!error)
462 value->unpack_dynamic_ids(buffer_act);
463
464 if (buffer != buffer_act)
465 {
466 /*
467 Release the buffer allocated while reading the server ids
468 from the file.
469 */
470 my_free(buffer_act);
471 }
472
473 return error;
474 }
475
do_get_description_info()476 char* Rpl_info_file::do_get_description_info()
477 {
478 return info_fname;
479 }
480
do_is_transactional()481 bool Rpl_info_file::do_is_transactional()
482 {
483 return FALSE;
484 }
485
do_update_is_transactional()486 bool Rpl_info_file::do_update_is_transactional()
487 {
488 return FALSE;
489 }
490
do_get_rpl_info_type()491 uint Rpl_info_file::do_get_rpl_info_type()
492 {
493 return INFO_REPOSITORY_FILE;
494 }
495
init_strvar_from_file(char * var,int max_size,IO_CACHE * f,const char * default_val)496 int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
497 const char *default_val)
498 {
499 uint length;
500 DBUG_ENTER("init_strvar_from_file");
501
502 if ((length=my_b_gets(f,var, max_size)))
503 {
504 char* last_p = var + length -1;
505 if (*last_p == '\n')
506 *last_p = 0; // if we stopped on newline, kill it
507 else
508 {
509 /*
510 If we truncated a line or stopped on last char, remove all chars
511 up to and including newline.
512 */
513 int c;
514 while (((c=my_b_get(f)) != '\n' && c != my_b_EOF)) ;
515 }
516 DBUG_RETURN(0);
517 }
518 else if (default_val)
519 {
520 strmake(var, default_val, max_size-1);
521 DBUG_RETURN(0);
522 }
523 DBUG_RETURN(1);
524 }
525
init_intvar_from_file(int * var,IO_CACHE * f,int default_val)526 int init_intvar_from_file(int* var, IO_CACHE* f, int default_val)
527 {
528 /*
529 32 bytes provide enough space:
530
531 INT_MIN –2,147,483,648
532 INT_MAX +2,147,483,647
533 */
534 char buf[32];
535 DBUG_ENTER("init_intvar_from_file");
536
537 if (my_b_gets(f, buf, sizeof(buf)))
538 {
539 *var = atoi(buf);
540 DBUG_RETURN(0);
541 }
542 else if (default_val)
543 {
544 *var = default_val;
545 DBUG_RETURN(0);
546 }
547 DBUG_RETURN(1);
548 }
549
init_ulongvar_from_file(ulong * var,IO_CACHE * f,ulong default_val)550 int init_ulongvar_from_file(ulong* var, IO_CACHE* f, ulong default_val)
551 {
552 /*
553 32 bytes provide enough space:
554
555 ULONG_MAX 32 bit compiler +4,294,967,295
556 64 bit compiler +18,446,744,073,709,551,615
557 */
558 char buf[32];
559 DBUG_ENTER("init_ulongvar_from_file");
560
561 if (my_b_gets(f, buf, sizeof(buf)))
562 {
563 *var = strtoul(buf, 0, 10);
564 DBUG_RETURN(0);
565 }
566 else if (default_val)
567 {
568 *var = default_val;
569 DBUG_RETURN(0);
570 }
571 DBUG_RETURN(1);
572 }
573
init_floatvar_from_file(float * var,IO_CACHE * f,float default_val)574 int init_floatvar_from_file(float* var, IO_CACHE* f, float default_val)
575 {
576 /*
577 64 bytes provide enough space considering that the precision is 3
578 bytes (See the appropriate set funciton):
579
580 FLT_MAX The value of this macro is the maximum number representable
581 in type float. It is supposed to be at least 1E+37.
582 FLT_MIN Similar to the FLT_MAX, we have 1E-37.
583
584 If a file is manually and not properly changed, this function may
585 crash the server.
586 */
587 char buf[64];
588 DBUG_ENTER("init_floatvar_from_file");
589
590 if (my_b_gets(f, buf, sizeof(buf)))
591 {
592 if (sscanf(buf, "%f", var) != 1)
593 DBUG_RETURN(1);
594 else
595 DBUG_RETURN(0);
596 }
597 else if (default_val != 0.0)
598 {
599 *var = default_val;
600 DBUG_RETURN(0);
601 }
602 DBUG_RETURN(1);
603 }
604
605 /**
606 TODO - Improve this function to use String and avoid this weird computation
607 to calculate the size of the buffers.
608
609 Particularly, this function is responsible for restoring IGNORE_SERVER_IDS
610 list of servers whose events the slave is going to ignore (to not log them
611 in the relay log).
612
613 Items being read are supposed to be decimal output of values of a type
614 shorter or equal of @c long and separated by the single space.
615
616 @param buffer Put the read values in this static buffer
617 @param buffer Size of the static buffer
618 @param buffer_act Points to the final buffer as dynamic buffer may
619 be used if the static buffer is not big enough.
620
621 @retval 0 All OK
622 @retval non-zero An error
623 */
init_dynarray_intvar_from_file(char * buffer,size_t size,char ** buffer_act,IO_CACHE * f)624 bool init_dynarray_intvar_from_file(char *buffer, size_t size,
625 char **buffer_act, IO_CACHE* f)
626 {
627 char *buf= buffer; // actual buffer can be dynamic if static is short
628 char *buf_act= buffer;
629 char *last;
630 uint num_items; // number of items of `arr'
631 size_t read_size;
632
633 DBUG_ENTER("init_dynarray_intvar_from_file");
634
635 if ((read_size= my_b_gets(f, buf_act, size)) == 0)
636 {
637 DBUG_RETURN(FALSE); // no line in master.info
638 }
639 if (read_size + 1 == size && buf[size - 2] != '\n')
640 {
641 /*
642 short read happend; allocate sufficient memory and make the 2nd read
643 */
644 char buf_work[(sizeof(long) * 3 + 1) * 16];
645 memcpy(buf_work, buf, sizeof(buf_work));
646 num_items= atoi(strtok_r(buf_work, " ", &last));
647 size_t snd_size;
648 /*
649 max size upper bound approximate estimation bases on the formula:
650 (the items number + items themselves) *
651 (decimal size + space) - 1 + `\n' + '\0'
652 */
653 size_t max_size= (1 + num_items) * (sizeof(long) * 3 + 1) + 1;
654 if (! (buf_act= (char*) my_malloc(max_size, MYF(MY_WME))))
655 DBUG_RETURN(TRUE);
656 *buffer_act= buf_act;
657 memcpy(buf_act, buf, read_size);
658 snd_size= my_b_gets(f, buf_act + read_size, max_size - read_size);
659 if (snd_size == 0 ||
660 ((snd_size + 1 == max_size - read_size) && buf[max_size - 2] != '\n'))
661 {
662 /*
663 failure to make the 2nd read or short read again
664 */
665 DBUG_RETURN(TRUE);
666 }
667 }
668 DBUG_RETURN(FALSE);
669 }
670