| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- ###############################################################################
- #
- # 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: 98_systemd_watchdog.pm 17206 2018-08-25 18:18:49Z hexenmeister $
- 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
- =item summary_DE Sendet periodisch eine keep-alive Nachricht an das Systemd.
- =begin html_DE
- <a name="systemd_watchdog"></a>
- <h3>Systemd Watchdog Client</h3>
- <ul>
- <p>
- Systemd erlaubt Ueberwachung von Programmen mittels eines Watchdogs.
- Sendet der Prozess innerhalnb eines definierten Interval kein 'Lebenszeichen',
- wird dieser gestoppt und neu gestartet.
- Dieses Modul sendet periodisch eine keep-alive Nachricht an das Systemd-Watchdog.
- </p>
- <p>
- FHEM muss unter Kontrolle von Systemd laufen und Watchdog muss korrekt konfiguriert sein.<br/>
- Folgendes Script kann benutzt werden:<br/>
- <code>
- [Unit]<br/>
- Description=FHEM Home Automation<br/>
- Requires=network.target<br/>
- #After=network.target<br/>
- After=dhcpcd.service<br/>
- <br/>
- [Service]<br/>
- Type=forking<br/>
- NotifyAccess=all<br/>
- User=fhem<br/>
- Group=dialout<br/>
- # Run ExecStartPre with root-permissions<br/>
- PermissionsStartOnly=true<br/>
- ExecStartPre=-/bin/mkdir -p /var/run/fhem<br/>
- ExecStartPre=/bin/chown -R fhem:dialout /var/run/fhem<br/>
- # Run ExecStart with defined user and group<br/>
- WorkingDirectory=/opt/fhem<br/>
- ExecStart=/usr/bin/perl fhem.pl fhem.cfg<br/>
- #ExecStart=/usr/bin/perl fhem.pl configDB<br/>
- TimeoutStartSec=240<br/>
- TimeoutStopSec=120<br/>
- #ExecStop=/usr/bin/pkill -U fhem perl<br/>
- ExecStop=/usr/bin/pkill -f -U fhem "fhem.pl fhem.cfg"<br/>
- # Restart options: no, on-success, on-failure, on-abnormal, on-watchdog, on-abort, or always.<br/>
- Restart=on-failure<br/>
- RestartSec=3<br/>
- WatchdogSec=180<br/>
- PIDFile=/var/run/fhem/fhem.pid<br/>
- <br/>
- [Install]<br/>
- WantedBy=multi-user.target<br/>
- </code><br/>
- Das Script kann unter "/etc/systemd/system/fhem.service" angelegt werden.
- Mit "sudo systemctl daemon-reload" wird sysgtemd-Konfiguration erneuert.
- Anschliessend kann FHEM mit folgendem Befehl gestartet werden: "sudo systemctl start fhem.service".
- <br/>
- Wenn in dem Script "Type=notify" verwendet wird, muss global Attribute "nofork=1" gesetzt sein.<br/>
- <code>attr global nofork 1</code><br/>
- Bei "Type=forking" muss in Script der korrekte Pfad zu dem PID-Datei angegeben werden,
- diese Datei muss auch in FHEM mit dem global Attribute "pidfilename" aktiviert sein.<br/>
- <code>attr global pidfilename /var/run/fhem/fhem.pid</code><br/>
- </p>
- <a name="MQTTdefine"></a>
- <p><b>Define</b></p>
- <ul>
- <p><code>define <name> systemd_watchdog</code></p>
- <p>Specifies the device.</p>
- </ul>
- </ul>
- =end html_DE
- =item summary Sends periodically keep-alive message to the systemd.
- =begin html
- <a name="systemd_watchdog"></a>
- <h3>Systemd Watchdog Client</h3>
- <ul>
- <p>
- Systemd allows monitoring of programs by a watchdog.
- If a process does not respond within a certain time interval, it will be stopped and restarted.
- This module periodically sends keep-alive message to the systemd.
- </p>
- <p>
- fhem must be started under control of systemd. Watchdog must be also configured properly.<br/>
- You can use the following script:<br/>
- <code>
- [Unit]<br/>
- Description=FHEM Home Automation<br/>
- Requires=network.target<br/>
- #After=network.target<br/>
- After=dhcpcd.service<br/>
- <br/>
- [Service]<br/>
- Type=forking<br/>
- NotifyAccess=all<br/>
- User=fhem<br/>
- Group=dialout<br/>
- # Run ExecStartPre with root-permissions<br/>
- PermissionsStartOnly=true<br/>
- ExecStartPre=-/bin/mkdir -p /var/run/fhem<br/>
- ExecStartPre=/bin/chown -R fhem:dialout /var/run/fhem<br/>
- # Run ExecStart with defined user and group<br/>
- WorkingDirectory=/opt/fhem<br/>
- ExecStart=/usr/bin/perl fhem.pl fhem.cfg<br/>
- #ExecStart=/usr/bin/perl fhem.pl configDB<br/>
- TimeoutStartSec=240<br/>
- TimeoutStopSec=120<br/>
- #ExecStop=/usr/bin/pkill -U fhem perl<br/>
- ExecStop=/usr/bin/pkill -f -U fhem "fhem.pl fhem.cfg"<br/>
- # Restart options: no, on-success, on-failure, on-abnormal, on-watchdog, on-abort, or always.<br/>
- Restart=on-failure<br/>
- RestartSec=3<br/>
- WatchdogSec=180<br/>
- PIDFile=/var/run/fhem/fhem.pid<br/>
- <br/>
- [Install]<br/>
- WantedBy=multi-user.target<br/>
- </code><br/>
- Create the script as "/etc/systemd/system/fhem.service".
- Use "sudo systemctl daemon-reload" to reload systemd configuration.
- Start fhem with: "sudo systemctl start fhem.service".
- <br/>
- If you like to use Type=notify, you must set fhem global attribute nofork=1.<br/>
- <code>attr global nofork 1</code><br/>
- If you use Type=forking, please set fhem global pidfilename.<br/>
- <code>attr global pidfilename /var/run/fhem/fhem.pid</code><br/>
- </p>
- <a name="MQTTdefine"></a>
- <p><b>Define</b></p>
- <ul>
- <p><code>define <name> systemd_watchdog</code></p>
- <p>Specifies the device.</p>
- </ul>
- </ul>
- =end html
- =cut
|