~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to xtrabackup/innobackupex

add xtrabackup

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