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