diff --git a/fhem/CHANGED b/fhem/CHANGED
index 59cc4f275..fbace6343 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -1,5 +1,6 @@
# 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.
+ - feature: 98_backup: add support for FileLog path
- bugfix: 93_DbLog: fix problem with delta-h/delta-d MySQL if value will be
extracted by Regex Forum:#99280
- bugfix: 70_BOTVAC: fix missing battery parameters
diff --git a/fhem/FHEM/98_backup.pm b/fhem/FHEM/98_backup.pm
index 5f1f3aabf..8e72a580c 100644
--- a/fhem/FHEM/98_backup.pm
+++ b/fhem/FHEM/98_backup.pm
@@ -1,9 +1,8 @@
################################################################
-# $Id$
-# vim: ts=2:et
+# Developed with Kate
#
-# (c) 2012 Copyright: Martin Fischer (m_fischer at gmx dot de)
-# Maintained by Marko Oldenburg since 2019
+# (c) 2012-2019 Copyright: Martin Fischer (m_fischer at gmx dot de)
+# Rewrite and Maintained by Marko Oldenburg since 2019
# All rights reserved
#
# This script free software; you can redistribute it and/or modify
@@ -21,211 +20,310 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
+#
+# $Id$
+#
################################################################
package main;
use strict;
use warnings;
+use FHEM::Meta;
-sub CommandBackup($$);
-sub parseConfig($);
-sub readModpath($$);
-sub createArchiv($$$);
+#####################################
+sub backup_Initialize($$) {
+ my %hash = (
+ Fn => 'FHEM::backup::CommandBackup',
+ Hlp => ',create a backup of fhem configuration, state and modpath'
+ );
+ $cmds{backup} = \%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
+ attr
+ Log
+ fhemForked
+ defs
+ configDBUsed
+ TimeNow
+ BC_searchTelnet
+ BC_telnetDevice
+ DoTrigger
+ devspec2array
+ configDB)
+ );
+}
my @pathname;
-#####################################
-sub
-backup_Initialize($$)
-{
- my %hash = ( Fn => "CommandBackup",
- Hlp => ",create a backup of fhem configuration, state and modpath" );
- $cmds{backup} = \%hash;
+sub CommandBackup($$) {
+ my ( $cl, $param ) = @_;
+
+ 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);
+ $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 );
+
+ @pathname = [];
+ undef @pathname;
+
+ return $ret;
}
-#####################################
-sub
-CommandBackup($$)
-{
- my ($cl, $param) = @_;
-
- my $byUpdate = ($param && $param eq "startedByUpdate");
- my $modpath = AttrVal("global", "modpath","");
- my $configfile = AttrVal("global", "configfile", "");
- my $statefile = AttrVal("global", "statefile", "");
- my $now = gettimeofday();
- my @t = localtime($now);
- $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;
-
- Log 1, "NOTE: make sure you have a database backup!" if(configDBUsed());
-
- # set backupdir
- my $backupdir;
- if (!defined($attr{global}{backupdir})) {
- $backupdir = "$modpath/backup";
- } else {
- if ($attr{global}{backupdir} =~ m/^\/.*/) {
- $backupdir = $attr{global}{backupdir};
- } elsif ($attr{global}{backupdir} =~ m/^\.+\/.*/) {
- $backupdir = "$modpath/$attr{global}{backupdir}";
- } else {
- $backupdir = "$modpath/$attr{global}{backupdir}";
+sub addConfDBFiles($$) {
+ my ($configfile,$statefile) = @_;
+ 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 );
+ $ret = parseConfig($configfile);
+ push( @pathname, $statefile ) if ($statefile);
+ Log( 2, 'backup include: ' . $statefile );
+ }
+
+ return $ret;
+}
+
+sub createBackupDir($$) {
+ my ($dir,$modpath) = @_;
+
+ 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($);
+
+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, $backupdir ) = @_;
+ 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 createArchiv($$$) {
+ my ( $backupdir, $cl, $byUpdate ) = @_;
+ my $backupcmd = AttrVal('global','backupcmd',undef);
+ my $symlink = AttrVal('global','backupsymlink','no');
+ my $tarOpts;
+ my $msg;
+ my $ret;
+
+ my $dateTime = TimeNow();
+ $dateTime =~ s/ /_/g;
+ $dateTime =~ s/(:|-)//g;
+
+ 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 - \"$pathlist\" |gzip > $backupdir/FHEM-$dateTime.tar.gz";
+ $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`;
- # 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;
+ chomp $ret;
+ Log( 1, 'backup ' . $ret );
}
- }
- if(configDBUsed()) {
- # add configDB configuration file
- push @pathname, 'configDB.conf';
- Log 4, "backup include: 'configDB.conf'";
- } else {
- # get pathnames to archiv
- push @pathname, $configfile if($configfile);
- Log 4, "backup include: '$configfile'";
- $ret = parseConfig($configfile);
- push @pathname, $statefile if($statefile);
- Log 4, "backup include: '$statefile'";
- }
- $ret = readModpath($modpath,$backupdir);
+ 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;
+ }
- # create archiv
- $ret = createArchiv($backupdir, $cl, $byUpdate);
-
- @pathname = [];
- undef @pathname;
-
- return $ret;
+ return $ret;
}
-sub
-parseConfig($)
-{
- my $configfile = shift;
- # we need default value to read included files
- $configfile = $configfile ? $configfile : 'fhem.cfg';
- my $fh;
- my $msg;
- my $ret;
+sub addLogPathToPathnameArray() {
+# my $modpath = shift;
- if (!open($fh,$configfile)) {
- $msg = "Can't open $configfile: $!";
- Log 1, "backup $msg";
- return $msg;
- }
+ my $ret;
+ my @logpathname;
+ my $extlogpath;
- 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,$backupdir) = @_;
- 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
-createArchiv($$$)
-{
- my ($backupdir,$cl,$byUpdate) = @_;
- my $backupcmd = (!defined($attr{global}{backupcmd}) ? undef : $attr{global}{backupcmd});
- my $symlink = (!defined($attr{global}{backupsymlink}) ? "no" : $attr{global}{backupsymlink});
- my $tarOpts;
- my $msg;
- my $ret;
-
- my $dateTime = TimeNow();
- $dateTime =~ s/ /_/g;
- $dateTime =~ s/(:|-)//g;
-
- my $pathlist = join( "\" \"", @pathname );
-
- my $cmd="";
- if (!defined($backupcmd)) {
- if (lc($symlink) eq "no") {
- $tarOpts = "cf";
- } else {
- $tarOpts = "chf";
+ 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 );
+ }
+ }
}
- # prevents tar's output of "Removing leading /" and return total bytes of
- # archive
- $cmd = "tar -$tarOpts - \"$pathlist\" |gzip > $backupdir/FHEM-$dateTime.tar.gz";
+ push( @pathname, @logpathname);
- } 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;
+ return $ret;
}
1;
@@ -260,6 +358,47 @@ createArchiv($$$)
-
=end html
+
+=for :application/json;q=META.json 98_backup.pm
+{
+ "abstract": "Modul to retrieves apt information about Debian update state",
+ "x_lang": {
+ "de": {
+ "abstract": "Modul um apt Updateinformationen von Debian Systemen zu bekommen"
+ }
+ },
+ "keywords": [
+ "fhem-mod-device",
+ "fhem-core",
+ "backup",
+ "tar"
+ ],
+ "release_status": "stable",
+ "license": "GPL_2",
+ "author": [
+ "Marko Oldenburg "
+ ],
+ "x_fhem_maintainer": [
+ "CoolTux"
+ ],
+ "x_fhem_maintainer_github": [
+ "LeonGaultier"
+ ],
+ "prereqs": {
+ "runtime": {
+ "requires": {
+ "FHEM": 5.00918799,
+ "perl": 5.016,
+ "Meta": 0
+ },
+ "recommends": {
+ },
+ "suggests": {
+ }
+ }
+ }
+}
+=end :application/json;q=META.json
+
=cut