]>
Commit | Line | Data |
---|---|---|
1d217da6 ER |
1 | #!/usr/bin/perl -ws |
2 | # Rudimentary switch parsing. Must be in main package. | |
3 | our $cleanup; | |
4 | ||
38d6af1e | 5 | package BBM; |
44be95f6 ER |
6 | use strict; |
7 | use POSIX qw(setuid setgid); | |
44be95f6 ER |
8 | use DBI; |
9 | use File::Temp qw(tempdir); | |
10 | use File::Path qw(rmtree); | |
44be95f6 | 11 | |
1d217da6 | 12 | |
947fde71 ER |
13 | # path to Apache HTTPd-style config |
14 | my $config = '/etc/bacula/backup-mysql.conf'; | |
38d6af1e | 15 | my $c = new BBM::Config($config); |
44be95f6 | 16 | |
7b895eeb | 17 | # now change to user mysql after we've read config |
44be95f6 ER |
18 | unless ($<) { |
19 | my $uid = getpwnam('mysql'); | |
20 | my $gid = getgrnam('mysql'); | |
21 | die "Can't find user/group mysql\n" unless $uid or $gid; | |
22 | ||
1d217da6 ER |
23 | # CWD could not be accessible for mysql user |
24 | chdir("/"); | |
25 | ||
44be95f6 ER |
26 | $) = "$gid $gid"; |
27 | $( = $gid; | |
28 | $> = $< = $uid; | |
29 | } | |
30 | ||
31 | # setup tmpdir | |
32 | my $backup_dir = $c->get('options', 'outdir') or die "'outdir' not defined in config\n"; | |
33 | my $tmpdir = $c->get('options', 'tmpdir') or die "'tmpdir' not defined in config\n"; | |
34 | ||
35 | if (!-d $backup_dir && !mkdir($backup_dir) && !-d $backup_dir) { | |
36 | die "backup dir '$backup_dir' not present and can't be created\n"; | |
37 | } | |
38 | if (!-d $tmpdir && !mkdir($tmpdir) && !-d $tmpdir) { | |
39 | die "tmpdir '$tmpdir' not present and can't be created\n"; | |
40 | } | |
41 | ||
42 | # process each cluster | |
43 | for my $cluster ($c->get('clusters', 'cluster')) { | |
44 | print ">>> $cluster\n"; | |
1d217da6 ER |
45 | if ($cleanup) { |
46 | cleanup_cluster($cluster); | |
47 | } else { | |
48 | backup_cluster($cluster); | |
49 | } | |
44be95f6 ER |
50 | print "<<< $cluster\n"; |
51 | } | |
52 | ||
53 | # | |
54 | # Usage: mysqlhotcopy $CLUSTER $DATABASE $USERNAME $PASSWORD $SOCKET | |
55 | # | |
56 | sub mysqlhotcopy { | |
57 | my ($cluster, $database, $user, $password, $socket) = @_; | |
58 | ||
62f7c875 ER |
59 | print ">>>> mysqlhotcopy $database\n"; |
60 | ||
44be95f6 ER |
61 | my $dstdir = tempdir("bbm.XXXXXX", DIR => $tmpdir); |
62 | ||
63 | # make backup with mysqlhotcopy | |
64 | my @shell = ('mysqlhotcopy'); | |
65 | push(@shell, '-u', $user) if $user; | |
66 | push(@shell, '-p', $password) if $password; | |
67 | push(@shell, '-S', $socket) if $socket; | |
68 | push(@shell, $database, $dstdir); | |
69 | system(@shell) == 0 or die "mysqlhotcopy failed: $?\n"; | |
70 | ||
71 | # put it to "production dir" | |
72 | my $cluster_dir = "$backup_dir/$cluster"; | |
73 | if (!-d $cluster_dir && !mkdir($cluster_dir) && !-d $cluster_dir) { | |
74 | die "cluster dir '$cluster_dir' not present and can't be created\n"; | |
75 | } | |
76 | ||
77 | my $dirname = "$backup_dir/$cluster/$database"; | |
78 | if (-d $dirname) { | |
79 | rmtree($dirname); | |
80 | } | |
81 | ||
82 | my $srcdir= "$dstdir/$database"; | |
83 | rename($srcdir, $dirname) or die "Rename '$srcdir'->'$dirname' failed: $!\n"; | |
84 | ||
85 | rmdir($dstdir) or warn $!; | |
62f7c875 ER |
86 | |
87 | print "<<<< mysqlhotcopy $database\n"; | |
44be95f6 ER |
88 | } |
89 | ||
1d217da6 ER |
90 | sub cleanup_cluster { |
91 | my ($cluster) = @_; | |
92 | my $cluster_dir = "$backup_dir/$cluster"; | |
93 | print ">>>> cleanup $cluster_dir\n"; | |
94 | rmtree($cluster_dir); | |
95 | print "<<<< cleanup $cluster_dir\n"; | |
96 | } | |
97 | ||
44be95f6 ER |
98 | sub backup_cluster { |
99 | my ($cluster) = @_; | |
100 | ||
101 | # get db connection info | |
102 | my $user = $c->get($cluster, 'user') || $c->get('client', 'user'); | |
103 | my $password = $c->get($cluster, 'password') || $c->get('client', 'password'); | |
104 | my $socket = $c->get($cluster, 'socket') || $c->get('client', 'socket'); | |
105 | ||
106 | # get databases to backup | |
38d6af1e ER |
107 | my @include = $c->get($cluster, 'include_database'); |
108 | my @exclude = $c->get($cluster, 'exclude_database'); | |
44be95f6 | 109 | |
38d6af1e ER |
110 | # start with include list |
111 | my %dbs = map { $_ => 1 } @include; | |
62f7c875 | 112 | |
44be95f6 | 113 | if (@exclude or !@include) { |
38d6af1e | 114 | my $dbh = new BBM::DB($user, $password, $socket); |
44be95f6 ER |
115 | my $sth = $dbh->prepare("show databases"); |
116 | $sth->execute(); | |
117 | while (my($dbname) = $sth->fetchrow_array) { | |
118 | next if lc($dbname) eq 'information_schema'; | |
38d6af1e | 119 | $dbs{$dbname} = 1; |
44be95f6 ER |
120 | } |
121 | undef $dbh; | |
122 | } | |
123 | ||
38d6af1e ER |
124 | # remove excluded databases |
125 | delete @dbs{@exclude}; | |
126 | ||
44be95f6 | 127 | # now do the backup |
38d6af1e | 128 | foreach my $db (keys %dbs) { |
44be95f6 ER |
129 | mysqlhotcopy($cluster, $db, $user, $password, $socket); |
130 | } | |
131 | } | |
132 | ||
38d6af1e ER |
133 | package BBM::DB; |
134 | use strict; | |
44be95f6 ER |
135 | |
136 | # DB class for simple Database connection | |
137 | sub new { | |
138 | my $self = shift; | |
139 | my $class = ref($self) || $self; | |
140 | ||
141 | my ($user, $password, $socket) = @_; | |
142 | my $dsn = ''; | |
143 | $dsn .= "mysql_socket=$socket" if $socket; | |
144 | my $dbh = DBI->connect("DBI:mysql:$dsn", $user, $password, { PrintError => 0, RaiseError => 1 }); | |
145 | return $dbh; | |
146 | } | |
38d6af1e ER |
147 | |
148 | package BBM::Config; | |
149 | use strict; | |
150 | use Config::General; | |
151 | ||
152 | sub new { | |
153 | my $self = shift; | |
154 | my $class = ref($self) || $self; | |
155 | my $file = shift; | |
156 | ||
157 | my $config = new Config::General(-ConfigFile => $file, -LowerCaseNames => 1); | |
158 | my $this = { $config->getall() }; | |
159 | bless($this, $class); | |
160 | } | |
161 | ||
162 | sub get { | |
163 | my ($self, $section, $key) = @_; | |
164 | my $h = $self; | |
165 | ||
166 | # descend to [cluster] if $section not present in root tree | |
167 | unless (exists $h->{$section}) { | |
168 | $h = $h->{cluster}; | |
169 | } | |
170 | ||
171 | # pay attention if callee wanted arrays | |
172 | return wantarray ? () : undef unless exists $h->{$section}; | |
173 | return wantarray ? () : undef unless exists $h->{$section}->{$key}; | |
62f7c875 ER |
174 | |
175 | # deref if wanted array and is arrayref | |
176 | return @{$h->{$section}->{$key}} if wantarray && ref $h->{$section}->{$key} eq 'ARRAY'; | |
177 | ||
38d6af1e ER |
178 | return $h->{$section}->{$key}; |
179 | } |