diff --git a/fhem/contrib/98_systemd_watchdog.pm b/fhem/contrib/98_systemd_watchdog.pm new file mode 100644 index 000000000..30b7dd14b --- /dev/null +++ b/fhem/contrib/98_systemd_watchdog.pm @@ -0,0 +1,343 @@ +############################################################################### +# +# Copyright notice +# +# (c) 2018 Alexander Schulz +# +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# The GNU General Public License can be found at +# http://www.gnu.org/copyleft/gpl.html. +# A copy is found in the textfile GPL.txt and important notices to the license +# from the author is found in LICENSE.txt distributed with these scripts. +# +# This script is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# This copyright notice MUST APPEAR in all copies of the script! +# +############################################################################### + +# $Id$ + +package main; + +use strict; +use warnings; +use POSIX; +use Time::HiRes qw(gettimeofday); +use Socket; + +sub watchdog_client_NotifySystemD($$); +sub watchdog_client_Stop($); +sub watchdog_client_Start($); +sub watchdog_client_ProcessTimer(@); +sub watchdog_client_IsWDAvailable($); + +sub systemd_watchdog_Initialize($) { + my ($hash) = @_; + + # Consumer + $hash->{DefFn} = "watchdog_client_Define"; + $hash->{UndefFn} = "watchdog_client_Undefine"; + $hash->{ShutdownFn} = "watchdog_client_Shutdown"; + $hash->{NotifyFn} = "watchdog_client_Notify"; + + #Log3($hash->{NAME},5,"Watchdog Client: Debug: watchdog_client_Initialize"); + RemoveInternalTimer($hash); + + return undef; +} + +sub watchdog_client_IsWDAvailable($) { + my ( $hash ) = @_; + #return 1; # TODO XXX TEST + return defined($hash->{'.systemd'}); +} + +sub watchdog_client_Define($$) { + my ( $hash, $def ) = @_; + #Log3($hash->{NAME},5,"Watchdog Client: Debug: watchdog_client_Define"); + my $name = $hash->{NAME}; + + # prevent multiple instances + my @devices = devspec2array("TYPE=watchdog_client"); + foreach my $dev (@devices) { + if($dev ne $name) { + return "only one instance is allowed"; + } + } + + # remove old timer + RemoveInternalTimer($hash); + + # check systemd watchdog available + my $sname = $ENV{NOTIFY_SOCKET}; + if(defined($sname)) { + $hash->{'systemd-watchdog'}="available"; + $hash->{'.systemd'}=1; + Log3($hash->{NAME},4,"Watchdog Client: systemd-watchdog available. starting watchdog client"); + } else { + $hash->{'systemd-watchdog'}="not available"; + $hash->{'.systemd'}=undef; + Log3($hash->{NAME},1,"Watchdog Client: systemd watchdog is not available. Module inactiv."); + } + # Initialize + watchdog_client_Start($hash); + return undef; +} + +sub watchdog_client_Undefine($) { + my ($hash) = @_; + #Log3($hash->{NAME},5,"Watchdog Client: Debug: watchdog_client_Undefine"); + # Clean up + watchdog_client_Stop($hash); + return undef; +} + +sub watchdog_client_Shutdown($) { + my ($hash) = @_; + #Log3($hash->{NAME},5,"Watchdog Client: Debug: watchdog_client_Shutdown"); + return undef unless defined $hash->{'.initialized'}; + # Shutdown => Deaktivate watchdog + my $name = $hash->{NAME}; + Log3($name,2,"Watchdog Client: Shutting down"); + watchdog_client_Stop($hash); + return undef; +} + +sub watchdog_client_Notify($$) { + my ($hash,$dev) = @_; + #Log3($hash->{NAME},1,"Watchdog Client: Debug: watchdog_client_Notify: --- "); + if( $dev->{NAME} eq "global" ) { + # if( grep(m/^INITIALIZED$/, @{$events}) ) { + # Log3($hash->{NAME},1,"Watchdog Client: Debug: watchdog_client_Notify: INITIALIZED"); + # watchdog_client_Start($hash) unless defined $hash->{'.initialized'}; + # return undef; + # } elsif( grep(m/^REREADCFG$/, @{$events}) ) { + # # + # return undef; + # } + if( grep(m/^(INITIALIZED|REREADCFG)$/, @{$dev->{CHANGED}}) ) { + #Log3($hash->{NAME},5,"Watchdog Client: Debug: watchdog_client_Notify: GLOBAL"); + watchdog_client_Start($hash) unless defined $hash->{'.initialized'}; + } + } +} + +sub watchdog_client_ProcessTimer(@) { + my ($hash) = @_; + #Log3($hash->{NAME},5,"Watchdog Client: Debug: watchdog_client_ProcessTimer"); + # Reset watchdog + watchdog_client_NotifySystemD($hash, "WATCHDOG=1\n"); + + my $sleep = $hash->{'sleep-time'}; + $sleep = 30 unless defined $sleep; + my $now = gettimeofday(); + my $next = int($now) + $sleep; + InternalTimer($next, 'watchdog_client_ProcessTimer', $hash, 0); + readingsSingleUpdate($hash,"next",FmtTime($next),1); +} + +sub watchdog_client_Start($) { + my ($hash) = @_; + #Log3($hash->{NAME},5,"Watchdog Client: Debug: watchdog_client_Start"); + unless ($main::init_done) { + return if $hash->{'.firsttime'}; + watchdog_client_NotifySystemD($hash, "STATUS=starting\n"); + watchdog_client_NotifySystemD($hash, "MAINPID=$$\n"); + readingsSingleUpdate($hash,"state","starting",1); + $hash->{'.firsttime'}=1; + return; + } + return if $hash->{'.initialized'}; + + unless (watchdog_client_IsWDAvailable($hash)) { + Log3($hash->{NAME},2,"Watchdog Client: no systemd watchdog available"); + readingsSingleUpdate($hash,"state","inactiv",1); + readingsSingleUpdate($hash,"next","none",1); + return; + } + + my $sleep = ($ENV{WATCHDOG_USEC} // 120000000) / 4 / 1000000; + $hash->{'sleep-time'} = $sleep; + $hash->{'.initialized'} = 1; + + my $next = int(gettimeofday()) + 1; + InternalTimer($next, 'watchdog_client_ProcessTimer', $hash, 0); + + # System ready + watchdog_client_NotifySystemD($hash, "READY=1\n"); + watchdog_client_NotifySystemD($hash, "MAINPID=$$\n"); + watchdog_client_NotifySystemD($hash, "STATUS=started\n"); + + Log3($hash->{NAME},2,"Watchdog Client: initialized"); + readingsSingleUpdate($hash,"state","active",1); +} + +sub watchdog_client_Stop($) { + my ($hash) = @_; + #Log3($hash->{NAME},5,"Watchdog Client: Debug: watchdog_client_Stop"); + watchdog_client_NotifySystemD($hash, "STOPPING=1\n"); + watchdog_client_NotifySystemD($hash, "STATUS=stopping\n"); + RemoveInternalTimer($hash); + $hash->{'.initialized'} = 0; + my $name = $hash->{NAME}; + Log3($name,2,"Watchdog Client: deactivated"); + readingsSingleUpdate($hash,"state","deactivated",1); +} + +sub watchdog_client_NotifySystemD($$) { + my ($hash,$cmd) = @_; + #Log3($hash->{NAME},5,"Watchdog Client: Debug: watchdog_client_NotifySystemD: $cmd"); + return unless defined $hash->{'.initialized'}; + return unless watchdog_client_IsWDAvailable($hash); + + my $name = $hash->{NAME}; + #Log3($name,1,"Watchdog Client: notify systemd-watchdog: $cmd"); + + my $sname = $ENV{NOTIFY_SOCKET}; + if(!defined($sname)) { + #watchdog_client_Stop($hash); + Log3($name,1,"Watchdog Client: NOTIFY_SOCKET not available. Please configure systemd-watchdog properly!"); + return; + } + + Log3($name,4,"Watchdog Client: notify systemd-watchdog: $cmd"); + my $sock_addr = sockaddr_un($sname); + socket(my $server, PF_UNIX,SOCK_DGRAM,0); + connect($server, $sock_addr); + print $server $cmd; + close($server); +} + +1; + +=pod +=begin html_DE + + +

Systemd Watchdog Client

+ + +=end html_DE + +=begin html + + +

Systemd Watchdog Client

+ + +=end html +=cut + +