From c5007ccd01c331b0c28d6ec60da5e49047ff929a Mon Sep 17 00:00:00 2001 From: rudolfkoenig Date: Mon, 29 Dec 2014 08:38:52 +0000 Subject: [PATCH] Utils.pm: add getUniqueId, getKeyValue, setKeyValue (Forum #30528) fheminfo.om: report modules updated from thirdparty sites git-svn-id: https://svn.fhem.de/fhem/trunk@7346 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 36 +++-- fhem/FHEM/98_fheminfo.pm | 322 +++++++-------------------------------- fhem/FHEM/99_Utils.pm | 72 ++++++++- 3 files changed, 148 insertions(+), 282 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index f18687f37..9302053b8 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,7 @@ # 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: fheminfo: report third-party modules + - feature: 99_Utils.pm: add getUniqueID, getKeyValue, setKeyValue - feature: SMARTMON: additional parameters for smartctl - added: 00_HXB / 10_HXBDevice for Hexabus devices - feature: HUEbridge: added group handling @@ -12,7 +14,8 @@ - feature: HUEDevice: allow ct presets in webCmd new subTypes extcolordimer and ctdimer start support for Lightify bulbs - - added: SONOS and SONOSPLAYER to support Sonos Multiroom Audiosystems (Reinerlein) + - added: SONOS and SONOSPLAYER to support Sonos Multiroom Audiosystems + (Reinerlein) - change: 64_ESA2000.pm: add batterystate - added: 42_SMARTMON: Frontend to smartctl (maintainer: hexenmeister) - feature: 70_PushNotifier added line break in Messages (xusader) @@ -22,7 +25,8 @@ - bugfix: FB_CALLMONITOR: fixing not working company numbers reverse search for search.ch - bugfix: 70_PushNotifier repair set function (xusader) - - bugfix: PRESENCE: fixing not working timer, when using set [...] statusRequest + - bugfix: PRESENCE: fixing not working timer, when using set [...] + statusRequest - bugfix: FB_CALLMONITOR: fixing reverse search for klicktel.de - feature: new module 52_I2C_MCP342x.pm added (klausw) - feature: SYSMON: read cpu temp on FritzBox @@ -36,7 +40,8 @@ - feature: new module 98_logProxy.pm added (justme1968) - change: 66_ECMD: ReadyFn added (fixes issue under Windows) - change: 02_RSS: use a GUID in RSS; urlq source for img command - - feature: 70_PushNotifier improve usebility, configuration without cURL (xusader) + - feature: 70_PushNotifier improve usebility, configuration without cURL + (xusader) - bugfix: SYSMON: prevent empty line im log by userReadings - feature: 10_IT empfang (by bjoernh) - bugfix: PRESENCE: fix race condition, when delete disabled attribute and @@ -51,26 +56,32 @@ - feature: 15_CUL_EM added attribute maxPeak (arnoaugustin) - bugfix: 10_IT changed "setstate" to avoid eventMap errors (arnoaugustin) - feature: new module 37_harmony.pm added (justme1968) - - change: WMBUS: use _ instead of : as readings separator, better support for EnergyCam + - change: WMBUS: use _ instead of : as readings separator, better support for + EnergyCam - feature: new module 23_KOSTALPIKO added (john) - - feature: new module 98_HourCounter added, 99_UtilsHourCounter.pm added to contrib (john) + - feature: new module 98_HourCounter added, 99_UtilsHourCounter.pm added to + contrib (john) - added: MYSENSORS: connect to serial or Ethernet MySensors Gateway - added: MYSENSORS_DEVICE: represent a MySensors sensor- or actor node - feature: global ATTR/DELETEATTR/MODIFIED events - feature: 55_GDS.pm - attr disable added - bugfix: SYSMON: prevent endless loop at startup with 'disable' attribute - - feature: SYSMON: added FritzBox informations: DSL rate, DSLAM sync time, count of CRC an FEC + - feature: SYSMON: added FritzBox informations: DSL rate, DSLAM sync time, + count of CRC an FEC - bugfix: SYSMON: unwanted characters in dsl info lines - change: 57_Calendar: process continuation lines, get/set syntax checks - bugfix: SYSMON: fix availability of cpu/kernel_max - bugfix: SYSMON: numeric check - change: 59_Weather: change icons for conditions 31, 34, 36 - added: MQTT: connect fhem with mqtt - - added: MQTT_BRIDGE: bidirectional mapping of existing fhem-device to mqtt-topic - - added: MQTT_DEVICE: fhem-device that can be controlled by and publishes to mqtt + - added: MQTT_BRIDGE: bidirectional mapping of existing fhem-device to + mqtt-topic + - added: MQTT_DEVICE: fhem-device that can be controlled by and publishes to + mqtt - added: I2C_LCD: module to drive PCF8574T based LCD connected via I2C - added: I2C_DS1307: module to read time and date from DS1307 connected by i2c - - added: OWX_ASYNC: asynchronous, non-blocking version of OWX for DS2480, DS9097 and FRM + - added: OWX_ASYNC: asynchronous, non-blocking version of OWX for DS2480, + DS9097 and FRM - feature FRM: work as physical IODev for I2C_XXX modules added: FRM_ROTENC: read rotary-encoders with FRM added: FRM_RGB: control rgb-leds with FRM @@ -89,15 +100,16 @@ - feature: 57_Calendar: deal with non-existent end times - bugfix: SOMFY: fix non-working on/off-for-timer methods made positioning attributes optional - - feature: SOMFY: support for exact positioning (one-time setup of run times required) - support for parse()-function, requires newest CULFW. + - feature: SOMFY: support for exact positioning (one-time setup of run times + required) support for parse()-function, requires newest CULFW. - feature: userattr is now also device attribute - feature: ZWave: Fibaro_FGRM222 MANUFACTURER_PROPRIETARY class - feature: sequence: reportEvents attribtue added - feature: SYSMON: RAM and SWAP Readings on OSX - change: 34_NUT: removed calculation of values. Use userReadings instead. removed autogeneration of attr model and serNo. - - feature: SYSMON: improvement: support network information (IP, IPv6) on german linux + - feature: SYSMON: improvement: support network information (IP, IPv6) on + german linux - feature: Synology DiskStation NAS basic spk file creation - change: 34_NUT: readingFnAttributes added; creation of units deleted; changed attr asReadings to use comma instead of space diff --git a/fhem/FHEM/98_fheminfo.pm b/fhem/FHEM/98_fheminfo.pm index d874975f0..cfac256c4 100644 --- a/fhem/FHEM/98_fheminfo.pm +++ b/fhem/FHEM/98_fheminfo.pm @@ -26,8 +26,8 @@ use strict; use warnings; use Config; + sub CommandFheminfo($$); -my %UPDATE; ######################################## sub @@ -37,11 +37,6 @@ fheminfo_Initialize($$) Fn => "CommandFheminfo", Hlp => "[send],show or send Fhem statistics", ); - $UPDATE{server} = "http://fhem.de"; - $UPDATE{path} = "fhemupdate4"; - $UPDATE{packages} = "FHEM"; - $UPDATE{FHEM}{control} = "controls_fhem.txt"; - $cmds{fheminfo} = \%hash; } @@ -72,86 +67,35 @@ CommandFheminfo($$) return "Unknown argument $args[0], usage: fheminfo [send]" if(@args && lc($args[0]) ne "send"); - return "Argument 'send' is not useful, if global attribute 'sendStatistics' is set to 'never'." - if(@args && lc($args[0]) eq "send" && lc(AttrVal("global","sendStatistics","")) eq "never"); - my $branch = "DEVELOPMENT"; + return "Won't send, as sendStatistics is set to 'never'." + if(@args && + lc($args[0]) eq "send" && + lc(AttrVal("global","sendStatistics","")) eq "never"); + + my $branch = "DEVELOPMENT"; # UNUSED my $release = "5.6"; my $os = $^O; my $arch = $Config{"archname"}; my $perl = $^V; - my $uniqueID = AttrVal("global","uniqueID",undef); + my $uniqueID = getUniqueId(); my $sendStatistics = AttrVal("global","sendStatistics",undef); my $moddir = $attr{global}{modpath}."/FHEM"; - my $uidFile = $moddir."/FhemUtils/uniqueID"; - my $upTime; - $upTime = fhemUptime(); + my $upTime = fhemUptime(); + + my %official_module; - if(defined($uniqueID) && $uniqueID eq $uidFile) { - my $fh; - if(open($fh,"<".$uidFile)) { - Log 5, "fheminfo get uniqueID from $uidFile"; - while (my $line = <$fh>) { - chomp $line; - if($line =~ m/^uniqueID:[\da-fA-F]{32}$/) { - (undef,$uniqueID) = split(":",$line); - } - } - close $fh; + opendir(DH, $moddir) || return("$moddir: $!"); + foreach my $file (grep /^controls.*.txt$/, readdir(DH)) { + open(FH, "$moddir/$file") || next; + while(my $l = ) { + $official_module{$1} = 1 if($l =~ m+^UPD.* FHEM/\d\d_(.*).pm+); } + close(FH); } - - if(!defined($uniqueID) || $uniqueID !~ m/^[\da-fA-F]{32}$/ || !-e $uidFile) { - my $fh; - if(!open($fh,">".$uidFile)) { - return "Can't open $uidFile: $!"; - } - if(!defined($uniqueID) || defined($uniqueID) && $uniqueID !~ m/^[\da-fA-F]{32}$/) { - $uniqueID = join "",map { unpack "H*", chr(rand(256)) } 1..16; - } - print $fh "################################################################\n"; - print $fh "# IMPORTANT NOTE:\n"; - print $fh "# This file is auto generated from the fheminfo command.\n"; - print $fh "# Please do not modify, move or delete this file!\n"; - print $fh "#\n"; - print $fh "# This file contains an unique ID for this installation. It is\n"; - print $fh "# to be used for statistical purposes only.\n"; - print $fh "# Based on this unique ID, no conclusions can be drawn to any\n"; - print $fh "# personal information of this installation.\n"; - print $fh "################################################################\n"; - print $fh "\n"; - print $fh "uniqueID:$uniqueID\n"; - close $fh; - Log 5, "fheminfo 'uniqueID' generated and stored in file '$uidFile'"; - } - - $attr{global}{uniqueID} = $uidFile; - - my $ret = checkConfigFile($uidFile); - - return $ret if($ret); - - # get list of files - my $fail; - my $control_ref = {}; - - my $pack = "FHEM"; - - if(!-e "$moddir/$UPDATE{$pack}{control}") { - my $server = $UPDATE{server}; - my $BRANCH = "SVN"; - my $srcdir = $UPDATE{path}."/".lc($BRANCH); - Log 5, "fheminfo get $server/$srcdir/$UPDATE{$pack}{control}"; - my $controlFile = GetFileFromURL("$server/$srcdir/$UPDATE{$pack}{control}"); - return "Can't get '$UPDATE{$pack}{control}' from $server" if (!$controlFile); - # parse remote controlfile - ($fail,$control_ref) = parseControlFile($pack,$controlFile,$control_ref,0); - return "$fail\nfheminfo canceled..." if ($fail); - } else { - Log 5, "fheminfo get $moddir/$UPDATE{$pack}{control}"; - # parse local controlfile - ($fail,$control_ref) = parseControlFile($pack,"$moddir/$UPDATE{$pack}{control}",$control_ref,1); - } + closedir(DH); + return "Can't read FHEM/controls_fhem.txt, execute update first." + if(!%official_module); foreach my $d (sort keys %defs) { my $n = $defs{$d}{NAME}; @@ -159,8 +103,7 @@ CommandFheminfo($$) my $m = "unknown"; $m = $defs{$d}{model} if( defined($defs{$d}{model}) ); $m = AttrVal($n,"model",$m); - if(exists $control_ref->{$t}) { - Log 5, "fheminfo name:$n type:$t model:$m"; + if($official_module{$t} && !$defs{$d}{TEMPORARY}) { $info{modules}{$t}{$n} = $m; } } @@ -170,7 +113,6 @@ CommandFheminfo($$) my $str; $str = "Fhem info:\n"; $str .= sprintf(" Release%*s: %s\n",2," ",$release); - $str .= sprintf(" Branch%*s: %s\n",3," ",$branch); $str .= sprintf(" OS%*s: %s\n",7," ",$os); $str .= sprintf(" Arch%*s: %s\n",5," ",$arch); $str .= sprintf(" Perl%*s: %s\n",5," ",$perl); @@ -185,7 +127,6 @@ CommandFheminfo($$) my $length = (reverse sort { $a <=> $b } map { length($_) } @modules)[0]; $str .= "Defined modules:\n"; - foreach my $t (sort keys %{$info{modules}}) { my $c = scalar keys %{$info{modules}{$t}}; my @models; @@ -197,7 +138,8 @@ CommandFheminfo($$) } $str .= sprintf(" %s%*s: %d\n",$t,$length-length($t)+1," ",$c); if(@models != 0) { - $modStr .= sprintf(" %s%*s: %s\n",$t,$length-length($t)+1," ",join(",",sort @models)); + $modStr .= sprintf(" %s%*s: %s\n", + $t,$length-length($t)+1," ", join(",",sort @models)); $contModels .= join(",",sort @models)."|"; } $contModules .= "$t:$c|"; @@ -209,12 +151,12 @@ CommandFheminfo($$) $str .= $modStr; } - my $transmitData = ($attr{global}{sendStatistics}) ? $attr{global}{sendStatistics} : "not set"; - $str .= "\n"; - $str .= "Transmitting this information during an update:\n"; - $str .= " $transmitData (Note: You can change this via the global attribute sendStatistics)\n"; + my $td = (lc(AttrVal("global", "sendStatistics", "")) eq "onupdate") ? + "yes" : "no"; - $ret = $str; + $str .= "\n"; + $str .= "Transmitting this information during an update: $td\n"; + $str .= "You can change this via the global attribute sendStatistics\n"; if(@args != 0 && $args[0] eq "send") { my $uri = "http://fhem.de/stats/statistics.cgi"; @@ -239,125 +181,15 @@ CommandFheminfo($$) timeout => 60); my $res = $ua->request($req); - $ret .= "\nserver response: "; + $str .= "\nserver response: "; if($res->is_success) { - $ret .= $res->content."\n"; + $str .= $res->content."\n"; } else { - $ret .= $res->status_line."\n"; - } - } - - return $ret; -} - -######################################## -sub parseControlFile($$$$) { - my ($pack,$controlFile,$control_ref,$local) = @_; - my %control = %$control_ref if ($control_ref && ref($control_ref) eq "HASH"); - my $from = ($local ? "local" : "remote"); - my $ret; - - if ($local) { - my $str = ""; - # read local controlfile in string - if (open FH, "$controlFile") { - $str = do { local $/; }; - } - close(FH); - $controlFile = $str - } - # parse file - if ($controlFile) { - foreach my $l (split("[\r\n]", $controlFile)) { - chomp($l); - Log 5, "fheminfo $from controls_".lc($pack).".txt: $l"; - my ($ctrl,$date,$size,$file,$move) = ""; - if ($l =~ m/^(UPD) (20\d\d-\d\d-\d\d_\d\d:\d\d:\d\d) (\d+) (\S+)$/) { - $ctrl = $1; - $date = $2; - $size = $3; - $file = $4; - } elsif ($l =~ m/^(DIR) (\S+)$/) { - $ctrl = $1; - $file = $2; - } elsif ($l =~ m/^(MOV) (\S+) (\S+)$/) { - $ctrl = $1; - $file = $2; - $move = $3; - } elsif ($l =~ m/^(DEL) (\S+)$/) { - $ctrl = $1; - $file = $2; - } else { - $ctrl = "ESC" - } - if ($ctrl eq "ESC") { - Log 1, "fheminfo File 'controls_".lc($pack).".txt' ($from) is corrupt"; - $ret = "File 'controls_".lc($pack).".txt' ($from) is corrupt"; - } - last if ($ret); - if ($l =~ m/^UPD/ && $file =~ m/^FHEM/) { - if ($file =~ m/^.*(\d\d_)(.*).pm$/) { - my $modName = $2; - $control{$modName} = $file; - } - } - } - } - return ($ret, \%control); -} - -######################################## -sub checkConfigFile($) { - my $uidFile = shift; - my $name = "fheminfo"; - my $configFile = AttrVal("global","configfile",""); - - if($configFile && !configDBUsed()) { - my $fh; - if(!open($fh,"<".$configFile)) { - return "Can't open $configFile: $!"; - } - - my @currentConfig = <$fh>; - close $fh; - - my @newConfig; - my $done = 0; - - if(grep {$_ =~ /uniqueID/} @currentConfig) { - Log 5, "fheminfo uniqueID in configfile"; - foreach my $line (@currentConfig) { - if($line =~ m/uniqueID/ && $line =~ m/[\da-fA-F]{32}/) { - Log 5, "fheminfo uniqueID in configfile and hex"; - $line = "attr global uniqueID $uidFile\n"; - $done = 1; - } - push(@newConfig,$line); - } - } else { - Log 5, "fheminfo uniqueID not in configfile"; - foreach my $line (@currentConfig) { - push(@newConfig,$line); - if($line =~ /modpath/ && $done == 0) { - push(@newConfig,"attr global uniqueID $uidFile\n"); - $done = 1; - } - } - } - - if($done) { - if(!open($fh,">".$configFile)) { - return "Can't open $configFile: $!"; - } - - foreach (@newConfig) { - print $fh $_; - } - close $fh; - Log 1, "$name global attributes 'uniqueID' added to configfile $configFile"; + $str .= $res->status_line."\n"; } } + return $str; } ######################################## @@ -372,25 +204,29 @@ sub checkModule($) { } } -sub fhemUptime { - my $diff = time - $fhem_started; - my ($d,$h,$m,$ret); - - ($d,$diff) = _myDiv($diff,86400); - ($h,$diff) = _myDiv($diff,3600); - ($m,$diff) = _myDiv($diff,60); +sub +fhemUptime() +{ + my $diff = time - $fhem_started; + my ($d,$h,$m,$ret); + + ($d,$diff) = _myDiv($diff,86400); + ($h,$diff) = _myDiv($diff,3600); + ($m,$diff) = _myDiv($diff,60); - $ret = ""; - $ret .= "$d days, " if($d > 1); - $ret .= "1 day, " if($d == 1); - $ret .= sprintf("%02s:%02s:%02s", $h, $m, $diff); + $ret = ""; + $ret .= "$d days, " if($d > 1); + $ret .= "1 day, " if($d == 1); + $ret .= sprintf("%02s:%02s:%02s", $h, $m, $diff); - return $ret; + return $ret; } -sub _myDiv($$) { - my ($p1,$p2) = @_; - return (int($p1/$p2), $p1 % $p2); +sub +_myDiv($$) +{ + my ($p1,$p2) = @_; + return (int($p1/$p2), $p1 % $p2); } 1; @@ -420,7 +256,7 @@ sub _myDiv($$) {
  • Operating System Information
  • Hardware architecture
  • Installed Perl version
  • -
  • Installed FHEM release and branch
  • +
  • Installed FHEM release
  • Defined modules (only official FHEM Modules are counted)
  • Defined models per module
  • @@ -430,7 +266,6 @@ sub _myDiv($$) { fhem> fheminfo Fhem info: Release : 5.3 - Branch : DEVELOPMENT OS : linux Arch : i686-linux-gnu-thread-multi-64int Perl : v5.14.2 @@ -477,31 +312,6 @@ sub _myDiv($$) {

      -
    • uniqueID
      - A randomly generated ID (16 pairs of hash values), e.g. - 87c5cca38dc75a4f388ef87bdcbfbf6f which is assigned to the transmitted - data to prevent duplicate entries. -
      - The uniqueID is stored automatically in a file named FhemUtils/uniqueID - in FHEM's modules path. -
      - IMPORTANT NOTE: -
      - Every installation of FHEM should have to have his own unique ID. -
      - Please do not modify, move or delete this file! You should always backup this file - (this is normally done by the update command automatically) and please restore - this file to the same path (FhemUtils in FHEM's modules path), if you plan to - reinstall your FHEM installation. This prevents duplicate entries for identical - installations on the same hardware in the statistics. -
      - Otherwise, please use different unique IDs for each installation of FHEM on different - hardware, e.g. one randomly generated unique ID for FRITZ!Box, another one for the first - Raspberry Pi, another one for the second Raspberry Pi, etc. -
      - Thanks for your support! -
    • -
    • sendStatistics
      This attribute is used in conjunction with the update command.
      @@ -540,7 +350,7 @@ sub _myDiv($$) {
    • Eingesetztes Betriebssystem
    • Hardware Architektur
    • Installierte Perl Version
    • -
    • Installierte FHEM release und "branch"
    • +
    • Installierte FHEM release
    • Definierte Module (nur offizielle FHEM Module werden ermittelt)
    • Definierte Modelle je Modul
    @@ -550,7 +360,6 @@ sub _myDiv($$) { fhem> fheminfo Fhem info: Release : 5.3 - Branch : DEVELOPMENT OS : linux Arch : i686-linux-gnu-thread-multi-64int Perl : v5.14.2 @@ -597,31 +406,6 @@ sub _myDiv($$) {

      -
    • uniqueID
      - Eine zufällig generierte ID (16 Paare aus Hash Werten), z.B. - 87c5cca38dc75a4f388ef87bdcbfbf6f welche den übertragenen Daten - zur Vermeidung von doppelten Einträge zugewiesen wird. -
      - Die uniqueID wird automatisch in einer Datei namens FHemUtils/uniqueID - im FHEM Modulverzeichnis gespeichert. -
      - WICHTIGER HINWEIS: -
      - Jede Installation von FHEM sollte seine eigene eindeutige ID haben. -
      - Bitte diese Datei nicht verändern, verschieben oder löschen! Diese Datei sollte - immer gesichert (wird normalerweise automatisch durch den update Befehl - erledigt) und bei einer Neuinstallation auf der gleichen Hardware im gleichen Verzeichnis - (FhemtUtils im FHEM Modulverzeichnis) wieder hergestellt werden. Dies verhindert - doppelte Einträge identischer Installationen auf der gleichen Hardware in der Statistik. -
      - Anderfalls, sollten bitte für jede Installation auf unterschiedlicher Hardware eigene - IDs genutzt werden, z.B. eine zufällig erzeugte ID für FRITZ!Box, eine weitere für - den ersten Raspberry Pi, eine weitere für einen zweiten Raspberry Pi, usw. -
      - Vielen Dank für die Unterstützung! -
    • -
    • sendStatistics
      Dieses Attribut wird in Verbindung mit dem update Befehl verwendet.
      diff --git a/fhem/FHEM/99_Utils.pm b/fhem/FHEM/99_Utils.pm index d51d1cfe2..ae41d7c5b 100644 --- a/fhem/FHEM/99_Utils.pm +++ b/fhem/FHEM/99_Utils.pm @@ -12,6 +12,58 @@ Utils_Initialize($$) my ($hash) = @_; } +sub +getUniqueId() +{ + my ($err, $uniqueID) = getKeyValue("uniqueID"); + return $uniqueID if(!$err); + srand(time); + $uniqueID = join "",map { unpack "H*", chr(rand(256)) } 1..16; + setKeyValue("uniqueID", $uniqueID); + return $uniqueID; +} + +sub +getKeyValue($) +{ + my ($key) = @_; + my $fName = $attr{global}{modpath}."/FHEM/FhemUtils/uniqueID"; + my ($err, @l) = FileRead($fName); + return ($err, undef) if($err); + for my $l (@l) { + return (undef, $1) if($l =~ m/^$key:(.*)/); + } + return ("Key not found", undef); +} + +sub +setKeyValue($$) +{ + my ($key,$value) = @_; + my $fName = $attr{global}{modpath}."/FHEM/FhemUtils/uniqueID"; + my ($err, @old) = FileRead($fName); + my @new; + if($err) { + push(@new, "# This file is auto generated.", + "# Please do not modify, move or delete it.", + ""); + @old = (); + } + + my $fnd; + foreach my $l (@old) { + if($l =~ m/^$key:/) { + $fnd = 1; + push @new, "$key:$value" if(defined($value)); + } else { + push @new, $l; + } + } + push @new, "$key:$value" if(!$fnd && defined($value)); + + return FileWrite($fName, @new); +} + sub time_str2num($) { @@ -336,8 +388,26 @@ myUtils_Initialize($$) nc replacement.

    • +
    • getUniqueId()
      + return the FHEM uniqueID used by the fheminfo command. Uses the + getKeyValue / setKeyValue functions. +

    • + +
    • setKeyValue(keyName, value)
      + store the value in the file $modpath/FHEM/FhemUtils/uniqueID (the name is + used for backward compatibility), or in the database, if using configDB. + value may not contain newlines, and only one value per key is stored. + The file/database entry will be written immediately, no explicit save is + required. If the value is undef, the entry will be deleted. + Returns an error-string or undef. +

    • + +
    • getKeyValue(keyName)
      + return ($error, $value), stored previously by setKeyValue. + $error is undef if there was no error, otherwise $value is undef. +

    • +
    =end html =cut -