From b59ab0dab465d5e2a8d42bacc48be37c4a806a98 Mon Sep 17 00:00:00 2001 From: mfr69bs <> Date: Sun, 20 May 2012 15:24:11 +0000 Subject: [PATCH] Added support for a cleaner installation of pgm2 via updatefhem. git-svn-id: https://svn.fhem.de/fhem/trunk@1569 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 6 +- fhem/FHEM/99_updatefhem.pm | 309 +++++++++++++++++++++++++--- fhem/HISTORY | 2 + fhem/docs/commandref.html | 72 +++++-- fhem/webfrontend/pgm2/01_FHEMWEB.pm | 37 +++- 5 files changed, 366 insertions(+), 60 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index 031795fc9..8e8da6427 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -26,7 +26,11 @@ - feature: at attribute alignTime added - feature: FHEMWEB attribute values via dropdown, slider for dimmer - feature: new attribute group for FHEMWEB (Boris) - - change: 11_FHT.pm, 50_WS300.pm, 59_Weather.pm migrated to redingsUpdate mechanism (Boris) + - change: 11_FHT.pm, 50_WS300.pm, 59_Weather.pm migrated to redingsUpdate + mechanism (Boris) + - change: updatefhem modifications to support a clean install of fhem and + pgm2 installation, see commandref.html (M. Fischer) + - change: FHEMWEB support for the new www/pgm2 directroy added (M. Fischer) - 2011-12-31 (5.2) - bugfix: applying smallscreen attributes to firefox/opera diff --git a/fhem/FHEM/99_updatefhem.pm b/fhem/FHEM/99_updatefhem.pm index 3a9287ffd..9985726d7 100644 --- a/fhem/FHEM/99_updatefhem.pm +++ b/fhem/FHEM/99_updatefhem.pm @@ -1,5 +1,6 @@ ############################################## # $Id$ +# modified by M. Fischer package main; use strict; use warnings; @@ -8,9 +9,11 @@ use IO::Socket; sub CommandUpdatefhem($$); sub CommandCULflash($$); sub GetHttpFile($$@); +sub CreateBackup($); +sub FileList($); my $server = "fhem.de:80"; -my $sdir = "/fhemupdate"; +my $sdir = "/fhemupdate2"; my $ftime = "filetimes.txt"; my $dfu = "dfu-programmer"; @@ -35,24 +38,157 @@ CommandUpdatefhem($$) my ($cl, $param) = @_; my $lt = ""; my $ret = ""; - my $moddir = (-d "FHEM.X" ? "FHEM.X" : "$attr{global}{modpath}/FHEM"); + my $modpath = (-d "updatefhem.dir" ? "updatefhem.dir" : $attr{global}{modpath}); + my $moddir = "$modpath/FHEM"; + my $wwwdir = "$modpath/www"; + my $backuppaths = ""; + my $preserve = 0; + my $housekeeping = 0; + my $clean = 0; + my $msg; - ## backup by RueBe, simplified by rudi + if(!$param && !-d $wwwdir) { + $ret = "Usage: updatefhem [|| [] []| []]\n"; + $ret .= "Please note: The update routine has changed! Please consider the manual of command 'updatefhem'!"; + return $ret; + } + + # split arguments my @args = split(/ +/,$param); - # Check if the first parameter is "backup" - if(@args && uc($args[0]) eq "BACKUP") { - my $bdir = AttrVal("global", "backupdir", "$moddir.backup"); - my $dateTime = TimeNow(); - $dateTime =~ s/ /_/g; - my $ret = `(mkdir -p $bdir && tar hcf - $moddir | gzip > $bdir/FHEM.$dateTime.tgz) 2>&1`; - return $ret if($ret); - shift @args; - $param = join("", @args); + if(@args) { + + # Check if the first parameter is "backup" + # backup by RueBe, simplified by rudi, modified by M.Fischer + if(uc($args[0]) eq "BACKUP") { + return "Usage: updatefhem " if(@args > 1); + + if(-d $wwwdir) { + # backup new structure + $backuppaths = "FHEM www"; + } else { + # backup old structure + $backuppaths = "FHEM"; + } + my $ret = CreateBackup($backuppaths); + if($ret !~ m/backup done.*/) { + Log 1, "updatefhem backup: The operation was canceled. Please check manually!"; + $msg = "Something went wrong during backup:\n$ret\n"; + $msg .= "The operation was canceled. Please check manually!"; + return $msg; + } else { + Log 1, "updatefhem $ret"; + } + return $ret if($ret); + + # Check whether the old structure to be maintained + } elsif (uc($args[0]) eq "PRESERVE") { + + # Check if new wwwdir already exists and an argument is given + if(-d $wwwdir && @args > 1) { + Log 1, "updatefhem The operation was canceled! Argument not allowed in new structure!"; + $ret = "Usage: updatefhem [|]\n"; + $ret .= "Please note: It seems as if 'updatefhem ' has already executed.\n"; + $ret .= "The operation was canceled. Argument is not allowed in new structure!"; + return $ret; + } + # Check if new wwwdir already exists + if(-d $wwwdir) { + Log 1, "updatefhem The operation was canceled. Please check manually!"; + $ret = "Please note: It seems as if 'updatefhem ' has already executed.\n"; + $ret .= "The operation was canceled. Please check manually!"; + return $ret; + } + # set old sourcedir for update + $sdir = "/fhemupdate"; + $preserve = 1; + # discard first argument + shift @args; + $param = join("", @args); + + # Check whether the new structure is to be established. + } elsif (uc($args[0]) eq "HOUSEKEEPING") { + + if(@args >3 || + (defined($args[1]) && uc($args[1]) ne "CLEAN") || + ((defined($args[1]) && uc($args[1]) eq "CLEAN") && (defined($args[2]) && uc($args[2]) ne "YES")) + ) { + return "Usage: updatefhem [] []"; + } + # Check if new wwwdir already exists + if(-d $wwwdir && @args == 1) { + Log 1, "updatefhem The operation was canceled. Please check manually!"; + $ret = "Please note: It seems as if 'updatefhem ' has already executed.\n"; + $ret .= "The operation is canceled now. Please check manually!"; + return $ret; + } + + # user decided to delete old files + if (@args == 2 && uc($args[1]) eq "CLEAN") { + + # returns a warning + $ret = "WARNING: The option will remove existing files!\n"; + $ret .= "If local changes have been made, they will be lost!\n"; + $ret .= "If you are sure, then call 'updatefhem '."; + return $ret; + + # user decided to delete old files, really + } elsif (@args == 3 && uc($args[1]) eq "CLEAN" && uc($args[2]) eq "YES") { + + # set cleanup structure + $clean = 1; + } + + # Backup existing structure + if(-d $wwwdir) { + # backup new structure + $backuppaths = "FHEM www"; + } else { + # backup old structure + $backuppaths = "FHEM"; + } + my $ret = CreateBackup($backuppaths); + if($ret !~ m/backup done.*/) { + Log 1, "updatefhem backup: The operation was canceled. Please check manually!"; + $msg = "Something went wrong during backup:\n$ret\n"; + $msg .= "The operation was canceled. Please check manually!"; + return $msg; + } else { + Log 1, "updatefhem $ret"; + } + + # prepare for housekeeping + $housekeeping = 1; + # set new sourcedir for update + $sdir = "/fhemupdate2"; + # Create new pgm2 path + $ret = `(mkdir -p $wwwdir/pgm2)`; + chomp $ret; + + # return on errors + if($ret) { + Log 1, "updatefhem \"$ret\""; + return $ret; + } + + # remove old filetimes.txt + if(-e "$moddir/$ftime") { + unlink("$moddir/$ftime"); + } + + # discard arguments + @args = (); + $param = join("", @args); + + # user wants to update a file / module of the old structure + } elsif (!-d $wwwdir) { + return "Usage: updatefhem [| [] []| []]"; + } + } # Read in the OLD filetimes.txt - my %oldtime; + my %oldtime = (); if(open FH, "$moddir/$ftime") { while(my $l = ) { chomp($l); @@ -65,7 +201,7 @@ CommandUpdatefhem($$) my $filetimes = GetHttpFile($server, "$sdir/$ftime"); return "Can't get $ftime from $server" if(!$filetimes); - my (%filetime, %filesize); + my (%filetime, %filesize) = (); foreach my $l (split("[\r\n]", $filetimes)) { chomp($l); return "Corrupted filetimes.txt file" @@ -77,26 +213,41 @@ CommandUpdatefhem($$) my @reload; my $newfhem = 0; + my $localfile; + my $remfile; + my $oldfile; + my $delfile; foreach my $f (sort keys %filetime) { if($param) { - next if($f ne $param); + next if($f !~ m/$param/); } else { - next if($oldtime{$f} && $filetime{$f} eq $oldtime{$f}); + if(!$clean) { + next if($oldtime{$f} && $filetime{$f} eq $oldtime{$f}); + } next if($f =~ m/.hex$/); # skip firmware files } - my $localfile = "$moddir/$f"; - my $remfile = $f; + if(!$preserve) { + $localfile = "$modpath/$f"; + if($f =~ m/^www\/pgm2\/(\d\d_.*\.pm)$/) { + my $pgm2 = $1; + $localfile = "$moddir/$pgm2"; + } + $remfile = $f; + } else { + $localfile = "$moddir/$f"; + $remfile = $f; + } - if($f eq "fhem.pl") { - $ret .= "updated fhem.pl, 'shutdown restart' is required\n"; + if($f =~ m/fhem.pl$/) { $newfhem = 1; - $localfile = $0 if(! -d "FHEM.X"); + $localfile = $0 if(! -d "updatefhem.dir"); $remfile = "$f.txt"; } - if($f =~ m/^(\d\d_)(.*).pm$/) { - my $m = $2; - push @reload, $f if($modules{$m} && $modules{$m}{LOADED}); + if($f =~ m/^.*(\d\d_)(.*).pm$/) { + my $mf = "$1$2"; + my $m = $2; + push @reload, $mf if($modules{$m} && $modules{$m}{LOADED}); } my $content = GetHttpFile($server, "$sdir/$remfile"); @@ -108,7 +259,19 @@ CommandUpdatefhem($$) print FH $content; close(FH); $ret .= "updated $f\n"; - Log 1, "updated $f"; + Log 1, "updatefhem updated $f"; + + if(!$preserve && $clean && $f =~ m/^www\/pgm2\/(.*)$/) { + my $oldfile = $1; + if($oldfile !~ m /^.*\.pm$/) { + $delfile = $oldfile; + if(-e "$moddir/$delfile") { + unlink("$moddir/$delfile"); + $ret .= "deleted FHEM/$delfile\n"; + Log 1, "updatefhem deleted FHEM/$delfile"; + } + } + } } return "Can't write $moddir/$ftime" if(!open(FH, ">$moddir/$ftime")); @@ -119,11 +282,41 @@ CommandUpdatefhem($$) foreach my $m (@reload) { $ret .= "reloading module $m\n"; my $cret = CommandReload($cl, $m); + Log 1, "updatefhem reloaded module $m" if($cret); return "$ret$cret" if($cret); } } - - $ret .= "update finished\n"; + + # final housekeeping + if($clean) { + my @fl; + push(@fl, FileList("$moddir/.*(example.*|gplot|html|css|js|gif|jpg|png|svg)")); + foreach my $file (@fl) { + my $cmdret .= `(mv $moddir/$file $wwwdir/pgm2/)`; + $ret .= "moved $moddir/$file\n"; + Log 1, "updatefhem move $file to www/pgm2 $cmdret"; + } + } + + if($housekeeping) { + $ret .= "Housekeeping finished. 'shutdown restart' is recommended!"; + my $backupdir; + if ($attr{global}{backupdir}) { + $backupdir = $attr{global}{backupdir}; + } else { + $backupdir = "$modpath/backup"; + } + $ret .= "\n=> Files for WebGUI pgm2 were moved to '$wwwdir/pgm2'" if($clean); + $ret .= "\n=> A backup has been created in '$backupdir'"; + Log 1, "updatefhem Housekeeping finished, 'shutdown restart' is recommended!"; + } else { + $ret .= "update finished"; + } + + if($newfhem) { + $ret .= "\nA new version of fhem.pl was installed, 'shutdown restart' is required!"; + Log 1, "updatefhem New version of fhem.pl, 'shutdown restart' is required!"; + } return $ret; } @@ -131,7 +324,8 @@ sub CommandCULflash($$) { my ($cl, $param) = @_; - my $moddir = (-d "FHEM.X" ? "FHEM.X" : "$attr{global}{modpath}/FHEM"); + my $modpath = (-d "update" ? "update" : $attr{global}{modpath}); + my $moddir = "$modpath/FHEM"; my %ctypes = ( CUL_V2 => "at90usb162", @@ -183,9 +377,9 @@ CommandCULflash($$) CUL_SimpleWrite($defs{$cul}, "B01"); sleep(4); # B01 needs 2 seconds for the reset } - Log 1, $cmd; + Log 1, "updatefhem $cmd"; my $result = `$cmd`; - Log 1, $result; + Log 1, "updatefhem $result"; return $result; } @@ -198,7 +392,8 @@ GetHttpFile($$@) $filename =~ s/%/%25/g; my $conn = IO::Socket::INET->new(PeerAddr => $host); if(!$conn) { - Log 1, "Can't connect to $host\n"; + Log 1, "updatefhem Can't connect to $host\n"; + undef $conn; return undef; } $host =~ s/:.*//; @@ -213,7 +408,8 @@ GetHttpFile($$@) vec($rin, $conn->fileno(), 1) = 1; my $nfound = select($rout=$rin, undef, undef, $timeout); if($nfound <= 0) { - Log 1, "GetHttpFile: Select timeout/error: $!"; + Log 1, "updatefhem GetHttpFile: Select timeout/error: $!"; + undef $conn; return undef; } @@ -223,8 +419,55 @@ GetHttpFile($$@) } $ret=~ s/(.*?)\r\n\r\n//s; # Not greedy: switch off the header. - Log 4, "Got http://$host$filename, length: ".length($ret); + Log 4, "updatefhem Got http://$host$filename, length: ".length($ret); + undef $conn; return $ret; } +sub +CreateBackup($) +{ + my ($backuppaths) = shift; + my $modpath = (-d "updatefhem.dir" ? "updatefhem.dir" : $attr{global}{modpath}); + my ($dir,$conf) = $attr{global}{configfile} =~ m/(.*\/)(.*)$/; + my $backupdir; + my $ret; + if ($attr{global}{backupdir}) { + $backupdir = $attr{global}{backupdir}; + } else { + $backupdir = "$modpath/backup"; + } + $ret = `(cp $attr{global}{configfile} $modpath/)`; + $backuppaths .= " $conf"; + my $dateTime = TimeNow(); + $dateTime =~ s/ /_/g; + # prevents tar's output of "Removing leading /" and return total bytes of archive + $ret = `(mkdir -p $backupdir && tar -C $modpath -cf - $backuppaths | gzip > $backupdir/FHEM.$dateTime.tar.gz) 2>&1`; + unlink("$modpath/$conf"); + if($ret) { + chomp $ret; + return $ret; + } + my $size = -s "$backupdir/FHEM.$dateTime.tar.gz"; + return "backup done: FHEM.$dateTime.tar.gz ($size Bytes)"; +} + +sub +FileList($) +{ + my ($fname) = @_; + $fname =~ m,^(.*)/([^/]*)$,; + my ($dir,$re) = ($1, $2); + return if(!$re); + $re =~ s/%./[A-Za-z0-9]*/g; + my @ret; + return @ret if(!opendir(DH, $dir)); + while(my $f = readdir(DH)) { + next if($f !~ m,^$re$,); + push(@ret, $f); + } + closedir(DH); + return sort @ret; +} + 1; diff --git a/fhem/HISTORY b/fhem/HISTORY index 0446a2ff8..8fe7a8527 100644 --- a/fhem/HISTORY +++ b/fhem/HISTORY @@ -494,3 +494,5 @@ - Fr Feb 24 2012 (Willi) - New modules TRX for RFXCOM RFXtrx transceiver +- So May 20 2012 (M. Fischer) + - Added support for a cleaner installation of pgm2 via updatefhem. diff --git a/fhem/docs/commandref.html b/fhem/docs/commandref.html index 0ac76836c..eb2eebb11 100644 --- a/fhem/docs/commandref.html +++ b/fhem/docs/commandref.html @@ -749,29 +749,65 @@ A line ending with \ will be concatenated with the next one, so long lines

updatefhem

    - updatefhem [backup] [filename]
    + updatefhem [<backup>|<filename>|<housekeeping> [<clean>] [<yes>]|<preserve> [<filename>]]

    - Update the fhem modules and documentation from a nightly SVN chekout. For + Update the fhem modules and documentation from a nightly SVN checkout. For this purpose fhem contacts http://fhem.de/fhemupdate, compares the stored timestamps of the local files with the filelist on the server, and downloads the files changed on the server. For all downloaded modules a - reload will be scheduled if the corresponding module is loaded.
    - If an explicit filename is given, then only this file will be downloaded. + reload will be scheduled if the corresponding module is loaded. +
    +
    + Note: Since Version 5.3 a new directory structure is used by Fhem. The + WebInterface pgm2 is separated from modpath/FHEM + directory. The directory modpath/www/pgm2 is the new home of pgm2 and + of its files. It is recommended to update to the new structure via the + <housekeeping> arguments. +
    +
    + If <backup> is specified, then the complete FHEM directory (containing + the modules), the WebInterface pgm2 (if installed) and the config-file will + be saved into a .tar.gz file. The file is stored with a timestamp in the + modpath/backup directory or to a directory specified + by the backupdir global attribute.
    + Note: tar and gzip must be installed to use this feature. +
    +
    + If an explicit <filename> is given, then only this file will be + downloaded. +
    +
    + If <housekeeping> is specified, then updatefhem switch your Fhem + installation to the new directory structure for the pgm2 WebInterface. + The old files are archived like the <backup> argument and the new + files are stored to modpath/www/pgm2. None of the existing files of pgm2 + in modpath/FHEM will be deleted, but are no longer in use.
    + If you like to delete the no more needed files for pgm2 in modpath/FHEM, + you should call <housekeeping> with the argument <clean> <yes>. + All files of pgm2 are removed from modpath/FHEM and files like + .*example.*, .gplot, .html .css, .js, and .(gif|jpg|png|svg) are moved + to modpath/www/pgm2. +
    +
    + If <preserve> is specified, you could preserve the old directory + structure. All files of pgm2 are updated and stored in modpath/FHEM like + the former (before Fhem 5.3) updatefhem funktion. To update an explicit + file to the old structure append the file as <filename>.
    + Note: After running the housekeeping process, the argument <preserve> + is no longer necessary and no more supported. +
    +
    + Notes: +
      +
    • If the main program (fhem.pl) is changed, a manual restart of fhem + will be necessary to apply the changes.
    • +
    • After running <housekeeping> it is recommended to restart Fhem.
    • +

    - Note: if the main program (fhem.pl) is changed, a manual restart of fhem - will be necessary to apply the changes. -
    - If backup is specified, then the complete FHEM directory (containing the - modules and .gplot files) will be saved into a .tar.gz file with a - timestamp in the modpath/FHEM.backup directory or to - a directory specified by the backupdir global - attribute. Note: tar and gzip must be installed to use this feature. -
    -
    - - Attributes
      + Attributes +
      • backupdir
        A folder where updatefhem will store the compressed backup file. @@ -779,8 +815,8 @@ A line ending with \ will be concatenated with the next one, so long lines
          attr global backupdir /Volumes/BigHD
        -

      • -

      + +
diff --git a/fhem/webfrontend/pgm2/01_FHEMWEB.pm b/fhem/webfrontend/pgm2/01_FHEMWEB.pm index c5d35b646..8bb3a0dee 100755 --- a/fhem/webfrontend/pgm2/01_FHEMWEB.pm +++ b/fhem/webfrontend/pgm2/01_FHEMWEB.pm @@ -36,7 +36,8 @@ sub FW_pH(@); sub FW_pHPlain(@); sub FW_pO(@); -use vars qw($FW_dir); # moddir (./FHEM), needed by SVG +use vars qw($FW_dir); # moddir (./FHEM in old structure, www/pgm2 in new structure), needed by SVG +use vars qw($MW_dir); # moddir (./FHEM), needed by edit Files in new structure use vars qw($FW_ME); # webname (default is fhem), needed by 97_GROUP use vars qw($FW_ss); # is smallscreen, needed by 97_GROUP/95_VIEW use vars qw($FW_tp); # is touchpad (iPad / etc) @@ -339,8 +340,12 @@ FW_AnswerCall($) $FW_RET = ""; $FW_RETTYPE = "text/html; charset=$FW_encoding"; $FW_ME = "/" . AttrVal($FW_wname, "webname", "fhem"); - #$FW_dir = AttrVal($FW_wname, "fwmodpath", "$attr{global}{modpath}/www/pgm2"); - $FW_dir = AttrVal($FW_wname, "fwmodpath", "$attr{global}{modpath}/FHEM"); + if(-d "$attr{global}{modpath}/www/pgm2") { + $FW_dir = AttrVal($FW_wname, "fwmodpath", "$attr{global}{modpath}/www/pgm2"); + } else { + $FW_dir = AttrVal($FW_wname, "fwmodpath", "$attr{global}{modpath}/FHEM"); + } + $MW_dir = AttrVal($FW_wname, "fwmodpath", "$attr{global}{modpath}/FHEM"); $FW_ss = AttrVal($FW_wname, "smallscreen", 0); $FW_tp = AttrVal($FW_wname, "touchpad", $FW_ss); my $prf = AttrVal($FW_wname, "stylesheetPrefix", ""); @@ -1537,7 +1542,8 @@ FW_style($$) my @fl = ("fhem.cfg"); push(@fl, ""); - push(@fl, FW_fileList("$FW_dir/.*(sh|Util.*|cfg|holiday)")); + #push(@fl, FW_fileList("$FW_dir/.*(sh|Util.*|cfg|holiday)")); + push(@fl, FW_fileList("$MW_dir/.*(sh|Util.*|cfg|holiday)")); push(@fl, ""); push(@fl, FW_fileList("$FW_dir/.*.(css|svg)")); push(@fl, ""); @@ -1587,8 +1593,16 @@ FW_style($$) } elsif($a[1] eq "edit") { $a[2] =~ s,/,,g; # little bit of security - my $f = ($a[2] eq "fhem.cfg" ? $attr{global}{configfile} : - "$FW_dir/$a[2]"); + #my $f = ($a[2] eq "fhem.cfg" ? $attr{global}{configfile} : + # "$FW_dir/$a[2]"); + my $f; + if($a[2] eq "fhem.cfg") { + $f = $attr{global}{configfile}; + } elsif ($a[2] =~ m/.*(sh|Util.*|cfg|holiday)/ && $a[2] ne "fhem.cfg") { + $f = "$MW_dir/$a[2]"; + } else { + $f = "$FW_dir/$a[2]"; + } if(!open(FH, $f)) { FW_pO "$f: $!"; return; @@ -1616,8 +1630,15 @@ FW_style($$) $fName = $FW_webArgs{saveName} if($FW_webArgs{saveAs} && $FW_webArgs{saveName}); $fName =~ s,/,,g; # little bit of security - $fName = ($fName eq "fhem.cfg" ? $attr{global}{configfile} : - "$FW_dir/$fName"); + #$fName = ($fName eq "fhem.cfg" ? $attr{global}{configfile} : + # "$FW_dir/$fName"); + if($fName eq "fhem.cfg") { + $fName = $attr{global}{configfile}; + } elsif ($fName =~ m/.*(sh|Util.*|cfg|holiday)/ && $fName ne "fhem.cfg") { + $fName = "$MW_dir/$fName"; + } else { + $fName = "$FW_dir/$fName"; + } if(!open(FH, ">$fName")) { FW_pO "$fName: $!"; return;