1#!/usr/local/bin/perl 2# 3# Author: Eric Bollengier, Copyright, 2006-2018 4# License: BSD 2-Clause; see file LICENSE-FOSS 5 6use strict; 7 8=head1 SCRIPT 9 10 This script dumps your Bacula catalog in ASCII format 11 It works for MySQL, SQLite, and PostgreSQL 12 13=head1 USAGE 14 15 make_catalog_backup.pl [-m|--maintenance-only] MyCatalog 16 17=head1 LICENSE 18 Author: Eric Bollengier, 2010 19 License: BSD 2-Clause; see file LICENSE-FOSS 20=cut 21 22my $cat = shift or die "Usage: $0 [-m|--maintenance-only] catalogname"; 23my $mode = "dump"; 24 25if ($cat eq '-m' || $cat eq '--maintenance-only') { 26 $mode = "analyse"; 27 $cat = shift or die "Usage: $0 [-m|--maintenance-only] catalogname"; 28} 29 30my $dir_conf='@sbindir@/dbcheck -B -c @sysconfdir@/bacula-dir.conf'; 31my $wd = "@working_dir@"; 32 33sub dump_sqlite3 34{ 35 my %args = @_; 36 37 exec("echo .dump | sqlite3 '$wd/$args{db_name}.db' > '$wd/$args{db_name}.sql'"); 38 print "Error while executing sqlite dump $!\n"; 39 return 1; 40} 41 42# TODO: use just ENV and drop the pg_service.conf file 43sub setup_env_pgsql 44{ 45 my %args = @_; 46 my $username = getpwuid $ENV{'UID'}; 47 umask(0077); 48 49 if ($args{db_address}) { 50 $ENV{PGHOST}=$args{db_address}; 51 } 52 if ($args{db_socket}) { 53 $ENV{PGHOST}=$args{db_socket}; 54 } 55 if ($args{db_port}) { 56 $ENV{PGPORT}=$args{db_port}; 57 } 58 if ($args{db_user}) { 59 $ENV{PGUSER}=$args{db_user}; 60 } 61 if ($args{db_password}) { 62 $ENV{PGPASSWORD}=$args{db_password}; 63 } 64 $ENV{PGDATABASE}=$args{db_name}; 65 system("echo '\\q' | HOME='$wd' psql") == 0 or die "$username doesn't have access to the catalog database\n"; 66} 67 68sub dump_pgsql 69{ 70 my %args = @_; 71 setup_env_pgsql(%args); 72 exec("HOME='$wd' pg_dump -c > '$wd/$args{db_name}.sql'"); 73 print "Error while executing postgres dump $!\n"; 74 return 1; # in case of error 75} 76 77sub analyse_pgsql 78{ 79 my %args = @_; 80 setup_env_pgsql(%args); 81 my @output =`LANG=C HOME='$wd' vacuumdb -z 2>&1`; 82 my $exitcode = $? >> 8; 83 print grep { !/^WARNING:\s+skipping\s\"(pg_|sql_)/ } @output; 84 if ($exitcode != 0) { 85 print "Error while executing postgres analyse. Exitcode=$exitcode\n"; 86 } 87 return $exitcode; 88} 89 90sub setup_env_mysql 91{ 92 my %args = @_; 93 umask(0077); 94 unlink("$wd/.my.cnf"); 95 open(MY, ">$wd/.my.cnf") 96 or die "Can't open $wd/.my.cnf for writing $@"; 97 98 $args{db_address} = $args{db_address} || "localhost"; 99 my $addr = "host=$args{db_address}"; 100 if ($args{db_socket}) { # unix socket is fastest than net socket 101 $addr = "socket=\"$args{db_socket}\""; 102 } 103 my $mode = $args{mode} || 'client'; 104 print MY "[$mode] 105$addr 106user=\"$args{db_user}\" 107password=\"$args{db_password}\" 108"; 109 if ($args{db_port}) { 110 print MY "port=$args{db_port}\n"; 111 } 112 close(MY); 113} 114 115sub dump_mysql 116{ 117 my %args = @_; 118 119 setup_env_mysql(%args); 120 exec("HOME='$wd' mysqldump -f --opt $args{db_name} > '$wd/$args{db_name}.sql'"); 121 print "Error while executing mysql dump $!\n"; 122 return 1; 123} 124 125sub analyse_mysql 126{ 127 my %args = @_; 128 129 $args{mode} = 'mysqlcheck'; 130 setup_env_mysql(%args); 131 132 exec("HOME='$wd' mysqlcheck -a $args{db_name}"); 133 print "Error while executing mysql analyse $!\n"; 134 return 1; 135} 136 137sub handle_catalog 138{ 139 my ($mode, %args) = @_; 140 if (exists $args{working_dir} and $wd ne $args{working_dir}) { 141 $wd = $args{working_dir}; 142 } 143 if ($args{db_type} eq 'SQLite3') { 144 $ENV{PATH}="@SQLITE_BINDIR@:$ENV{PATH}"; 145 if ($mode eq 'dump') { 146 dump_sqlite3(%args); 147 } 148 } elsif ($args{db_type} eq 'PostgreSQL') { 149 $ENV{PATH}="@POSTGRESQL_BINDIR@:$ENV{PATH}"; 150 if ($mode eq 'dump') { 151 dump_pgsql(%args); 152 } else { 153 analyse_pgsql(%args); 154 } 155 } elsif ($args{db_type} eq 'MySQL') { 156 $ENV{PATH}="@MYSQL_BINDIR@:$ENV{PATH}"; 157 if ($mode eq 'dump') { 158 dump_mysql(%args); 159 } else { 160 analyse_mysql(%args); 161 } 162 } else { 163 die "This database type isn't supported"; 164 } 165} 166 167open(FP, "$dir_conf -C '$cat'|") or die "Can't get catalog information $@"; 168# catalog=MyCatalog 169# db_type=SQLite 170# db_name=regress 171# db_driver= 172# db_user=regress 173# db_password= 174# db_address= 175# db_port=0 176# db_socket= 177my %cfg; 178 179while(my $l = <FP>) 180{ 181 if ($l =~ /catalog=(.+)/) { 182 if (exists $cfg{catalog} and $cfg{catalog} eq $cat) { 183 exit handle_catalog($mode, %cfg); 184 } 185 %cfg = (); # reset 186 } 187 188 if ($l =~ /(\w+)=(.+)/) { 189 $cfg{$1}=$2; 190 } 191} 192 193if (exists $cfg{catalog} and $cfg{catalog} eq $cat) { 194 exit handle_catalog($mode, %cfg); 195} 196 197print "Can't find your catalog ($cat) in director configuration\n"; 198exit 1; 199