mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-04-20 01:06:04 +00:00
98_backup: split code in to two files, add code for error handling
git-svn-id: https://svn.fhem.de/fhem/trunk@24332 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
099ba7fbb0
commit
0918cc928f
@ -1,5 +1,7 @@
|
|||||||
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
||||||
# Do not insert empty lines here, update check depends on it.
|
# Do not insert empty lines here, update check depends on it.
|
||||||
|
- feature: 98_backup: split modul code in to two files. add code for
|
||||||
|
error handling
|
||||||
- bugfix: lib/FHEM/Core/Authentication/Passwords.pm:
|
- bugfix: lib/FHEM/Core/Authentication/Passwords.pm:
|
||||||
fix rename bug, new method to create object
|
fix rename bug, new method to create object
|
||||||
You musst pass a instance TYPE to the function if you create a objekt
|
You musst pass a instance TYPE to the function if you create a objekt
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
################################################################
|
################################################################
|
||||||
# Developed with Kate
|
# Developed with Kate
|
||||||
#
|
#
|
||||||
# (c) 2012-2020 Copyright: Martin Fischer (m_fischer at gmx dot de)
|
# (c) 2012-2021 Copyright: Martin Fischer (m_fischer at gmx dot de)
|
||||||
# Rewrite and Maintained by Marko Oldenburg since 2019
|
# Rewrite and Maintained by Marko Oldenburg since 2019
|
||||||
# All rights reserved
|
# All rights reserved
|
||||||
#
|
#
|
||||||
# Contributors:
|
# Contributors:
|
||||||
# - Marko Oldenburg (CoolTux)
|
# - Marko Oldenburg (CoolTux - fhemdevelopment at cooltux dot net)
|
||||||
#
|
#
|
||||||
# This script free software; you can redistribute it and/or modify
|
# This script free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@ -33,11 +33,13 @@ use strict;
|
|||||||
use warnings;
|
use warnings;
|
||||||
use FHEM::Meta;
|
use FHEM::Meta;
|
||||||
|
|
||||||
|
require FHEM::Core::Utils::FHEMbackup;
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
sub backup_Initialize {
|
sub backup_Initialize {
|
||||||
|
|
||||||
my %hash = (
|
my %hash = (
|
||||||
Fn => 'FHEM::backup::CommandBackup',
|
Fn => \&FHEM::Core::Utils::FHEMbackup::CommandBackup,
|
||||||
Hlp => ',create a backup of fhem configuration, state and modpath'
|
Hlp => ',create a backup of fhem configuration, state and modpath'
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -46,312 +48,6 @@ sub backup_Initialize {
|
|||||||
return FHEM::Meta::InitMod( __FILE__, \%hash );
|
return FHEM::Meta::InitMod( __FILE__, \%hash );
|
||||||
}
|
}
|
||||||
|
|
||||||
######################################
|
|
||||||
## unserer packagename
|
|
||||||
package FHEM::backup;
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
use FHEM::Meta;
|
|
||||||
|
|
||||||
use GPUtils qw(GP_Import)
|
|
||||||
; # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt
|
|
||||||
|
|
||||||
## Import der FHEM Funktionen
|
|
||||||
BEGIN {
|
|
||||||
GP_Import(
|
|
||||||
qw(AttrVal
|
|
||||||
InternalVal
|
|
||||||
gettimeofday
|
|
||||||
ResolveDateWildcards
|
|
||||||
readingsSingleUpdate
|
|
||||||
attr
|
|
||||||
Log
|
|
||||||
fhemForked
|
|
||||||
defs
|
|
||||||
configDBUsed
|
|
||||||
TimeNow
|
|
||||||
BC_searchTelnet
|
|
||||||
BC_telnetDevice
|
|
||||||
DoTrigger
|
|
||||||
devspec2array
|
|
||||||
configDB)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
my @pathname;
|
|
||||||
|
|
||||||
sub CommandBackup {
|
|
||||||
my $cl = shift;
|
|
||||||
my $param = shift;
|
|
||||||
|
|
||||||
my $byUpdate = ( $param && $param eq 'startedByUpdate' );
|
|
||||||
my $modpath = AttrVal( 'global', 'modpath', '.' );
|
|
||||||
my $configfile = AttrVal( 'global', 'configfile', $modpath . '/fhem.cfg' );
|
|
||||||
my $statefile = AttrVal( 'global', 'statefile', $modpath . '/log/fhem.save' );
|
|
||||||
my $dir = AttrVal( 'global', 'backupdir', $modpath . '/backup');
|
|
||||||
my $now = gettimeofday();
|
|
||||||
my @t = localtime($now);
|
|
||||||
my $dateTime = dateTime();
|
|
||||||
|
|
||||||
$statefile = ResolveDateWildcards( $statefile, @t );
|
|
||||||
|
|
||||||
# prevent duplicate entries in backup list for default config, forum #54826
|
|
||||||
$configfile = '' if ( $configfile eq 'fhem.cfg' || configDBUsed() );
|
|
||||||
$statefile = '' if ( $statefile eq './log/fhem.save' );
|
|
||||||
my $msg;
|
|
||||||
my $ret;
|
|
||||||
|
|
||||||
my ($err,$backupdir) = createBackupDir( $dir, $modpath );
|
|
||||||
|
|
||||||
return Log( 1, 'ERROR: if create backup directory!' )
|
|
||||||
if ( defined($err) and $err );
|
|
||||||
|
|
||||||
Log( 1, 'NOTE: make sure you have a database backup!' )
|
|
||||||
if ( configDBUsed() );
|
|
||||||
$ret = addConfDBFiles( $configfile, $statefile );
|
|
||||||
$ret = readModpath( $modpath, $backupdir );
|
|
||||||
|
|
||||||
## add all logfile path to pathname array
|
|
||||||
$ret = addLogPathToPathnameArray();
|
|
||||||
|
|
||||||
### remove double entries from pathname array
|
|
||||||
my %all=();
|
|
||||||
@all{@pathname}=1;
|
|
||||||
@pathname = keys %all;
|
|
||||||
|
|
||||||
### create archiv
|
|
||||||
$ret = createArchiv( $backupdir, $cl, $byUpdate, $dateTime );
|
|
||||||
|
|
||||||
### support for backupToStorage Modul
|
|
||||||
readingsSingleUpdate($defs{join(' ',
|
|
||||||
devspec2array('TYPE=backupToStorage'))}
|
|
||||||
, 'fhemBackupFile'
|
|
||||||
, "$backupdir/FHEM-$dateTime.tar.gz"
|
|
||||||
, 0
|
|
||||||
)
|
|
||||||
if ( devspec2array('TYPE=backupToStorage') > 0 );
|
|
||||||
|
|
||||||
@pathname = [];
|
|
||||||
undef @pathname;
|
|
||||||
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub addConfDBFiles {
|
|
||||||
my $configfile = shift;
|
|
||||||
my $statefile = shift;
|
|
||||||
|
|
||||||
my $ret;
|
|
||||||
|
|
||||||
if ( configDBUsed() ) {
|
|
||||||
# add configDB configuration file
|
|
||||||
push( @pathname, 'configDB.conf' );
|
|
||||||
Log( 2, 'backup include: \'configDB.conf\'' );
|
|
||||||
|
|
||||||
## check if sqlite db file outside of modpath
|
|
||||||
if ( $configDB{type} eq 'SQLITE'
|
|
||||||
and defined($configDB{filename})
|
|
||||||
and $configDB{filename} !~ m#^[a-zA-Z].*|^\.\/[a-zA-Z].*# )
|
|
||||||
{
|
|
||||||
## backup sqlite db file
|
|
||||||
Log( 2, 'backup include SQLite DB File: ' . $configDB{filename} );
|
|
||||||
push( @pathname, $configDB{filename} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# get pathnames to archiv
|
|
||||||
push( @pathname, $configfile ) if ($configfile);
|
|
||||||
Log( 2, 'backup include: ' . $configfile )
|
|
||||||
if ($configfile);
|
|
||||||
|
|
||||||
$ret = parseConfig($configfile);
|
|
||||||
push( @pathname, $statefile ) if ($statefile);
|
|
||||||
Log( 2, 'backup include: ' . $statefile )
|
|
||||||
if ($statefile);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub createBackupDir {
|
|
||||||
my $dir = shift;
|
|
||||||
my $modpath = shift;
|
|
||||||
|
|
||||||
my $msg;
|
|
||||||
my $ret;
|
|
||||||
my $backupdir = $dir =~ m#^\.(\/.*)$# ? $modpath.$1 : $dir =~ m#^\.\.\/# ? $modpath.'/'.$dir : $dir;
|
|
||||||
|
|
||||||
# create backupdir if not exists
|
|
||||||
if ( !-d $backupdir ) {
|
|
||||||
Log( 4, 'backup create backupdir: ' . $backupdir );
|
|
||||||
$ret = `(mkdir -p $backupdir) 2>&1`;
|
|
||||||
if ($ret) {
|
|
||||||
chomp($ret);
|
|
||||||
$msg = 'backup: ' . $ret;
|
|
||||||
return ($msg,undef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (undef,$backupdir);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub parseConfig {
|
|
||||||
my $configfile = shift;
|
|
||||||
|
|
||||||
# we need default value to read included files
|
|
||||||
$configfile = $configfile ? $configfile : 'fhem.cfg';
|
|
||||||
my $fh;
|
|
||||||
my $msg;
|
|
||||||
my $ret;
|
|
||||||
|
|
||||||
if ( !open( $fh, $configfile ) ) {
|
|
||||||
$msg = 'Can\'t open ' . $configfile . ': ' . $!;
|
|
||||||
Log( 1, 'backup ' . $msg );
|
|
||||||
return $msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ( my $l = <$fh> ) {
|
|
||||||
$l =~ s/[\r\n]//g;
|
|
||||||
if ( $l =~ m/^\s*include\s+(\S+)\s*.*$/ ) {
|
|
||||||
if ( -e $1 ) {
|
|
||||||
push @pathname, $1;
|
|
||||||
Log( 4, 'backup include: ' . $1 );
|
|
||||||
$ret = parseConfig($1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Log( 1,
|
|
||||||
'backup configfile: '
|
|
||||||
. $1
|
|
||||||
. ' does not exists! File not included.' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close $fh;
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub readModpath {
|
|
||||||
my $modpath = shift;
|
|
||||||
my $backupdir = shift;
|
|
||||||
|
|
||||||
my $msg;
|
|
||||||
my $ret;
|
|
||||||
|
|
||||||
if ( !opendir( DH, $modpath ) ) {
|
|
||||||
$msg = 'Can\'t open $modpath: ' . $!;
|
|
||||||
Log( 1, 'backup ' . $msg );
|
|
||||||
return $msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
my @files = <$modpath/*>;
|
|
||||||
foreach my $file (@files) {
|
|
||||||
if ( $file eq $backupdir && ( -d $file || -l $file ) ) {
|
|
||||||
Log( 4, 'backup exclude: ' . $file );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Log( 4, 'backup include: ' . $file );
|
|
||||||
push @pathname, $file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub dateTime {
|
|
||||||
my $dateTime = TimeNow();
|
|
||||||
$dateTime =~ s/ /_/g;
|
|
||||||
$dateTime =~ s/(:|-)//g;
|
|
||||||
|
|
||||||
return $dateTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub createArchiv {
|
|
||||||
my ($backupdir, $cl, $byUpdate, $dateTime) = @_;
|
|
||||||
|
|
||||||
my $backupcmd = AttrVal('global','backupcmd',undef);
|
|
||||||
my $symlink = AttrVal('global','backupsymlink','no');
|
|
||||||
my $tarOpts;
|
|
||||||
my $msg;
|
|
||||||
my $ret;
|
|
||||||
|
|
||||||
my $pathlist = join( '" "', @pathname );
|
|
||||||
|
|
||||||
my $cmd = '';
|
|
||||||
if ( !defined($backupcmd) ) {
|
|
||||||
if ( lc($symlink) eq 'no' ) {
|
|
||||||
$tarOpts = 'czf';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$tarOpts = 'chzf';
|
|
||||||
}
|
|
||||||
|
|
||||||
# prevents tar's output of "Removing leading /" and return total bytes of
|
|
||||||
# archive
|
|
||||||
$cmd = "tar $tarOpts $backupdir/FHEM-$dateTime.tar.gz \"$pathlist\"";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$cmd = $backupcmd . ' \"' . $pathlist . '\"';
|
|
||||||
}
|
|
||||||
|
|
||||||
Log( 2, 'Backup with command: ' . $cmd );
|
|
||||||
if ( !$fhemForked && !$byUpdate ) {
|
|
||||||
use Blocking;
|
|
||||||
our $BC_telnetDevice;
|
|
||||||
BC_searchTelnet('backup');
|
|
||||||
my $tp = $defs{$BC_telnetDevice}{PORT};
|
|
||||||
|
|
||||||
system( "($cmd; echo Backup done;"
|
|
||||||
. "$^X $0 localhost:$tp 'trigger global backup done')2>&1 &" );
|
|
||||||
|
|
||||||
return
|
|
||||||
"Started the backup in the background, watch the log for details";
|
|
||||||
}
|
|
||||||
|
|
||||||
$ret = `($cmd) 2>&1`;
|
|
||||||
|
|
||||||
if ($ret) {
|
|
||||||
chomp $ret;
|
|
||||||
Log( 1, 'backup ' . $ret );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !defined($backupcmd) && -e "$backupdir/FHEM-$dateTime.tar.gz" ) {
|
|
||||||
my $size = -s "$backupdir/FHEM-$dateTime.tar.gz";
|
|
||||||
$msg = "backup done: FHEM-$dateTime.tar.gz ($size Bytes)";
|
|
||||||
DoTrigger( 'global', $msg );
|
|
||||||
Log( 1, $msg );
|
|
||||||
$ret .= "\n" . $msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub addLogPathToPathnameArray {
|
|
||||||
my $ret;
|
|
||||||
my @logpathname;
|
|
||||||
my $extlogpath;
|
|
||||||
|
|
||||||
Log( 4, 'addLogPathToPathnameArray' );
|
|
||||||
|
|
||||||
foreach my $logFile (devspec2array('TYPE=FileLog')) {
|
|
||||||
Log( 5, 'found logFiles: ' . $logFile );
|
|
||||||
my $logpath = InternalVal($logFile,'currentlogfile','');
|
|
||||||
Log( 4, 'found logpath: ' . $logpath );
|
|
||||||
if ( $logpath =~ m#^(.+?)\/[\_|\-|\w]+\.log$# ) {
|
|
||||||
$extlogpath = $1;
|
|
||||||
Log( 4, 'found extlogpath: ' . $extlogpath );
|
|
||||||
if ( $1 =~ /^\/[A-Za-z]/ ) {
|
|
||||||
push( @logpathname, $extlogpath ) ;
|
|
||||||
Log( 4, 'external logpath include: ' . $extlogpath );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
push( @pathname, @logpathname);
|
|
||||||
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
||||||
@ -370,17 +66,17 @@ sub addLogPathToPathnameArray {
|
|||||||
pgm2 (if installed) and the config-file will be saved into a .tar.gz
|
pgm2 (if installed) and the config-file will be saved into a .tar.gz
|
||||||
file by default. The file is stored with a timestamp in the
|
file by default. The file is stored with a timestamp in the
|
||||||
<a href="#modpath">modpath</a>/backup directory or to a directory
|
<a href="#modpath">modpath</a>/backup directory or to a directory
|
||||||
specified by the global attribute <a href="#backupdir">backupdir</a>.<br>
|
specified by the global Attribute <a href="#backupdir">backupdir</a>.<br>
|
||||||
Note: tar and gzip must be installed to use this feature.
|
Note: tar and gzip must be installed to use this feature.
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
If you need to call tar with support for symlinks, you could set the
|
If you need to call tar with support for symlinks, you could set the
|
||||||
global attribute <a href="#backupsymlink">backupsymlink</a> to everything
|
global Attribute <a href="#backupsymlink">backupsymlink</a> to everything
|
||||||
else as "no".
|
else as "no".
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
You could pass the backup to your own command / script by using the
|
You could pass the backup to your own command / script by using the
|
||||||
global attribute <a href="#backupcmd">backupcmd</a>.
|
global ::attribute <a href="#backupcmd">backupcmd</a>.
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
</ul>
|
</ul>
|
||||||
@ -403,8 +99,9 @@ sub addLogPathToPathnameArray {
|
|||||||
],
|
],
|
||||||
"release_status": "stable",
|
"release_status": "stable",
|
||||||
"license": "GPL_2",
|
"license": "GPL_2",
|
||||||
|
"version": "v2.0.1",
|
||||||
"author": [
|
"author": [
|
||||||
"Marko Oldenburg <fhemsupport@cooltux.net>"
|
"Marko Oldenburg <fhemdevelopment@cooltux.net>"
|
||||||
],
|
],
|
||||||
"x_fhem_maintainer": [
|
"x_fhem_maintainer": [
|
||||||
"CoolTux"
|
"CoolTux"
|
||||||
|
315
fhem/lib/FHEM/Core/Utils/FHEMbackup.pm
Normal file
315
fhem/lib/FHEM/Core/Utils/FHEMbackup.pm
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
################################################################
|
||||||
|
# Developed with Kate
|
||||||
|
#
|
||||||
|
# (c) 2012-2021 Copyright: Martin Fischer (m_fischer at gmx dot de)
|
||||||
|
# Rewrite and Maintained by Marko Oldenburg since 2019
|
||||||
|
# All rights reserved
|
||||||
|
#
|
||||||
|
# Contributors:
|
||||||
|
# - Marko Oldenburg (CoolTux - fhemdevelopment at cooltux dot net)
|
||||||
|
#
|
||||||
|
# This script free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# The GNU General Public License can be found at
|
||||||
|
# http://www.gnu.org/copyleft/gpl.html.
|
||||||
|
# A copy is found in the textfile GPL.txt and important notices to the license
|
||||||
|
# from the author is found in LICENSE.txt distributed with these scripts.
|
||||||
|
#
|
||||||
|
# This script is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
package FHEM::Core::Utils::FHEMbackup;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
|
||||||
|
my @pathname;
|
||||||
|
|
||||||
|
sub CommandBackup {
|
||||||
|
my $cl = shift;
|
||||||
|
my $param = shift;
|
||||||
|
|
||||||
|
my $byUpdate = ( $param && $param eq 'startedByUpdate' );
|
||||||
|
my $modpath = ::AttrVal( 'global', 'modpath', '.' );
|
||||||
|
my $configfile = ::AttrVal( 'global', 'configfile', $modpath . '/fhem.cfg' );
|
||||||
|
my $statefile = ::AttrVal( 'global', 'statefile', $modpath . '/log/fhem.save' );
|
||||||
|
my $dir = ::AttrVal( 'global', 'backupdir', $modpath . '/backup');
|
||||||
|
my $now = ::gettimeofday();
|
||||||
|
my @t = localtime($now);
|
||||||
|
my $dateTime = dateTime();
|
||||||
|
|
||||||
|
$statefile = ::ResolveDateWildcards( $statefile, @t );
|
||||||
|
|
||||||
|
# prevent duplicate entries in backup list for default config, forum #54826
|
||||||
|
$configfile = '' if ( $configfile eq 'fhem.cfg' || ::configDBUsed() );
|
||||||
|
$statefile = '' if ( $statefile eq './log/fhem.save' );
|
||||||
|
my $msg;
|
||||||
|
my $ret;
|
||||||
|
|
||||||
|
my ($err,$backupdir) = createBackupDir( $dir, $modpath );
|
||||||
|
|
||||||
|
return ::Log(1, q(ERROR: if create backup directory!))
|
||||||
|
if ( defined($err) && $err );
|
||||||
|
|
||||||
|
::Log(1, q(NOTE: make sure you have a database backup!))
|
||||||
|
if ( ::configDBUsed() );
|
||||||
|
$ret = addConfDBFiles( $configfile, $statefile );
|
||||||
|
return ::Log(1, qq(Backup ERROR - addConfDBFiles: $ret))
|
||||||
|
if ( defined($ret)
|
||||||
|
&& $ret =~ m{\ACan\'t\sopen.*:\s.*}xms);
|
||||||
|
|
||||||
|
$ret = readModpath( $modpath, $backupdir );
|
||||||
|
return ::Log(1, qq(Backup ERROR - readModpath: $ret))
|
||||||
|
if ( defined($ret)
|
||||||
|
&& $ret =~ m{\ACan\'t\sopen\s\$modpath:\s.*}xms);
|
||||||
|
|
||||||
|
## add all logfile path to pathname array
|
||||||
|
$ret = addLogPathToPathnameArray();
|
||||||
|
|
||||||
|
### remove double entries from pathname array
|
||||||
|
my %all=();
|
||||||
|
@all{@pathname}=1;
|
||||||
|
@pathname = keys %all;
|
||||||
|
|
||||||
|
### create archiv
|
||||||
|
$ret = createArchiv( $backupdir, $cl, $byUpdate, $dateTime );
|
||||||
|
|
||||||
|
### support for backupToStorage Modul
|
||||||
|
::readingsSingleUpdate($::defs{join(' ',
|
||||||
|
::devspec2array('TYPE=backupToStorage'))}
|
||||||
|
, 'fhemBackupFile'
|
||||||
|
, "$backupdir/FHEM-$dateTime.tar.gz"
|
||||||
|
, 0
|
||||||
|
)
|
||||||
|
if ( ::devspec2array('TYPE=backupToStorage') > 0 );
|
||||||
|
|
||||||
|
@pathname = [];
|
||||||
|
undef @pathname;
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub addConfDBFiles {
|
||||||
|
my $configfile = shift;
|
||||||
|
my $statefile = shift;
|
||||||
|
|
||||||
|
my $ret;
|
||||||
|
|
||||||
|
if ( ::configDBUsed() ) {
|
||||||
|
# add ::configDB configuration file
|
||||||
|
push( @pathname, 'configDB.conf' );
|
||||||
|
::Log(2, q(backup include: 'configDB.conf'));
|
||||||
|
|
||||||
|
## check if sqlite db file outside of modpath
|
||||||
|
if ( $::configDB{type} eq 'SQLITE'
|
||||||
|
&& defined($::configDB{filename})
|
||||||
|
&& $::configDB{filename} !~ m{\A[a-zA-Z].*|^\.\/[a-zA-Z].*}xms )
|
||||||
|
{
|
||||||
|
## backup sqlite db file
|
||||||
|
::Log(2, qq(backup include SQLite DB File: $::configDB{filename}));
|
||||||
|
push( @pathname, $::configDB{filename} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# get pathnames to archiv
|
||||||
|
push( @pathname, $configfile ) if ($configfile);
|
||||||
|
::Log(2, qq(backup include: $configfile))
|
||||||
|
if ($configfile);
|
||||||
|
|
||||||
|
$ret = parseConfig($configfile);
|
||||||
|
push( @pathname, $statefile ) if ($statefile);
|
||||||
|
::Log(2, qq(backup include: $statefile))
|
||||||
|
if ($statefile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub createBackupDir {
|
||||||
|
my $dir = shift;
|
||||||
|
my $modpath = shift;
|
||||||
|
|
||||||
|
my $msg;
|
||||||
|
my $ret;
|
||||||
|
my $backupdir = $dir =~ m{\A\.(\/.*)\z}xms ? $modpath.$1 : $dir =~ m{\A\.\.\/}xms ? $modpath.'/'.$dir : $dir;
|
||||||
|
|
||||||
|
# create backupdir if not exists
|
||||||
|
if ( !-d $backupdir ) {
|
||||||
|
::Log(4, qq(backup create backupdir: $backupdir));
|
||||||
|
$ret = `(mkdir -p $backupdir) 2>&1`;
|
||||||
|
if ($ret) {
|
||||||
|
chomp($ret);
|
||||||
|
$msg = 'backup: ' . $ret;
|
||||||
|
return ($msg,undef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (undef,$backupdir);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub parseConfig {
|
||||||
|
my $configfile = shift;
|
||||||
|
|
||||||
|
# we need default value to read included files
|
||||||
|
$configfile = $configfile ? $configfile : 'fhem.cfg';
|
||||||
|
my $fh;
|
||||||
|
my $msg;
|
||||||
|
my $ret;
|
||||||
|
|
||||||
|
if ( !open( $fh, $configfile ) ) {
|
||||||
|
$msg = 'Can\'t open ' . $configfile . ': ' . $!;
|
||||||
|
::Log(1, qq(backup $msg));
|
||||||
|
return $msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ( my $l = <$fh> ) {
|
||||||
|
$l =~ s/[\r\n]//g;
|
||||||
|
if ( $l =~ m{\A\s*include\s+(\S+)\s*.*\z}xms ) {
|
||||||
|
if ( -e $1 ) {
|
||||||
|
push @pathname, $1;
|
||||||
|
::Log(4, qq(backup include: $1));
|
||||||
|
$ret = parseConfig($1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
::Log(1, qq(backup configfile: $1 does not exists! File not included.));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close $fh;
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub readModpath {
|
||||||
|
my $modpath = shift;
|
||||||
|
my $backupdir = shift;
|
||||||
|
|
||||||
|
my $msg;
|
||||||
|
my $ret;
|
||||||
|
|
||||||
|
if ( !opendir( DH, $modpath ) ) {
|
||||||
|
$msg = qq(Can't open \$modpath: $!);
|
||||||
|
::Log(1, qq(backup $msg));
|
||||||
|
return $msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
my @files = <$modpath/*>;
|
||||||
|
foreach my $file (@files) {
|
||||||
|
if ( $file eq $backupdir && ( -d $file || -l $file ) ) {
|
||||||
|
::Log(4, qq(backup exclude: $file));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
::Log(4, qq(backup include: $file));
|
||||||
|
push @pathname, $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub dateTime {
|
||||||
|
my $dateTime = ::TimeNow();
|
||||||
|
$dateTime =~ s/ /_/g;
|
||||||
|
$dateTime =~ s/(:|-)//g;
|
||||||
|
|
||||||
|
return $dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub createArchiv {
|
||||||
|
my ($backupdir, $cl, $byUpdate, $dateTime) = @_;
|
||||||
|
|
||||||
|
my $backupcmd = ::AttrVal('global','backupcmd',undef);
|
||||||
|
my $symlink = ::AttrVal('global','backupsymlink','no');
|
||||||
|
my $tarOpts;
|
||||||
|
my $msg;
|
||||||
|
my $ret;
|
||||||
|
|
||||||
|
my $pathlist = join( '" "', @pathname );
|
||||||
|
|
||||||
|
my $cmd = '';
|
||||||
|
if ( !defined($backupcmd) ) {
|
||||||
|
if ( lc($symlink) eq 'no' ) {
|
||||||
|
$tarOpts = 'czf';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$tarOpts = 'chzf';
|
||||||
|
}
|
||||||
|
|
||||||
|
# prevents tar's output of "Removing leading /" and return total bytes of
|
||||||
|
# archive
|
||||||
|
$cmd = "tar $tarOpts $backupdir/FHEM-$dateTime.tar.gz \"$pathlist\"";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$cmd = $backupcmd . ' \"' . $pathlist . '\"';
|
||||||
|
}
|
||||||
|
|
||||||
|
::Log(2, qq(Backup with command: $cmd));
|
||||||
|
if ( !$::fhemForked && !$byUpdate ) {
|
||||||
|
require Blocking;
|
||||||
|
::BC_searchTelnet('backup');
|
||||||
|
my $tp = $::defs{$::BC_telnetDevice}{PORT};
|
||||||
|
|
||||||
|
system( "($cmd; echo Backup done;"
|
||||||
|
. "$^X $0 localhost:$tp 'trigger global backup done')2>&1 &" );
|
||||||
|
|
||||||
|
return
|
||||||
|
"Started the backup in the background, watch the log for details";
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = `($cmd) 2>&1`;
|
||||||
|
|
||||||
|
if ($ret) {
|
||||||
|
chomp $ret;
|
||||||
|
::Log(1, qq(backup $ret));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !defined($backupcmd) && -e "$backupdir/FHEM-$dateTime.tar.gz" ) {
|
||||||
|
my $size = -s "$backupdir/FHEM-$dateTime.tar.gz";
|
||||||
|
$msg = "backup done: FHEM-$dateTime.tar.gz ($size Bytes)";
|
||||||
|
::DoTrigger( 'global', $msg );
|
||||||
|
::Log(1, $msg);
|
||||||
|
$ret .= "\n" . $msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub addLogPathToPathnameArray {
|
||||||
|
my $ret;
|
||||||
|
my @logpathname;
|
||||||
|
my $extlogpath;
|
||||||
|
|
||||||
|
::Log(4, q(addLogPathToPathnameArray));
|
||||||
|
|
||||||
|
foreach my $logFile (::devspec2array('TYPE=FileLog')) {
|
||||||
|
::Log(5, qq(found logFiles: $logFile));
|
||||||
|
my $logpath = ::InternalVal($logFile,'currentlogfile','');
|
||||||
|
::Log(4, qq(found logpath: $logpath));
|
||||||
|
if ( $logpath =~ m{\A(.+?)\/[\_|\-|\w]+\.log\z}xms ) {
|
||||||
|
$extlogpath = $1;
|
||||||
|
::Log(4, qq(found extlogpath: $extlogpath));
|
||||||
|
|
||||||
|
if ( $1 =~ m{\A\/[A-Za-z]}xms ) {
|
||||||
|
push( @logpathname, $extlogpath ) ;
|
||||||
|
::Log(4, qq(external logpath include: $extlogpath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
push( @pathname, @logpathname);
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
Loading…
x
Reference in New Issue
Block a user