mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-12 16:46:35 +00:00
fhem.pl: save writes a copy into restoreDirs (Forum #78769)
git-svn-id: https://svn.fhem.de/fhem/trunk@15377 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
c74f8a0060
commit
130bd1476c
@ -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: save writes a copy into restoreDirs (Forum #78769)
|
||||
- feature: WMBus: preliminary support for SML payload
|
||||
- feature: 98_DOIFtools: unattended save removed, Forum(78769)
|
||||
DOIFtools will perform save if allowed by
|
||||
|
@ -379,8 +379,7 @@ autocreate_Notify($$)
|
||||
}
|
||||
|
||||
CommandSave(undef, undef) if(!$ret && $nrcreated &&
|
||||
AttrVal($me,"autosave",
|
||||
AttrVal("global","autosave",1)));
|
||||
AttrVal($me,"autosave", 1));
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
@ -11,8 +11,6 @@ use Blocking;
|
||||
sub CommandUpdate($$);
|
||||
sub upd_getUrl($);
|
||||
sub upd_initRestoreDirs($);
|
||||
sub upd_mkDir($$$);
|
||||
sub upd_rmTree($);
|
||||
sub upd_writeFile($$$$);
|
||||
sub upd_mv($$);
|
||||
sub upd_metainit($);
|
||||
@ -21,7 +19,6 @@ sub upd_saveConfig($$$);
|
||||
|
||||
my $updateInBackground;
|
||||
my $updRet;
|
||||
my %updDirs;
|
||||
my $updArg;
|
||||
my $mainPgm = "/fhem.pl\$";
|
||||
my %upd_connecthash;
|
||||
@ -274,7 +271,7 @@ doUpdate($$$$)
|
||||
###########################
|
||||
# read in & digest the local control file
|
||||
my $root = $attr{global}{modpath};
|
||||
my $restoreDir = ($arg eq "check" ? "" : upd_initRestoreDirs($root));
|
||||
my $restoreDir = ($arg eq "check" ? "" : restoreDir_init());
|
||||
|
||||
my @locList;
|
||||
if(($arg eq "check" || $arg eq "all") &&
|
||||
@ -318,7 +315,7 @@ doUpdate($$$$)
|
||||
uLog 1, "Suspicious line $r, aborting";
|
||||
return 1;
|
||||
}
|
||||
upd_mkDir($root, $r[2], 0);
|
||||
restoreDir_mkDir($root, $r[2], 0);
|
||||
my $mvret = upd_mv("$root/$r[1]", "$root/$r[2]");
|
||||
uLog 4, "mv $root/$r[1] $root/$r[2]". ($mvret ? " FAILED:$mvret":"");
|
||||
}
|
||||
@ -463,26 +460,6 @@ upd_mv($$)
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub
|
||||
upd_mkDir($$$)
|
||||
{
|
||||
my ($root, $dir, $isFile) = @_;
|
||||
if($isFile) { # Delete the file Component
|
||||
$dir =~ m,^(.*)/([^/]*)$,;
|
||||
$dir = $1;
|
||||
}
|
||||
return if($updDirs{$dir});
|
||||
$updDirs{$dir} = 1;
|
||||
my @p = split("/", $dir);
|
||||
for(my $i = 0; $i < int(@p); $i++) {
|
||||
my $path = "$root/".join("/", @p[0..$i]);
|
||||
if(!-d $path) {
|
||||
mkdir $path;
|
||||
uLog 4, "MKDIR $root/".join("/", @p[0..$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub
|
||||
upd_getChanges($$)
|
||||
{
|
||||
@ -538,7 +515,7 @@ upd_saveConfig($$$)
|
||||
my($root, $restoreDir, $fName) = @_;
|
||||
|
||||
return if(!$fName || !$restoreDir || configDBUsed() || !-r "$root/$fName");
|
||||
upd_mkDir($root, "$restoreDir/$fName", 1);
|
||||
restoreDir_mkDir($root, "$restoreDir/$fName", 1);
|
||||
Log 1, "saving $fName";
|
||||
if(!copy("$root/$fName", "$root/$restoreDir/$fName")) {
|
||||
uLog 1, "copy $root/$fName $root/$restoreDir/$fName failed:$!, ".
|
||||
@ -553,8 +530,8 @@ upd_writeFile($$$$)
|
||||
my($root, $restoreDir, $fName, $content) = @_;
|
||||
|
||||
# copy the old file and save the new
|
||||
upd_mkDir($root, $fName, 1);
|
||||
upd_mkDir($root, "$restoreDir/$fName", 1) if($restoreDir);
|
||||
restoreDir_mkDir($root, $fName, 1);
|
||||
restoreDir_mkDir($root, "$restoreDir/$fName", 1) if($restoreDir);
|
||||
if($restoreDir && -f "$root/$fName" &&
|
||||
! copy("$root/$fName", "$root/$restoreDir/$fName")) {
|
||||
uLog 1, "copy $root/$fName $root/$restoreDir/$fName failed:$!, ".
|
||||
@ -587,69 +564,6 @@ upd_writeFile($$$$)
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub
|
||||
upd_rmTree($)
|
||||
{
|
||||
my ($dir) = @_;
|
||||
|
||||
my $dh;
|
||||
if(!opendir($dh, $dir)) {
|
||||
uLog 1, "opendir $dir: $!";
|
||||
return;
|
||||
}
|
||||
my @files = grep { $_ ne "." && $_ ne ".." } readdir($dh);
|
||||
closedir($dh);
|
||||
|
||||
foreach my $f (@files) {
|
||||
if(-d "$dir/$f") {
|
||||
upd_rmTree("$dir/$f");
|
||||
} else {
|
||||
uLog 4, "rm $dir/$f";
|
||||
if(!unlink("$dir/$f")) {
|
||||
uLog 1, "rm $dir/$f failed: $!";
|
||||
}
|
||||
}
|
||||
}
|
||||
uLog 4, "rmdir $dir";
|
||||
if(!rmdir($dir)) {
|
||||
uLog 1, "rmdir $dir failed: $!";
|
||||
}
|
||||
}
|
||||
|
||||
sub
|
||||
upd_initRestoreDirs($)
|
||||
{
|
||||
my ($root) = @_;
|
||||
|
||||
my $nDirs = AttrVal("global","restoreDirs", 3);
|
||||
if($nDirs !~ m/^\d+$/ || $nDirs < 0) {
|
||||
uLog 1, "invalid restoreDirs value $nDirs, setting it to 3";
|
||||
$nDirs = 3;
|
||||
}
|
||||
return "" if($nDirs == 0);
|
||||
|
||||
my $rdName = "restoreDir";
|
||||
my @t = localtime;
|
||||
my $restoreDir = sprintf("$rdName/%04d-%02d-%02d",
|
||||
$t[5]+1900, $t[4]+1, $t[3]);
|
||||
Log 1, "MKDIR $restoreDir" if(! -d "$root/restoreDir");
|
||||
upd_mkDir($root, $restoreDir, 0);
|
||||
|
||||
if(!opendir(DH, "$root/$rdName")) {
|
||||
uLog 1, "opendir $root/$rdName: $!";
|
||||
return "";
|
||||
}
|
||||
my @oldDirs = sort grep { $_ !~ m/^\./ && $_ ne $restoreDir } readdir(DH);
|
||||
closedir(DH);
|
||||
while(int(@oldDirs) > $nDirs) {
|
||||
my $dir = "$root/$rdName/". shift(@oldDirs);
|
||||
next if($dir =~ m/$restoreDir/); # Just in case
|
||||
uLog 1, "RMDIR: $dir";
|
||||
upd_rmTree($dir);
|
||||
}
|
||||
|
||||
return $restoreDir;
|
||||
}
|
||||
1;
|
||||
|
||||
=pod
|
||||
@ -741,17 +655,7 @@ upd_initRestoreDirs($)
|
||||
updating it from another source, specify fhem.de.*:FILE.pm
|
||||
</li><br>
|
||||
|
||||
<a name="restoreDirs"></a>
|
||||
<li>restoreDirs<br>
|
||||
update saves each file before overwriting it with the new version from
|
||||
the Web. For this purpose update creates a directory restoreDir in the
|
||||
global modpath directory, then a subdirectory with the current date,
|
||||
where the old version of the currently replaced file is stored.
|
||||
The default value of this attribute is 3, meaning that 3 old versions
|
||||
(i.e. date-directories) are kept, and the older ones are deleted. If
|
||||
the attribute is set to 0, the feature is deactivated.
|
||||
</li><br>
|
||||
|
||||
<li><a href="#restoreDirs">restoreDirs</a></li><br>
|
||||
|
||||
</ul>
|
||||
</ul>
|
||||
@ -854,16 +758,7 @@ upd_initRestoreDirs($)
|
||||
|
||||
</li><br>
|
||||
|
||||
<li><a href="#restoreDirs">restoreDirs</a>
|
||||
update sichert jede Datei vor dem Überschreiben mit der neuen
|
||||
Version aus dem Web. Für diesen Zweck wird zuerst ein restoreDir
|
||||
Verzeichnis in der global modpath Verzeichnis angelegt, und danach
|
||||
ein Unterverzeichnis mit dem aktuellen Datum. In diesem Verzeichnis
|
||||
werden vor dem Überschreiben die alten Versionen der Dateien
|
||||
gerettet. Die Voreinstellung ist 3, d.h. die letzten 3
|
||||
Datums-Verzeichnisse werden aufgehoben, und die älteren entfernt.
|
||||
Falls man den Wert auf 0 setzt, dann ist dieses Feature deaktiviert.
|
||||
</li><br>
|
||||
<li><a href="#restoreDirs">restoreDirs</a></li><br>
|
||||
|
||||
</ul>
|
||||
</ul>
|
||||
|
@ -1082,6 +1082,9 @@ The following local attributes are used by a wider range of devices:
|
||||
structures, but it won't work correctly if some of these files are not
|
||||
writeable.</li>
|
||||
|
||||
<li>before overwriting the files, the old version will be saved, see the <a
|
||||
href="#restoreDirs">restoreDirs</a> global attribute for details.
|
||||
|
||||
</ul>
|
||||
</ul>
|
||||
<!-- save end -->
|
||||
@ -1539,6 +1542,20 @@ The following local attributes are used by a wider range of devices:
|
||||
regexp for hosts to exclude when using a proxy
|
||||
</li><br>
|
||||
|
||||
<a name="restoreDirs"></a>
|
||||
<li>restoreDirs<br>
|
||||
update saves each file before overwriting it with the new version from
|
||||
the Web. For this purpose update creates a directory restoreDir in the
|
||||
global modpath directory, then a subdirectory with the current date,
|
||||
where the old version of the currently replaced file is stored.
|
||||
The default value of this attribute is 3, meaning that 3 old versions
|
||||
(i.e. date-directories) are kept, and the older ones are deleted.<br>
|
||||
fhem.cfg and fhem.state will be also copied with this method before
|
||||
executing save. To restore the files, you can use the restore FHEM
|
||||
command.<br>
|
||||
<br>If the attribute is set to 0, the feature is deactivated.
|
||||
</li><br>
|
||||
|
||||
<li><a href="#fheminfo">sendStatistics</a><br>
|
||||
|
||||
<a name="statefile"></a>
|
||||
|
@ -1148,6 +1148,8 @@ Die folgenden lokalen Attribute werden von mehreren Geräten verwendet:
|
||||
nicht korrekt wenn FHEM für diese Dateien keine Schreibrechte
|
||||
besitzt.</li>
|
||||
|
||||
<li>Vor dem Überschreiben der Dateien wird die alte Version gesichert,
|
||||
siehe <a href="#restoreDirs">restoreDirs</a> für Einzelheiten.</li>
|
||||
</ul>
|
||||
</ul>
|
||||
<!-- save end -->
|
||||
@ -1644,6 +1646,21 @@ Die folgenden lokalen Attribute werden von mehreren Geräten verwendet:
|
||||
Regexp, um bestimmte Hosts nicht via proxy zu kontaktieren.
|
||||
</li><br>
|
||||
|
||||
<a name="restoreDirs"></a>
|
||||
<li><a name="restoreDirs">restoreDirs</a><br>
|
||||
update sichert jede Datei vor dem Überschreiben mit der neuen
|
||||
Version aus dem Web. Für diesen Zweck wird zuerst ein restoreDir
|
||||
Verzeichnis in der global modpath Verzeichnis angelegt, und danach
|
||||
ein Unterverzeichnis mit dem aktuellen Datum. In diesem Verzeichnis
|
||||
werden vor dem Überschreiben die alten Versionen der Dateien
|
||||
gerettet. Die Voreinstellung ist 3, d.h. die letzten 3
|
||||
Datums-Verzeichnisse werden aufgehoben, und die älteren entfernt.
|
||||
<br>
|
||||
Auch fhem.cfg und fhem.state wird auf diese Weise vor dem ausfüren
|
||||
von save gesichert. Zum restaurieren der alten Dateien kann man das
|
||||
restore Befehl verwenden.<br>
|
||||
Falls man den Wert auf 0 setzt, dann ist dieses Feature deaktiviert.
|
||||
</li><br>
|
||||
|
||||
<li><a href="#fheminfo">sendStatistics</a><br>
|
||||
|
||||
|
119
fhem/fhem.pl
119
fhem/fhem.pl
@ -38,6 +38,7 @@ use IO::Socket::INET;
|
||||
use Time::HiRes qw(gettimeofday);
|
||||
use Scalar::Util qw(looks_like_number);
|
||||
use POSIX;
|
||||
use File::Copy qw(copy);
|
||||
|
||||
##################################################
|
||||
# Forward declarations
|
||||
@ -134,6 +135,10 @@ sub readingsEndUpdate($$);
|
||||
sub readingsSingleUpdate($$$$);
|
||||
sub redirectStdinStdErr();
|
||||
sub rejectDuplicate($$$);
|
||||
sub restoreDir_init();
|
||||
sub restoreDir_rmTree($);
|
||||
sub restoreDir_saveFile($$);
|
||||
sub restoreDir_mkDir($$$);
|
||||
sub setGlobalAttrBeforeFork($);
|
||||
sub setReadingsVal($$$$);
|
||||
sub toJSON($);
|
||||
@ -551,7 +556,8 @@ if(configDBUsed()) {
|
||||
}
|
||||
|
||||
if($cfgRet) {
|
||||
$attr{global}{motd} = "$cfgErrMsg\n$cfgRet";
|
||||
$attr{global}{autosave} = 0;
|
||||
$attr{global}{motd} = "$cfgErrMsg\n$cfgRet\nAutosave deactivated";
|
||||
Log 1, $cfgRet;
|
||||
|
||||
} elsif($attr{global}{motd} && $attr{global}{motd} =~ m/^$cfgErrMsg/) {
|
||||
@ -1566,10 +1572,19 @@ CommandSave($$)
|
||||
return "Last 10 structural changes:\n ".join("\n ", @structChangeHist);
|
||||
}
|
||||
|
||||
if(!$cl && !AttrVal("global", "autosave", 1)) { # Forum #78769
|
||||
Log 4, "Skipping save, as autosave is disabled";
|
||||
return;
|
||||
}
|
||||
my $restoreDir;
|
||||
$restoreDir = restoreDir_init() if(!configDBUsed());
|
||||
|
||||
@structChangeHist = ();
|
||||
DoTrigger("global", "SAVE", 1);
|
||||
|
||||
restoreDir_saveFile($restoreDir, $attr{global}{statefile});
|
||||
my $ret = WriteStatefile();
|
||||
|
||||
return $ret if($ret);
|
||||
$ret = ""; # cfgDB_SaveState may return undef
|
||||
|
||||
@ -1580,6 +1595,7 @@ CommandSave($$)
|
||||
|
||||
$param = $attr{global}{configfile} if(!$param);
|
||||
return "No configfile attribute set and no argument specified" if(!$param);
|
||||
restoreDir_saveFile($restoreDir, $param);
|
||||
if(!open(SFH, ">$param")) {
|
||||
return "Cannot open $param: $!";
|
||||
}
|
||||
@ -1606,6 +1622,7 @@ CommandSave($$)
|
||||
my $cfgfile = $h->{CFGFN} ? $h->{CFGFN} : "configfile";
|
||||
my $fh = $fh{$cfgfile};
|
||||
if(!$fh) {
|
||||
restoreDir_saveFile($restoreDir, $cfgfile);
|
||||
if(!open($fh, ">$cfgfile")) {
|
||||
$ret .= "Cannot open $cfgfile: $!, ignoring its content\n";
|
||||
$fh{$cfgfile} = 1;
|
||||
@ -5238,4 +5255,104 @@ computeAlignTime($$@)
|
||||
return (undef, $ttime);
|
||||
}
|
||||
|
||||
############################
|
||||
my %restoreDir_dirs;
|
||||
sub
|
||||
restoreDir_mkDir($$$)
|
||||
{
|
||||
my ($root, $dir, $isFile) = @_;
|
||||
if($isFile) { # Delete the file Component
|
||||
$dir =~ m,^(.*)/([^/]*)$,;
|
||||
$dir = $1;
|
||||
}
|
||||
return if($restoreDir_dirs{$dir});
|
||||
$restoreDir_dirs{$dir} = 1;
|
||||
my @p = split("/", $dir);
|
||||
for(my $i = 0; $i < int(@p); $i++) {
|
||||
my $path = "$root/".join("/", @p[0..$i]);
|
||||
if(!-d $path) {
|
||||
mkdir $path;
|
||||
Log 4, "MKDIR $root/".join("/", @p[0..$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub
|
||||
restoreDir_rmTree($)
|
||||
{
|
||||
my ($dir) = @_;
|
||||
|
||||
my $dh;
|
||||
if(!opendir($dh, $dir)) {
|
||||
Log 1, "opendir $dir: $!";
|
||||
return;
|
||||
}
|
||||
my @files = grep { $_ ne "." && $_ ne ".." } readdir($dh);
|
||||
closedir($dh);
|
||||
|
||||
foreach my $f (@files) {
|
||||
if(-d "$dir/$f") {
|
||||
restoreDir_rmTree("$dir/$f");
|
||||
} else {
|
||||
Log 4, "rm $dir/$f";
|
||||
if(!unlink("$dir/$f")) {
|
||||
Log 1, "rm $dir/$f failed: $!";
|
||||
}
|
||||
}
|
||||
}
|
||||
Log 4, "rmdir $dir";
|
||||
if(!rmdir($dir)) {
|
||||
Log 1, "rmdir $dir failed: $!";
|
||||
}
|
||||
}
|
||||
|
||||
sub
|
||||
restoreDir_init()
|
||||
{
|
||||
my $root = $attr{global}{modpath};
|
||||
|
||||
my $nDirs = AttrVal("global","restoreDirs", 3);
|
||||
if($nDirs !~ m/^\d+$/ || $nDirs < 0) {
|
||||
Log 1, "invalid restoreDirs value $nDirs, setting it to 3";
|
||||
$nDirs = 3;
|
||||
}
|
||||
return "" if($nDirs == 0);
|
||||
|
||||
my $rdName = "restoreDir";
|
||||
my @t = localtime;
|
||||
my $restoreDir = sprintf("$rdName/%04d-%02d-%02d",
|
||||
$t[5]+1900, $t[4]+1, $t[3]);
|
||||
Log 1, "MKDIR $restoreDir" if(! -d "$root/restoreDir");
|
||||
restoreDir_mkDir($root, $restoreDir, 0);
|
||||
|
||||
if(!opendir(DH, "$root/$rdName")) {
|
||||
Log 1, "opendir $root/$rdName: $!";
|
||||
return "";
|
||||
}
|
||||
my @oldDirs = sort grep { $_ !~ m/^\./ && $_ ne $restoreDir } readdir(DH);
|
||||
closedir(DH);
|
||||
while(int(@oldDirs) > $nDirs) {
|
||||
my $dir = "$root/$rdName/". shift(@oldDirs);
|
||||
next if($dir =~ m/$restoreDir/); # Just in case
|
||||
Log 1, "RMDIR: $dir";
|
||||
restoreDir_rmTree($dir);
|
||||
}
|
||||
|
||||
return $restoreDir;
|
||||
}
|
||||
|
||||
sub
|
||||
restoreDir_saveFile($$)
|
||||
{
|
||||
my($restoreDir, $fName) = @_;
|
||||
|
||||
return if(!$restoreDir || !$fName);
|
||||
|
||||
my $root = $attr{global}{modpath};
|
||||
restoreDir_mkDir($root, "$restoreDir/$fName", 1);
|
||||
if(!copy($fName, "$root/$restoreDir/$fName")) {
|
||||
log 1, "copy $fName $root/$restoreDir/$fName failed:$!";
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user