Huhu,
die PERL WARNINGs nach der Zwangstrennung, die ich 2022 (https://forum.fhem.de/index.php?msg=1209097) schon mal angesprochen hatte, haben mich nie ganz losgelassen.
Jetzt hab ich mich mit meinem Agenten mal drüber hergemacht, und rausgekommen ist ein Patch, der das direkt in 32_mailcheck.pm löst, ohne an Mail::IMAPClient rumzuschrauben. Nebenbei erledigt er auch das taube Device, das Benni damals beschrieben hat.
Du hattest in #86864 (https://forum.fhem.de/index.php?topic=86864.0) geschrieben, dass die Verbindungsprüfung an einigen Stellen fehlt. Genau daher kommen die Warnungen. Das Log von 2022 zeigt sie in poll Zeile 275/276 (select/idle) und in Read Zeile 378/382/483 (idle_data/done/idle).
- Read: Schließt der Server die IDLE-Verbindung, erkennt idle_data() das EOF. Danach gehen trotzdem noch DONE und IDLE raus, erst dann kommt die IsConnected-Prüfung.
- poll, toter Socket: Nach "Found and deleted bad fileno for mc_..." ruft fhem.pl mailcheck_Read nie wieder auf, und poll verbindet nicht neu. Das Device zeigt "Logged in", ist aber taub (bei mir 2026 bisher 54-mal, teils stundenlang). Das ist der Zustand, den Benni damals mit dem readLog-notify abgefangen hat.
- poll, select: Oft merkt erst select() den toten Socket, das folgende idle() geht ins Leere.
- Connect: Ohne Timeout blockiert der Socket-Aufbau bei nicht erreichbarem Server FHEM bis zum TCP-Timeout des Systems. Ein fehlgeschlagener Connect wird nicht wiederholt.
Vorschlag (Patch unten):
- Read: direkt nach idle_data() IsConnected prüfen und neu verbinden.
- poll: neu verbinden, wenn der Client tot ist oder das Device nicht mehr in %selectlist steht. Nach select() noch einmal IsConnected prüfen.
- poll: einen fehlgeschlagenen Connect beim nächsten Intervall wiederholen, aber nicht nach abgelehntem Login, damit ein falsches Passwort das Konto nicht sperrt. Dafür setzt Connect STATE "Login failed" und loggt auf Level 2.
- Connect: Timeout => 10 für IO::Socket::INET/SSL.
Bei mir mit vier Devices (Gmail und T-Online) in Betrieb, Read/poll seit 14.10., Timeout/select seit 23.10.: keine PERL WARNING mehr, nach "bad fileno" wieder eingeloggt nach 45–50 s statt Stunden, ein fehlgeschlagener Connect nach einem Intervall nachgeholt. Meine notifies von damals (inactive/active bei mqtt2Cloud:disconnected) habe ich gelöscht, sie sind damit überflüssig.
$Id 16299, Mail::IMAPClient 3.43:
--- a/FHEM/32_mailcheck.pm
+++ b/FHEM/32_mailcheck.pm
@@ -137,10 +137,12 @@
if( AttrVal($name, "nossl", 0) ) {
$socket = IO::Socket::INET->new( PeerAddr => $hash->{Host},
PeerPort => 143, #AttrVal($name, "port", 143)
+ Timeout => 10,
);
} else {
$socket = IO::Socket::SSL->new( PeerAddr => $hash->{Host},
PeerPort => 993, #AttrVal($name, "port", 993)
+ Timeout => 10,
);
}
@@ -194,6 +196,8 @@
} else {
mailcheck_Disconnect($hash);
+ $hash->{STATE} = "Login failed";
+ Log3 $name, 2, "$name: login failed for $hash->{helper}{user}";
}
} else {
#$hash->{STATE} = "Connected";
@@ -269,10 +273,33 @@
RemoveInternalTimer($hash);
InternalTimer(gettimeofday()+$hash->{INTERVAL}, "mailcheck_poll", $hash, 0);
+ # socket may be gone without mailcheck_Read noticing (e.g. fhem.pl dropped
+ # a bad fileno from %selectlist): reconnect instead of polling a dead client
my $client = $hash->{CLIENT};
- if( $client && $client->IsConnected && $client->IsAuthenticated ) {
- $client->done;
+ $client->done if( $client && $client->IsConnected && $client->IsAuthenticated );
+ if( !$client || !$client->IsConnected || !$selectlist{$hash->{NAME}} ) {
+ mailcheck_Disconnect($hash);
+ mailcheck_Connect($hash);
+
+ # keep retrying while the server is unreachable, but not after a
+ # rejected login (wrong password would lock the account)
+ InternalTimer(gettimeofday()+$hash->{INTERVAL}, "mailcheck_poll", $hash, 0)
+ if( !$hash->{CLIENT} && $hash->{STATE} ne "Login failed" );
+
+ return undef;
+ }
+
+ if( $client->IsAuthenticated ) {
$client->select($hash->{Folder});
+
+ # select may be the first command to notice a dead socket
+ unless( $client->IsConnected ) {
+ mailcheck_Disconnect($hash);
+ mailcheck_Connect($hash);
+
+ return undef;
+ }
+
$hash->{tag} = $client->idle;
$hash->{LAST_POLL} = FmtDateTime( gettimeofday() );
}
@@ -377,6 +404,15 @@
my $ret = $client->idle_data();
+ # server closed the connection (e.g. IDLE timeout): reconnect before
+ # sending DONE/IDLE on a dead socket
+ unless( $client->IsConnected ) {
+ mailcheck_Disconnect($hash);
+ mailcheck_Connect($hash);
+
+ return undef;
+ }
+
if( !defined($ret) || !$ret ) {
$hash->{tag} = undef;
$ret = $client->done;
Ich stell das einfach mal zur Diskussion. Vielleicht übersehe ich ja was, oder es geht eleganter.
Gruß Thomas