98_update.pm 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. ################################################################
  2. # $Id: 98_update.pm 15862 2018-01-12 21:01:28Z rudolfkoenig $
  3. package main;
  4. use strict;
  5. use warnings;
  6. use HttpUtils;
  7. use File::Copy qw(mv copy);
  8. use Blocking;
  9. sub CommandUpdate($$);
  10. sub upd_getUrl($);
  11. sub upd_initRestoreDirs($);
  12. sub upd_writeFile($$$$);
  13. sub upd_mv($$);
  14. sub upd_metainit($);
  15. sub upd_metacmd($@);
  16. sub upd_saveConfig($$$);
  17. my $updateInBackground;
  18. my $updRet;
  19. my $updArg;
  20. my $mainPgm = "/fhem.pl\$";
  21. my %upd_connecthash;
  22. my $upd_needJoin;
  23. my $upd_nChanged;
  24. my $upd_running;
  25. eval "require IO::Socket::SSL"; # Forum #74387
  26. my $upd_hasSSL = $@ ? 0 : 1;
  27. ########################################
  28. sub
  29. update_Initialize($$)
  30. {
  31. my %hash = (
  32. Fn => "CommandUpdate",
  33. Hlp => "[<fileName>|all|check|force] [http://.../controlfile],update FHEM",
  34. );
  35. $cmds{update} = \%hash;
  36. }
  37. ########################################
  38. sub
  39. CommandUpdate($$)
  40. {
  41. my ($cl,$param) = @_;
  42. my @args = split(/ +/,$param);
  43. my $err = upd_metainit(0);
  44. return $err if($err);
  45. if($args[0] &&
  46. ($args[0] eq "list" ||
  47. $args[0] eq "add" ||
  48. $args[0] eq "delete" ||
  49. $args[0] eq "reset")) {
  50. return upd_metacmd($cl, @args);
  51. }
  52. my $arg = (defined($args[0]) ? $args[0] : "all");
  53. my $src = (defined($args[1]) ? $args[1] : "");
  54. my $ret = eval { "Hello" =~ m/$arg/ };
  55. return "first argument must be a valid regexp, all, force or check"
  56. if($arg =~ m/^[-\?\*]/ || $ret);
  57. $arg = lc($arg) if($arg =~ m/^(check|all|force)$/i);
  58. $updateInBackground = AttrVal("global","updateInBackground",1);
  59. $updateInBackground = 0 if($arg ne "all");
  60. $updArg = $arg;
  61. return "An update is already running" if($upd_running);
  62. $upd_running = 1;
  63. if($updateInBackground) {
  64. CallFn($cl->{NAME}, "ActivateInformFn", $cl, "log");
  65. sub updDone(@) { $upd_running=0 }
  66. BlockingCall("doUpdateInBackground", {src=>$src,arg=>$arg}, "updDone");
  67. return "Executing the update the background.";
  68. } else {
  69. doUpdateLoop($src, $arg);
  70. my $ret = $updRet; $updRet = "";
  71. $upd_running = 0;
  72. return $ret;
  73. }
  74. }
  75. sub
  76. upd_metainit($)
  77. {
  78. my $force = shift;
  79. my $mpath = $attr{global}{modpath}."/FHEM/controls.txt";
  80. if($force || ! -f $mpath || -s $mpath == 0) {
  81. if(!open(FH, ">$mpath")) {
  82. my $msg = "Can't open $mpath: $!";
  83. Log 1, $msg;
  84. return $msg;
  85. }
  86. print FH "http://fhem.de/fhemupdate/controls_fhem.txt\n";
  87. close(FH);
  88. }
  89. return undef;
  90. }
  91. sub
  92. upd_metacmd($@)
  93. {
  94. my ($cl, @args) = @_;
  95. my $mpath = $attr{global}{modpath}."/FHEM/controls.txt";
  96. if($args[0] eq "list") {
  97. open(FH, $mpath) || return "Can't open $mpath: $!";
  98. my $ret = join("", <FH>);
  99. close(FH);
  100. return $ret;
  101. }
  102. if($args[0] eq "add") {
  103. return "Usage: update add http://.../controls_.*.txt"
  104. if(int(@args) != 2 || $args[1] !~ m,^http.*/(controls_.*.txt)$,);
  105. my $fname = $1;
  106. open(FH, $mpath) || return "Can't open $mpath: $!";
  107. my (%fulls, %parts);
  108. map {chomp($_);$fulls{$_}=1; my $x=$_; $x =~ s,^.*/,,; $parts{$x}=$_;} <FH>;
  109. close(FH);
  110. return "$args[1] is already in the list" if($fulls{$args[1]});
  111. return "$fname is already present in $parts{$fname}" if($parts{$fname});
  112. open(FH, ">>$mpath") || return "Can't write $mpath: $!";
  113. print FH $args[1],"\n";
  114. close(FH);
  115. return undef;
  116. }
  117. if($args[0] eq "delete") {
  118. return "Usage: update delete http://.../controls_.*.txt"
  119. if(int(@args) != 2 || $args[1] !~ m,^http.*/(controls_.*.txt)$,);
  120. open(FH, $mpath) || return "Can't open $mpath: $!";
  121. my @list = grep { $_ ne $args[1]."\n"; } <FH>;
  122. close(FH);
  123. open(FH, ">$mpath") || return "Can't write $mpath: $!";
  124. print FH join("", @list);
  125. close(FH);
  126. return undef;
  127. }
  128. if($args[0] eq "reset") {
  129. return upd_metainit(1);
  130. }
  131. }
  132. sub
  133. uLog($$)
  134. {
  135. my ($loglevel, $arg) = @_;
  136. return if($loglevel > $attr{global}{verbose} || !defined($arg));
  137. if($updateInBackground) {
  138. Log 1, $arg;
  139. } else {
  140. Log $loglevel, $arg if($updArg ne "check");
  141. $updRet .= "$arg\n";
  142. }
  143. }
  144. my $inLog = 0;
  145. sub
  146. update_Log2Event($$)
  147. {
  148. my ($level, $text) = @_;
  149. return if($inLog || $level > $attr{global}{verbose});
  150. $inLog = 1;
  151. $text =~ s/\n/ /g; # Multiline text causes havoc in Analyze
  152. BlockingInformParent("Log", [$level, $text], 0);
  153. $inLog = 0;
  154. }
  155. sub
  156. doUpdateInBackground($)
  157. {
  158. my ($h) = @_;
  159. no warnings 'redefine'; # The main process is not affected
  160. *Log = \&update_Log2Event;
  161. sleep(2); # Give time for ActivateInform / FHEMWEB / JavaScript
  162. doUpdateLoop($h->{src}, $h->{arg});
  163. }
  164. sub
  165. doUpdateLoop($$)
  166. {
  167. my ($src, $arg) = @_;
  168. $upd_needJoin = 0;
  169. $upd_nChanged = 0;
  170. if($src =~ m/^http.*/) {
  171. doUpdate(1,1, $src, $arg);
  172. HttpUtils_Close(\%upd_connecthash);
  173. return;
  174. }
  175. my $mpath = $attr{global}{modpath}."/FHEM/controls.txt";
  176. if(!open(LFH, $mpath)) {
  177. my $msg = "Can't open $mpath: $!";
  178. uLog 1, $msg;
  179. return $msg;
  180. }
  181. my @list = <LFH>;
  182. close(LFH);
  183. chomp @list;
  184. my ($max,$curr) = (0,0);
  185. foreach my $srcLine (@list) {
  186. next if($src && $srcLine !~ m/controls_${src}.txt/);
  187. $max++;
  188. }
  189. uLog 1, "No source file named controls_$src found" if($src && !$max);
  190. foreach my $srcLine (@list) {
  191. next if($src && $srcLine !~ m/controls_${src}.txt/);
  192. doUpdate(++$curr, $max, $srcLine, $arg);
  193. HttpUtils_Close(\%upd_connecthash);
  194. }
  195. if($upd_nChanged) {
  196. if($updateInBackground) {
  197. BlockingInformParent("DoTrigger", ["global", "UPDATE", 0 ], 0)
  198. } else {
  199. DoTrigger("global","UPDATE", 0);
  200. }
  201. }
  202. }
  203. sub
  204. doUpdate($$$$)
  205. {
  206. my ($curr, $max, $src, $arg) = @_;
  207. my ($basePath, $ctrlFileName);
  208. $src =~ s'^http://fhem\.de'https://fhem.de' if($upd_hasSSL);
  209. if($src !~ m,^(.*)/([^/]*)$,) {
  210. uLog 1, "Cannot parse $src, probably not a valid http control file";
  211. return;
  212. }
  213. $basePath = $1;
  214. $ctrlFileName = $2;
  215. $ctrlFileName =~ m/controls_(.*).txt/;
  216. my $srcName = $1;
  217. if(AttrVal("global", "backup_before_update", 0) &&
  218. $arg ne "check" && $curr==1) {
  219. my $cmdret = AnalyzeCommand(undef, "backup startedByUpdate");
  220. if ($cmdret !~ m/backup done.*/) {
  221. uLog 1, "Something went wrong during backup: $cmdret";
  222. uLog 1, "update was canceled. Please check manually!";
  223. return;
  224. }
  225. }
  226. if($max != 1) {
  227. uLog 1, "";
  228. uLog 1, $srcName;
  229. }
  230. my $remCtrlFile = upd_getUrl($src);
  231. return if(!$remCtrlFile);
  232. my @remList = split(/\R/, $remCtrlFile);
  233. uLog 4, "Got remote $ctrlFileName with ".int(@remList)." entries.";
  234. ###########################
  235. # read in & digest the local control file
  236. my $root = $attr{global}{modpath};
  237. my $restoreDir = ($arg eq "check" ? "" : restoreDir_init());
  238. my @locList;
  239. if(($arg eq "check" || $arg eq "all") &&
  240. open(FD, "$root/FHEM/$ctrlFileName")) {
  241. @locList = map { $_ =~ s/[\r\n]//; $_ } <FD>;
  242. close(FD);
  243. uLog 4, "Got local $ctrlFileName with ".int(@locList)." entries.";
  244. }
  245. my %lh;
  246. foreach my $l (@locList) {
  247. my @l = split(" ", $l, 4);
  248. next if($l[0] ne "UPD");
  249. $lh{$l[3]}{TS} = $l[1];
  250. $lh{$l[3]}{LEN} = $l[2];
  251. }
  252. my $canJoin;
  253. my $cmod = AttrVal('global', 'commandref', 'full');
  254. my $cj = "$root/contrib/commandref_".
  255. ($cmod eq "full" ? "join":"modular").".pl";
  256. if(-f $cj &&
  257. -f "$root/docs/commandref_frame.html" &&
  258. -w "$root/docs/commandref.html" &&
  259. (AttrVal('global','exclude_from_update','') !~ m/commandref/) ) {
  260. $canJoin = 1;
  261. }
  262. my @excl = split(" ", AttrVal("global", "exclude_from_update", ""));
  263. my $noSzCheck = AttrVal("global", "updateNoFileCheck", configDBUsed());
  264. my @rl = upd_getChanges($root, $basePath);
  265. ###########################
  266. # process the remote controlfile
  267. my ($nChanged,$nSkipped) = (0,0);
  268. my $isSingle = ($arg ne "all" && $arg ne "force" && $arg ne "check");
  269. foreach my $r (@remList) {
  270. my @r = split(" ", $r, 4);
  271. if($r[0] eq "MOV" && ($arg eq "all" || $arg eq "force")) {
  272. if($r[1] =~ m+\.\.+ || $r[2] =~ m+\.\.+) {
  273. uLog 1, "Suspicious line $r, aborting";
  274. return 1;
  275. }
  276. restoreDir_mkDir($root, $r[2], 0);
  277. my $mvret = upd_mv("$root/$r[1]", "$root/$r[2]");
  278. uLog 4, "mv $root/$r[1] $root/$r[2]". ($mvret ? " FAILED:$mvret":"");
  279. }
  280. next if($r[0] ne "UPD");
  281. my $fName = $r[3];
  282. if($fName =~ m+\.\.+) {
  283. uLog 1, "Suspicious line $r, aborting";
  284. return 1;
  285. }
  286. if($isSingle) {
  287. next if($fName !~ m/$arg/);
  288. } else {
  289. my $isExcl;
  290. foreach my $ex (@excl) {
  291. $isExcl = 1 if($fName =~ m/$ex/ || "$src:$fName" =~ m/$ex/);
  292. }
  293. my $fPath = "$root/$fName";
  294. $fPath = $0 if($fPath =~ m/$mainPgm/);
  295. my $fileOk = ($lh{$fName} &&
  296. $lh{$fName}{TS} eq $r[1] &&
  297. $lh{$fName}{LEN} eq $r[2]);
  298. if($isExcl && !$fileOk) {
  299. uLog 1, "update: skipping $fName, matches exclude_from_update";
  300. $nSkipped++;
  301. next;
  302. }
  303. if($noSzCheck) {
  304. next if($isExcl || $fileOk);
  305. } else {
  306. my $sz = -s $fPath;
  307. next if($isExcl || ($fileOk && defined($sz) && $sz eq $r[2]));
  308. }
  309. }
  310. $upd_needJoin = 1 if($fName =~ m/commandref_frame/ || $fName=~ m/\d+.*.pm/);
  311. next if($fName =~ m/commandref.*html/ && $fName !~ m/frame/ && $canJoin);
  312. uLog 1, "List of new / modified files since last update:"
  313. if($arg eq "check" && $nChanged == 0);
  314. $nChanged++;
  315. uLog 1, "$r[0] $fName";
  316. next if($arg eq "check");
  317. my $remFile = upd_getUrl("$basePath/$fName");
  318. return if(!$remFile); # Error already reported
  319. if(length($remFile) ne $r[2]) {
  320. uLog 1, "Got ".length($remFile)." bytes for $fName, expected $r[2]";
  321. if($attr{global}{verbose} == 5) {
  322. upd_writeFile($root, $restoreDir, "$fName.corrupt", $remFile);
  323. uLog 1, "saving it to $fName.corrupt .";
  324. next;
  325. } else {
  326. uLog 1, "aborting.";
  327. return;
  328. }
  329. }
  330. return if(!upd_writeFile($root, $restoreDir, $fName, $remFile));
  331. }
  332. if($nChanged) {
  333. for my $f ($attr{global}{configfile}, $attr{global}{statefile}) {
  334. upd_saveConfig($root, $restoreDir, $f) if($f && $f !~ m,(^/|\.\.),);
  335. }
  336. }
  337. uLog 1, "nothing to do..." if($nChanged == 0 && $nSkipped == 0);
  338. if(@rl && ($nChanged || $nSkipped)) {
  339. uLog(1, "");
  340. uLog 1, "New entries in the CHANGED file:";
  341. map { uLog 1, $_ } @rl;
  342. }
  343. return if($arg eq "check");
  344. if(($arg eq "all" || $arg eq "force") && ($nChanged || $nSkipped)) {
  345. return if(!upd_writeFile($root, $restoreDir,
  346. "FHEM/$ctrlFileName", $remCtrlFile));
  347. }
  348. if($canJoin && $upd_needJoin && $curr == $max) {
  349. chdir($root);
  350. $cj .= " -noWarnings" if($cmod eq "full");
  351. uLog(1, "Calling $^X $cj, this may take a while");
  352. my $ret = `$^X $cj`;
  353. foreach my $l (split(/[\r\n]+/, $ret)) {
  354. uLog(1, $l);
  355. }
  356. }
  357. $upd_nChanged += $nChanged;
  358. return "" if(!$upd_nChanged);
  359. uLog(1, "");
  360. if($curr == $max) {
  361. uLog 1,
  362. 'update finished, "shutdown restart" is needed to activate the changes.';
  363. my $ss = AttrVal("global","sendStatistics",undef);
  364. if(!defined($ss)) {
  365. uLog(1, "");
  366. uLog(1, "Please consider using the global attribute sendStatistics");
  367. } elsif(defined($ss) && lc($ss) eq "onupdate") {
  368. uLog(1, "");
  369. my $ret = AnalyzeCommandChain(undef, "fheminfo send");
  370. $ret =~ s/.*server response:/server response:/ms;
  371. uLog(1, "fheminfo $ret");
  372. }
  373. }
  374. }
  375. sub
  376. upd_mv($$)
  377. {
  378. my ($src, $dest) = @_;
  379. if($src =~ m/\*/) {
  380. $src =~ m,^(.*)/([^/]+)$,;
  381. my ($dir, $pat) = ($1, $2);
  382. $pat = "^$pat\$";
  383. opendir(my $dh, $dir) || return "$dir: $!";
  384. while(my $r = readdir($dh)) {
  385. next if($r !~ m/$pat/ || "$dir/$r" eq $dest);
  386. my $mvret = mv("$dir/$r", $dest);
  387. return "MV $dir/$r $dest: $!" if(!$mvret);
  388. Log 3, "MV $dir/$r $dest";
  389. }
  390. closedir($dh);
  391. } else {
  392. return "MV $src $dest: $!" if(mv($src, $dest));
  393. }
  394. return undef;
  395. }
  396. sub
  397. upd_getChanges($$)
  398. {
  399. my ($root, $basePath) = @_;
  400. my $lFile = "";
  401. if(open(FH, "$root/CHANGED")) {
  402. foreach my $l (<FH>) { # first non-comment line
  403. next if($l =~ m/^#/);
  404. chomp $l;
  405. $lFile = $l;
  406. last;
  407. }
  408. close(FH);
  409. }
  410. my @lines = split(/[\r\n]/,upd_getUrl("$basePath/CHANGED"));
  411. my $maxLines = 25;
  412. my @ret;
  413. foreach my $line (@lines) {
  414. next if($line =~ m/^#/);
  415. last if($line eq "" || $line eq $lFile);
  416. push @ret, $line;
  417. if($maxLines-- < 1) {
  418. push @ret, "... rest of lines skipped.";
  419. last;
  420. }
  421. }
  422. return @ret;
  423. }
  424. sub
  425. upd_getUrl($)
  426. {
  427. my ($url) = @_;
  428. $url =~ s/%/%25/g;
  429. $upd_connecthash{url} = $url;
  430. $upd_connecthash{keepalive} = ($url =~ m/localUpdate/ ? 0 : 1); # Forum #49798
  431. my ($err, $data) = HttpUtils_BlockingGet(\%upd_connecthash);
  432. if($err) {
  433. uLog 1, $err;
  434. return "";
  435. }
  436. if(!$data) {
  437. uLog 1, "$url: empty file received";
  438. return "";
  439. }
  440. return $data;
  441. }
  442. sub
  443. upd_saveConfig($$$)
  444. {
  445. my($root, $restoreDir, $fName) = @_;
  446. return if(!$fName || !$restoreDir || configDBUsed() || !-r "$root/$fName");
  447. restoreDir_mkDir($root, "$restoreDir/$fName", 1);
  448. Log 1, "saving $fName";
  449. if(!copy("$root/$fName", "$root/$restoreDir/$fName")) {
  450. uLog 1, "copy $root/$fName $root/$restoreDir/$fName failed:$!, ".
  451. "aborting the update";
  452. return 0;
  453. }
  454. }
  455. sub
  456. upd_writeFile($$$$)
  457. {
  458. my($root, $restoreDir, $fName, $content) = @_;
  459. # copy the old file and save the new
  460. restoreDir_mkDir($root, $fName, 1);
  461. restoreDir_mkDir($root, "$restoreDir/$fName", 1) if($restoreDir);
  462. if($restoreDir && -f "$root/$fName" &&
  463. ! copy("$root/$fName", "$root/$restoreDir/$fName")) {
  464. uLog 1, "copy $root/$fName $root/$restoreDir/$fName failed:$!, ".
  465. "aborting the update";
  466. return 0;
  467. }
  468. my $rest = ($restoreDir ? "trying to restore the previous version and ":"").
  469. "aborting the update";
  470. my $fPath = "$root/$fName";
  471. $fPath = $0 if($fPath =~ m/$mainPgm/);
  472. if(!open(FD, ">$fPath")) {
  473. uLog 1, "open $fPath failed: $!, $rest";
  474. copy "$root/$restoreDir/$fName", "$root/$fName" if($restoreDir);
  475. return 0;
  476. }
  477. binmode(FD);
  478. print FD $content;
  479. close(FD);
  480. my $written = -s "$fPath";
  481. if($written != length($content)) {
  482. uLog 1, "writing $fPath failed: $!, $rest";
  483. copy "$root/$restoreDir/$fName", "$fPath" if($restoreDir);
  484. return;
  485. }
  486. cfgDB_FileUpdate("$fPath") if(configDBUsed());
  487. return 1;
  488. }
  489. 1;
  490. =pod
  491. =item command
  492. =item summary update FHEM program files from the central repository
  493. =item summary_DE FHEM Programmdateien aktualisieren
  494. =begin html
  495. <a name="update"></a>
  496. <h3>update</h3>
  497. <ul>
  498. <code>update [&lt;fileName&gt;|all|check|force]
  499. [http://.../controlfile]</code>
  500. <br>or<br>
  501. <code>update [add source|delete source|list|reset]</code>
  502. <br>
  503. <br>
  504. Update the FHEM installation. Technically this means update will download
  505. the controlfile(s) first, compare it to the local version of the file in the
  506. moddir/FHEM directory, and download each file where the attributes (timestamp
  507. and filelength) are different. Upon completion it triggers the global:UPDATE
  508. event.
  509. <br>
  510. With the commands add/delete/list/reset you can manage the list of
  511. controlfiles, e.g. for thirdparty packages.
  512. Notes:
  513. <ul>
  514. <li>The contrib directory will not be updated.</li>
  515. <li>The files are automatically transferred from the source repository
  516. (SVN) to the web site once a day, at 7:45 CET / CEST.</li>
  517. <li>The all argument is default.</li>
  518. <li>The force argument will disregard the local file.</li>
  519. <li>The check argument will only display the files it would download, and
  520. the last section of the CHANGED file.</li>
  521. <li>Specifying a filename will only download matching files (regexp).</li>
  522. </ul>
  523. See also the restore command.<br>
  524. <br>
  525. Examples:<br>
  526. <ul>
  527. <li>update check</li>
  528. <li>update</li>
  529. <li>update force</li>
  530. <li>update check http://fhem.de/fhemupdate/controls_fhem.txt</li>
  531. </ul>
  532. <a name="updateattr"></a>
  533. <br>
  534. <b>Attributes</b> (use attr global ...)
  535. <ul>
  536. <a name="updateInBackground"></a>
  537. <li>updateInBackground<br>
  538. If this attribute is set (to 1), the update will be executed in a
  539. background process. The return message is communicated via events, and
  540. in telnet the inform command is activated, in FHEMWEB the Event
  541. Monitor. Default is set. Set it to 0 to switch it off.
  542. </li><br>
  543. <a name="updateNoFileCheck"></a>
  544. <li>updateNoFileCheck<br>
  545. If set, the command won't compare the local file size with the expected
  546. size. This attribute was introduced to satisfy some experienced FHEM
  547. user, its default value is 0.
  548. </li><br>
  549. <a name="backup_before_update"></a>
  550. <li>backup_before_update<br>
  551. If this attribute is set, an update will back up your complete
  552. installation via the <a href="#backup">backup</a> command. The default
  553. is not set as update relies on the restore feature (see below).<br>
  554. Example:<br>
  555. <ul>
  556. attr global backup_before_update
  557. </ul>
  558. </li><br>
  559. <a name="exclude_from_update"></a>
  560. <li>exclude_from_update<br>
  561. Contains a space separated list of fileNames (regexps) which will be
  562. excluded by an update. The special value commandref will disable calling
  563. commandref_join at the end, i.e commandref.html will be out of date.
  564. The module-only documentation is not affected and is up-to-date.<br>
  565. Example:<br>
  566. <ul>
  567. attr global exclude_from_update 21_OWTEMP.pm FS20.off.png
  568. </ul>
  569. The regexp is checked against the filename and the source:filename
  570. combination. To exclude the updates for FILE.pm from fhem.de, as you are
  571. updating it from another source, specify fhem.de.*:FILE.pm
  572. </li><br>
  573. <li><a href="#restoreDirs">restoreDirs</a></li><br>
  574. </ul>
  575. </ul>
  576. =end html
  577. =begin html_DE
  578. <a name="update"></a>
  579. <h3>update</h3>
  580. <ul>
  581. <code>update [&lt;fileName&gt;|all|check|force]
  582. [http://.../controlfile]</code>
  583. <br>oder<br>
  584. <code>update [add source|delete source|list|reset]</code>
  585. <br>
  586. <br>
  587. Erneuert die FHEM Installation. D.h. es wird (werden) zuerst die
  588. Kontroll-Datei(en) heruntergeladen, und mit der lokalen Version dieser Datei
  589. in moddir/FHEM verglichen. Danach werden alle in der Kontroll-Datei
  590. spezifizierten Dateien heruntergeladen, deren Gr&ouml;&szlig;e oder
  591. Zeitstempel sich unterscheidet. Wenn dieser Ablauf abgeschlossen ist, wird
  592. das globale UPDATE Ereignis ausgel&ouml;st.
  593. <br>
  594. Mit den Befehlen add/delete/list/reset kann man die Liste der Kontrolldateien
  595. pflegen.
  596. <br>
  597. <br>
  598. Zu beachten:
  599. <ul>
  600. <li>Das contrib Verzeichnis wird nicht heruntergeladen.</li>
  601. <li>Die Dateien werden auf der Webseite einmal am Tag um 07:45 MET/MEST aus
  602. der Quell-Verwaltungssystem (SVN) bereitgestellt.</li>
  603. <li>Das all Argument ist die Voreinstellung.</li>
  604. <li>Das force Argument beachtet die lokale controls_fhem.txt Datei
  605. nicht.</li>
  606. <li>Das check Argument zeigt die neueren Dateien an, und den letzten
  607. Abschnitt aus der CHANGED Datei</li>
  608. <li>Falls man &lt;fileName&gt; spezifiziert, dann werden nur die Dateien
  609. heruntergeladen, die diesem Regexp entsprechen.</li>
  610. </ul>
  611. Siehe also das restore Befehl.<br>
  612. <br>
  613. Beispiele:<br>
  614. <ul>
  615. <li>update check</li>
  616. <li>update</li>
  617. <li>update force</li>
  618. <li>update check http://fhem.de/fhemupdate/controls_fhem.txt</li>
  619. </ul>
  620. <a name="updateattr"></a>
  621. <br>
  622. <b>Attribute</b> (sind mit attr global zu setzen)
  623. <ul>
  624. <a name="updateInBackground"></a>
  625. <li>updateInBackground<br>
  626. Wenn dieses Attribut gesetzt ist, wird das update Befehl in einem
  627. separaten Prozess ausgef&uuml;hrt, und alle Meldungen werden per Event
  628. &uuml;bermittelt. In der telnet Sitzung wird inform, in FHEMWEB wird
  629. das Event Monitor aktiviert. Die Voreinstellung ist an, zum
  630. Deaktivieren bitte Attribut auf 0 setzen.
  631. </li><br>
  632. <a name="updateNoFileCheck"></a>
  633. <li>updateNoFileCheck<br>
  634. Wenn dieses Attribut gesetzt ist, wird die Gr&ouml;&szlig;e der bereits
  635. vorhandenen, lokalen Datei nicht mit der Sollgr&ouml;&szlig;e
  636. verglichen. Dieses Attribut wurde nach nicht genau spezifizierten Wnsch
  637. erfahrener FHEM Benutzer eingefuehrt, die Voreinstellung ist 0.
  638. </li><br>
  639. <a name="backup_before_update"></a>
  640. <li>backup_before_update<br>
  641. Wenn dieses Attribut gesetzt ist, erstellt FHEM eine Sicherheitskopie
  642. der FHEM Installation vor dem update mit dem backup Befehl. Die
  643. Voreinstellung is "nicht gesetzt", da update sich auf das restore
  644. Feature verl&auml;sst, s.u.<br>
  645. Beispiel:<br>
  646. <ul>
  647. attr global backup_before_update
  648. </ul>
  649. </li><br>
  650. <a name="exclude_from_update"></a>
  651. <li>exclude_from_update<br>
  652. Enth&auml;lt eine Liste durch Leerzeichen getrennter Dateinamen
  653. (regexp), welche nicht im update ber&uuml;cksichtigt werden.<br>
  654. Falls der Wert commandref enth&auml;lt, dann wird commandref_join.pl
  655. nach dem update nicht aufgerufen, d.h. die Gesamtdokumentation ist
  656. nicht mehr aktuell. Die Moduldokumentation bleibt weiterhin aktuell.
  657. <br>
  658. Beispiel:<br>
  659. <ul>
  660. attr global exclude_from_update 21_OWTEMP.pm temp4hum4.gplot
  661. </ul>
  662. Der Regexp wird gegen den Dateinamen und gegen Quelle:Dateiname
  663. gepr&uuml;ft. Um die Datei FILE.pm von updates von fhem.de
  664. auszuschlie&szlig;en, weil sie von einer anderen Quelle bezogen wird,
  665. kann man fhem.de.*:FILE.pm spezifizieren.
  666. </li><br>
  667. <li><a href="#restoreDirs">restoreDirs</a></li><br>
  668. </ul>
  669. </ul>
  670. =end html_DE
  671. =cut