1#!/usr/local/bin/perl
2# --
3# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/
4# --
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.txt.
17# --
18
19use strict;
20use warnings;
21
22# use ../ as lib location
23use File::Basename;
24use File::Spec qw(catfile);
25use FindBin qw($RealBin);
26use lib dirname($RealBin);
27use lib dirname($RealBin) . "/Kernel/cpan-lib";
28
29use Getopt::Std;
30
31use Kernel::System::ObjectManager;
32
33# get options
34my %Opts;
35my $DB            = '';
36my $DBDump        = '';
37my $DecompressCMD = '';
38my $Installed     = 0;
39getopt( 'hbd', \%Opts );
40if ( exists $Opts{h} ) {
41    print <<EOF;
42
43Restore an OTRS system from backup.
44
45Usage:
46 restore.pl -b /data_backup/<TIME>/ -d /opt/otrs/
47
48Options:
49 -b                     - Directory of the backup files.
50 -d                     - Target OTRS home directory.
51 [-h]                   - Display help for this command.
52
53EOF
54    exit 1;
55}
56if ( !$Opts{b} ) {
57    print STDERR "ERROR: Need -b for backup directory\n";
58    exit 1;
59}
60elsif ( !-d $Opts{b} ) {
61    print STDERR "ERROR: No such directory: $Opts{b}\n";
62    exit 1;
63}
64if ( !$Opts{d} ) {
65    print STDERR "ERROR: Need -d for destination directory\n";
66    exit 1;
67}
68elsif ( !-d $Opts{d} ) {
69    print STDERR "ERROR: No such directory: $Opts{d}\n";
70    exit 1;
71}
72
73if ( -e File::Spec->catfile( $Opts{b}, 'DatabaseBackup.sql.bz2' ) ) {
74    $DecompressCMD = 'bunzip2';
75}
76else {
77    $DecompressCMD = 'gunzip';
78}
79
80# check needed programs
81for my $CMD ( 'cp', 'tar', $DecompressCMD ) {
82    $Installed = 0;
83    open( my $Input, '-|', "which $CMD" );    ## no critic
84    while (<$Input>) {
85        $Installed = 1;
86    }
87    close $Input;
88    if ( !$Installed ) {
89        print STDERR "ERROR: Can't locate $CMD!\n";
90        exit 1;
91    }
92}
93
94# restore config
95chdir( $Opts{d} );
96
97my $ConfigBackupGz  = File::Spec->catfile( $Opts{b}, 'Config.tar.gz' );
98my $ConfigBackupBz2 = File::Spec->catfile( $Opts{b}, 'Config.tar.bz2' );
99if ( -e $ConfigBackupGz ) {
100    print "Restore $ConfigBackupGz ...\n";
101    system("tar -xzf $ConfigBackupGz");
102}
103elsif ( -e $ConfigBackupBz2 ) {
104    print "Restore $ConfigBackupBz2 ...\n";
105    system("tar -xjf $ConfigBackupBz2");
106}
107
108# create common objects
109local $Kernel::OM = Kernel::System::ObjectManager->new(
110    'Kernel::System::Log' => {
111        LogPrefix => 'OTRS-restore.pl',
112    },
113);
114
115my $DatabaseHost = $Kernel::OM->Get('Kernel::Config')->Get('DatabaseHost');
116my $Database     = $Kernel::OM->Get('Kernel::Config')->Get('Database');
117my $DatabaseUser = $Kernel::OM->Get('Kernel::Config')->Get('DatabaseUser');
118my $DatabasePw   = $Kernel::OM->Get('Kernel::Config')->Get('DatabasePw');
119my $DatabaseDSN  = $Kernel::OM->Get('Kernel::Config')->Get('DatabaseDSN');
120my $ArticleDir   = $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Article::Backend::MIMEBase::ArticleDataDir');
121
122# decrypt pw (if needed)
123if ( $DatabasePw =~ m/^\{(.*)\}$/ ) {
124    $DatabasePw = $Kernel::OM->Get('Kernel::System::DB')->_Decrypt($1);
125}
126
127# check db backup support
128if ( $DatabaseDSN =~ m/:mysql/i ) {
129    $DB     = 'MySQL';
130    $DBDump = 'mysql';
131
132    $Installed = 0;
133    open( my $Input, '-|', "which $DBDump" );    ## no critic
134    while (<$Input>) {
135        $Installed = 1;
136    }
137    close $Input;
138    if ( !$Installed ) {
139        print STDERR "ERROR: Can't locate $DBDump!\n";
140        exit 1;
141    }
142}
143elsif ( $DatabaseDSN =~ m/:pg/i ) {
144    $DB     = 'PostgreSQL';
145    $DBDump = 'psql';
146    if ( $DatabaseDSN !~ m/host=/i ) {
147        $DatabaseHost = '';
148    }
149
150    $Installed = 0;
151    open( my $Input, '-|', "which $DBDump" );    ## no critic
152    while (<$Input>) {
153        $Installed = 1;
154    }
155    close $Input;
156    if ( !$Installed ) {
157        print STDERR "ERROR: Can't locate $DBDump!\n";
158        exit 1;
159    }
160}
161else {
162    print STDERR "ERROR: Can't backup, no database dump support!\n";
163    exit 1;
164}
165
166# check database env
167if ( $DB =~ m/mysql/i ) {
168    $Kernel::OM->Get('Kernel::System::DB')->Prepare( SQL => "SHOW TABLES" );
169    my $Check = 0;
170    while ( my @RowTmp = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
171        $Check++;
172    }
173    if ($Check) {
174        print STDERR
175            "ERROR: Already existing tables in this database. A empty database is required for restore!\n";
176        exit 1;
177    }
178}
179else {
180    $Kernel::OM->Get('Kernel::System::DB')->Prepare(
181        SQL =>
182            "SELECT table_name FROM information_schema.tables WHERE table_catalog = 'otrs' AND table_schema = 'public'",
183    );
184    my $Check = 0;
185    while ( my @RowTmp = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
186        $Check++;
187    }
188    if ($Check) {
189        print STDERR
190            "ERROR: Already existing tables in this database. A empty database is required for restore!\n";
191        exit 1;
192    }
193}
194
195# restore
196my $Home = $Kernel::OM->Get('Kernel::Config')->Get('Home');
197chdir($Home);
198
199# extract application
200my $ApplicationBackupGz  = File::Spec->catfile( $Opts{b}, 'Application.tar.gz' );
201my $ApplicationBackupBz2 = File::Spec->catfile( $Opts{b}, 'Application.tar.bz2' );
202if ( -e $ApplicationBackupGz ) {
203    print "Restore $ApplicationBackupGz ...\n";
204    system("tar -xzf $ApplicationBackupGz");
205}
206elsif ( -e $ApplicationBackupBz2 ) {
207    print "Restore $ApplicationBackupBz2 ...\n";
208    system("tar -xjf $ApplicationBackupBz2");
209}
210
211# extract vardir
212my $VarDirBackupGz  = File::Spec->catfile( $Opts{b}, 'VarDir.tar.gz' );
213my $VarDirBackupBz2 = File::Spec->catfile( $Opts{b}, 'VarDir.tar.bz2' );
214if ( -e $VarDirBackupGz ) {
215    print "Restore $VarDirBackupGz ...\n";
216    system("tar -xzf $VarDirBackupGz");
217}
218elsif ( -e $VarDirBackupBz2 ) {
219    print "Restore $VarDirBackupBz2 ...\n";
220    system("tar -xjf $VarDirBackupBz2");
221}
222
223# extract datadir
224my $DataDirBackupGz  = File::Spec->catfile( $Opts{b}, 'DataDir.tar.gz' );
225my $DataDirBackupBz2 = File::Spec->catfile( $Opts{b}, 'DataDir.tar.bz2' );
226if ( -e $DataDirBackupGz ) {
227    print "Restore $DataDirBackupGz ...\n";
228    system("tar -xzf $DataDirBackupGz");
229}
230if ( -e $DataDirBackupBz2 ) {
231    print "Restore $DataDirBackupBz2 ...\n";
232    system("tar -xjf $DataDirBackupBz2");
233}
234
235# import database
236my $DatabaseBackupGz  = File::Spec->catfile( $Opts{b}, 'DatabaseBackup.sql.gz' );
237my $DatabaseBackupBz2 = File::Spec->catfile( $Opts{b}, 'DatabaseBackup.sql.bz2' );
238if ( $DB =~ m/mysql/i ) {
239    print "create $DB\n";
240    if ($DatabasePw) {
241        $DatabasePw = "-p'$DatabasePw'";
242    }
243    if ( -e $DatabaseBackupGz ) {
244        print "Restore database into $DB ...\n";
245        system(
246            "gunzip -c $DatabaseBackupGz | mysql -f -u$DatabaseUser $DatabasePw -h$DatabaseHost $Database"
247        );
248    }
249    elsif ( -e $DatabaseBackupBz2 ) {
250        print "Restore database into $DB ...\n";
251        system(
252            "bunzip2 -c $DatabaseBackupBz2 | mysql -f -u$DatabaseUser $DatabasePw -h$DatabaseHost $Database"
253        );
254    }
255}
256else {
257    if ($DatabaseHost) {
258        $DatabaseHost = "-h $DatabaseHost";
259    }
260
261    if ( -e $DatabaseBackupGz ) {
262
263        # set password via environment variable if there is one
264        if ($DatabasePw) {
265            $ENV{'PGPASSWORD'} = $DatabasePw;    ## no critic
266        }
267        print "Restore database into $DB ...\n";
268        system(
269            "gunzip -c $DatabaseBackupGz | psql -U$DatabaseUser $DatabaseHost $Database"
270        );
271    }
272    elsif ( -e $DatabaseBackupBz2 ) {
273
274        # set password via environment variable if there is one
275        if ($DatabasePw) {
276            $ENV{'PGPASSWORD'} = $DatabasePw;    ## no critic
277        }
278        print "Restore database into $DB ...\n";
279        system(
280            "bunzip2 -c $DatabaseBackupBz2 | psql -U$DatabaseUser $DatabaseHost $Database"
281        );
282    }
283}
284