]> git.pld-linux.org Git - packages/bacula-backup-mysql.git/blob - bacula-backup-mysql
- add performance_schema for mysql 5.5 to default exclude list
[packages/bacula-backup-mysql.git] / bacula-backup-mysql
1 #!/usr/bin/perl -ws
2 # This program is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, write to:
14 #
15 #  Free Software Foundation, Inc.
16 #  59 Temple Place - Suite 330
17 #  Boston, MA 02111-1307, USA.
18
19 # Rudimentary switch parsing. Must be in main package.
20 our $cleanup;
21
22 package BBM;
23 use strict;
24 use POSIX qw(setuid setgid);
25 use DBI;
26 use File::Temp qw(tempdir);
27 use File::Path qw(rmtree);
28
29
30 # path to Apache HTTPd-style config
31 my $config = '/etc/bacula/backup-mysql.conf';
32 my $c = new BBM::Config($config);
33
34 # now change to user mysql after we've read config
35 unless ($<) {
36         my $uid = getpwnam('mysql');
37         my $gid = getgrnam('mysql');
38         die "Can't find user/group mysql\n" unless $uid or $gid;
39
40         # CWD could not be accessible for mysql user
41         chdir("/");
42
43         $) = "$gid $gid";
44         $( = $gid;
45         $> = $< = $uid;
46 }
47
48 # setup tmpdir
49 my $backup_dir = $c->get('options', 'outdir') or die "'outdir' not defined in config\n";
50 my $tmpdir = $c->get('options', 'tmpdir')  or die "'tmpdir' not defined in config\n";
51
52 if (!-d $backup_dir && !mkdir($backup_dir) && !-d $backup_dir) {
53         die "backup dir '$backup_dir' not present and can't be created\n";
54 }
55 if (!-d $tmpdir && !mkdir($tmpdir) && !-d $tmpdir) {
56         die "tmpdir '$tmpdir' not present and can't be created\n";
57 }
58
59 # process each cluster
60 for my $cluster ($c->get('clusters', 'cluster')) {
61         print ">>> $cluster\n";
62         if ($cleanup) {
63                 cleanup_cluster($cluster);
64         } else {
65                 backup_cluster($cluster);
66         }
67         print "<<< $cluster\n";
68 }
69
70 #
71 # Usage: mysqlhotcopy $CLUSTER $DATABASE $USERNAME $PASSWORD $SOCKET
72 #
73 sub mysqlhotcopy {
74         my ($cluster, $database, $user, $password, $socket) = @_;
75
76         print ">>>> mysqlhotcopy $database\n";
77
78         my $dstdir = tempdir("bbm.XXXXXX", DIR => $tmpdir);
79
80         # make backup with mysqlhotcopy
81         my @shell = ('mysqlhotcopy');
82         push(@shell, '-u', $user) if $user;
83         push(@shell, '-p', $password) if $password;
84         push(@shell, '-S', $socket) if $socket;
85         push(@shell, $database, $dstdir);
86         system(@shell) == 0 or die "mysqlhotcopy failed: $?\n";
87
88         # put it to "production dir"
89         my $cluster_dir = "$backup_dir/$cluster";
90         if (!-d $cluster_dir && !mkdir($cluster_dir) && !-d $cluster_dir) {
91                 rmtree($dstdir);
92                 die "cluster dir '$cluster_dir' not present and can't be created\n";
93         }
94
95         my $dirname = "$backup_dir/$cluster/$database";
96         if (-d $dirname) {
97                 rmtree($dirname);
98         }
99
100         my $srcdir = "$dstdir/$database";
101         unless (rename($srcdir, $dirname)) {
102                 rmtree($dstdir);
103                 die "Rename '$srcdir'->'$dirname' failed: $!\n";
104         }
105
106         rmdir($dstdir) or warn $!;
107
108         print "<<<< mysqlhotcopy $database\n";
109 }
110
111 sub cleanup_cluster {
112         my ($cluster) = @_;
113         my $cluster_dir = "$backup_dir/$cluster";
114         print ">>>> cleanup $cluster_dir\n";
115         rmtree($cluster_dir);
116         print "<<<< cleanup $cluster_dir\n";
117 }
118
119 sub backup_cluster {
120         my ($cluster) = @_;
121
122         # get db connection info
123         my $user = $c->get($cluster, 'user') || $c->get('client', 'user');
124         my $password = $c->get($cluster, 'password') || $c->get('client', 'password');
125         my $socket = $c->get($cluster, 'socket') || $c->get('client', 'socket');
126
127         # get databases to backup
128         my @include = $c->get($cluster, 'include_database');
129         my @exclude = $c->get($cluster, 'exclude_database');
130
131         # start with include list
132         my %dbs = map { $_ => 1 } @include;
133
134         if (@exclude or !@include) {
135                 my $dbh = new BBM::DB($user, $password, $socket);
136                 my $sth = $dbh->prepare("show databases");
137                 $sth->execute();
138                 while (my($dbname) = $sth->fetchrow_array) {
139                         next if lc($dbname) eq 'information_schema';
140                         next if lc($dbname) eq 'performance_schema';
141                         $dbs{$dbname} = 1;
142                 }
143                 undef $dbh;
144         }
145
146         # remove excluded databases
147         delete @dbs{@exclude};
148
149         # now do the backup
150         foreach my $db (keys %dbs) {
151                 mysqlhotcopy($cluster, $db, $user, $password, $socket);
152         }
153 }
154
155 package BBM::DB;
156 use strict;
157
158 # DB class for simple Database connection
159 sub new {
160         my $self = shift;
161         my $class = ref($self) || $self;
162
163         my ($user, $password, $socket) = @_;
164         my $dsn = '';
165         $dsn .= "mysql_socket=$socket" if $socket;
166         my $dbh = DBI->connect("DBI:mysql:$dsn", $user, $password, { PrintError => 0, RaiseError => 1 });
167         return $dbh;
168 }
169
170 package BBM::Config;
171 use strict;
172 use Config::General;
173
174 sub new {
175         my $self = shift;
176         my $class = ref($self) || $self;
177         my $file = shift;
178
179         my $config = new Config::General(-ConfigFile => $file, -LowerCaseNames => 1);
180         my $this = { $config->getall() };
181         bless($this, $class);
182 }
183
184 sub get {
185         my ($self, $section, $key) = @_;
186         my $h = $self;
187
188         # descend to [cluster] if $section not present in root tree
189         unless (exists $h->{$section}) {
190                 $h = $h->{cluster};
191         }
192
193         # pay attention if callee wanted arrays
194         return wantarray ? () : undef unless exists $h->{$section};
195         return wantarray ? () : undef unless exists $h->{$section}->{$key};
196
197         # deref if wanted array and is arrayref
198         return @{$h->{$section}->{$key}} if wantarray && ref $h->{$section}->{$key} eq 'ARRAY';
199
200         return $h->{$section}->{$key};
201 }
202
203
204 __END__
205
206 =head1 NAME
207
208 bacula-backup-mysql - A hook for Bacula to backup mysql databases using mysqlhotcopy.
209
210 =head1 SYNOPSIS
211
212   Job {
213     Name = "example.org-mysql"
214     ...
215     # This prepares the backup
216     Client Run Before Job = "/usr/sbin/bacula-backup-mysql"
217     # This deletes the copy of the catalog
218     Client Run After Job = "/usr/sbin/bacula-backup-mysql -cleanup"
219   }
220
221 =head1 DESCRIPTION
222
223 This is a script to be setup as C<Client Run Before Job> in Bacula.
224
225 =head1 AUTHOR
226
227 Copyright (C) 2009-2011, Elan RuusamE<auml>e <glen@delfi.ee>
228
229 =head1 SEE ALSO
230
231 http://www.bacula.org/
232
233 =cut
This page took 0.082918 seconds and 3 git commands to generate.