~skinny.moey/drizzle/bug755201

« back to all changes in this revision

Viewing changes to plugin/innobase/xtrabackup/innobackupex

  • Committer: Lee Bieber
  • Date: 2011-03-31 14:40:56 UTC
  • mfrom: (2242.1.133 drizzle-xtrabackup)
  • Revision ID: kalebral@gmail.com-20110331144056-hdhp6cj0zo1keuc9
Merge Stewart - xtrabackup port to drizzle

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/perl -w
 
2
#
 
3
# A script for making backups of InnoDB and MyISAM tables, indexes and .frm
 
4
# files.
 
5
#
 
6
# Copyright 2003, 2009 Innobase Oy and Percona Inc 2009-2011. All Rights Reserved.
 
7
#
 
8
 
 
9
use strict;
 
10
use Getopt::Long;
 
11
use File::Spec;
 
12
use Pod::Usage qw(pod2usage);
 
13
use POSIX "strftime";
 
14
use POSIX ":sys_wait_h";
 
15
use POSIX "tmpnam";
 
16
use FileHandle;
 
17
use File::Basename;
 
18
use English qw(-no_match_vars);
 
19
 
 
20
# version of this script
 
21
my $innobackup_version = '1.5.1-xtrabackup';
 
22
my $innobackup_script = basename($0);
 
23
 
 
24
# copyright notice
 
25
my $copyright_notice = 
 
26
"InnoDB Backup Utility v${innobackup_version}; Copyright 2003, 2009 Innobase Oy
 
27
and Percona Inc 2009-2011.  All Rights Reserved.
 
28
 
 
29
This software is published under
 
30
the GNU GENERAL PUBLIC LICENSE Version 2, June 1991.
 
31
 
 
32
";
 
33
 
 
34
# required Perl version (5.005)
 
35
my @required_perl_version = (5, 0, 5);
 
36
my $required_perl_version_old_style = 5.005;
 
37
 
 
38
# force flush after every write and print
 
39
$| = 1;
 
40
 
 
41
######################################################################
 
42
# modifiable parameters
 
43
######################################################################
 
44
 
 
45
# maximum number of files in a database directory which are
 
46
# separately printed when a backup is made
 
47
my $backup_file_print_limit = 9;
 
48
 
 
49
# timeout in seconds for a reply from mysql
 
50
my $mysql_response_timeout = 900;
 
51
 
 
52
# default compression level (this is an argument to ibbackup)
 
53
my $default_compression_level = 1;
 
54
 
 
55
# time in seconds after which a dummy query is sent to mysql server
 
56
# in order to keep the database connection alive
 
57
my $mysql_keep_alive_timeout = 1800;
 
58
 
 
59
######################################################################
 
60
# end of modifiable parameters
 
61
######################################################################
 
62
 
 
63
 
 
64
# command line options
 
65
my $option_help = '';
 
66
my $option_version = '';
 
67
my $option_apply_log = '';
 
68
my $option_redo_only = '';
 
69
my $option_copy_back = '';
 
70
my $option_include = '';
 
71
my $option_databases = '';
 
72
my $option_tables_file = '';
 
73
my $option_throttle = '';
 
74
my $option_sleep = '';
 
75
my $option_compress = 999;
 
76
my $option_uncompress = '';
 
77
my $option_export = '';
 
78
my $option_use_memory = '';
 
79
my $option_mysql_password = '';
 
80
my $option_mysql_user = '';
 
81
my $option_mysql_port = '';
 
82
my $option_mysql_socket = '';
 
83
my $option_mysql_host = '';
 
84
my $option_no_timestamp = '';
 
85
my $option_slave_info = '';
 
86
my $option_no_lock = '';
 
87
my $option_ibbackup_binary = 'autodetect';
 
88
 
 
89
my $option_defaults_file = '';
 
90
my $option_incremental = '';
 
91
my $option_incremental_basedir = '';
 
92
my $option_incremental_dir = '';
 
93
my $option_incremental_lsn = '';
 
94
my $option_extra_lsndir = '';
 
95
my $option_remote_host = '';
 
96
my $option_stream = '';
 
97
my $option_tmpdir = '';
 
98
 
 
99
my $option_tar4ibd = '';
 
100
my $option_force_tar = '';
 
101
my $option_scp_opt = '-Cp -c arcfour';
 
102
 
 
103
my $option_parallel = '';
 
104
 
 
105
my $option_safe_slave_backup = '';
 
106
my $option_safe_slave_backup_timeout = 300;
 
107
 
 
108
# name of the my.cnf configuration file
 
109
#my $config_file = '';
 
110
 
 
111
# root of the backup directory
 
112
my $backup_root = '';
 
113
 
 
114
# backup directory pathname
 
115
my $backup_dir = '';
 
116
 
 
117
# name of the ibbackup suspend-at-end file
 
118
my $suspend_file = '';
 
119
 
 
120
# name of the temporary transaction log file during the backup
 
121
my $tmp_logfile = '';
 
122
 
 
123
# home directory of innoDB log files
 
124
my $innodb_log_group_home_dir = '';
 
125
 
 
126
# backup my.cnf file
 
127
my $backup_config_file = '';
 
128
 
 
129
# options from the options file
 
130
my %config;
 
131
 
 
132
# options from the backup options file
 
133
#my %backup_config;
 
134
 
 
135
# list of databases to be included in a backup
 
136
my %databases_list;
 
137
 
 
138
# list of tables to be included in a backup
 
139
my %table_list;
 
140
 
 
141
# prefix for output lines
 
142
my $prefix = "$innobackup_script:";
 
143
 
 
144
# process id of mysql client program (runs as a child process of this script)
 
145
my $mysql_pid = '';
 
146
 
 
147
# mysql server version string
 
148
my $mysql_server_version = '';
 
149
 
 
150
# name of the file where stderr of mysql process is directed
 
151
my $mysql_stderr = 'stderr';
 
152
 
 
153
# name of the file where stdout of mysql process is directed
 
154
my $mysql_stdout = 'stdout';
 
155
 
 
156
# name of the file where binlog position info is written
 
157
my $binlog_info;
 
158
 
 
159
# name of the file where slave info is written
 
160
my $slave_info;
 
161
 
 
162
# mysql binlog position as given by "SHOW MASTER STATUS" command
 
163
my $mysql_binlog_position = '';
 
164
 
 
165
# mysql master's binlog position as given by "SHOW SLAVE STATUS" command
 
166
# run on a slave server
 
167
my $mysql_slave_position = '';
 
168
 
 
169
# time of the most recent mysql_check call. (value returned by time() function)
 
170
my $mysql_last_access_time = 0;
 
171
 
 
172
# process id of ibbackup program (runs as a child process of this script)
 
173
my $ibbackup_pid = '';
 
174
 
 
175
# a counter for numbering mysql connection checks
 
176
my $hello_id = 0;
 
177
 
 
178
# the request which has been sent to mysqld, but to which
 
179
# mysqld has not yet replied. Empty string denotes that no
 
180
# request has been sent to mysqld or that mysqld has replied
 
181
# to all requests.
 
182
my $current_mysql_request = '';
 
183
 
 
184
# escape sequences for options files
 
185
my %option_value_escapes = ('b' => "\b",
 
186
                            't' => "\t",
 
187
                            'n' => "\n",
 
188
                            'r' => "\r",
 
189
                            "\\" => "\\",
 
190
                            's' => ' ');
 
191
 
 
192
# signal that is sent to child processes when they are killed
 
193
my $kill_signal = 15;
 
194
 
 
195
# current local time
 
196
my $now;
 
197
 
 
198
# incremental backup base directory
 
199
my $incremental_basedir = '';
 
200
 
 
201
my $src_name;
 
202
my $dst_name;
 
203
my $win = ($^O eq 'MSWin32' ? 1 : 0);
 
204
my $CP_CMD = ($win eq 1 ? "copy /Y" : "cp -p");
 
205
my $xtrabackup_binary_file = 'xtrabackup_binary';
 
206
######################################################################
 
207
# program execution begins here
 
208
######################################################################
 
209
 
 
210
# check command-line args
 
211
check_args();
 
212
 
 
213
# print program version and copyright
 
214
print_version();
 
215
 
 
216
# initialize global variables and perform some checks
 
217
if ($option_copy_back) {
 
218
    $option_ibbackup_binary = 'xtrabackup_51';
 
219
} elsif ($option_apply_log) {
 
220
        # Read XtraBackup version from backup dir
 
221
        if (-e "$backup_dir/$xtrabackup_binary_file") {
 
222
                # Read XtraBackup version from file
 
223
                open XTRABACKUP_BINARY, "$backup_dir/$xtrabackup_binary_file"
 
224
                        or die "Cannot open file $backup_dir/$xtrabackup_binary_file: $!\n";
 
225
                $option_ibbackup_binary = <XTRABACKUP_BINARY>;
 
226
                close XTRABACKUP_BINARY;
 
227
                }
 
228
 
 
229
        else {
 
230
                if( $option_ibbackup_binary eq "autodetect" ){
 
231
                        # Try to connect MySQL and get the version
 
232
                        print "option_ibbackup_binary is autodetect, trying to connect to MySQL\n";
 
233
                        my $options = get_mysql_options();
 
234
                        $mysql_pid = open(*MYSQL_WRITER, "| mysql $options >$mysql_stdout 2>$mysql_stderr ");
 
235
                        print "Connected to MySQL with pid $mysql_pid\n";
 
236
                        sleep 1;
 
237
                        if ($mysql_pid && $mysql_pid == waitpid($mysql_pid, &WNOHANG)) {
 
238
                                my $reason = `cat $mysql_stderr`;
 
239
                                $mysql_pid = '';
 
240
                                # Failed to connect to MySQL
 
241
                                die "Failed to connect to MySQL server to detect version.\nYou must set xtrabackup version to use with --ibbackup option.\nPossible values are xtrabackup_51 (for MySQL 5.0 and 5.1) or xtrabackup (for MySQL 5.1 with InnoDB plugin or Percona Server)\n";
 
242
                                }
 
243
                        else{
 
244
                                mysql_close();
 
245
                                print "Connected successfully\n";
 
246
                                $option_ibbackup_binary = set_xtrabackup_version();
 
247
                                }
 
248
                        }
 
249
                }
 
250
} else {
 
251
    $option_ibbackup_binary = set_xtrabackup_version();
 
252
}
 
253
init();
 
254
 
 
255
my $ibbackup_exit_code = 0;
 
256
 
 
257
if ($option_copy_back) {
 
258
    # copy files from backup directory back to their original locations
 
259
    copy_back();
 
260
} elsif ($option_apply_log) {
 
261
    # expand data files in backup directory by applying log files to them
 
262
    apply_log();
 
263
} else {
 
264
    # make a backup of InnoDB and MyISAM tables, indexes and .frm files.
 
265
    $ibbackup_exit_code = backup();
 
266
    open XTRABACKUP_BINARY, "> $backup_dir/$xtrabackup_binary_file"
 
267
        or die "Cannot open file $backup_dir/$xtrabackup_binary_file: $!\n";
 
268
    print XTRABACKUP_BINARY $option_ibbackup_binary;
 
269
    close XTRABACKUP_BINARY;
 
270
}
 
271
 
 
272
$now = current_time();
 
273
 
 
274
if ($option_stream eq 'tar') {
 
275
   print STDERR "$prefix You must use -i (--ignore-zeros) option for extraction of the tar stream.\n";
 
276
}
 
277
 
 
278
if ( $ibbackup_exit_code == 0 ) {
 
279
   # program has completed successfully
 
280
   print STDERR "$now  $prefix completed OK!\n";
 
281
}
 
282
else {
 
283
   print STDERR "$now  $prefix $option_ibbackup_binary failed! (exit code $ibbackup_exit_code)  The backup may not be complete.\n";
 
284
}
 
285
 
 
286
exit $ibbackup_exit_code;
 
287
 
 
288
######################################################################
 
289
# end of program execution
 
290
######################################################################
 
291
 
 
292
 
 
293
#
 
294
# print_version subroutine prints program version and copyright.
 
295
#
 
296
sub print_version {
 
297
    printf(STDERR $copyright_notice);
 
298
}
 
299
 
 
300
 
 
301
#
 
302
# usage subroutine prints instructions of how to use this program to stdout.
 
303
#
 
304
sub usage {
 
305
   my $msg = shift || '';
 
306
   pod2usage({ -msg => $msg, -verbose => 1});
 
307
   return 0;
 
308
}
 
309
 
 
310
 
 
311
#
 
312
# return current local time as string in form "070816 12:23:15"
 
313
#
 
314
sub current_time {
 
315
    return strftime("%y%m%d %H:%M:%S", localtime());
 
316
}
 
317
 
 
318
 
 
319
#
 
320
# Die subroutine kills all child processes and exits this process.
 
321
# This subroutine takes the same argument as the built-in die function.
 
322
#    Parameters:
 
323
#       message   string which is printed to stdout
 
324
#
 
325
sub Die {
 
326
    my $message = shift;
 
327
    my $extra_info = '';
 
328
 
 
329
    # kill all child processes of this process
 
330
    kill_child_processes();
 
331
 
 
332
    if ($current_mysql_request) {
 
333
        $extra_info = " while waiting for reply to MySQL request:" .
 
334
            " '$current_mysql_request'";
 
335
    }
 
336
    die "$prefix Error: $message$extra_info";
 
337
}
 
338
    
 
339
 
 
340
#
 
341
# backup subroutine makes a backup of InnoDB and MyISAM tables, indexes and 
 
342
# .frm files. It connects to the database server and runs ibbackup as a child
 
343
# process.
 
344
#
 
345
sub backup {
 
346
    my $orig_datadir = get_option(\%config, 'mysqld', 'datadir');
 
347
 
 
348
    # check that we can connect to the database. This done by
 
349
    # connecting, issuing a query, and closing the connection.
 
350
    mysql_open();
 
351
    mysql_check();
 
352
    mysql_close();
 
353
 
 
354
    # start ibbackup as a child process
 
355
    start_ibbackup();
 
356
 
 
357
    # wait for ibbackup to suspend itself
 
358
    if (!$option_remote_host && !$option_stream) {
 
359
        wait_for_ibbackup_suspend();
 
360
    }
 
361
 
 
362
    if (!$option_incremental) {
 
363
        # connect to database
 
364
        mysql_open();
 
365
 
 
366
        if ( $option_safe_slave_backup ) {
 
367
          wait_for_safe_slave();
 
368
        }
 
369
 
 
370
        # flush tables with read lock
 
371
        mysql_check();
 
372
        mysql_lockall() if !$option_no_lock;
 
373
 
 
374
    }
 
375
 
 
376
    # backup .frm, .MRG, .MYD, .MYI, .TRG, .TRN, .ARM, .ARZ, .CSM, .CSV and .opt files
 
377
    backup_files();
 
378
 
 
379
    # resume ibbackup and wait till it has finished
 
380
    my $ibbackup_exit_code = resume_ibbackup();
 
381
 
 
382
    if (!$option_incremental) {
 
383
        # release read locks on all tables
 
384
        mysql_unlockall() if !$option_no_lock;
 
385
 
 
386
       if ( $option_safe_slave_backup ) {
 
387
         print STDERR "$prefix: Starting slave SQL thread\n";
 
388
         mysql_send('START SLAVE SQL_THREAD;');
 
389
       }
 
390
 
 
391
        # disconnect from database
 
392
        mysql_close();
 
393
    }
 
394
 
 
395
    if ($option_remote_host) {
 
396
        system("scp $option_scp_opt '$tmp_logfile' '$option_remote_host:$backup_dir/xtrabackup_logfile'")
 
397
            and Die "Failed to scp file '$option_remote_host:$backup_dir/xtrabackup_logfile': $!";
 
398
        unlink $tmp_logfile || Die "Failed to delete '$tmp_logfile': $!";
 
399
 
 
400
        system("scp $option_scp_opt '$orig_datadir/xtrabackup_checkpoints' '$option_remote_host:$backup_dir/xtrabackup_checkpoints'")
 
401
            and Die "Failed to scp file '$option_remote_host:$backup_dir/xtrabackup_checkpoints': $!";
 
402
        unlink "$orig_datadir/xtrabackup_checkpoints" || Die "Failed to delete '$orig_datadir/xtrabackup_checkpoints': $!";
 
403
    } elsif ($option_stream eq 'tar') {
 
404
        system("cd $option_tmpdir; tar chf - xtrabackup_logfile")
 
405
            and Die "Failed to stream 'xtrabackup_logfile': $!";
 
406
        unlink $tmp_logfile || Die "Failed to delete '$tmp_logfile': $!";
 
407
 
 
408
        system("cd $orig_datadir; tar chf - xtrabackup_checkpoints")
 
409
            and Die "Failed to stream 'xtrabackup_checkpoints': $!";
 
410
        unlink "$orig_datadir/xtrabackup_checkpoints" || Die "Failed to delete '$orig_datadir/xtrabackup_checkpoints': $!";
 
411
    }
 
412
 
 
413
    print STDERR "\n$prefix Backup created in directory '$backup_dir'\n";
 
414
    if ($mysql_binlog_position) {
 
415
        print STDERR "$prefix MySQL binlog position: $mysql_binlog_position\n";
 
416
    }
 
417
    if ($mysql_slave_position && $option_slave_info) {
 
418
        print STDERR "$prefix MySQL slave binlog position: $mysql_slave_position\n";
 
419
    }
 
420
 
 
421
    return $ibbackup_exit_code;
 
422
}
 
423
 
 
424
 
 
425
#
 
426
# are_equal_innodb_data_file_paths subroutine checks if the given
 
427
# InnoDB data file option values are equal.
 
428
#   Parameters:
 
429
#     str1    InnoDB data file path option value
 
430
#     str2    InnoDB data file path option value
 
431
#   Return value:
 
432
#     1  if values are equal
 
433
#     0  otherwise
 
434
#
 
435
sub are_equal_innodb_data_file_paths {
 
436
    my $str1 = shift;
 
437
    my $str2 = shift;
 
438
    my @array1 = split(/;/, $str1);
 
439
    my @array2 = split(/;/, $str2);
 
440
    
 
441
    if ($#array1 != $#array2) { return 0; }
 
442
 
 
443
    for (my $i = 0; $i <= $#array1; $i++) {
 
444
        my @def1 = split(/:/, $array1[$i]);
 
445
        my @def2 = split(/:/, $array2[$i]);
 
446
        
 
447
        if ($#def1 != $#def2) { return 0; }
 
448
 
 
449
        for (my $j = 0; $j <= $#def1; $j++) {
 
450
            if ($def1[$j] ne $def2[$j]) { return 0; }
 
451
        }
 
452
    }
 
453
    return 1;
 
454
}        
 
455
 
 
456
 
 
457
#
 
458
# is_in_array subroutine checks if the given string is in the array.
 
459
#   Parameters:
 
460
#     str       a string
 
461
#     array_ref a reference to an array of strings
 
462
#   Return value:
 
463
#     1  if string is in the array
 
464
#     0  otherwise
 
465
 
466
sub is_in_array {
 
467
    my $str = shift;
 
468
    my $array_ref = shift;
 
469
 
 
470
    if (grep { $str eq $_ } @{$array_ref}) {
 
471
        return 1;
 
472
    }
 
473
    return 0;
 
474
}
 
475
 
 
476
 
 
477
#
 
478
# copy_back subroutine copies data and index files from backup directory 
 
479
# back to their original locations.
 
480
#
 
481
sub copy_back {
 
482
    my $orig_datadir = get_option(\%config, 'mysqld', 'datadir');
 
483
    my $orig_ibdata_dir = 
 
484
        get_option(\%config, 'mysqld', 'innodb_data_home_dir');
 
485
    my $orig_innodb_data_file_path = 
 
486
        get_option(\%config, 'mysqld', 'innodb_data_file_path');
 
487
    my $orig_iblog_dir =
 
488
        get_option(\%config, 'mysqld', 'innodb_log_group_home_dir');
 
489
    my $excluded_files = 
 
490
        '^(\.\.?|backup-my\.cnf|xtrabackup_logfile|mysql-std(err|out)|.*\.ibz)$';
 
491
    my @ibdata_files;
 
492
    my $iblog_files = '^ib_logfile.*$';
 
493
    my $compressed_data_file = '.*\.ibz$';
 
494
    my $file;
 
495
    my $backup_innodb_data_file_path;
 
496
    # check that original data directory exists
 
497
    if (! -d $orig_datadir) {
 
498
        Die "Original data directory '$orig_datadir' does not exist!";
 
499
    }
 
500
    # check that original InnoDB data directory exists
 
501
    if (! -d $orig_ibdata_dir) {
 
502
        Die "Original InnoDB data directory '$orig_ibdata_dir' does not exist!";
 
503
    }
 
504
    # check that original InnoDB log directory exists
 
505
    if (! -d $orig_iblog_dir) {
 
506
        Die "Original InnoDB log directory '$orig_iblog_dir' does not exist!";
 
507
    }
 
508
 
 
509
    # check that the original options file and the backup options file have
 
510
    # the same value for "innodb_data_file_path" option
 
511
    #$backup_innodb_data_file_path = 
 
512
    #    get_option(\%backup_config, 'mysqld', 'innodb_data_file_path');
 
513
    #if (!are_equal_innodb_data_file_paths($orig_innodb_data_file_path, 
 
514
    #                                      $backup_innodb_data_file_path)
 
515
    #) {
 
516
    #    Die "The value of 'innodb_data_file_path' option in the original "
 
517
    #      . "my.cnf file '$config_file' is different from the value "
 
518
    #      . "in the backup my.cnf file '$backup_config_file'.\n(original: "
 
519
    #      . "'$orig_innodb_data_file_path')\n"
 
520
    #      . "(backup:   '$backup_innodb_data_file_path')";
 
521
    #}
 
522
 
 
523
    # make a list of all ibdata files in the backup directory and all
 
524
    # directories in the backup directory under which there are ibdata files
 
525
    foreach my $a (split(/;/, $orig_innodb_data_file_path)) {
 
526
        my $path = (split(/:/,$a))[0];
 
527
        my $filename = (split(/\/+/, $path))[-1];
 
528
 
 
529
        # check that the backup data file exists
 
530
        if (! -e "$backup_dir/$filename") {
 
531
            if (-e "$backup_dir/${filename}.ibz") {
 
532
                Die "Backup data file '$backup_dir/$filename' does not exist, but "
 
533
                  . "its compressed copy '${path}.ibz' exists. Check "
 
534
                  . "that you have run '$innobackup_script --apply-log --uncompress "
 
535
                  . "...' before attempting '$innobackup_script --copy-back ...' !";
 
536
            } else {
 
537
                Die "Backup data file '$backup_dir/$filename' does not exist.";
 
538
            }
 
539
        }
 
540
 
 
541
        if (!is_in_array($filename, \@ibdata_files)) {
 
542
            push(@ibdata_files, $filename);
 
543
        }
 
544
    }
 
545
 
 
546
    # copy files from backup dir to their original locations
 
547
 
 
548
    # copy files to original data directory
 
549
    opendir(DIR, $backup_dir) 
 
550
        || Die "Can't open directory '$backup_dir': $!\n";
 
551
    print STDERR "$prefix Starting to copy MyISAM tables, indexes,\n"; 
 
552
    print STDERR "$prefix .MRG, .TRG, .TRN, .ARM, .ARZ, .CSM, .CSV, .opt, and .frm files\n";
 
553
    print STDERR "$prefix in '$backup_dir'\n";
 
554
    print STDERR "$prefix back to original data directory '$orig_datadir'\n";
 
555
    while (defined($file = readdir(DIR))) {
 
556
        if ($file =~ /$excluded_files/) { next; }
 
557
        if (is_in_array($file, \@ibdata_files)) { next; }
 
558
        if ($file =~ /$iblog_files/) { next; }
 
559
        if (-d "$backup_dir/$file") {
 
560
            my $subdir = "$backup_dir/$file";
 
561
            my $file2;
 
562
 
 
563
            print STDERR "$prefix Copying directory '$subdir'\n";
 
564
            if (! -x "\"".escape_path("$orig_datadir/$file")."\"") {
 
565
                system("mkdir \"".escape_path("$orig_datadir/$file")."\"") 
 
566
                    and Die "Failed to create directory "
 
567
                            . "'$orig_datadir/$file' : $!";
 
568
            }
 
569
            opendir(SUBDIR, "$subdir") 
 
570
                || Die "Can't open directory '$subdir': $!\n";
 
571
            while (defined($file2 = readdir(SUBDIR))) {
 
572
                if (-d "$subdir/$file2") { next; }
 
573
                if ($file2 =~ /$compressed_data_file/) { next; }
 
574
                $src_name = escape_path("$subdir/$file2");
 
575
                $dst_name = escape_path("$orig_datadir/$file");
 
576
                system("$CP_CMD \"$src_name\" \"$dst_name\"")
 
577
                    and Die "Failed to copy file '$file': $!";
 
578
            }
 
579
            closedir(SUBDIR);
 
580
        } else { 
 
581
            print STDERR "$prefix Copying file " . 
 
582
                "'$backup_dir/$file'\n";
 
583
            $src_name = escape_path("$backup_dir/$file");
 
584
            $dst_name = escape_path("$orig_datadir");
 
585
            system("$CP_CMD \"$src_name\" \"$dst_name\"")
 
586
                and Die "Failed to copy file '$file': $!";
 
587
        }
 
588
    }
 
589
    closedir(DIR);
 
590
 
 
591
    # copy InnoDB data files to original InnoDB data directory
 
592
    print STDERR "\n$prefix Starting to copy InnoDB tables and indexes\n";
 
593
    print STDERR "$prefix in '$backup_dir'\n";
 
594
    print STDERR "$prefix back to original InnoDB data directory '$orig_ibdata_dir'\n";
 
595
    foreach my $a (split(/;/, $orig_innodb_data_file_path)) {
 
596
        # get the relative pathname of a data file
 
597
        my $path = (split(/:/,$a))[0];
 
598
        my $filename = (split(/\/+/, $path))[-1];
 
599
        print STDERR "$prefix Copying file '$backup_dir/$filename'\n";
 
600
        $src_name = escape_path("$backup_dir/$filename");
 
601
        $dst_name = escape_path("$orig_ibdata_dir/$path");
 
602
        system("$CP_CMD \"$src_name\" \"$dst_name\"")
 
603
            and Die "Failed to copy file '$filename': $!";
 
604
    }
 
605
 
 
606
    # copy InnoDB log files to original InnoDB log directory
 
607
    opendir(DIR, $backup_dir) 
 
608
        || Die "Can't open directory '$backup_dir': $!\n";
 
609
    print STDERR "\n$prefix Starting to copy InnoDB log files\n";
 
610
    print STDERR "$prefix in '$backup_dir'\n";
 
611
    print STDERR "$prefix back to original InnoDB log directory '$orig_iblog_dir'\n";
 
612
    while (defined($file = readdir(DIR))) {
 
613
        if ($file =~ /$iblog_files/ && -f "$backup_dir/$file") {
 
614
            print STDERR "$prefix Copying file '$backup_dir/$file'\n";
 
615
            $src_name = escape_path("$backup_dir/$file");
 
616
            $dst_name = escape_path("$orig_iblog_dir");
 
617
            system("$CP_CMD \"$src_name\" \"$dst_name\"")
 
618
                and Die "Failed to copy file '$file': $!";
 
619
        }
 
620
    }
 
621
    closedir(DIR);
 
622
 
 
623
    print STDERR "$prefix Finished copying back files.\n\n";
 
624
}
 
625
 
 
626
 
 
627
#
 
628
# apply_log subroutine prepares a backup for starting database server
 
629
# on the backup. It applies InnoDB log files to the InnoDB data files.
 
630
#
 
631
sub apply_log {
 
632
    my $rcode;
 
633
    my $cmdline = '';
 
634
    my $options = '';
 
635
 
 
636
    if ($option_defaults_file) {
 
637
        $options = $options . " --defaults-file=\"$option_defaults_file\" ";
 
638
    }
 
639
 
 
640
    $options = $options . "--prepare --target-dir=$backup_dir";
 
641
 
 
642
    if ($option_uncompress) {
 
643
        $options = $options . ' --uncompress';
 
644
    }
 
645
    if ($option_export) {
 
646
        $options = $options . ' --export';
 
647
    }
 
648
    if ($option_redo_only) {
 
649
        $options = $options . ' --apply-log-only';
 
650
    }
 
651
    if ($option_use_memory) {
 
652
        $options = $options . " --use-memory=$option_use_memory";
 
653
    }
 
654
 
 
655
    if ($option_incremental_dir) {
 
656
        $options = $options . " --incremental-dir=$option_incremental_dir";
 
657
    }
 
658
 
 
659
    # run ibbackup as a child process
 
660
    $cmdline = "$option_ibbackup_binary $options";
 
661
    $now = current_time();
 
662
    print STDERR "\n$now  $prefix Starting ibbackup with command: $cmdline\n\n";
 
663
    $rcode = system("$cmdline");
 
664
    if ($rcode) {
 
665
        # failure
 
666
        Die "\n$prefix ibbackup failed";
 
667
    }
 
668
 
 
669
    # We should not create ib_logfile files if we prepare for following incremental applies
 
670
    # Also we do not prepare ib_logfile if we applied incremental changes
 
671
    if (!( ($option_redo_only) or ($option_incremental_dir))) { 
 
672
    $now = current_time();
 
673
    print STDERR "\n$now  $prefix Restarting xtrabackup with command: $cmdline\nfor creating ib_logfile*\n\n";
 
674
    $rcode = system("$cmdline");
 
675
    if ($rcode) {
 
676
        # failure
 
677
        Die "\n$prefix xtrabackup (2nd execution) failed";
 
678
    }
 
679
    }
 
680
}
 
681
 
 
682
 
 
683
#
 
684
# wait_for_ibbackup_suspend subroutine waits until ibbackup has suspended 
 
685
# itself.
 
686
#
 
687
sub wait_for_ibbackup_suspend {
 
688
    print STDERR "$prefix Waiting for ibbackup (pid=$ibbackup_pid) to suspend\n";
 
689
    print STDERR "$prefix Suspend file '$suspend_file'\n\n";
 
690
    for (;;) {
 
691
        sleep 2;
 
692
        last if -e $suspend_file;
 
693
 
 
694
        # check that ibbackup child process is still alive
 
695
        if ($ibbackup_pid == waitpid($ibbackup_pid, &WNOHANG)) {
 
696
            $ibbackup_pid = '';
 
697
            Die "ibbackup child process has died";
 
698
        }
 
699
    }
 
700
    $now = current_time();
 
701
    print STDERR "\n$now  $prefix Continuing after ibbackup has suspended\n";
 
702
}
 
703
 
 
704
 
 
705
#
 
706
# resume_ibbackup subroutine signals ibbackup to complete its execution
 
707
# by deleting the 'ibbackup_suspended' file.
 
708
#
 
709
sub resume_ibbackup {
 
710
    print STDERR "$prefix Resuming ibbackup\n\n";
 
711
    unlink $suspend_file || Die "Failed to delete '$suspend_file': $!";
 
712
 
 
713
    # wait for ibbackup to finish
 
714
    waitpid($ibbackup_pid, 0);
 
715
    $ibbackup_pid = '';
 
716
    return $CHILD_ERROR >> 8;
 
717
}
 
718
    
 
719
 
 
720
#
 
721
# start_ibbackup subroutine spawns a child process running ibbackup
 
722
# program for backing up InnoDB tables and indexes.
 
723
#
 
724
sub start_ibbackup {
 
725
    my $options = '';
 
726
    my $cmdline = '';
 
727
    my $pid = undef;
 
728
 
 
729
    if ($option_defaults_file) {
 
730
        $options = $options . " --defaults-file=\"$option_defaults_file\" ";
 
731
    }
 
732
 
 
733
    $options = $options . "--backup --suspend-at-end";
 
734
 
 
735
    if (!$option_remote_host && !$option_stream) {
 
736
        $options = $options . " --target-dir=$backup_dir";
 
737
    } else {
 
738
        #(datadir) for 'xtrabackup_suspended' and 'xtrabackup_checkpoints'
 
739
        $options = $options . " --log-stream --target-dir=./";
 
740
    }
 
741
 
 
742
    # prepare command line for running ibbackup
 
743
    if ($option_throttle) {
 
744
        $options = $options . " --throttle=$option_throttle";
 
745
    }
 
746
    if ($option_sleep) {
 
747
        $options = $options . " --sleep=$option_sleep";
 
748
    }
 
749
    if ($option_compress) {
 
750
        $options = $options . " --compress=$option_compress";
 
751
    }
 
752
    if ($option_use_memory) {
 
753
        $options = $options . " --use-memory=$option_use_memory";
 
754
    }
 
755
    if ($option_include) {
 
756
        $options = $options . " --tables='$option_include'";
 
757
    }
 
758
    if ($option_extra_lsndir) {
 
759
        $options = $options . " --extra-lsndir='$option_extra_lsndir'";
 
760
    }
 
761
 
 
762
    if ($option_incremental) {
 
763
        if($option_incremental_lsn) {
 
764
          $options = $options . " --incremental-lsn='$option_incremental_lsn'";
 
765
        } else {
 
766
          $options = $options . " --incremental-basedir='$incremental_basedir'";
 
767
        }
 
768
    }
 
769
 
 
770
    if ($option_tables_file) {
 
771
        $options = $options . " --tables_file='$option_tables_file'";
 
772
    }
 
773
    if ($option_parallel) {
 
774
        $options = $options. " --parallel=$option_parallel";
 
775
    }
 
776
    $cmdline = "$option_ibbackup_binary $options";
 
777
 
 
778
    # run ibbackup as a child process
 
779
    $now = current_time();
 
780
    print STDERR "\n$now  $prefix Starting ibbackup with command: $cmdline\n";
 
781
    if (defined($pid = fork)) {
 
782
        if ($pid) {
 
783
            # parent process
 
784
            $ibbackup_pid = $pid;
 
785
 
 
786
            if($option_remote_host || $option_stream) {
 
787
                #direct copy to remote
 
788
                my $orig_datadir = get_option(\%config, 'mysqld', 'datadir');
 
789
                my $orig_ibdata_dir =
 
790
                    get_option(\%config, 'mysqld', 'innodb_data_home_dir');
 
791
                my $orig_innodb_data_file_path =
 
792
                    get_option(\%config, 'mysqld', 'innodb_data_file_path');
 
793
                my $innodb_flush_method = 
 
794
                    get_option(\%config, 'mysqld', 'innodb_flush_method');
 
795
                my $innodb_use_odirect;
 
796
                $innodb_use_odirect = 1 if $innodb_flush_method =~ m/O_DIRECT/i;
 
797
 
 
798
                my $subdir;
 
799
                my @list;
 
800
 
 
801
                if($option_remote_host) {
 
802
                    if (system("ssh $option_remote_host test -e $backup_dir/ib_logfile0")
 
803
                            == 0) {
 
804
                        print STDERR "$prefix Remove $option_remote_host:$backup_dir/ib_logfile*\n";
 
805
                        system("ssh $option_remote_host rm $backup_dir/ib_logfile\*")
 
806
                            and Die "Failed to rm file '$backup_dir/ib_logfile*': $!";
 
807
                    }
 
808
                }
 
809
 
 
810
                wait_for_ibbackup_suspend();
 
811
 
 
812
                #InnoDB data files from original InnoDB data directory
 
813
                print STDERR "\n$prefix Starting to backup InnoDB tables and indexes\n";
 
814
                if($option_remote_host) {
 
815
                    print STDERR "$prefix to '$backup_dir'\n";
 
816
                }
 
817
                print STDERR "$prefix from original InnoDB data directory '$orig_ibdata_dir'\n";
 
818
                foreach my $a (split(/;/, $orig_innodb_data_file_path)) {
 
819
                    my $path = (split(/:/,$a))[0];
 
820
                    $path=~s/([\$\\\" ])/\\$1/g;
 
821
                    if($option_remote_host) {
 
822
                        print STDERR "$prefix Backing up file '$orig_ibdata_dir/$path'\n";
 
823
                        system("scp $option_scp_opt '$orig_ibdata_dir/$path' '$option_remote_host:$backup_dir/$path'")
 
824
                            and Die "Failed to scp file '$path': $!";
 
825
                    } elsif($option_stream eq 'tar') {
 
826
                        my $ret = 0;
 
827
                        my $tarcmd;
 
828
                        print STDERR "$prefix Backing up as tar stream '$path'\n";
 
829
                        if (!$option_tar4ibd) {
 
830
                            $tarcmd = "tar chf - -b 32";
 
831
                        } else {
 
832
                            $tarcmd = "tar4ibd";
 
833
                            if ($innodb_use_odirect) {
 
834
                                $tarcmd = "$tarcmd -d";
 
835
                            }
 
836
                            $tarcmd = "$tarcmd -c";
 
837
                        }
 
838
                        $ret = system("cd $orig_ibdata_dir; $tarcmd $path") >> 8;
 
839
                        if ($ret == 1) {
 
840
                            print STDERR "$prefix If you use GNU tar, this warning can be ignored.\n";
 
841
                        } elsif ($ret != 0) {
 
842
                            print STDERR "$prefix tar returned with exit code $ret.\n";
 
843
                            if ( -e "$orig_ibdata_dir/$path" ) {
 
844
                                Die "Failed to stream '$orig_ibdata_dir/$path': $!";
 
845
                            }
 
846
                            else {
 
847
                                print STDERR "$prefix Ignoring nonexistent file '$orig_ibdata_dir/$path'.\n";
 
848
                            }
 
849
                        }
 
850
                    }
 
851
                }
 
852
 
 
853
                #copy *.ibd files
 
854
                opendir(DIR, $orig_datadir)
 
855
                    || Die "Can't open directory '$orig_datadir': $!\n";
 
856
                while (defined($subdir = readdir(DIR))) {
 
857
                    my $print_each_file = 0;
 
858
                    my $file_c;
 
859
                    my $file;
 
860
                    if ($subdir eq '.' || $subdir eq '..') { next; }
 
861
                    next unless -d "$orig_datadir/$subdir";
 
862
                    next unless check_if_required($subdir);
 
863
 
 
864
                    @list = glob("$orig_datadir/$subdir/" . '*.ibd');
 
865
 
 
866
                    $file_c = @list;
 
867
                    if ($file_c <= $backup_file_print_limit) {
 
868
                        $print_each_file = 1;
 
869
                    } else {
 
870
                        print STDERR "$prefix Backing up files " .
 
871
                            "'$orig_datadir/$subdir/*.ibd' ($file_c files)\n";
 
872
                    }
 
873
                    foreach $file (@list) {
 
874
                        next unless check_if_required($subdir, $file);
 
875
                        if($option_include) {
 
876
                            my $table_name;
 
877
 
 
878
                            $table_name = substr($file, rindex($file, '/'));
 
879
                            $table_name = substr($table_name, 1, rindex($table_name, '.') - 1);
 
880
                            $table_name = $subdir . "." . $table_name;
 
881
 
 
882
                            if (!($table_name =~ /$option_include/)) {
 
883
                                print STDERR "'$file' is skipped.\n";
 
884
                                next;
 
885
                            }
 
886
                        }
 
887
 
 
888
                        if ($print_each_file) {
 
889
                            print STDERR "$prefix Backing up file '$file'\n";
 
890
                        }
 
891
                        if($option_remote_host) {
 
892
                            if (system("ssh $option_remote_host test -e $backup_dir/$subdir")
 
893
                                    != 0) {
 
894
                                system("ssh $option_remote_host mkdir $backup_dir/$subdir");
 
895
                            }
 
896
                            system("scp $option_scp_opt '$file' '$option_remote_host:$backup_dir/$subdir/'")
 
897
                                and Die "Failed to scp file '$file': $!";
 
898
                        } elsif($option_stream eq 'tar') {
 
899
                            my $ret = 0;
 
900
                            my $file_name = substr($file, rindex($file, '/') + 1);
 
901
                            $file_name=~s/([\$\\\" ])/\\$1/g;
 
902
                            if (!$option_tar4ibd) {
 
903
                                $ret = system("cd $orig_datadir; tar chf - -b 32 $subdir/$file_name") >> 8;
 
904
                            } else {
 
905
                                $ret = system("cd $orig_datadir; tar4ibd -c $subdir/$file_name") >> 8;
 
906
                            }
 
907
                            if ($ret == 1) {
 
908
                                print STDERR "$prefix If you use GNU tar, this warning can be ignored.\n";
 
909
                            } elsif ($ret != 0) {
 
910
                                print STDERR "$prefix tar returned with exit code $ret.\n";
 
911
                                if ( -e "$orig_datadir/$subdir/$file_name" ) {
 
912
                                    Die "Failed to stream '$orig_datadir/$subdir/$file_name': $!";
 
913
                                }
 
914
                                else {
 
915
                                    print STDERR "$prefix Ignoring nonexistent file '$orig_datadir/$subdir/$file_name'.\n";
 
916
                                }
 
917
                            }
 
918
                        }
 
919
                    }
 
920
                }
 
921
                closedir(DIR);
 
922
            }
 
923
        } else {
 
924
            if($option_remote_host || $option_stream) {
 
925
                open(STDOUT, "> $tmp_logfile")
 
926
                || Die "Failed to open file '$tmp_logfile': $!"
 
927
            }
 
928
 
 
929
            # child process
 
930
            exec($cmdline) || Die "Failed to exec ibbackup: $!";
 
931
        }
 
932
    } else {
 
933
        Die "failed to fork ibbackup child process: $!";
 
934
    }
 
935
}
 
936
                  
 
937
 
 
938
#
 
939
# get_mysql_options subroutine returns the options to mysql client program
 
940
# as a string. The options are determined from the options given by the
 
941
# user to innobackup.
 
942
#
 
943
sub get_mysql_options {
 
944
    my $options = '';
 
945
 
 
946
    # this option has to be first
 
947
    if ($option_defaults_file) {
 
948
        $options = "$options --defaults-file='$option_defaults_file'";
 
949
    }
 
950
 
 
951
    if ($option_mysql_password) {
 
952
        $options = "$options --password='$option_mysql_password'";
 
953
    }
 
954
    if ($option_mysql_user) {
 
955
        $options = "$options --user='$option_mysql_user'";
 
956
    }
 
957
    if ($option_mysql_host) {
 
958
        $options = "$options --host='$option_mysql_host'";
 
959
    }
 
960
    if ($option_mysql_port) {
 
961
        $options = "$options --port='$option_mysql_port'";
 
962
    }
 
963
    if ($option_mysql_socket) {
 
964
        $options = "$options --socket='$option_mysql_socket'";
 
965
    }
 
966
    $options = "$options --unbuffered --";
 
967
    return $options;
 
968
}
 
969
 
 
970
 
 
971
#
 
972
# mysql_open subroutine starts mysql as a child process with
 
973
# a pipe connection.
 
974
#
 
975
sub mysql_open {
 
976
    my $options = get_mysql_options();
 
977
    # run mysql as a child process with a pipe connection
 
978
    $now = current_time();
 
979
    print STDERR "$now  $prefix Starting mysql with options: $options\n";
 
980
    $mysql_pid = open(*MYSQL_WRITER, "| mysql $options >$mysql_stdout 2>$mysql_stderr ") or Die "Failed to spawn mysql child process: $!";
 
981
    MYSQL_WRITER->autoflush(1);
 
982
    $now = current_time();
 
983
    print STDERR "$now  $prefix Connected to database with mysql child process (pid=$mysql_pid)\n";
 
984
 
 
985
    mysql_check();
 
986
}
 
987
 
 
988
 
 
989
#
 
990
# mysql_check subroutine checks that the connection to mysql child process 
 
991
# is ok.
 
992
#
 
993
sub mysql_check {
 
994
    my $mysql_pid_copy = $mysql_pid;
 
995
 
 
996
    # send a dummy query to mysql child process
 
997
    $hello_id++;
 
998
    my $hello_message = "innobackup hello $hello_id";
 
999
    print MYSQL_WRITER "select '$hello_message';\n" 
 
1000
        or Die "Connection to mysql child process failed: $!";
 
1001
 
 
1002
    # wait for reply
 
1003
    eval {
 
1004
        local $SIG{ALRM} = sub { die "alarm clock restart" };
 
1005
        my $stdout = '';
 
1006
        my $stderr = '';
 
1007
        alarm $mysql_response_timeout;
 
1008
        while (index($stdout, $hello_message) < 0) {
 
1009
            sleep 2;
 
1010
            if ($mysql_pid && $mysql_pid == waitpid($mysql_pid, &WNOHANG)) {
 
1011
                my $reason = `cat $mysql_stderr`;
 
1012
                $mysql_pid = '';
 
1013
                die "mysql child process has died: $reason";
 
1014
            }
 
1015
            $stdout = `cat $mysql_stdout`;
 
1016
            $stderr = `cat $mysql_stderr | grep -v ^Warning`;
 
1017
            if ($stderr) {
 
1018
                # mysql has reported an error, do exit
 
1019
                die "mysql error: $stderr";
 
1020
            }
 
1021
        }
 
1022
        alarm 0;
 
1023
    };
 
1024
    if ($@ =~ /alarm clock restart/) {
 
1025
        Die "Connection to mysql child process (pid=$mysql_pid_copy) timedout."
 
1026
            . " (Time limit of $mysql_response_timeout seconds exceeded."
 
1027
            . " You may adjust time limit by editing the value of parameter"
 
1028
            . " \"\$mysql_response_timeout\" in this script.)";
 
1029
    } elsif ($@) { Die $@; }
 
1030
    
 
1031
    $mysql_last_access_time = time();
 
1032
}
 
1033
 
 
1034
 
 
1035
#
 
1036
# mysql_keep_alive subroutine tries to keep connection to the mysqld database
 
1037
# server alive by sending a dummy query when the connection has been idle
 
1038
# for the specified time.
 
1039
#
 
1040
sub mysql_keep_alive {
 
1041
    if ((time() - $mysql_last_access_time) > $mysql_keep_alive_timeout) {
 
1042
        # too long idle, send a dummy query
 
1043
        mysql_check();
 
1044
    }
 
1045
}
 
1046
 
 
1047
 
 
1048
#
 
1049
# mysql_send subroutine send a request string to mysql child process.
 
1050
# This subroutine appends a newline character to the request and checks 
 
1051
# that mysqld receives the query.
 
1052
# Parameters:
 
1053
#    request  request string
 
1054
#
 
1055
sub mysql_send {
 
1056
    my $request = shift;
 
1057
 
 
1058
    $current_mysql_request = $request;
 
1059
    print MYSQL_WRITER "$request\n";
 
1060
    mysql_check();
 
1061
    $current_mysql_request = '';
 
1062
}
 
1063
    
 
1064
 
 
1065
#
 
1066
# mysql_close subroutine terminates mysql child process gracefully.
 
1067
 
1068
sub mysql_close {
 
1069
    print MYSQL_WRITER "quit\n";
 
1070
    $now = current_time();
 
1071
    print STDERR "$now  $prefix Connection to database server closed\n";
 
1072
    $mysql_pid = '';
 
1073
}
 
1074
 
 
1075
 
 
1076
#
 
1077
# write_binlog_info subroutine retrieves MySQL binlog position and
 
1078
# saves it in a file. It also prints it to stdout.
 
1079
#
 
1080
sub write_binlog_info {
 
1081
    my @lines;
 
1082
    my @info_lines = ();
 
1083
    my $position = '';
 
1084
    my $filename = '';
 
1085
 
 
1086
    # get binlog position
 
1087
    mysql_send "SHOW MASTER STATUS;";
 
1088
 
 
1089
    # get "show master status" output lines (2) from mysql output
 
1090
    file_to_array($mysql_stdout, \@lines);
 
1091
    foreach my $line (@lines) {
 
1092
        if ($line =~ m/innobackup hello/) {
 
1093
            # this is a hello message, ignore it
 
1094
        } else {
 
1095
            # this is output line from "show master status"
 
1096
            push(@info_lines, $line);
 
1097
        }
 
1098
    }
 
1099
 
 
1100
    # write binlog info file
 
1101
    if (!defined $info_lines[1]) {
 
1102
        $info_lines[1] = "";
 
1103
    }
 
1104
    if (!$option_remote_host) {
 
1105
        open(FILE, ">$binlog_info") || 
 
1106
            Die "Failed to open file '$binlog_info': $!";
 
1107
    } else {
 
1108
        open(FILE, "| ssh $option_remote_host 'cat > $binlog_info'") ||
 
1109
            Die "Failed to open file '$option_remote_host:$binlog_info': $!";
 
1110
    }
 
1111
    print FILE  "$info_lines[1]\n";
 
1112
    close(FILE);
 
1113
 
 
1114
    if ($option_stream eq 'tar') {
 
1115
        system("cd $option_tmpdir; tar chf - xtrabackup_binlog_info")
 
1116
            and Die "Failed to stream 'xtrabackup_binlog_info': $!";
 
1117
        unlink $binlog_info || Die "Failed to delete '$binlog_info': $!";
 
1118
    }
 
1119
 
 
1120
    # get the name of the last binlog file and position in it
 
1121
    ($filename, $position) = $info_lines[1] =~ /^\s*([^\s]+)\s+(.*)$/;
 
1122
 
 
1123
    if (defined $filename && defined $position) {
 
1124
        $mysql_binlog_position = "filename '$filename', position $position";
 
1125
    } else {
 
1126
        $mysql_binlog_position = "filename '', position ";
 
1127
    }        
 
1128
}
 
1129
 
 
1130
 
 
1131
 
1132
# write_slave_info subroutine retrieves MySQL binlog position of the
 
1133
# master server in a replication setup and saves it in a file. It
 
1134
# also saves it in $msql_slave_position variable.
 
1135
#
 
1136
sub write_slave_info {
 
1137
    my @lines;
 
1138
    my @info_lines;
 
1139
    my $position = '';
 
1140
    my $filename = '';
 
1141
    my $master= '';
 
1142
        
 
1143
    # get slave status. Use single quotes here, otherwise
 
1144
    # \G is evaluated as a control character.
 
1145
    mysql_send 'SHOW SLAVE STATUS\G;';
 
1146
 
 
1147
    # get output of the "show slave status" command from mysql output
 
1148
    # and extract binlog position of the master server
 
1149
    file_to_array($mysql_stdout, \@lines);
 
1150
    for (@lines) {
 
1151
        $master = $1 if /Master_Host:\s*(\S*)\s*$/;
 
1152
        $filename = $1 if /Master_Log_File:\s*(\S*)\s*$/;
 
1153
        $position = $1 if /Master_Log_Pos:\s*(\S*)\s*$/;
 
1154
    }
 
1155
 
 
1156
    # print slave status to a file
 
1157
    if (!$option_remote_host) {
 
1158
        open(FILE, ">$slave_info") || 
 
1159
            Die "Failed to open file '$slave_info': $!";
 
1160
    } else {
 
1161
        open(FILE, "| ssh $option_remote_host 'cat > $slave_info'") ||
 
1162
            Die "Failed to open file '$option_remote_host:$slave_info': $!";
 
1163
    }
 
1164
    print FILE  "CHANGE MASTER TO MASTER_LOG_FILE='$filename', MASTER_LOG_POS=$position\n";
 
1165
    close(FILE);
 
1166
 
 
1167
    if ($option_stream eq 'tar') {
 
1168
        system("cd $option_tmpdir; tar chf - xtrabackup_slave_info")
 
1169
            and Die "Failed to stream 'xtrabackup_slave_info': $!";
 
1170
        unlink $slave_info || Die "Failed to delete '$slave_info': $!";
 
1171
    }
 
1172
 
 
1173
    $mysql_slave_position = "master host '$master', filename '$filename', position $position";
 
1174
}
 
1175
 
 
1176
 
 
1177
#
 
1178
# mysql_lockall subroutine puts a read lock on all tables in all databases.
 
1179
 
1180
sub mysql_lockall {
 
1181
    $now = current_time();
 
1182
    print STDERR "$now  $prefix Starting to lock all tables...\n";
 
1183
 
 
1184
    mysql_send "USE mysql;";
 
1185
#    mysql_send "DROP TABLE IF EXISTS ibbackup_binlog_marker;";
 
1186
#    if (compare_versions($mysql_server_version, '4.1.0') == -1) {
 
1187
#        # MySQL server version is 4.0 or older, ENGINE keyword not supported
 
1188
#        mysql_send "CREATE TABLE ibbackup_binlog_marker(a INT) TYPE=INNODB;";
 
1189
#    } else {
 
1190
#        # MySQL server version is 4.1 or newer, use ENGINE keyword
 
1191
#        mysql_send "CREATE TABLE ibbackup_binlog_marker(a INT) ENGINE=INNODB;";
 
1192
#    }
 
1193
    mysql_send "SET AUTOCOMMIT=0;";
 
1194
#    mysql_send "INSERT INTO ibbackup_binlog_marker VALUES (1);";
 
1195
    if (compare_versions($mysql_server_version, '4.0.22') == 0
 
1196
        || compare_versions($mysql_server_version, '4.1.7') == 0) {
 
1197
        # MySQL server version is 4.0.22 or 4.1.7
 
1198
        mysql_send "COMMIT;";
 
1199
        mysql_send "FLUSH TABLES WITH READ LOCK;";
 
1200
    } else {
 
1201
        # MySQL server version is other than 4.0.22 or 4.1.7
 
1202
        mysql_send "FLUSH TABLES WITH READ LOCK;";
 
1203
        mysql_send "COMMIT;";
 
1204
    }
 
1205
    write_binlog_info;
 
1206
    write_slave_info if $option_slave_info;
 
1207
        
 
1208
    $now = current_time();
 
1209
    print STDERR "$now  $prefix All tables locked and flushed to disk\n";
 
1210
}
 
1211
 
 
1212
 
 
1213
#
 
1214
# mysql_unlockall subroutine releases read locks on all tables in all 
 
1215
# databases.
 
1216
 
1217
sub mysql_unlockall {
 
1218
    mysql_send "UNLOCK TABLES;";
 
1219
#    mysql_send "DROP TABLE IF EXISTS ibbackup_binlog_marker;";
 
1220
 
 
1221
    $now = current_time();
 
1222
    print STDERR "$now  $prefix All tables unlocked\n";
 
1223
}
 
1224
 
 
1225
 
 
1226
#
 
1227
# catch_sigpipe subroutine is a signal handler for SIGPIPE.
 
1228
#
 
1229
sub catch_sigpipe {
 
1230
    my $rcode;
 
1231
 
 
1232
    if ($mysql_pid && (-1 == ($rcode = waitpid($mysql_pid, &WNOHANG))
 
1233
                       || $rcode == $mysql_pid)) {
 
1234
        my $reason = `cat $mysql_stderr`;
 
1235
        print STDERR "Pipe to mysql child process broken: $reason at";
 
1236
        system("date +'%H:%M:%S'");
 
1237
        exit(1);
 
1238
    } else {
 
1239
        Die "Broken pipe";
 
1240
    }
 
1241
}
 
1242
 
 
1243
 
 
1244
#
 
1245
# kill_child_processes subroutine kills all child processes of this process.
 
1246
#
 
1247
sub kill_child_processes {
 
1248
    if ($ibbackup_pid) {
 
1249
        kill($kill_signal, $ibbackup_pid);
 
1250
        $ibbackup_pid = '';
 
1251
    }
 
1252
    
 
1253
    if ($mysql_pid) {
 
1254
        kill($kill_signal, $mysql_pid);
 
1255
        $mysql_pid = '';
 
1256
    }
 
1257
}
 
1258
 
 
1259
 
 
1260
#
 
1261
# require_external subroutine checks that an external program is runnable
 
1262
# via the shell. This is tested by calling the program with the
 
1263
# given arguments. It is checked that the program returns 0 and does 
 
1264
# not print anything to stderr. If this check fails, this subroutine exits.
 
1265
#    Parameters:
 
1266
#       program      pathname of the program file
 
1267
#       args         arguments to the program
 
1268
#       pattern      a string containing a regular expression for finding 
 
1269
#                    the program version.
 
1270
#                    this pattern should contain a subpattern enclosed
 
1271
#                    in parentheses which is matched with the version.
 
1272
#       version_ref  a reference to a variable where the program version
 
1273
#                    string is returned. Example "2.0-beta2".
 
1274
#
 
1275
sub require_external {
 
1276
    my $program = shift;
 
1277
    my $args = shift;
 
1278
    my $pattern = shift;
 
1279
    my $version_ref = shift;
 
1280
    my @lines;
 
1281
    my $tmp_stdout = tmpnam();
 
1282
    my $tmp_stderr = tmpnam();
 
1283
    my $rcode;
 
1284
    my $error;
 
1285
 
 
1286
    $rcode = system("$program $args >$tmp_stdout 2>$tmp_stderr");
 
1287
    if ($rcode) {
 
1288
        $error = $!;
 
1289
    }
 
1290
    my $stderr = `cat $tmp_stderr | grep -v ^Warning`;
 
1291
    unlink $tmp_stderr;
 
1292
    if ($stderr ne '') {
 
1293
        # failure
 
1294
        unlink $tmp_stdout;
 
1295
        Die "Couldn't run $program: $stderr";
 
1296
    } elsif ($rcode) {
 
1297
        # failure
 
1298
        unlink $tmp_stdout;
 
1299
        Die "Couldn't run $program: $error";
 
1300
    }
 
1301
 
 
1302
    # success
 
1303
    my $stdout = `cat $tmp_stdout`;
 
1304
    unlink $tmp_stdout;
 
1305
    @lines = split(/\n|;/,$stdout);
 
1306
    print STDERR "$prefix Using $lines[0]\n";
 
1307
 
 
1308
    # get version string from the first output line of the program
 
1309
    ${$version_ref} = '';
 
1310
    if ($lines[0] =~ /$pattern/) {
 
1311
        ${$version_ref} = $1;
 
1312
    }
 
1313
}
 
1314
 
 
1315
 
 
1316
# compare_versions subroutine compares two GNU-style version strings.
 
1317
# A GNU-style version string consists of three decimal numbers delimitted 
 
1318
# by dots, and optionally followed by extra attributes.
 
1319
# Examples: "4.0.1", "4.1.1-alpha-debug". 
 
1320
#    Parameters:
 
1321
#       str1   a GNU-style version string
 
1322
#       str2   a GNU-style version string
 
1323
#    Return value:
 
1324
#       -1   if str1 < str2
 
1325
#        0   if str1 == str2
 
1326
#        1   is str1 > str2
 
1327
sub compare_versions {
 
1328
    my $str1 = shift;
 
1329
    my $str2 = shift;
 
1330
    my $extra1 = '';
 
1331
    my $extra2 = '';
 
1332
    my @array1 = ();
 
1333
    my @array2 = ();
 
1334
    my $i;
 
1335
 
 
1336
    # remove possible extra attributes
 
1337
    ($str1, $extra1) = $str1 =~ /^([0-9.]*)(.*)/;
 
1338
    ($str2, $extra2) = $str2 =~ /^([0-9.]*)(.*)/;
 
1339
 
 
1340
    # split "dotted" decimal number string into an array
 
1341
    @array1 = split('\.', $str1);
 
1342
    @array2 = split('\.', $str2);
 
1343
 
 
1344
    # compare in lexicographic order
 
1345
    for ($i = 0; $i <= $#array1 && $i <= $#array2; $i++) {
 
1346
        if ($array1[$i] < $array2[$i]) {
 
1347
            return -1;
 
1348
        } elsif ($array1[$i] > $array2[$i]) {
 
1349
            return 1;
 
1350
        }
 
1351
    }
 
1352
    if ($#array1 < $#array2) {
 
1353
        return -1;
 
1354
    } elsif ($#array1 > $#array2) {
 
1355
        return 1;
 
1356
    } else {
 
1357
        return 0;
 
1358
    }
 
1359
}
 
1360
 
 
1361
 
 
1362
#
 
1363
# init subroutine initializes global variables and performs some checks on the
 
1364
# system we are running on.
 
1365
#
 
1366
sub init {
 
1367
    my $mysql_version = '';
 
1368
    my $ibbackup_version = '';
 
1369
    my $run = '';
 
1370
 
 
1371
    # print some instructions to the user
 
1372
    if (!$option_apply_log && !$option_copy_back) {
 
1373
        $run = 'backup';
 
1374
    } elsif ($option_copy_back) {
 
1375
        $run = 'copy-back';
 
1376
    } else {
 
1377
        $run = 'apply-log';
 
1378
    }
 
1379
    print STDERR "IMPORTANT: Please check that the $run run completes successfully.\n";
 
1380
    print STDERR "           At the end of a successful $run run $innobackup_script\n";
 
1381
    print STDERR "           prints \"completed OK!\".\n\n";
 
1382
 
 
1383
    # check that MySQL client program and InnoDB Hot Backup program
 
1384
    # are runnable via shell
 
1385
    if (!$option_copy_back) {
 
1386
        # we are making a backup or applying log to backup
 
1387
        if (!$option_apply_log) {
 
1388
            # we are making a backup, we need mysql server
 
1389
            my $output = '';
 
1390
            my @lines = ();
 
1391
 
 
1392
            # check that we have mysql client program
 
1393
            require_external('mysql', '--version', 'Ver ([^,]+)', 
 
1394
                             \$mysql_version);
 
1395
            
 
1396
            # get mysql server version
 
1397
            my $options = get_mysql_options();
 
1398
            @lines = split('\n', 
 
1399
                           `mysql $options -e "select \@\@version"`);
 
1400
            $mysql_server_version = $lines[1];
 
1401
            print STDERR "$prefix Using mysql server version $mysql_server_version\n";
 
1402
        }
 
1403
        #require_external($option_ibbackup_binary, '--license', 
 
1404
        #                 'version (\S+)', \$ibbackup_version);
 
1405
        print STDERR "\n";
 
1406
        
 
1407
        if ($option_include 
 
1408
            && $ibbackup_version 
 
1409
            && $ibbackup_version le "2.0") {
 
1410
            # --include option was given, but ibbackup is too
 
1411
            # old to support it
 
1412
            Die "--include option was given, but ibbackup is too old"
 
1413
                . " to support it. You must upgrade to InnoDB Hot Backup"
 
1414
                . " v2.0 in order to use --include option.\n";
 
1415
        }
 
1416
    }
 
1417
    
 
1418
    # set signal handlers
 
1419
    $SIG{PIPE} = \&catch_sigpipe;
 
1420
 
 
1421
    # read MySQL options file
 
1422
    #read_config_file($config_file, \%config);
 
1423
    read_config_file(\%config);
 
1424
 
 
1425
    if(!$option_tmpdir) {
 
1426
        $option_tmpdir = get_option(\%config, 'mysqld', 'tmpdir');
 
1427
    }
 
1428
 
 
1429
    # get innodb log home directory from options file
 
1430
    #$innodb_log_group_home_dir = 
 
1431
    #    get_option(\%config, 'mysqld', 'innodb_log_group_home_dir');
 
1432
 
 
1433
    if (!$option_apply_log && !$option_copy_back) {
 
1434
        # we are making a backup, create a new backup directory
 
1435
        if (!$option_remote_host) {
 
1436
            $backup_dir = File::Spec->rel2abs(make_backup_dir());
 
1437
        } else {
 
1438
            $backup_dir = make_backup_dir();
 
1439
        }
 
1440
        print STDERR "$prefix Created backup directory $backup_dir\n";
 
1441
        if (!$option_remote_host && !$option_stream) {
 
1442
        $backup_config_file = $backup_dir . '/backup-my.cnf';
 
1443
        $suspend_file = $backup_dir . '/xtrabackup_suspended';
 
1444
        $mysql_stdout = $backup_dir . '/mysql-stdout';
 
1445
        $mysql_stderr = $backup_dir . '/mysql-stderr';
 
1446
        $binlog_info = $backup_dir . '/xtrabackup_binlog_info';
 
1447
        $slave_info = $backup_dir . '/xtrabackup_slave_info';
 
1448
        } else {
 
1449
        $suspend_file = get_option(\%config, 'mysqld', 'datadir') . '/xtrabackup_suspended';
 
1450
        $tmp_logfile = $option_tmpdir . '/xtrabackup_logfile';
 
1451
        $mysql_stdout = $option_tmpdir . '/mysql-stdout';
 
1452
        $mysql_stderr = $option_tmpdir . '/mysql-stderr';
 
1453
        if ($option_stream) {
 
1454
            $backup_config_file = $option_tmpdir . '/backup-my.cnf';
 
1455
            $binlog_info = $option_tmpdir . '/xtrabackup_binlog_info';
 
1456
            $slave_info = $option_tmpdir . '/xtrabackup_slave_info';
 
1457
        } else {
 
1458
            $backup_config_file = $backup_dir . '/backup-my.cnf';
 
1459
            $binlog_info = $backup_dir . '/xtrabackup_binlog_info';
 
1460
            $slave_info = $backup_dir . '/xtrabackup_slave_info';
 
1461
        }
 
1462
        }
 
1463
        write_backup_config_file($backup_config_file);
 
1464
    } elsif ($option_copy_back) {
 
1465
        #$backup_config_file = $backup_dir . '/backup-my.cnf';
 
1466
        #read_config_file($backup_config_file, \%backup_config);
 
1467
    }         
 
1468
}
 
1469
 
 
1470
 
 
1471
#
 
1472
# write_backup_config_file subroutine creates a backup options file for
 
1473
# ibbackup program. It writes to the file only those options that
 
1474
# are required by ibbackup.
 
1475
#    Parameters:
 
1476
#       filename  name for the created options file
 
1477
#
 
1478
sub write_backup_config_file {
 
1479
    my $filename = shift;
 
1480
    my $innodb_data_file_path = 
 
1481
        get_option(\%config, 'mysqld', 'innodb_data_file_path');
 
1482
    my $innodb_log_files_in_group =
 
1483
        get_option(\%config, 'mysqld', 'innodb_log_files_in_group');
 
1484
    my $innodb_log_file_size =
 
1485
        get_option(\%config, 'mysqld', 'innodb_log_file_size');
 
1486
    my $root;
 
1487
 
 
1488
    my @array = split(/;/, $innodb_data_file_path);
 
1489
    for (my $i = 0; $i <= $#array; $i++) {
 
1490
        my @tmp = split(/\/+/, $array[$i]);
 
1491
        $array[$i] = $tmp[-1];
 
1492
    }
 
1493
    $innodb_data_file_path = join(";", @array);
 
1494
 
 
1495
    if (!$option_remote_host) {
 
1496
        $root = $backup_dir;
 
1497
        open(FILE, "> $filename") || Die "Failed to open file '$filename': $!";
 
1498
    } else {
 
1499
        $root = `ssh $option_remote_host 'cd $backup_dir; pwd'`;
 
1500
        open(FILE, "| ssh $option_remote_host 'cat > $filename'")
 
1501
            || Die "Failed to open file '$option_remote_host:$filename': $!";
 
1502
    }
 
1503
 
 
1504
    print FILE "# This MySQL options file was generated by $innobackup_script.\n\n" .
 
1505
          "# The MySQL server\n" .
 
1506
          "[mysqld]\n" .
 
1507
          "datadir=$root\n" .
 
1508
          "innodb_data_home_dir=$root\n" .
 
1509
          "innodb_data_file_path=$innodb_data_file_path\n" .
 
1510
          "innodb_log_group_home_dir=$root\n" .
 
1511
          "innodb_log_files_in_group=$innodb_log_files_in_group\n" .
 
1512
          "innodb_log_file_size=$innodb_log_file_size\n";
 
1513
    close(FILE);
 
1514
 
 
1515
    if ($option_stream) {
 
1516
        my $filename_dir = dirname($filename);
 
1517
        my $filename_name = basename($filename);
 
1518
        if ($option_stream eq 'tar') {
 
1519
            system("cd $filename_dir; tar chf - $filename_name")
 
1520
                and Die "Failed to stream '$filename_name': $!";
 
1521
        }
 
1522
        unlink $filename || Die "Failed to delete '$filename': $!";
 
1523
    }
 
1524
}
 
1525
 
 
1526
 
 
1527
#
 
1528
# check_args subroutine checks command line arguments. If there is a problem,
 
1529
# this subroutine prints error message and exits.
 
1530
#
 
1531
sub check_args {
 
1532
    my $i;
 
1533
    my $rcode;
 
1534
    my $buf;
 
1535
    my $perl_version;
 
1536
 
 
1537
    # check the version of the perl we are running
 
1538
    if (!defined $^V) {
 
1539
        # this perl is prior to 5.6.0 and uses old style version string
 
1540
        my $required_version = $required_perl_version_old_style;
 
1541
        if ($] lt $required_version) {
 
1542
            print STDERR "$prefix Warning: " . 
 
1543
                "Your perl is too old! Innobackup requires\n";
 
1544
            print STDERR "$prefix Warning: perl $required_version or newer!\n";
 
1545
        }
 
1546
    }
 
1547
 
 
1548
    if (@ARGV == 0) {
 
1549
        # no command line arguments
 
1550
        print STDERR "$prefix You must specify the backup directory.\n";
 
1551
        exit(1);
 
1552
    }
 
1553
 
 
1554
    # read command line options
 
1555
    $rcode = GetOptions('compress:i' => \$option_compress,
 
1556
                        'help' => \$option_help,
 
1557
                        'version' => \$option_version,
 
1558
                        'throttle=i' => \$option_throttle,
 
1559
                        'sleep=i' => \$option_sleep,
 
1560
                        'apply-log' => \$option_apply_log,
 
1561
                        'redo-only' => \$option_redo_only,
 
1562
                        'copy-back' => \$option_copy_back,
 
1563
                        'include=s' => \$option_include,
 
1564
                        'databases=s' => \$option_databases,
 
1565
                        'tables-file=s', => \$option_tables_file,
 
1566
                        'use-memory=s' => \$option_use_memory,
 
1567
                        'uncompress' => \$option_uncompress,
 
1568
                        'export' => \$option_export,
 
1569
                        'password=s' => \$option_mysql_password,
 
1570
                        'user=s' => \$option_mysql_user,
 
1571
                        'host=s' => \$option_mysql_host,
 
1572
                        'port=s' => \$option_mysql_port,
 
1573
                        'slave-info' => \$option_slave_info,
 
1574
                        'socket=s' => \$option_mysql_socket,
 
1575
                        'no-timestamp' => \$option_no_timestamp,
 
1576
                        'defaults-file=s' => \$option_defaults_file,
 
1577
                        'incremental' => \$option_incremental,
 
1578
                        'incremental-basedir=s' => \$option_incremental_basedir,
 
1579
                        'incremental-lsn=s' => \$option_incremental_lsn,
 
1580
                        'incremental-dir=s' => \$option_incremental_dir,
 
1581
                        'extra-lsndir=s' => \$option_extra_lsndir,
 
1582
                        'remote-host=s' => \$option_remote_host,
 
1583
                        'stream=s' => \$option_stream,
 
1584
                        'tmpdir=s' => \$option_tmpdir,
 
1585
                        'no-lock' => \$option_no_lock,
 
1586
                        'ibbackup=s' => \$option_ibbackup_binary,
 
1587
                        'scpopt=s' => \$option_scp_opt,
 
1588
                        'force-tar', => \$option_force_tar,
 
1589
                        'parallel=i' => \$option_parallel,
 
1590
                        'safe-slave-backup' => \$option_safe_slave_backup,
 
1591
                        'safe-slave-backup-timeout' => $option_safe_slave_backup_timeout,
 
1592
    );
 
1593
                        
 
1594
    if (!$rcode) {
 
1595
        # failed to read options
 
1596
        print STDERR "$prefix Bad command line arguments\n";
 
1597
        exit(1);
 
1598
    }
 
1599
    if ($option_help) {
 
1600
        # print help text and exit
 
1601
        usage();
 
1602
        exit(0);
 
1603
    }
 
1604
    if ($option_version) {
 
1605
        # print program version and copyright
 
1606
        print_version();
 
1607
        exit(0);
 
1608
    }
 
1609
 
 
1610
    if ($option_compress == 0) {
 
1611
        # compression level no specified, use default level
 
1612
        $option_compress = $default_compression_level;
 
1613
    } 
 
1614
 
 
1615
    if ($option_compress == 999) {
 
1616
        # compress option not given in the command line
 
1617
        $option_compress = 0;
 
1618
    }
 
1619
 
 
1620
    if (@ARGV < 1) {
 
1621
        print STDERR "$prefix Missing command line argument\n";
 
1622
        exit(1);
 
1623
    } elsif (@ARGV > 1) {
 
1624
        print STDERR "$prefix Too many command line arguments\n";
 
1625
        exit(1);
 
1626
    }
 
1627
 
 
1628
    if ($option_stream) {
 
1629
        if ($option_stream eq 'tar') {
 
1630
           if ( !$option_force_tar ) {
 
1631
              $option_tar4ibd = 'tar4ibd';
 
1632
            }
 
1633
            else {
 
1634
               print STDERR "Forcing tar instead of tar4ibd\n";
 
1635
            }
 
1636
        } elsif ($option_stream eq 'tar4ibd') {
 
1637
            $option_stream = 'tar';
 
1638
            $option_tar4ibd = 'tar4ibd';
 
1639
        } elsif ($option_stream eq 'cpio') {
 
1640
            print STDERR "$prefix --stream=cpio is not supported yet\n";
 
1641
            exit(1);
 
1642
        } else {
 
1643
            print STDERR "$prefix Unknown option --stream=$option_stream\n";
 
1644
            exit(1);
 
1645
        }
 
1646
    }
 
1647
 
 
1648
    # get options file name
 
1649
    #$config_file = $ARGV[0];
 
1650
 
 
1651
    if (!$option_apply_log && !$option_copy_back) {
 
1652
        # we are making a backup, get backup root directory
 
1653
        $backup_root = $ARGV[0];
 
1654
        if ($option_incremental) {
 
1655
            my @dirs = `ls -1 -t $backup_root`;
 
1656
            my $inc_dir = $dirs[0];
 
1657
            chomp($inc_dir);
 
1658
            if ($option_incremental_basedir) {
 
1659
                $incremental_basedir = $option_incremental_basedir;
 
1660
            } else {
 
1661
                $incremental_basedir = File::Spec->catfile($backup_root, $inc_dir);
 
1662
            }
 
1663
 
 
1664
            #print STDERR "--incremental_basedir=$incremental_basedir\n";
 
1665
            #print STDERR "incremental backup is not supported for now.\n";
 
1666
            #exit(1);
 
1667
        }
 
1668
    } else {
 
1669
        # get backup directory
 
1670
        $backup_dir = File::Spec->rel2abs($ARGV[0]);
 
1671
    }        
 
1672
 
 
1673
    print STDERR "\n";
 
1674
 
 
1675
    parse_databases_option_value();
 
1676
    parse_tables_file_option_value($option_tables_file);
 
1677
}
 
1678
 
 
1679
 
 
1680
#
 
1681
# make_backup_dir subroutine creates a new backup directory and returns
 
1682
# its name.
 
1683
#
 
1684
sub make_backup_dir {
 
1685
    my $dir;
 
1686
    my $innodb_data_file_path = 
 
1687
        get_option(\%config, 'mysqld', 'innodb_data_file_path');
 
1688
 
 
1689
    # create backup directory
 
1690
    $dir = $backup_root;
 
1691
    if ($option_stream) {
 
1692
        return $dir;
 
1693
    }
 
1694
 
 
1695
    $dir .= '/' . strftime("%Y-%m-%d_%H-%M-%S", localtime())
 
1696
       unless $option_no_timestamp;
 
1697
    if (!$option_remote_host) {
 
1698
        mkdir($dir, 0777) || Die "Failed to create backup directory $dir: $!";
 
1699
    } else {
 
1700
        system("ssh $option_remote_host mkdir $dir");
 
1701
    }
 
1702
 
 
1703
    # create subdirectories for ibdata files if needed
 
1704
#    foreach my $a (split(/;/, $innodb_data_file_path)) {
 
1705
#        my $path = (split(/:/,$a))[0];
 
1706
#        my @relative_path = split(/\/+/, $path);
 
1707
#        pop @relative_path;
 
1708
#        if (@relative_path) {
 
1709
#            # there is a non-trivial path from the backup directory
 
1710
#            # to the directory of this backup ibdata file, check
 
1711
#            # that all the directories in the path exist.
 
1712
#            create_path_if_needed($dir, \@relative_path);
 
1713
#        }
 
1714
#    }
 
1715
 
 
1716
    return $dir;
 
1717
}
 
1718
 
 
1719
 
 
1720
#
 
1721
# create_path_if_needed subroutine checks that all components
 
1722
# in the given relative path are directories. If the
 
1723
# directories do not exist, they are created.
 
1724
#    Parameters:
 
1725
#       root           a path to the root directory of the relative pathname
 
1726
#       relative_path  a relative pathname (a reference to an array of 
 
1727
#                      pathname components) 
 
1728
#
 
1729
sub create_path_if_needed {
 
1730
    my $root = shift;
 
1731
    my $relative_path = shift;
 
1732
    my $path;
 
1733
 
 
1734
    $path = $root;
 
1735
    foreach $a (@{$relative_path}) {
 
1736
        $path = $path . "/" . $a;
 
1737
        if (!$option_remote_host) {
 
1738
            if (! -d $path) {
 
1739
                # this directory does not exist, create it !
 
1740
                mkdir($path, 0777) || Die "Failed to create backup directory: $!";
 
1741
            }
 
1742
        } else {
 
1743
            if (system("ssh $option_remote_host test -d $path") != 0) {
 
1744
                system("ssh $option_remote_host mkdir $path");
 
1745
            }
 
1746
        }
 
1747
    }
 
1748
}
 
1749
 
 
1750
 
 
1751
#
 
1752
# remove_from_array subroutine removes excluded element from the array.
 
1753
#    Parameters:
 
1754
#       array_ref   a reference to an array of strings
 
1755
#       excluded   a string to be excluded from the copy
 
1756
#  
 
1757
sub remove_from_array {
 
1758
    my $array_ref = shift;
 
1759
    my $excluded = shift;
 
1760
    my @copy = ();
 
1761
    my $size = 0;
 
1762
 
 
1763
    foreach my $str (@{$array_ref}) {
 
1764
        if ($str ne $excluded) {
 
1765
            $copy[$size] = $str;
 
1766
            $size = $size + 1;
 
1767
        }
 
1768
    }
 
1769
    @{$array_ref} = @copy;
 
1770
}
 
1771
 
 
1772
 
 
1773
#
 
1774
# backup_files subroutine copies .frm, .MRG, .MYD and .MYI files to 
 
1775
# backup directory.
 
1776
#
 
1777
sub backup_files {
 
1778
    my $source_dir = get_option(\%config, 'mysqld', 'datadir');
 
1779
    my @list;
 
1780
    my $file;
 
1781
    my $database;
 
1782
    my $wildcard = '*.{frm,MYD,MYI,MRG,TRG,TRN,ARM,ARZ,CSM,CSV,opt,par}';
 
1783
 
 
1784
    opendir(DIR, $source_dir) 
 
1785
        || Die "Can't open directory '$source_dir': $!\n";
 
1786
    $now = current_time();
 
1787
    print STDERR "\n$now  $prefix Starting to backup .frm, .MRG, .MYD, .MYI,\n";
 
1788
    print STDERR "$prefix .TRG, .TRN, .ARM, .ARZ, .CSM, .CSV and .opt files in\n";
 
1789
    print STDERR "$prefix subdirectories of '$source_dir'\n";
 
1790
    # loop through all database directories
 
1791
    while (defined($database = readdir(DIR))) {
 
1792
        my $print_each_file = 0;
 
1793
        my $file_c;
 
1794
        my @scp_files;
 
1795
        # skip files that are not database directories
 
1796
        if ($database eq '.' || $database eq '..') { next; }
 
1797
        next unless -d "$source_dir/$database";
 
1798
             next unless check_if_required($database);
 
1799
        
 
1800
        if (!$option_remote_host && !$option_stream) {
 
1801
            if (! -e "$backup_dir/$database") {
 
1802
                # create database directory for the backup
 
1803
                mkdir("$backup_dir/$database", 0777)
 
1804
                    || Die "Couldn't create directory '$backup_dir/$database': $!";
 
1805
            }
 
1806
        } elsif ($option_remote_host) {
 
1807
            if (system("ssh $option_remote_host test -e $backup_dir/$database")
 
1808
                    != 0) {
 
1809
                system("ssh $option_remote_host mkdir $backup_dir/$database");
 
1810
            }
 
1811
        }
 
1812
 
 
1813
        # copy files of this database
 
1814
        opendir(DBDIR, "$source_dir/$database");
 
1815
        @list = grep(/\.(frm)|(MYD)|(MYI)|(MRG)|(TRG)|(TRN)|(ARM)|(ARZ)|(CSM)|(CSV)|(opt)|(par)$/, readdir(DBDIR)); 
 
1816
        closedir DBDIR;
 
1817
        $file_c = @list;
 
1818
        if ($file_c <= $backup_file_print_limit) {
 
1819
            $print_each_file = 1;
 
1820
        } else {
 
1821
            print STDERR "$prefix Backing up files " . 
 
1822
                "'$source_dir/$database/$wildcard' ($file_c files)\n";
 
1823
        }
 
1824
        foreach $file (@list) {
 
1825
            # copying may take a long time, so we have to prevent
 
1826
            # mysql connection from timing out
 
1827
            mysql_keep_alive();
 
1828
            next unless check_if_required($database, $file);
 
1829
 
 
1830
            if($option_include) {
 
1831
              if (!("$database.$file" =~ /$option_include/)) {
 
1832
                print STDERR "$database.$file is skipped because it does not match $option_include.\n";
 
1833
                next;
 
1834
              }
 
1835
            }
 
1836
 
 
1837
               
 
1838
            if ($print_each_file) {
 
1839
                print STDERR "$prefix Backing up file '$source_dir/$database/$file'\n";
 
1840
            }
 
1841
            if (!$option_remote_host && !$option_stream) {
 
1842
                $src_name = escape_path("$source_dir/$database/$file");
 
1843
                $dst_name = escape_path("$backup_dir/$database");
 
1844
                system("$CP_CMD \"$src_name\" \"$dst_name\"")
 
1845
                    and Die "Failed to copy file '$file': $!";
 
1846
            } elsif ($option_remote_host) {
 
1847
                # Queue up files for one single scp per database.
 
1848
                push(@scp_files, "'$file'");
 
1849
            } elsif($option_stream eq 'tar') {
 
1850
                my $ret = 0;
 
1851
                my $file_name = substr($file, rindex($file, '/') + 1);
 
1852
                $file_name=~s/([\$\\\" ])/\\$1/g;
 
1853
                $ret = system("cd $source_dir; tar cf - $database/$file_name") >> 8;
 
1854
                if ($ret == 1) {
 
1855
                    print STDERR "$prefix If you use GNU tar, this warning can be ignored.\n";
 
1856
                } elsif ($ret != 0) {
 
1857
                    print STDERR "$prefix tar returned with exit code $ret.\n";
 
1858
                    Die "Failed to stream '$database/$file_name': $!";
 
1859
                }
 
1860
            }
 
1861
        }
 
1862
        if ($option_remote_host and @scp_files) {
 
1863
          my $scp_file_list = join(" ", map { "$source_dir/$database/$_" } @scp_files);
 
1864
          system("scp $option_scp_opt $scp_file_list '$option_remote_host:$backup_dir/$database/'")
 
1865
              and Die "Failed to execute \"scp $option_scp_opt $scp_file_list '$option_remote_host:$backup_dir/$database/'\": $!";
 
1866
        }
 
1867
    }
 
1868
    closedir(DIR);
 
1869
 
 
1870
    $now = current_time();
 
1871
    print STDERR "$now  $prefix Finished backing up .frm, .MRG, .MYD, .MYI, .TRG, .TRN, .ARM, .ARZ, .CSV, .CSM and .opt files\n\n";
 
1872
}
 
1873
 
 
1874
 
 
1875
#
 
1876
# file_to_array subroutine reads the given text file into an array and
 
1877
# stores each line as an element of the array. The end-of-line
 
1878
# character(s) are removed from the lines stored in the array.
 
1879
#    Parameters:
 
1880
#       filename   name of a text file
 
1881
#       lines_ref  a reference to an array
 
1882
#
 
1883
sub file_to_array {
 
1884
    my $filename = shift;
 
1885
    my $lines_ref = shift;
 
1886
    
 
1887
    open(FILE, $filename) || Die "can't open file '$filename': $!";
 
1888
    @{$lines_ref} = <FILE>;
 
1889
    close(FILE) || Die "can't close file '$filename': $!";
 
1890
 
 
1891
    foreach my $a (@{$lines_ref}) {
 
1892
        chomp($a);
 
1893
    }
 
1894
}
 
1895
 
 
1896
 
 
1897
#
 
1898
# unescape_string subroutine expands escape sequences found in the string and
 
1899
# returns the expanded string. It also removes possible single or double quotes
 
1900
# around the value.
 
1901
#    Parameters:
 
1902
#       value   a string
 
1903
#    Return value:
 
1904
#       a string with expanded escape sequences
 
1905
 
1906
sub unescape_string {
 
1907
    my $value = shift;
 
1908
    my $result = '';
 
1909
    my $offset = 0;
 
1910
 
 
1911
    # remove quotes around the value if they exist
 
1912
    if (length($value) >= 2) {
 
1913
        if ((substr($value, 0, 1) eq "'" && substr($value, -1, 1) eq "'")
 
1914
            || (substr($value, 0, 1) eq '"' && substr($value, -1, 1) eq '"')) {
 
1915
            $value = substr($value, 1, -1);
 
1916
        }
 
1917
    }
 
1918
    
 
1919
    # expand escape sequences
 
1920
    while ($offset < length($value)) {
 
1921
        my $pos = index($value, "\\", $offset);
 
1922
        if ($pos < 0) {
 
1923
            $pos = length($value);
 
1924
            $result = $result . substr($value, $offset, $pos - $offset);
 
1925
            $offset = $pos;
 
1926
        } else {
 
1927
            my $replacement = substr($value, $pos, 2);
 
1928
            my $escape_code = substr($value, $pos + 1, 1);
 
1929
            if (exists $option_value_escapes{$escape_code}) {
 
1930
                $replacement = $option_value_escapes{$escape_code};
 
1931
            }
 
1932
            $result = $result 
 
1933
                . substr($value, $offset, $pos - $offset)
 
1934
                . $replacement;
 
1935
            $offset = $pos + 2;
 
1936
        }
 
1937
    }
 
1938
 
 
1939
    return $result;
 
1940
}
 
1941
 
 
1942
 
 
1943
#
 
1944
# read_config_file subroutine reads MySQL options file and
 
1945
# returns the options in a hash containing one hash per group.
 
1946
#    Parameters:
 
1947
#       filename    name of a MySQL options file
 
1948
#       groups_ref  a reference to hash variable where the read
 
1949
#                   options are returned
 
1950
#
 
1951
sub read_config_file {
 
1952
    #my $filename = shift;
 
1953
    my $groups_ref = shift;
 
1954
    my @lines ;
 
1955
    my $i;
 
1956
    my $group;
 
1957
    my $group_hash_ref;
 
1958
 
 
1959
    my $cmdline = '';
 
1960
    my $options = '';
 
1961
 
 
1962
    if ($option_defaults_file) {
 
1963
        $options = $options . " --defaults-file=\"$option_defaults_file\" ";
 
1964
    }
 
1965
 
 
1966
    $options = $options . "--print-param";
 
1967
 
 
1968
 
 
1969
    # read file to an array, one line per element
 
1970
    #file_to_array($filename, \@lines);
 
1971
    $cmdline = "$option_ibbackup_binary $options";
 
1972
    @lines = `$cmdline`;
 
1973
 
 
1974
    # classify lines and save option values
 
1975
    $group = 'default';
 
1976
    $group_hash_ref = {}; 
 
1977
    ${$groups_ref}{$group} = $group_hash_ref;
 
1978
    # this pattern described an option value which may be
 
1979
    # quoted with single or double quotes. This pattern
 
1980
    # does not work by its own. It assumes that the first
 
1981
    # opening parenthesis in this string is the second opening
 
1982
    # parenthesis in the full pattern. 
 
1983
    my $value_pattern = q/((["'])([^\\\4]|(\\[^\4]))*\4)|([^\s]+)/;
 
1984
    for ($i = 0; $i < @lines; $i++) {
 
1985
      SWITCH: for ($lines[$i]) {
 
1986
          # comment
 
1987
          /^\s*(#|;)/
 
1988
             && do { last; };
 
1989
 
 
1990
          # group      
 
1991
          /^\s*\[(.*)\]/ 
 
1992
                && do { 
 
1993
                    $group = $1; 
 
1994
                    if (!exists ${$groups_ref}{$group}) {
 
1995
                        $group_hash_ref = {}; 
 
1996
                        ${$groups_ref}{$group} = $group_hash_ref;
 
1997
                    } else {
 
1998
                        $group_hash_ref = ${$groups_ref}{$group};
 
1999
                    }
 
2000
                    last; 
 
2001
                };
 
2002
 
 
2003
          # option
 
2004
          /^\s*([^\s=]+)\s*(#.*)?$/
 
2005
              && do { 
 
2006
                  ${$group_hash_ref}{$1} = '';
 
2007
                  last; 
 
2008
              };
 
2009
 
 
2010
          # set-variable = option = value
 
2011
          /^\s*set-variable\s*=\s*([^\s=]+)\s*=\s*($value_pattern)\s*(#.*)?$/
 
2012
              && do { ${$group_hash_ref}{$1} = unescape_string($2); last; };
 
2013
 
 
2014
          # option = value
 
2015
          /^\s*([^\s=]+)\s*=\s*($value_pattern)\s*(#.*)?$/
 
2016
              && do { ${$group_hash_ref}{$1} = unescape_string($2); last; };
 
2017
 
 
2018
          # empty line
 
2019
          /^\s*$/
 
2020
              && do { last; };
 
2021
 
 
2022
          # unknown
 
2023
          print("$prefix: Warning: Ignored unrecognized line ",
 
2024
                $i + 1,
 
2025
                " in options : '${lines[$i]}'\n"
 
2026
                );
 
2027
      }
 
2028
   }
 
2029
}
 
2030
    
 
2031
 
 
2032
#
 
2033
# get_option subroutine returns the value of given option in the config
 
2034
# structure. If option is missing, this subroutine calls exit.
 
2035
#    Parameters:
 
2036
#       config_ref   a reference to a config data
 
2037
#       group        option group name
 
2038
#       option_name  name of the option
 
2039
#    Return value:
 
2040
#       option value as a string
 
2041
#
 
2042
sub get_option {
 
2043
    my $config_ref = shift;
 
2044
    my $group = shift;
 
2045
    my $option_name = shift;
 
2046
    my $group_hash_ref;
 
2047
 
 
2048
    if (!exists $config{$group}) {
 
2049
        # no group
 
2050
        print STDERR "$prefix fatal error: no '$group' group in MySQL options\n";
 
2051
        print STDERR "$prefix fatal error: OR no 'datadir' option in group '$group' in MySQL options\n";
 
2052
        exit(1);
 
2053
    }
 
2054
    
 
2055
    $group_hash_ref = ${$config_ref}{$group};
 
2056
    if (!exists ${$group_hash_ref}{$option_name}) {
 
2057
        # no option
 
2058
        print STDERR "$prefix fatal error: no '$option_name' option in group '$group' in MySQL options\n";
 
2059
        exit(1);
 
2060
    }
 
2061
 
 
2062
    return ${$group_hash_ref}{$option_name};
 
2063
}
 
2064
 
 
2065
# check_if_required subroutine returns 1 if the specified database and
 
2066
# table needs to be backed up.
 
2067
#    Parameters:
 
2068
#       $_[0]        name of database to be checked 
 
2069
#       $_[1]        full path of table file (This argument is optional)
 
2070
#    Return value:
 
2071
#       1 if backup should be done and 0 if not
 
2072
#
 
2073
sub check_if_required {
 
2074
   my ( $db, $table_path ) = @_;
 
2075
   my $db_count  = scalar keys %databases_list;
 
2076
   my $tbl_count = scalar keys %table_list;
 
2077
   my $filename;
 
2078
   my $table;
 
2079
 
 
2080
   if ( $db_count == 0 && $tbl_count == 0 ) {
 
2081
      # No databases defined with --databases option, include all databases,
 
2082
      # and no tables defined with --tables-file option, include all tables.
 
2083
      return 1;
 
2084
   }
 
2085
   else {
 
2086
      if ( $table_path ) {
 
2087
         # get the last component in the table pathname 
 
2088
         $filename = (reverse(split(/\//, $table_path)))[0];
 
2089
         # get name of the table by removing file suffix
 
2090
         $table = (split(/\./, $filename))[0];
 
2091
      }
 
2092
   }
 
2093
 
 
2094
   # Filter for --databases.
 
2095
   if ( $db_count ) {
 
2096
      if (defined $databases_list{$db}) {
 
2097
         if (defined $table_path) {
 
2098
            my $db_hash = $databases_list{$db};
 
2099
            $db_count = keys %$db_hash;
 
2100
            if ($db_count > 0 && ! defined $databases_list{$db}->{$table}) {
 
2101
               # --databases option specified, but table is not included
 
2102
               return 0;
 
2103
            }
 
2104
         }
 
2105
         # include this database and table
 
2106
         return 1;
 
2107
      }
 
2108
      else {
 
2109
         # --databases option given, but database is not included
 
2110
         return 0;
 
2111
      }
 
2112
   }
 
2113
 
 
2114
   # Filter for --tables-file.
 
2115
   if ( $tbl_count ) {
 
2116
      return 0 unless exists $table_list{$db};
 
2117
      return 0 if $table && !$table_list{$db}->{$table};
 
2118
   }
 
2119
 
 
2120
   return 1;  # backup the table
 
2121
}
 
2122
 
 
2123
 
 
2124
# parse_databases_option_value subroutine parses the value of 
 
2125
# --databases option. If the option value begins with a slash
 
2126
# it is considered a pathname and the option value is read
 
2127
# from the file.
 
2128
 
2129
# This subroutine sets the global "databases_list" variable.
 
2130
#
 
2131
sub parse_databases_option_value {
 
2132
    my $item;
 
2133
 
 
2134
    if ($option_databases =~ /^\//) {
 
2135
        # the value of the --databases option begins with a slash,
 
2136
        # the option value is pathname of the file containing
 
2137
        # list of databases
 
2138
        if (! -f $option_databases) {
 
2139
            Die "can't find file '$option_databases'";
 
2140
        }
 
2141
 
 
2142
        # read from file the value of --databases option
 
2143
        my @lines;
 
2144
        file_to_array($option_databases, \@lines);
 
2145
        $option_databases = join(" ", @lines);
 
2146
    }
 
2147
 
 
2148
    # mark each database or database.table definition in the
 
2149
    # global databases_list.
 
2150
    foreach $item (split(/\s/, $option_databases)) {
 
2151
        my $db = "";
 
2152
        my $table = "";
 
2153
        my %hash;
 
2154
 
 
2155
        if ($item eq "") {
 
2156
            # ignore empty strings
 
2157
            next;
 
2158
        }
 
2159
 
 
2160
        # get database and table names
 
2161
        if ($item =~ /(\S*)\.(\S*)/) {
 
2162
            # item is of the form DATABASE.TABLE
 
2163
            $db = $1;
 
2164
            $table = $2;
 
2165
        } else {
 
2166
            # item is database name, table is undefined
 
2167
            $db = $item;
 
2168
        }
 
2169
 
 
2170
        if (! defined $databases_list{$db}) {
 
2171
            # create empty hash for the database
 
2172
            $databases_list{$db} = \%hash;
 
2173
        }
 
2174
        if ($table ne "") {
 
2175
            # add mapping table --> 1 to the database hash
 
2176
            my $h = $databases_list{$db};
 
2177
            $h->{$table} = 1;
 
2178
        }
 
2179
    }
 
2180
}
 
2181
 
 
2182
# Parse the --tables-file file to determine which InnoDB tables
 
2183
# are backedup up.  Only backedup tables have their .frm, etc.
 
2184
# files copied.
 
2185
sub parse_tables_file_option_value {
 
2186
   my ( $filename ) = @_;
 
2187
 
 
2188
   return unless $filename;
 
2189
 
 
2190
   open my $fh, '<', $filename;
 
2191
   if ( $fh ) {
 
2192
      while ( my $line = <$fh> ) {
 
2193
         chomp $line;
 
2194
         my ( $db, $tbl ) = $line =~ m/\s*([^\.]+)\.([^\.]+)\s*/;
 
2195
         if ( $db && $tbl ) {
 
2196
            $table_list{$db}->{$tbl} = 1;
 
2197
            print STDERR "$prefix $db.$tbl will be registerd to the list\n";
 
2198
         }
 
2199
         else {
 
2200
            warn "$prefix Invalid line in $filename: $line";
 
2201
         }
 
2202
      }
 
2203
   }
 
2204
   else {
 
2205
      warn "$prefix Cannot read --tables-file $filename: $OS_ERROR";
 
2206
   }
 
2207
 
 
2208
   return;
 
2209
}
 
2210
 
 
2211
sub escape_path {
 
2212
  my $str = shift;
 
2213
  if ($win eq 1) {
 
2214
    $str =~ s/\//\\/g;
 
2215
    $str =~ s/\\\\/\\/g;
 
2216
    }
 
2217
  else{
 
2218
    $str =~ s/\/\//\//g;
 
2219
    }
 
2220
  return $str;
 
2221
 
 
2222
}
 
2223
 
 
2224
sub set_xtrabackup_version {
 
2225
# Based on MySQL version choose correct binary
 
2226
#  MySQL 5.0.* - xtrabackup_51
 
2227
#  MySQL 5.1.* - xtrabackup_51
 
2228
#  MySQL 5.1.* with InnoDB plugin - xtrabackup
 
2229
#  Percona Server >= 11.0 - xtrabackup
 
2230
#  MySQL 5.5.* - xtrabackup_55 
 
2231
 
 
2232
my @lines;
 
2233
my $var_version = '';
 
2234
my $var_innodb_version = '';
 
2235
my $ibbackup_binary;
 
2236
mysql_open();
 
2237
mysql_send "SHOW VARIABLES LIKE 'version'\\G";
 
2238
file_to_array($mysql_stdout, \@lines);
 
2239
for (@lines) {
 
2240
        $var_version = $1 if /Value:\s+(\S+)/;
 
2241
        }
 
2242
mysql_send "SHOW VARIABLES LIKE 'innodb_version'\\G";
 
2243
file_to_array($mysql_stdout, \@lines);
 
2244
for (@lines) {
 
2245
        $var_innodb_version = $1 if /Value:\s+(\S+)/;
 
2246
        }
 
2247
if($var_version =~ m/5\.0\.\d/){
 
2248
        $ibbackup_binary = ($win eq 1 ? 'xtrabackup.exe' : 'xtrabackup_51');
 
2249
}
 
2250
if($var_version =~ m/5\.1\.\d/ and $var_innodb_version =~ m//){
 
2251
        $ibbackup_binary = ($win eq 1 ? 'xtrabackup.exe' : 'xtrabackup_51');
 
2252
}
 
2253
if($var_version =~ m/5\.1\.\d/ and $var_innodb_version =~ m/1\.0\.\d+$/){
 
2254
        $ibbackup_binary = ($win eq 1 ? 'xtrabackup.exe' : 'xtrabackup');
 
2255
}
 
2256
if($var_version =~ m/5\.1\.\d/ and $var_innodb_version =~ m/1\.0\.\d+-\d/){
 
2257
        $ibbackup_binary = ($win eq 1 ? 'xtrabackup.exe' : 'xtrabackup');
 
2258
}
 
2259
if($var_version =~ m/5\.5\.\d/){
 
2260
        $ibbackup_binary = ($win eq 1 ? 'xtrabackup.exe' : 'xtrabackup_55');
 
2261
}
 
2262
mysql_close();
 
2263
return $ibbackup_binary;
 
2264
}
 
2265
 
 
2266
# Wait until it's safe to backup a slave.  Returns immediately if
 
2267
# the host isn't a slave.  Currently there's only one check:
 
2268
# Slave_open_temp_tables has to be zero.  Dies on timeout.
 
2269
sub wait_for_safe_slave {
 
2270
   my @lines;
 
2271
 
 
2272
   my $host_is_slave = 0;
 
2273
   mysql_send 'SHOW SLAVE STATUS\G;';
 
2274
   file_to_array($mysql_stdout, \@lines);
 
2275
   foreach my $line ( @lines ) {
 
2276
      if ( $line =~ m/Read_Master_Log_Pos/ ) {
 
2277
         $host_is_slave = 1;
 
2278
         last;
 
2279
      }
 
2280
   }
 
2281
   if ( !$host_is_slave ) {
 
2282
      print STDERR "$prefix: Not checking slave open temp tables for --safe-slave-backup because host is not a slave\n";
 
2283
      return;
 
2284
   }
 
2285
 
 
2286
   mysql_send 'STOP SLAVE SQL_THREAD;';
 
2287
 
 
2288
   my $open_temp_tables = get_slave_open_temp_tables();
 
2289
   print STDERR "$prefix: Slave open temp tables: $open_temp_tables\n";
 
2290
 
 
2291
   return if $open_temp_tables == 0;
 
2292
 
 
2293
   my $sleep_time = 3;
 
2294
   my $n_attempts = int($option_safe_slave_backup_timeout / $sleep_time) || 1;
 
2295
   while ( $n_attempts-- ) {
 
2296
      print STDERR "$prefix: Starting slave SQL thread, waiting $sleep_time seconds, then checking Slave_open_temp_tables again ($n_attempts attempts remaining)...\n";
 
2297
      
 
2298
      mysql_send 'START SLAVE SQL_THREAD;';
 
2299
      sleep $sleep_time;
 
2300
      mysql_send 'STOP SLAVE SQL_THREAD;';
 
2301
 
 
2302
      $open_temp_tables = get_slave_open_temp_tables();
 
2303
      print STDERR "$prefix: Slave open temp tables: $open_temp_tables\n";
 
2304
      if ( !$open_temp_tables ) {
 
2305
         print STDERR "$prefix: Slave is safe to backup\n";
 
2306
         return;
 
2307
      }
 
2308
   } 
 
2309
 
 
2310
   Die "Slave_open_temp_tables did not become zero after waiting $option_safe_slave_backup_timeout seconds";
 
2311
}
 
2312
 
 
2313
sub get_slave_open_temp_tables {
 
2314
   my @lines;
 
2315
   mysql_send 'SHOW STATUS LIKE "slave_open_temp_tables"\G;';
 
2316
   file_to_array($mysql_stdout, \@lines);
 
2317
   my $last_value;
 
2318
   for my $i ( 0..$#lines ) {
 
2319
      $last_value = $i + 1
 
2320
         if $lines[$i] =~ m/Variable_name: Slave_open_temp_tables/i;
 
2321
   }
 
2322
   Die "SHOW STATUS LIKE 'slave_open_temp_tables' did not return anything"
 
2323
      unless $last_value;
 
2324
 
 
2325
   Die "Failed to get Slave_open_temp_tables from SHOW STATUS"
 
2326
      unless defined $lines[$last_value];
 
2327
 
 
2328
   my ($n) = $lines[$last_value] =~ m/(\d+)/;
 
2329
   return $n;
 
2330
}
 
2331
 
 
2332
=pod
 
2333
 
 
2334
=head1 NAME
 
2335
 
 
2336
innobackupex - Non-blocking backup tool for InnoDB, XtraDB and HailDB databases
 
2337
 
 
2338
=head1 SYNOPOSIS
 
2339
 
 
2340
innobackupex [--compress[=LEVEL]] [--include=REGEXP] [--user=NAME] 
 
2341
             [--password=WORD] [--port=PORT] [--socket=SOCKET]
 
2342
             [--no-timestamp] [--ibbackup-binary=IBBACKUP-BINARY]
 
2343
             [--slave-info] [--stream=tar] [--force-tar]
 
2344
             [--scpopt=OPTIONS-FOR-SCP] [--defaults-file=MY.CNF]
 
2345
             [--databases=LIST] [--remote-host=HOSTNAME] [--no-lock] 
 
2346
             [--tmpdir=DIRECTORY] [--tables-file=FILE]
 
2347
             [--incremental] [--incremental-basedir]
 
2348
             [--incremental-dir] [--incremental-lsn]
 
2349
             BACKUP-ROOT-DIR
 
2350
 
 
2351
innobackupex --apply-log [--use-memory=MB] [--uncompress]
 
2352
             [--defaults-file=MY.CNF]
 
2353
             [--export] [--redo-only] [--ibbackup=IBBACKUP-BINARY]
 
2354
             BACKUP-DIR
 
2355
 
 
2356
innobackupex --copy-back [--defaults-file=MY.CNF] BACKUP-DIR
 
2357
 
 
2358
=head1 DESCRIPTION
 
2359
 
 
2360
The first command line above makes a hot backup of a MySQL database.
 
2361
By default it creates a backup directory (named by the current date
 
2362
and time) in the given backup root directory.  With the --no-timestamp
 
2363
option it does not create a time-stamped backup directory, but it puts
 
2364
the backup in the given directory (which must not exist).  This
 
2365
command makes a complete backup of all MyISAM and InnoDB tables and
 
2366
indexes in all databases or in all of the databases specified with the
 
2367
--databases option.  The created backup contains .frm, .MRG, .MYD,
 
2368
.MYI, .TRG, .TRN, .ARM, .ARZ, .CSM, CSV, .opt, .par, and InnoDB data and log files.
 
2369
The MY.CNF options file defines the location of the database.  This command
 
2370
connects to the MySQL server using the mysql client program, and runs
 
2371
xtrabackup as a child process.
 
2372
 
 
2373
The --apply-log command prepares a backup for starting a MySQL
 
2374
server on the backup. This command recovers InnoDB data files as specified
 
2375
in BACKUP-DIR/backup-my.cnf using BACKUP-DIR/xtrabackup_logfile,
 
2376
and creates new InnoDB log files as specified in BACKUP-DIR/backup-my.cnf.
 
2377
The BACKUP-DIR should be the path to a backup directory created by
 
2378
xtrabackup. This command runs xtrabackup as a child process, but it does not 
 
2379
connect to the database server.
 
2380
 
 
2381
The --copy-back command copies data, index, and log files
 
2382
from the backup directory back to their original locations.
 
2383
The MY.CNF options file defines the original location of the database.
 
2384
The BACKUP-DIR is the path to a backup directory created by xtrabackup.
 
2385
 
 
2386
On success the exit code innobackupex is 0. A non-zero exit code 
 
2387
indicates an error.
 
2388
 
 
2389
 
 
2390
=head1 OPTIONS
 
2391
 
 
2392
=over
 
2393
 
 
2394
=item --apply-log
 
2395
 
 
2396
Prepare a backup in BACKUP-DIR by applying the transaction log file named "xtrabackup_logfile" located in the same directory. Also, create new transaction logs. The InnoDB configuration is read from the file "backup-my.cnf".
 
2397
 
 
2398
=item --copy-back
 
2399
 
 
2400
Copy all the files in a previously made backup from the backup directory to their original locations.
 
2401
 
 
2402
=item --databases=LIST
 
2403
 
 
2404
This option specifies the list of databases that innobackupex should back up.  The option accepts a string argument. The list is of the form "databasename1[.table_name1] databasename2[.table_name2] . . .". If this option is not specified, all databases containing MyISAM and InnoDB tables will be backed up. Please make sure that --databases contains all of the InnoDB databases and tables, so that all of the innodb.frm files are also backed up. In case the list is very long, this can be specified in a file, and the full path of the file can be specified instead of the list. (See option --tables-file.)
 
2405
 
 
2406
=item --defaults-file=[MY.CNF]
 
2407
 
 
2408
This option specifies what file to read the default MySQL options from.  The option accepts a string argument. It is also passed directly to xtrabackup's --defaults-file option. See the xtrabackup documentation for details.
 
2409
 
 
2410
=item --export
 
2411
 
 
2412
This option is passed directly to xtrabackup's --export option. It enables exporting individual tables for import into another server. See the xtrabackup documentation for details.
 
2413
 
 
2414
=item --extra-lsndir=DIRECTORY
 
2415
 
 
2416
This option specifies the directory in which to save an extra copy of the "xtrabackup_checkpoints" file.  The option accepts a string argument. It is passed directly to xtrabackup's --extra-lsndir option. See the xtrabackup documentation for details.
 
2417
 
 
2418
=item --force-tar
 
2419
 
 
2420
This option forces the use of tar when creating a streamed backup, rather than tar4ibd, which is the default.
 
2421
 
 
2422
=item --help
 
2423
 
 
2424
This option displays a help screen and exits.
 
2425
 
 
2426
=item --host=HOST
 
2427
 
 
2428
This option specifies the host to use when connecting to the database server with TCP/IP.  The option accepts a string argument. It is passed to the mysql child process without alteration. See mysql --help for details.
 
2429
 
 
2430
=item --ibbackup-binary=IBBACKUP-BINARY
 
2431
 
 
2432
This option specifies which xtrabackup binary should be used.  The option accepts a string argument. IBBACKUP-BINARY should be the command used to run XtraBackup. The option can be useful if the xtrabackup binary is not in your search path or working directory. If this option is not specified, innobackupex attempts to determine the binary to use automatically. By default, "xtrabackup" is the command used. However, when option --copy-back is specified, "xtrabackup_51" is the command used. And when option --apply-log is specified, the binary is used whose name is in the file "xtrabackup_binary" in the backup directory, if that file exists.
 
2433
 
 
2434
=item --include=REGEXP
 
2435
 
 
2436
This option is a regular expression to be matched against table names in databasename.tablename format. It is passed directly to xtrabackup's --tables option. See the xtrabackup documentation for details.
 
2437
 
 
2438
=item --incremental
 
2439
 
 
2440
This option tells xtrabackup to create an incremental backup, rather than a full one. It is passed to the xtrabackup child process. When this option is specified, either --incremental-lsn or --incremental-basedir can also be given. If neither option is given, option --incremental-basedir is passed to xtrabackup by default, set to the first timestamped backup directory in the backup base directory.
 
2441
 
 
2442
=item --incremental-basedir=DIRECTORY
 
2443
 
 
2444
This option specifies the directory containing the full backup that is the base dataset for the incremental backup.  The option accepts a string argument. It is used with the --incremental option.
 
2445
 
 
2446
=item --incremental-dir=DIRECTORY
 
2447
 
 
2448
This option specifies the directory where the incremental backup will be combined with the full backup to make a new full backup.  The option accepts a string argument. It is used with the --incremental option.
 
2449
 
 
2450
=item --incremental-lsn
 
2451
 
 
2452
This option specifies the log sequence number (LSN) to use for the incremental backup.  The option accepts a string argument. It is used with the --incremental option. It is used instead of specifying --incremental-basedir. For databases created by MySQL and Percona Server 5.0-series versions, specify the LSN as two 32-bit integers in high:low format. For databases created in 5.1 and later, specify the LSN as a single 64-bit integer.
 
2453
 
 
2454
=item --no-lock
 
2455
 
 
2456
Use this option to disable table lock with "FLUSH TABLES WITH READ LOCK". Use it only if ALL your tables are InnoDB and you DO NOT CARE about the binary log position of the backup.
 
2457
 
 
2458
=item --no-timestamp
 
2459
 
 
2460
This option prevents creation of a time-stamped subdirectory of the BACKUP-ROOT-DIR given on the command line. When it is specified, the backup is done in BACKUP-ROOT-DIR instead.
 
2461
 
 
2462
=item --parallel=NUMBER-OF-THREADS
 
2463
 
 
2464
This option specifies the number of threads the xtrabackup child process should use to back up files concurrently.  The option accepts an integer argument. It is passed directly to xtrabackup's --parallel option. See the xtrabackup documentation for details.
 
2465
 
 
2466
 
 
2467
=item --password=WORD
 
2468
 
 
2469
This option specifies the password to use when connecting to the database. It accepts a string argument.  It is passed to the mysql child process without alteration. See mysql --help for details.
 
2470
 
 
2471
=item --port=PORT
 
2472
 
 
2473
This option specifies the port to use when connecting to the database server with TCP/IP.  The option accepts a string argument. It is passed to the mysql child process. It is passed to the mysql child process without alteration. See mysql --help for details.
 
2474
 
 
2475
=item --redo-only
 
2476
 
 
2477
This option is passed directly to xtrabackup's --apply-log-only option. This forces xtrabackup to skip the "rollback" phase and do a "redo" only. This is necessary if the backup will have incremental changes applied to it later. See the xtrabackup documentation for details. 
 
2478
 
 
2479
=item --remote-host=HOSTNAME 
 
2480
 
 
2481
This option specifies the remote host on which the backup files will be created, by using an ssh connection.  The option accepts a string argument.
 
2482
 
 
2483
=item --safe-slave-backup
 
2484
 
 
2485
Stop slave SQL thread and wait to start backup until Slave_open_temp_tables in "SHOW STATUS" is zero. If there are no open temporary tables, the backup will take place, otherwise the SQL thread will be started and stopped until there are no open temporary tables. The backup will fail if Slave_open_temp_tables does not become zero after --safe-slave-backup-timeout seconds. The slave SQL thread will be restarted when the backup finishes.
 
2486
 
 
2487
=item --safe-slave-backup-timeout
 
2488
 
 
2489
How many seconds --safe-slave-backup should wait for Slave_open_temp_tables to become zero. (default 300)
 
2490
 
 
2491
=item --scpopt=SCP-OPTIONS
 
2492
 
 
2493
This option specifies the command line options to pass to scp when the option --remost-host is specified.  The option accepts a string argument. If the option is not specified, the default options are "-Cp -c arcfour".
 
2494
 
 
2495
=item --slave-info
 
2496
 
 
2497
This option is useful when backing up a replication slave server. It prints the binary log position and name of the master server. It also writes this information to the "xtrabackup_slave_info" file as a "CHANGE MASTER" command. A new slave for this master can be set up by starting a slave server on this backup and issuing a "CHANGE MASTER" command with the binary log position saved in the "xtrabackup_slave_info" file.
 
2498
 
 
2499
=item --socket=SOCKET
 
2500
 
 
2501
This option specifies the socket to use when connecting to the local database server with a UNIX domain socket.  The option accepts a string argument. It is passed to the mysql child process without alteration. See mysql --help for details.
 
2502
 
 
2503
=item --stream=[tar|. . .]
 
2504
 
 
2505
This option specifies the format in which to do the streamed backup.  The option accepts a string argument. The backup will be done to STDOUT in the specified format. Currently, the only supported format is tar. Uses tar4ibd, which is available in XtraBackup distributions.
 
2506
 
 
2507
=item --tables-file=FILE
 
2508
 
 
2509
This option specifies the file in which there are a list of names of the form database.  The option accepts a string argument.table, one per line. The option is passed directly to xtrabackup's --tables-file option.
 
2510
 
 
2511
=item --throttle=IOS
 
2512
 
 
2513
This option specifies a number of I/O operations (pairs of read+write) per second.  It accepts an integer argument.  It is passed directly to xtrabackup's --throttle option.
 
2514
 
 
2515
=item --tmpdir=DIRECTORY
 
2516
 
 
2517
This option specifies the location where a temporary file will be stored.  The option accepts a string argument. It should be used when --remote-host or --stream is specified. For these options, the transaction log will first be stored to a temporary file, before streaming or copying to a remote host. This option specifies the location where that temporary file will be stored. If the option is not specifed, the default is to use the value of tmpdir read from the server configuration.
 
2518
 
 
2519
=item --use-memory=MB
 
2520
 
 
2521
This option specifies the amount of memory in megabytes for xtrabackup to use for crash recovery while preparing a backup.  The option accepts a string argument. It is used only with the option --apply-log. It is passed directly to xtrabackup's --use-memory option. See the xtrabackup documentation for details.
 
2522
 
 
2523
=item --user=NAME
 
2524
 
 
2525
This option specifies the MySQL username used when connecting to the server, if that's not the current user. The option accepts a string argument.  It is passed to the mysql child process without alteration. See mysql --help for details.
 
2526
 
 
2527
=item --version
 
2528
 
 
2529
This option displays the xtrabackup version and copyright notice and then exits.
 
2530
 
 
2531
=back
 
2532
 
 
2533
=head1 BUGS
 
2534
 
 
2535
Bugs can be reported on Launchpad: https://bugs.launchpad.net/percona-xtrabackup/+filebug
 
2536
 
 
2537
=head1 COPYRIGHT
 
2538
 
 
2539
InnoDB Backup Utility Copyright 2003, 2009 Innobase Oy and Percona, Inc 2009-2011. All Rights Reserved.
 
2540
 
 
2541
This software is published under the GNU GENERAL PUBLIC LICENSE Version 2, June 1991.
 
2542
 
 
2543
=cut