]>
Commit | Line | Data |
---|---|---|
1d217da6 | 1 | #!/usr/bin/perl -ws |
46478424 ER |
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 | ||
1d217da6 ER |
19 | # Rudimentary switch parsing. Must be in main package. |
20 | our $cleanup; | |
21 | ||
38d6af1e | 22 | package BBM; |
44be95f6 ER |
23 | use strict; |
24 | use POSIX qw(setuid setgid); | |
44be95f6 ER |
25 | use DBI; |
26 | use File::Temp qw(tempdir); | |
27 | use File::Path qw(rmtree); | |
44be95f6 | 28 | |
1d217da6 | 29 | |
947fde71 ER |
30 | # path to Apache HTTPd-style config |
31 | my $config = '/etc/bacula/backup-mysql.conf'; | |
38d6af1e | 32 | my $c = new BBM::Config($config); |
44be95f6 | 33 | |
7b895eeb | 34 | # now change to user mysql after we've read config |
44be95f6 ER |
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 | ||
1d217da6 ER |
40 | # CWD could not be accessible for mysql user |
41 | chdir("/"); | |
42 | ||
44be95f6 ER |
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"; | |
1d217da6 ER |
62 | if ($cleanup) { |
63 | cleanup_cluster($cluster); | |
64 | } else { | |
65 | backup_cluster($cluster); | |
66 | } | |
44be95f6 ER |
67 | print "<<< $cluster\n"; |
68 | } | |
69 | ||
46478424 | 70 | # |
44be95f6 ER |
71 | # Usage: mysqlhotcopy $CLUSTER $DATABASE $USERNAME $PASSWORD $SOCKET |
72 | # | |
73 | sub mysqlhotcopy { | |
74 | my ($cluster, $database, $user, $password, $socket) = @_; | |
75 | ||
62f7c875 ER |
76 | print ">>>> mysqlhotcopy $database\n"; |
77 | ||
44be95f6 ER |
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) { | |
49da4557 | 91 | rmtree($dstdir); |
44be95f6 ER |
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 | ||
49da4557 ER |
100 | my $srcdir = "$dstdir/$database"; |
101 | unless (rename($srcdir, $dirname)) { | |
102 | rmtree($dstdir); | |
103 | die "Rename '$srcdir'->'$dirname' failed: $!\n"; | |
104 | } | |
44be95f6 ER |
105 | |
106 | rmdir($dstdir) or warn $!; | |
62f7c875 ER |
107 | |
108 | print "<<<< mysqlhotcopy $database\n"; | |
44be95f6 ER |
109 | } |
110 | ||
1d217da6 ER |
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 | ||
44be95f6 ER |
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 | |
38d6af1e ER |
128 | my @include = $c->get($cluster, 'include_database'); |
129 | my @exclude = $c->get($cluster, 'exclude_database'); | |
44be95f6 | 130 | |
38d6af1e ER |
131 | # start with include list |
132 | my %dbs = map { $_ => 1 } @include; | |
62f7c875 | 133 | |
44be95f6 | 134 | if (@exclude or !@include) { |
38d6af1e | 135 | my $dbh = new BBM::DB($user, $password, $socket); |
44be95f6 ER |
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'; | |
38d6af1e | 140 | $dbs{$dbname} = 1; |
44be95f6 ER |
141 | } |
142 | undef $dbh; | |
143 | } | |
144 | ||
38d6af1e ER |
145 | # remove excluded databases |
146 | delete @dbs{@exclude}; | |
147 | ||
44be95f6 | 148 | # now do the backup |
38d6af1e | 149 | foreach my $db (keys %dbs) { |
44be95f6 ER |
150 | mysqlhotcopy($cluster, $db, $user, $password, $socket); |
151 | } | |
152 | } | |
153 | ||
38d6af1e ER |
154 | package BBM::DB; |
155 | use strict; | |
44be95f6 ER |
156 | |
157 | # DB class for simple Database connection | |
158 | sub new { | |
159 | my $self = shift; | |
160 | my $class = ref($self) || $self; | |
161 | ||
162 | my ($user, $password, $socket) = @_; | |
163 | my $dsn = ''; | |
164 | $dsn .= "mysql_socket=$socket" if $socket; | |
165 | my $dbh = DBI->connect("DBI:mysql:$dsn", $user, $password, { PrintError => 0, RaiseError => 1 }); | |
166 | return $dbh; | |
167 | } | |
38d6af1e ER |
168 | |
169 | package BBM::Config; | |
170 | use strict; | |
171 | use Config::General; | |
172 | ||
173 | sub new { | |
174 | my $self = shift; | |
175 | my $class = ref($self) || $self; | |
176 | my $file = shift; | |
177 | ||
178 | my $config = new Config::General(-ConfigFile => $file, -LowerCaseNames => 1); | |
179 | my $this = { $config->getall() }; | |
180 | bless($this, $class); | |
181 | } | |
182 | ||
183 | sub get { | |
184 | my ($self, $section, $key) = @_; | |
185 | my $h = $self; | |
186 | ||
187 | # descend to [cluster] if $section not present in root tree | |
188 | unless (exists $h->{$section}) { | |
189 | $h = $h->{cluster}; | |
190 | } | |
191 | ||
192 | # pay attention if callee wanted arrays | |
193 | return wantarray ? () : undef unless exists $h->{$section}; | |
194 | return wantarray ? () : undef unless exists $h->{$section}->{$key}; | |
62f7c875 ER |
195 | |
196 | # deref if wanted array and is arrayref | |
197 | return @{$h->{$section}->{$key}} if wantarray && ref $h->{$section}->{$key} eq 'ARRAY'; | |
198 | ||
38d6af1e ER |
199 | return $h->{$section}->{$key}; |
200 | } | |
46478424 ER |
201 | |
202 | ||
203 | __END__ | |
204 | ||
205 | =head1 NAME | |
206 | ||
207 | bacula-backup-mysql - A hook for Bacula to backup mysql databases using mysqlhotcopy. | |
208 | ||
209 | =head1 SYNOPSIS | |
210 | ||
211 | Job { | |
212 | Name = "example.org-mysql" | |
213 | ... | |
72b32af8 | 214 | # This prepares the backup |
46478424 ER |
215 | Client Run Before Job = "/usr/sbin/bacula-backup-mysql" |
216 | # This deletes the copy of the catalog | |
217 | Client Run After Job = "/usr/sbin/bacula-backup-mysql -cleanup" | |
218 | } | |
219 | ||
220 | =head1 DESCRIPTION | |
221 | ||
222 | This is a script to be setup as C<Client Run Before Job> in Bacula. | |
223 | ||
224 | =head1 AUTHOR | |
225 | ||
72b32af8 | 226 | Copyright (C) 2009-2010, Elan RuusamE<auml>e <glen@delfi.ee> |
46478424 ER |
227 | |
228 | =head1 SEE ALSO | |
229 | ||
230 | http://www.bacula.org/ | |
231 | ||
232 | =cut |