1 /* Copyright (C) 2007 MySQL AB
2    Copyright (C) 2010 Monty Program Ab
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 Street, Fifth Floor, Boston, MA 02110-1335 USA */
16 
17 #include "maria_def.h"
18 #include "ma_recovery.h"
19 #include <my_getopt.h>
20 
21 #define LOG_FLAGS 0
22 
23 static const char *load_default_groups[]= { "aria_read_log",0 };
24 static void get_options(int *argc,char * * *argv);
25 #ifndef DBUG_OFF
26 #if defined(__WIN__)
27 const char *default_dbug_option= "d:t:O,\\aria_read_log.trace";
28 #else
29 const char *default_dbug_option= "d:t:o,/tmp/aria_read_log.trace";
30 #endif
31 #endif /* DBUG_OFF */
32 static my_bool opt_display_only, opt_apply, opt_apply_undo, opt_silent;
33 static my_bool opt_check;
34 static my_bool opt_print_aria_log_control;
35 static const char *opt_tmpdir;
36 static ulong opt_translog_buffer_size;
37 static ulonglong opt_page_buffer_size;
38 static ulonglong opt_start_from_lsn, opt_end_lsn, opt_start_from_checkpoint;
39 static MY_TMPDIR maria_chk_tmpdir;
40 
41 
main(int argc,char ** argv)42 int main(int argc, char **argv)
43 {
44   LSN lsn;
45   char **default_argv;
46   uint warnings_count;
47   MY_INIT(argv[0]);
48 
49   maria_data_root= (char *)".";
50   sf_leaking_memory=1; /* don't report memory leaks on early exits */
51   load_defaults_or_exit("my", load_default_groups, &argc, &argv);
52   default_argv= argv;
53   get_options(&argc, &argv);
54 
55   maria_in_recovery= TRUE;
56 
57   if (maria_init())
58   {
59     fprintf(stderr, "Can't init Aria engine (%d)\n", errno);
60     goto err;
61   }
62   maria_block_size= 0;                          /* Use block size from file */
63   if (opt_print_aria_log_control)
64   {
65     if (print_aria_log_control())
66       goto err;
67     goto end;
68   }
69   /* we don't want to create a control file, it MUST exist */
70   if (ma_control_file_open(FALSE, TRUE))
71   {
72     fprintf(stderr, "Can't open control file (%d)\n", errno);
73     goto err;
74   }
75   if (last_logno == FILENO_IMPOSSIBLE)
76   {
77     fprintf(stderr, "Can't find any log\n");
78     goto err;
79   }
80   if (init_pagecache(maria_pagecache, (size_t)opt_page_buffer_size, 0, 0,
81                      maria_block_size, 0, MY_WME) == 0)
82   {
83     fprintf(stderr, "Got error in init_pagecache() (errno: %d)\n", errno);
84     goto err;
85   }
86   /*
87     If log handler does not find the "last_logno" log it will return error,
88     which is good.
89     But if it finds a log and this log was crashed, it will create a new log,
90     which is useless. TODO: start log handler in read-only mode.
91   */
92   if (init_pagecache(maria_log_pagecache, opt_translog_buffer_size,
93                      0, 0, TRANSLOG_PAGE_SIZE, 0, MY_WME) == 0 ||
94       translog_init(maria_data_root, TRANSLOG_FILE_SIZE,
95                     0, 0, maria_log_pagecache, TRANSLOG_DEFAULT_FLAGS,
96                     opt_display_only))
97   {
98     fprintf(stderr, "Can't init loghandler (%d)\n", errno);
99     goto err;
100   }
101 
102   if (opt_display_only)
103     printf("You are using --display-only, NOTHING will be written to disk\n");
104 
105   lsn= translog_first_lsn_in_log();
106   if (lsn == LSN_ERROR)
107   {
108     fprintf(stderr, "Opening transaction log failed\n");
109     goto end;
110   }
111   if (lsn == LSN_IMPOSSIBLE)
112   {
113      fprintf(stdout, "The transaction log is empty\n");
114   }
115   if (opt_start_from_checkpoint && !opt_start_from_lsn &&
116       last_checkpoint_lsn != LSN_IMPOSSIBLE)
117   {
118     lsn= LSN_IMPOSSIBLE;             /* LSN set in maria_apply_log() */
119     fprintf(stdout, "Starting from checkpoint " LSN_FMT "\n",
120             LSN_IN_PARTS(last_checkpoint_lsn));
121   }
122   else
123     fprintf(stdout, "The transaction log starts from lsn " LSN_FMT "\n",
124             LSN_IN_PARTS(lsn));
125 
126   if (opt_start_from_lsn)
127   {
128     if (opt_start_from_lsn < (ulonglong) lsn)
129     {
130       fprintf(stderr, "start_from_lsn is too small. Aborting\n");
131       maria_end();
132       goto err;
133     }
134     lsn= (LSN) opt_start_from_lsn;
135     fprintf(stdout, "Starting reading log from lsn " LSN_FMT "\n",
136             LSN_IN_PARTS(lsn));
137   }
138 
139   if (opt_end_lsn != LSN_IMPOSSIBLE)
140   {
141     /* We can't apply undo if we use end_lsn */
142     opt_apply_undo= 0;
143   }
144 
145   fprintf(stdout, "TRACE of the last aria_read_log\n");
146   if (maria_apply_log(lsn, opt_end_lsn, opt_apply ?  MARIA_LOG_APPLY :
147                       (opt_check ? MARIA_LOG_CHECK :
148                        MARIA_LOG_DISPLAY_HEADER), opt_silent ? NULL : stdout,
149                       opt_apply_undo, FALSE, FALSE, &warnings_count))
150     goto err;
151   if (warnings_count == 0)
152     fprintf(stdout, "%s: SUCCESS\n", my_progname_short);
153   else
154     fprintf(stdout, "%s: DOUBTFUL (%u warnings, check previous output)\n",
155             my_progname_short, warnings_count);
156 
157 end:
158   maria_end();
159   free_tmpdir(&maria_chk_tmpdir);
160   free_defaults(default_argv);
161   my_end(0);
162   sf_leaking_memory=0;
163   exit(0);
164   return 0;				/* No compiler warning */
165 
166 err:
167   /* don't touch anything more, in case we hit a bug */
168   fprintf(stderr, "%s: FAILED\n", my_progname_short);
169   free_tmpdir(&maria_chk_tmpdir);
170   free_defaults(default_argv);
171   exit(1);
172 }
173 
174 
175 #include "ma_check_standalone.h"
176 
177 enum options_mc {
178   OPT_CHARSETS_DIR=256, OPT_FORCE_CRASH, OPT_TRANSLOG_BUFFER_SIZE
179 };
180 
181 static struct my_option my_long_options[] =
182 {
183   {"apply", 'a',
184    "Apply log to tables: modifies tables! you should make a backup first! "
185    " Displays a lot of information if not run with --silent",
186    (uchar **) &opt_apply, (uchar **) &opt_apply, 0,
187    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
188   {"character-sets-dir", OPT_CHARSETS_DIR,
189    "Directory where character sets are.",
190    (char**) &charsets_dir, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
191   {"check", 'c',
192    "if --display-only, check if record is fully readable (for debugging)",
193    (uchar **) &opt_check, (uchar **) &opt_check, 0,
194    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
195 #ifndef DBUG_OFF
196   {"debug", '#', "Output debug log. Often the argument is 'd:t:o,filename'.",
197    0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
198   {"force-crash", OPT_FORCE_CRASH, "Force crash after # recovery events",
199    &maria_recovery_force_crash_counter, 0,0, GET_ULONG, REQUIRED_ARG,
200    0, 0, ~(long) 0, 0, 0, 0},
201 #endif
202   {"help", '?', "Display this help and exit.",
203    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
204   {"display-only", 'd', "display brief info read from records' header",
205    &opt_display_only, &opt_display_only, 0, GET_BOOL,
206    NO_ARG,0, 0, 0, 0, 0, 0},
207   { "end-lsn", 'e', "Stop applying at this lsn. If end-lsn is used, UNDO:s "
208     "will not be applied", &opt_end_lsn, &opt_end_lsn,
209     0, GET_ULL, REQUIRED_ARG, 0, 0, ~(longlong) 0, 0, 0, 0 },
210   {"aria-log-dir-path", 'h',
211     "Path to the directory where to store transactional log",
212     (uchar **) &maria_data_root, (uchar **) &maria_data_root, 0,
213     GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
214   { "page-buffer-size", 'P',
215     "The size of the buffer used for index blocks for Aria tables",
216     &opt_page_buffer_size, &opt_page_buffer_size, 0,
217     GET_ULL, REQUIRED_ARG, PAGE_BUFFER_INIT,
218     PAGE_BUFFER_INIT, SIZE_T_MAX, MALLOC_OVERHEAD, (long) IO_SIZE, 0},
219   { "print-log-control-file", 'l',
220     "Print the content of the aria_log_control_file",
221     &opt_print_aria_log_control, &opt_print_aria_log_control, 0,
222     GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
223   { "start-from-lsn", 'o', "Start reading log from this lsn",
224     &opt_start_from_lsn, &opt_start_from_lsn,
225     0, GET_ULL, REQUIRED_ARG, 0, 0, ~(longlong) 0, 0, 0, 0 },
226   {"start-from-checkpoint", 'C', "Start applying from last checkpoint",
227    &opt_start_from_checkpoint, &opt_start_from_checkpoint, 0,
228    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
229   {"silent", 's', "Print less information during apply/undo phase",
230    &opt_silent, &opt_silent, 0,
231    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
232   {"tables-to-redo", 'T',
233    "List of tables sepearated with , that we should apply REDO on. Use this if you only want to recover some tables",
234    0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
235   {"tmpdir", 't', "Path for temporary files. Multiple paths can be specified, "
236    "separated by "
237 #if defined( __WIN__) || defined(__NETWARE__)
238    "semicolon (;)"
239 #else
240    "colon (:)"
241 #endif
242    , (char**) &opt_tmpdir, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
243   { "translog-buffer-size", OPT_TRANSLOG_BUFFER_SIZE,
244     "The size of the buffer used for transaction log for Aria tables",
245     &opt_translog_buffer_size, &opt_translog_buffer_size, 0,
246     GET_ULONG, REQUIRED_ARG, (long) TRANSLOG_PAGECACHE_SIZE,
247     1024L*1024L, (long) ~(ulong) 0, (long) MALLOC_OVERHEAD,
248     (long) IO_SIZE, 0},
249   {"undo", 'u', "Apply UNDO records to tables. (disable with --disable-undo)",
250    (uchar **) &opt_apply_undo, (uchar **) &opt_apply_undo, 0,
251    GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
252   {"verbose", 'v', "Print more information during apply/undo phase",
253    &maria_recovery_verbose, &maria_recovery_verbose, 0,
254    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
255   {"version", 'V', "Print version and exit.",
256    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
257   { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
258 };
259 
260 
print_version(void)261 static void print_version(void)
262 {
263   printf("%s Ver 1.4 for %s on %s\n",
264               my_progname_short, SYSTEM_TYPE, MACHINE_TYPE);
265 }
266 
267 
usage(void)268 static void usage(void)
269 {
270   print_version();
271   puts("Copyright (C) 2007 MySQL AB, 2009-2011 Monty Program Ab");
272   puts("This software comes with ABSOLUTELY NO WARRANTY. This is free software,");
273   puts("and you are welcome to modify and redistribute it under the GPL license\n");
274 
275   puts("Display or apply log records from a Aria transaction log");
276   puts("found in the current directory (for now)");
277 #ifndef IDENTICAL_PAGES_AFTER_RECOVERY
278   puts("\nNote: Aria is compiled without -DIDENTICAL_PAGES_AFTER_RECOVERY\n"
279        "which means that the table files are not byte-to-byte identical to\n"
280        "files created during normal execution. This should be ok, except for\n"
281        "test scripts that tries to compare files before and after recovery.");
282 #endif
283   printf("\nUsage: %s OPTIONS [-d | -a] -h `aria_log_directory`\n",
284          my_progname_short);
285   printf("or\n");
286   printf("Usage: %s OPTIONS -h `aria_log_directory` "
287          "--print-aria-log-control\n\n",
288          my_progname_short);
289 
290   my_print_help(my_long_options);
291   print_defaults("my", load_default_groups);
292   my_print_variables(my_long_options);
293 }
294 
295 
my_hash_get_string(const uchar * record,size_t * length,my_bool first)296 static uchar* my_hash_get_string(const uchar *record, size_t *length,
297                                 my_bool first __attribute__ ((unused)))
298 {
299   *length= (size_t) (strcend((const char*) record,',')- (const char*) record);
300   return (uchar*) record;
301 }
302 
303 
304 static my_bool
get_one_option(int optid,const struct my_option * opt,char * argument)305 get_one_option(int optid __attribute__((unused)),
306                const struct my_option *opt __attribute__((unused)),
307                char *argument)
308 {
309   switch (optid) {
310   case '?':
311     usage();
312     exit(0);
313   case 'V':
314     print_version();
315     exit(0);
316   case 'T':
317   {
318     char *pos;
319     if (!my_hash_inited(&tables_to_redo))
320     {
321       my_hash_init2(&tables_to_redo, 16, &my_charset_bin,
322                     16, 0, 0, my_hash_get_string, 0, 0, HASH_UNIQUE);
323     }
324     do
325     {
326       pos= strcend(argument, ',');
327       if (pos != argument)                      /* Skip empty strings */
328         my_hash_insert(&tables_to_redo, (uchar*) argument);
329       argument= pos+1;
330     } while (*(pos++));
331     break;
332   }
333 #ifndef DBUG_OFF
334   case '#':
335     DBUG_SET_INITIAL(argument ? argument : default_dbug_option);
336     break;
337 #endif
338   }
339   return 0;
340 }
341 
get_options(int * argc,char *** argv)342 static void get_options(int *argc,char ***argv)
343 {
344   int ho_error;
345   my_bool need_help= 0;
346 
347   if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
348     exit(ho_error);
349 
350   if (!opt_apply)
351     opt_apply_undo= FALSE;
352 
353   if (*argc > 0)
354   {
355     need_help= 1;
356     fprintf(stderr, "Too many arguments given\n");
357   }
358   if ((opt_display_only + opt_apply + opt_print_aria_log_control) != 1)
359   {
360     need_help= 1;
361     fprintf(stderr,
362             "You must use one and only one of the options 'display-only', \n"
363             "'print-log-control-file' and 'apply'\n");
364   }
365 
366   if (need_help)
367   {
368     fflush(stderr);
369     need_help =1;
370     usage();
371     exit(1);
372   }
373   if (init_tmpdir(&maria_chk_tmpdir, opt_tmpdir))
374     exit(1);
375   maria_tmpdir= &maria_chk_tmpdir;
376 }
377 
378