HMCCURPC - Inkompatibilität mit HUEBridge (JSON)

Begonnen von EfkaPE, 01 August 2017, 15:10:42

Vorheriges Thema - Nächstes Thema

justme1968

@jochenj: es hat nichts mit dem problem zu tun, aber die zeilen der fehlermeldungen passen nicht zur aktuellen HUEBridge modul version. d.h. du hast kein update gemacht.

@zap: die hue bridge selber lauscht auf port 80 wie ein normaler web server.

aber warum sollte das relevant sein?

zum eigentlichen problem: ich verstehe immer noch nicht was hier schief geht. die meldung besagt ja das decode_json einen object oder ein array erwartet. das ist aber doch blödsinn. decode_json bekommt einen string übergeben und macht daraus einen hash.

da ich es mangels ccu nicht selber testen kann tippe ich auf irgendein thread problem mit bestimmten perl versionen. eventuell wenn die perl version selber keine threads kann? fhem ist im übrigen auch nicht thread safe.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

zap

#16
Zitat von: justme1968 am 02 Oktober 2017, 18:31:51
@zap: die hue bridge selber lauscht auf port 80 wie ein normaler web server.

Irgendein Portkonflikt ... aber in dem Fall nicht. Alles nur Strohhalme, da das Problem nicht bei jedem auftritt.

Zitat
da ich es mangels ccu nicht selber testen kann tippe ich auf irgendein thread problem mit bestimmten perl versionen. eventuell wenn die perl version selber keine threads kann? fhem ist im übrigen auch nicht thread safe.

Kann sein. Was dagegen spricht: Das Problem haben auch Leute, die gar kein HMCCU einsetzen. Möglicherweise nutzen die aber SONOS, das auch Threads verwendet. Ich nutze HMCCU, HUEBridge und SONOS ohne Probleme.

Die Thread-Funktionen in HMCCURPC sind "vorsichtig" programmiert. Soll heißen, ich verwende dort nur eigenen Code ohne Bezug zu FHEM. Lediglich in Read() werden die Shared-Memory Queues ausgelesen.

Aber vielleicht ist es ja ein Shared-Memory Problem. Vielleicht werden irgendwelche Speicherbereiche überschrieben, sodass sich die Module in die Haare bekommen.

Ggf. sollte man also mal eine andere Version von Thread::Queue verwenden (CPAN oder das fertige Linux-Paket, je nachdem, was nicht funkioniert). Es ist ja auch nicht so, dass Threads in Perl was völlig neues sind, genauso wie Shared Memory Queues. Möglicherweise sind nur neuere Linux / Perl Versionen betroffen. Habe aber gerade weder Zeit noch Nerven, mein System upzudaten.
2xCCU3 mit ca. 100 Aktoren, Sensoren
Entwicklung: FHEM auf Proxmox Debian VM
Produktiv inzwischen auf Home Assistant gewechselt.
Maintainer: HMCCU, (Fully, AndroidDB)

Barit

Seltsam ist, dass das decode-Problem in den Modulen dann behoben zu sein scheint wenn man bei JSON das "allow_nonref"-Attribut setzt. Ich hatte das mal hier beschrieben:
https://forum.fhem.de/index.php/topic,76842.msg687580.html#msg687580

Warum das allerdings abhängig von der Verwendung des RPC-Servers ist, ist mir auch unklar.

zap

Außer Module zu aktualisieren habe ich keine Idee mehr.

@justme1968: Weist Du, wann im JSON Modul allow_nonref eingeführt wurde? Es scheint zu helfen, wenn man da enabled.
Weiß aber nicht, ob das nur die Symptome oder die Ursache behebt. Wäre auch ein ziemlicher Aufwand, das bei allen Modulen zu ändern, die JSON verwenden.
2xCCU3 mit ca. 100 Aktoren, Sensoren
Entwicklung: FHEM auf Proxmox Debian VM
Produktiv inzwischen auf Home Assistant gewechselt.
Maintainer: HMCCU, (Fully, AndroidDB)

justme1968

ich habe keine idee was hier schief läuft. zumal die perl doku zu decode_json explizit sagt das ein string erwartet wird.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

zap

#20
Habe überlegt, einen neuen Thread zu starten. Da es aber so viele gibt zu dem Thema, nehme ich jetzt einfach mal den hier.

@justme1986: Schau Dir bitte mal die Doku zu JSON::XS an. Verstehst Du das auch so wie ich? (s. Auszug unten).

Ich habe nun einen neuen Raspi mit Debian Stretch aufgesetzt. Die JSON Fehlermeldung kann ich nachvollziehen, d.h. sie tritt bei mir auch auf. Der Fehler kommt bei der nächsten HUEBridge-Interaktion, die JSON nutzt (decode). Aber nur dann, wenn der externe RPC-Server von HMCCURPC läuft, d.h. die Threads gestartet sind. Die Definition des Device HMCCURPC alleine führt zu keinem JSON Fehler.

Soweit so gut bzw. schlecht.

Die Fehlermeldung lautet konkret:

"json error: JSON text must be an object or array (but found number, string, true, false or null, use allow_nonref to allow this) at ./FHEM/30_HUEBridge.pm line 1122."

JSON beschwert sich also, dass an decode_json() ein String übergeben wurde. Das könnte in Zusammenhang mit JSON::XS stehen, das zur Beschleunigung der Dekodierung verwendet wird, sofern es vorhanden ist. Bei diesem Modul wurde aus Sicherheitsgründen das Verhalten geändert. Es muss nun explizit allow_nonref eingeschaltet werden, damit man an decode_json einen String übergeben kann. Warum der Fehler nur auftritt, wenn die Threads von HMCCURPC gestartet werden kann ich nur vermuten. Vielleicht wird in dem Moment die XS.so Shared Library nachgeladen und ab diesem Zeitpunkt von JSON verwendet.

Workaround: Man muss also verhindern, dass JSON zur Beschleunigung JSON::XS verwendet. Am einfachsten erreicht man das, indem man das Paket deinstalliert:


dpkg --purge libjson-xs-perl


Danach tritt der Fehler nicht mehr auf  :)

Alternative ist, dass alle Modulautoren ihre Module mit JSON anpassen und allow_nonref einschalten. Das wäre mittelfristig die besserer Lösung. Irgendwann wird das Problem auch ohne HMCCURPC auftauchen.

Im Beispiel zu JSON::XS auf CPAN:



use JSON::XS;

# exported functions, they croak on error
# and expect/generate UTF-8

$utf8_encoded_json_text = encode_json $perl_hash_or_arrayref;
$perl_hash_or_arrayref  = decode_json $utf8_encoded_json_text;

# OO-interface

$coder = JSON::XS->new->ascii->pretty->allow_nonref;
$pretty_printed_unencoded = $coder->encode ($perl_scalar);
$perl_scalar = $coder->decode ($unicode_json_text);

# Note that JSON version 2.0 and above will automatically use JSON::XS
# if available, at virtually no speed overhead either, so you should
# be able to just:

use JSON;


Im Nicht-OO Interface scheint man weiterhin einen String übergeben zu können. Im OO-Interface muss man allow_nonref einschalten.

Hier der Auszug aus der Doku von JSON::XS:


"OLD" VS. "NEW" JSON (RFC 4627 VS. RFC 7159)

Due to security concerns, JSON::XS will not allow scalar data in JSON texts by default - you need to create your own JSON::XS object and enable allow_nonref:

   my $json = JSON::XS->new->allow_nonref;

   $text = $json->encode ($data);
   $data = $json->decode ($text);
The long version: JSON being an important and supposedly stable format, the IETF standardised it as RFC 4627 in 2006. Unfortunately, the inventor of JSON, Dougles Crockford, unilaterally changed the definition of JSON in javascript. Rather than create a fork, the IETF decided to standardise the new syntax (apparently, so Iw as told, without finding it very amusing).

The biggest difference between thed original JSON and the new JSON is that the new JSON supports scalars (anything other than arrays and objects) at the toplevel of a JSON text. While this is strictly backwards compatible to older versions, it breaks a number of protocols that relied on sending JSON back-to-back, and is a minor security concern.

For example, imagine you have two banks communicating, and on one side, trhe JSON coder gets upgraded. Two messages, such as 10 and 1000 might then be confused to mean 101000, something that couldn't happen in the original JSON, because niether of these messages would be valid JSON.

If one side accepts these messages, then an upgrade in the coder on either side could result in this becoming exploitable.

This module has always allowed these messages as an optional extension, by default disabled. The security concerns are the reason why the default is still disabled, but future versions might/will likely upgrade to the newer RFC as default format, so you are advised to check your implementation and/or override the default with ->allow_nonref (0) to ensure that future versions are safe.
2xCCU3 mit ca. 100 Aktoren, Sensoren
Entwicklung: FHEM auf Proxmox Debian VM
Produktiv inzwischen auf Home Assistant gewechselt.
Maintainer: HMCCU, (Fully, AndroidDB)

herrmannj

ich hab an der Diagnose Zweifel.

non_ref beschäftigt sich mit der Tatsache das JSON streams, Toplevel, nicht aus Objekten oder Arrays bestehen müssen: supports scalars (anything other than arrays and objects) at the toplevel of a JSON text.

Meine vermute wäre eher das JSON::XS nicht thread sicher ist.

Die Umstellung aller Module oder die De-installation des XS (auf Verdacht) ist in meinen Augen kein guter Weg.

Hast Du mal versucht JSON::PP als Alternative zu verwenden (ab 5.14 core modul) ?
Dazu reicht es im head das "use JSON ;" bzw "use JSON::XS;" durch ein "use JSON:PP" zu ersetzen. Die meiden sind weitestgehend kompatibel und das PP steht für "pure perl".

vg
joerg

justme1968

ich glaube der text bezieht sich nicht darauf ob ein perl string oder perl hash/array übergeben wird sondern das die json daten auf oberster ebene keinen skala enthalten dürfen. das tun sie im hue beispiel auch nicht.

um das problem einkreisen ist es eigentlich garnicht schlecht JSON::XS mal zu deinstallieren.

eigentlich soll man ja PP oder XS garnicht direkt selber einbinden sondern nur JSON und das system kümmert sich dann darum die 'passende' bzw. vorhandene variante einzubinden. XS ist auf der fritzbox z.b. nicht vorhanden.

hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

zap

#23
Ich verwende JSON ja gar nicht in HMCCU. Andere Module wie HUEBridge benutzen es. Dort steht aber nur ein

use JSON;

Das Modul JSON.pm prüft dann eigenständig, ob JSON::XS bzw. XS.so installiert ist und nutzt die schnelleren Routinen von XS.so zur Dekodierung. Das lässt sich nicht verhindern, außer man sorgt dafür, dass JSON::XS verschwindet. Möglicherweise nutzt JSON dann JSON::PP.

Für meine Theorie spricht auch, dass der Fehler nach Deinstallation von JSON::XS nicht mehr auftritt. Ein User hat eigenständig HUEBridge modifiziert und allow_nonref enabled. Auch dann tritt der Fehler nicht mehr auf.
2xCCU3 mit ca. 100 Aktoren, Sensoren
Entwicklung: FHEM auf Proxmox Debian VM
Produktiv inzwischen auf Home Assistant gewechselt.
Maintainer: HMCCU, (Fully, AndroidDB)

justme1968

ja. JSON nimmt automatisch PP wenn XS nicht vorhanden ist.

edit: wenn man sucht findet man viele hinweise das perl thread 'discouraged' sind. sogar direkt in der thread doku, das es probleme mit XS modulen gibt wenn man nicht aufpasst und in der XS doku steht explizit das XS nicht thread save ist und sich das auch nicht ändern wird.

und noch etwas: vielleicht hilft das hier: http://alvar.a-blast.org/vortraege/perl-workshop/forks.pdf
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

herrmannj

#25
@justme: sag ich doch :)

bei "use JSON" kann man das backend explizit auswählen, muss man also nix deinstallieren ;)

wenn jetzt aber HMCCU gar kein JSON verwendet wirds komplizierter. Dann entsteht das prob durch die Verwendung der threads.

Kleiner Ausflug: perl threads sind nicht "wie sonst" threads die vom OS nebenläufig gestartet werden. Für Perl threads wird der komplette Speicher einmal kopiert und dann ein ein neuer Interpreter gestartet. Das hat mich bei WifiLight auch "zurückgeworfen". Da hatte ich die schon implementiert und dann festgestellt das sich mit jedem thread der RAM Verbrauch direkt verdoppelt (1). JSON:XS wird auch mit jedem thread einmal alle internen Strukturen kopieren.

Für threads lautet die "allgemeine" Empfehlung beim Start vom pl einen pool zu erzeugen (BEGIN { .. }). Das funktioniert im Context fhem aber nicht (weil die module ja später geladen werden). Als Alternative starte ich derzeit einen zweiten perl Prozess der dann die threads bei Bedarf erzeugt (2). Dadurch wir allerdings auch die Kommunikation viel anspruchsvoller. Lese also mit Interesse hier mit :)

edit, Fußnoten
1: aus 200MB gleich mal 400MB usw
2: 2MB pro thread

herrmannj

naja, so schlimm sind threads auch wieder nicht. Fork hat auch seine Eigenheiten ...

justme1968

eigenheiten vielleicht. aber prozesse sind leichter zu debuggen, stabiler, portabler, ... :) ich kann mir im fhem umfeld eigentlich kein problem vorstellen das mit prozessen nicht zu lösen ist und bei dem es auf den vielleicht vorhandenen effizienzvorteil der threads ankommt. erst recht weil die perl threads ja gerade nicht besonders effizient sind. aber das ist eine andere diskussion...
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

zap

Zitat von: herrmannj am 06 Oktober 2017, 16:57:32
bei "use JSON" kann man das backend explizit auswählen, muss man also nix deinstallieren ;)

Ist in den Modulen die JSON verwenden aber leider nicht ausgewählt.

Zitat
wenn jetzt aber HMCCU gar kein JSON verwendet wirds komplizierter. Dann entsteht das prob durch die Verwendung der threads.

Sehr wahrscheinlich. Vielleicht aber auch durch die Verwendung von Shared Memory Queues (Thread::Queue).

Zitat
Kleiner Ausflug: perl threads sind nicht "wie sonst" threads die vom OS nebenläufig gestartet werden. Für Perl threads wird der komplette Speicher einmal kopiert und dann ein ein neuer Interpreter gestartet. Das hat mich bei WifiLight auch "zurückgeworfen". Da hatte ich die schon implementiert und dann festgestellt das sich mit jedem thread der RAM Verbrauch direkt verdoppelt (1). JSON:XS wird auch mit jedem thread einmal alle internen Strukturen kopieren.

Das ist bei forks nicht anders. Auch da wird der Parent mitsamt seinem Speicher geklont.

Zitat
Für threads lautet die "allgemeine" Empfehlung beim Start vom pl einen pool zu erzeugen (BEGIN { .. }). Das funktioniert im Context fhem aber nicht (weil die module ja später geladen werden). Als Alternative starte ich derzeit einen zweiten perl Prozess der dann die threads bei Bedarf erzeugt (2). Dadurch wir allerdings auch die Kommunikation viel anspruchsvoller. Lese also mit Interesse hier mit :)

Ich erzeuge in HMCCURPC nicht ständig Threads und beende sie wieder. Die Threads werden beim Start des RPC-Servers einmalig alle gestartet (quasi wie ein Pool). Ein Thread zur Datenverarbeitung und zusätzlich ein Thread je CCU-Protokoll/Schnittstelle (in Summe 2-5 Threads). Die Daten werden von den Threads in Shared Memory Queues an FHEM übergeben. Ein Thread informiert FHEM per Socket-Pair, dass Daten in den Queues anstehen und FHEM liest in Read() die Daten aus den Queues.
Die reine Kommunikation über die Socket-Pairs funktioniert nicht, da FHEM die Daten nicht schnell genug verarbeiten kann und dann in den Threads Write-Errors bei Schreiben in die Sockets entstehen. Mein erster Ansatz waren geforkte Prozesse. Ich habe es aber nie geschafft, die Shared-Memory Queues mit geforkten Prozessen zum Laufen zu bekommen. Daher bin ich auf Threads umgestiegen.

@justme: Mich "discouragen" nicht die Threads sondern eher die Antiquiertheit von Perl ;-)

2xCCU3 mit ca. 100 Aktoren, Sensoren
Entwicklung: FHEM auf Proxmox Debian VM
Produktiv inzwischen auf Home Assistant gewechselt.
Maintainer: HMCCU, (Fully, AndroidDB)

herrmannj

Fork verwendet copy on write. Das ist was anderes.
Shared memory läuft nicht mit fork.

Das mit den Write errors bei sockets verstehe ich nicht. Non blocking socket, select, bei e would block in einen Puffer schreiben.