commit 3ac7e485921bbd7738347f2932f57f8b3fa4d1dd Author: Marko Oldenburg Date: Tue Feb 19 21:22:56 2019 +0100 first commit diff --git a/98_backup.pm b/98_backup.pm new file mode 100644 index 0000000..b8beb17 --- /dev/null +++ b/98_backup.pm @@ -0,0 +1,265 @@ +################################################################ +# $Id: 98_backup.pm 18643 2019-02-19 14:33:01Z CoolTux $ +# vim: ts=2:et +# +# (c) 2012 Copyright: Martin Fischer (m_fischer at gmx dot de) +# Maintained by Marko Oldenburg since 2019 +# All rights reserved +# +# 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. +# +################################################################ + +package main; +use strict; +use warnings; + +sub CommandBackup($$); +sub parseConfig($); +sub readModpath($$); +sub createArchiv($$$); + +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", ""); + 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}"; + } + } + + # 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; + } + } + + 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); + + # create archiv + $ret = createArchiv($backupdir, $cl, $byUpdate); + + @pathname = []; + undef @pathname; + + 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; + + 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 = (!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"; + } + + # prevents tar's output of "Removing leading /" and return total bytes of + # archive + $cmd = "tar -$tarOpts - \"$pathlist\" |gzip > $backupdir/FHEM-$dateTime.tar.gz"; + + } 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; +} + +1; + +=pod +=item command +=item summary create a backup of the FHEM installation +=item summary_DE erzeugt eine Sicherungsdatei der FHEM Installation +=begin html + + +

backup

+ + + +=end html +=cut