2005-12-16 18:05:29 +08:00
#!/usr/bin/perl -wT
#
# ==========================================================================
#
# ZoneMinder WatchDog Script, $Date$, $Revision$
2008-07-25 17:48:16 +08:00
# Copyright (C) 2001-2008 Philip Coombes
2005-12-16 18:05:29 +08:00
#
# This program 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.
#
# This program 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
2016-12-26 23:23:16 +08:00
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2005-12-16 18:05:29 +08:00
#
# ==========================================================================
2015-04-16 13:23:40 +08:00
= head1 NAME
zmwatch . pl - ZoneMinder WatchDog Script
= head1 SYNOPSIS
zmwatch . pl
= head1 DESCRIPTION
This does some basic setup for ZoneMinder to run and then periodically
checks the fps output of the active daemons to check they haven ' t
locked up . If they have then they are killed and restarted
= cut
2005-12-16 18:05:29 +08:00
use strict ;
use bytes ;
# ==========================================================================
#
# These are the elements you can edit to suit your installation
#
# ==========================================================================
2005-12-16 21:16:37 +08:00
use constant START_DELAY = > 30 ; # To give everything else time to start
2005-12-16 18:05:29 +08:00
# ==========================================================================
#
# Don't change anything below here
#
# ==========================================================================
2009-06-08 17:11:56 +08:00
@ EXTRA_PERL_LIB @
2005-12-16 18:05:29 +08:00
use ZoneMinder ;
2017-11-15 00:55:00 +08:00
use ZoneMinder::Storage ;
2005-12-16 18:05:29 +08:00
use POSIX ;
use DBI ;
2015-04-16 13:42:56 +08:00
use autouse 'Data::Dumper' = > qw( Dumper ) ;
2005-12-16 18:05:29 +08:00
$| = 1 ;
2016-03-12 05:28:16 +08:00
$ ENV { PATH } = '/bin:/usr/bin:/usr/local/bin' ;
2005-12-16 18:05:29 +08:00
$ ENV { SHELL } = '/bin/sh' if exists $ ENV { SHELL } ;
delete @ ENV { qw( IFS CDPATH ENV BASH_ENV ) } ;
2011-06-21 17:19:10 +08:00
logInit ( ) ;
logSetSignal ( ) ;
2005-12-21 01:03:33 +08:00
2005-12-16 21:36:24 +08:00
Info ( "Watchdog starting\n" ) ;
2005-12-16 20:17:10 +08:00
Info ( "Watchdog pausing for " . START_DELAY . " seconds\n" ) ;
2005-12-16 18:05:29 +08:00
sleep ( START_DELAY ) ;
2007-09-07 23:39:44 +08:00
my $ dbh = zmDbConnect ( ) ;
2005-12-16 18:05:29 +08:00
2015-07-16 04:24:01 +08:00
my $ sql = $ Config { ZM_SERVER_ID } ? 'SELECT * FROM Monitors WHERE ServerId=?' : 'SELECT * FROM Monitors' ;
2015-04-16 13:23:40 +08:00
my $ sth = $ dbh - > prepare_cached ( $ sql )
or Fatal ( "Can't prepare '$sql': " . $ dbh - > errstr ( ) ) ;
2005-12-16 18:05:29 +08:00
2017-11-23 23:29:05 +08:00
my $ eventcounts_sql = q `
UPDATE Monitors SET
TotalEvents = ( SELECT COUNT ( Id ) FROM Events WHERE MonitorId = Monitors . Id ) ,
2017-11-23 09:03:37 +08:00
TotalEventDiskSpace = ( SELECT SUM ( DiskSpace ) FROM Events WHERE MonitorId = Monitors . Id AND DiskSpace IS NOT NULL ) ,
HourEvents = ( SELECT COUNT ( Id ) FROM Events WHERE MonitorId = Monitors . Id AND StartTime > DATE_SUB ( NOW ( ) , INTERVAL 1 hour ) ) ,
HourEventDiskSpace = ( SELECT SUM ( DiskSpace ) FROM Events WHERE MonitorId = Monitors . Id AND StartTime > DATE_SUB ( NOW ( ) , INTERVAL 1 hour ) AND DiskSpace IS NOT NULL ) ,
DayEvents = ( SELECT COUNT ( Id ) FROM Events WHERE MonitorId = Monitors . Id AND StartTime > DATE_SUB ( NOW ( ) , INTERVAL 1 day ) ) ,
DayEventDiskSpace = ( SELECT SUM ( DiskSpace ) FROM Events WHERE MonitorId = Monitors . Id AND StartTime > DATE_SUB ( NOW ( ) , INTERVAL 1 day ) AND DiskSpace IS NOT NULL ) ,
WeekEvents = ( SELECT COUNT ( Id ) FROM Events WHERE MonitorId = Monitors . Id AND StartTime > DATE_SUB ( NOW ( ) , INTERVAL 1 week ) ) ,
WeekEventDiskSpace = ( SELECT SUM ( DiskSpace ) FROM Events WHERE MonitorId = Monitors . Id AND StartTime > DATE_SUB ( NOW ( ) , INTERVAL 1 week ) AND DiskSpace IS NOT NULL ) ,
MonthEvents = ( SELECT COUNT ( Id ) FROM Events WHERE MonitorId = Monitors . Id AND StartTime > DATE_SUB ( NOW ( ) , INTERVAL 1 month ) ) ,
MonthEventDiskSpace = ( SELECT SUM ( DiskSpace ) FROM Events WHERE MonitorId = Monitors . Id AND StartTime > DATE_SUB ( NOW ( ) , INTERVAL 1 month ) AND DiskSpace IS NOT NULL ) ,
ArchivedEvents = ( SELECT COUNT ( Id ) FROM Events WHERE MonitorId = Monitors . Id AND Archived = 1 ) ,
ArchivedEventDiskSpace = ( SELECT SUM ( DiskSpace ) FROM Events WHERE MonitorId = Monitors . Id AND Archived = 1 AND DiskSpace IS NOT NULL )
WHERE Id = ? ` ;
2017-11-23 23:29:05 +08:00
my $ eventcounts_sth = $ dbh - > prepare_cached ( $ eventcounts_sql ) ;
2017-11-23 09:03:37 +08:00
2016-08-22 23:41:26 +08:00
while ( 1 ) {
my $ res = $ sth - > execute ( $ Config { ZM_SERVER_ID } ? $ Config { ZM_SERVER_ID } : ( ) )
or Fatal ( "Can't execute: " . $ sth - > errstr ( ) ) ;
while ( my $ monitor = $ sth - > fetchrow_hashref ( ) ) {
2017-12-07 23:31:25 +08:00
$ eventcounts_sth - > execute ( $$ monitor { Id } ) or Error ( "Can't execute: " . $ eventcounts_sth - > errstr ( ) ) ;
2017-11-23 23:29:05 +08:00
my $ now = time ( ) ;
2016-08-22 23:41:26 +08:00
next if $ monitor - > { Function } eq 'None' ;
my $ restart = 0 ;
2017-01-11 04:29:54 +08:00
if ( zmMemVerify ( $ monitor ) ) {
2016-08-22 23:41:26 +08:00
# Check we have got an image recently
2017-11-25 04:37:50 +08:00
my $ capture_time = zmGetLastWriteTime ( $ monitor ) ;
if ( ! defined ( $ capture_time ) ) {
2016-08-22 23:41:26 +08:00
# Can't read from shared data
Debug ( "LastWriteTime is not defined." ) ;
2016-12-23 22:26:38 +08:00
zmMemInvalidate ( $ monitor ) ;
2016-08-22 23:41:26 +08:00
next ;
}
2017-11-25 04:37:50 +08:00
Debug ( "LastWriteTime is = $capture_time." ) ;
if ( ! $ capture_time ) {
2016-09-15 23:16:05 +08:00
my $ startup_time = zmGetStartupTime ( $ monitor ) ;
2016-09-15 23:41:42 +08:00
if ( $ now - $ startup_time > $ Config { ZM_WATCH_MAX_DELAY } ) {
Info ( "Restarting capture daemon for " . $ monitor - > { Name } . ", no image since startup. Startup time was $startup_time - now $now > $Config{ZM_WATCH_MAX_DELAY}\n" ) ;
2016-09-15 23:16:05 +08:00
$ restart = 1 ;
} else {
2017-01-11 04:29:54 +08:00
# We can't get the last capture time so can't be sure it's died, it might just be starting up.
zmMemInvalidate ( $ monitor ) ;
2017-11-25 04:37:50 +08:00
next ;
2016-09-15 23:16:05 +08:00
}
2016-08-22 23:41:26 +08:00
}
2016-09-15 23:16:05 +08:00
if ( ! $ restart ) {
my $ max_image_delay = ( $ monitor - > { MaxFPS }
&& ( $ monitor - > { MaxFPS } > 0 )
&& ( $ monitor - > { MaxFPS } < 1 )
) ? ( 3 / $ monitor - > { MaxFPS } )
: $ Config { ZM_WATCH_MAX_DELAY }
;
2017-11-25 04:37:50 +08:00
my $ image_delay = $ now - $ capture_time ;
2016-09-15 23:16:05 +08:00
Debug ( "Monitor $monitor->{Id} last captured $image_delay seconds ago, max is $max_image_delay\n" ) ;
if ( $ image_delay > $ max_image_delay ) {
Info ( "Restarting capture daemon for "
2017-11-25 04:37:50 +08:00
. $ monitor - > { Name } . ", time since last capture $image_delay seconds ($now-$capture_time)\n"
2016-09-15 23:16:05 +08:00
) ;
$ restart = 1 ;
}
} # end if ! restart
2016-08-22 23:41:26 +08:00
} else {
Info ( "Restarting capture daemon for " . $ monitor - > { Name } . ", shared data not valid\n" ) ;
$ restart = 1 ;
}
if ( $ restart ) {
my $ command ;
if ( $ monitor - > { Type } eq 'Local' ) {
$ command = "zmdc.pl restart zmc -d $monitor->{Device}" ;
} else {
$ command = "zmdc.pl restart zmc -m $monitor->{Id}" ;
}
runCommand ( $ command ) ;
} elsif ( $ monitor - > { Function } ne 'Monitor' ) {
# Now check analysis daemon
$ restart = 0 ;
# Check we have got an image recently
my $ image_time = zmGetLastReadTime ( $ monitor ) ;
if ( ! defined ( $ image_time ) ) {
# Can't read from shared data
$ restart = 1 ;
2017-08-24 22:44:14 +08:00
Error ( "Error reading shared data for $$monitor{Id} $$monitor{Name}\n" ) ;
2016-08-22 23:41:26 +08:00
} elsif ( ! $ image_time ) {
# We can't get the last capture time so can't be sure it's died.
$ restart = 1 ;
2017-11-25 04:37:50 +08:00
Error ( "Error getting last analyse time for $$monitor{Id} $$monitor{Name}\n" ) ;
2016-08-22 23:41:26 +08:00
} else {
my $ max_image_delay = ( $ monitor - > { MaxFPS }
&& ( $ monitor - > { MaxFPS } > 0 )
&& ( $ monitor - > { MaxFPS } < 1 )
) ? ( 3 / $ monitor - > { MaxFPS } )
: $ Config { ZM_WATCH_MAX_DELAY }
;
my $ image_delay = $ now - $ image_time ;
Debug ( "Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay\n" ) ;
if ( $ image_delay > $ max_image_delay ) {
2017-08-24 22:44:14 +08:00
Info ( "Analysis daemon for $$monitor{Id} $$monitor{Name} needs restarting,"
2016-08-22 23:41:26 +08:00
. " time since last analysis $image_delay seconds ($now-$image_time)\n"
) ;
$ restart = 1 ;
2015-06-19 20:59:49 +08:00
}
2016-08-22 23:41:26 +08:00
}
if ( $ restart ) {
2017-08-24 22:44:14 +08:00
Info ( "Restarting analysis daemon for $$monitor{Id} $$monitor{Name}\n" ) ;
2017-12-04 00:09:01 +08:00
my $ command ;
if ( $ monitor - > { Type } eq 'Local' ) {
$ command = "zmdc.pl restart zmc -d $monitor->{Device}" ;
} else {
$ command = "zmdc.pl restart zmc -m $monitor->{Id}" ;
}
2016-08-22 23:41:26 +08:00
runCommand ( $ command ) ;
} # end if restart
} # end if check analysis daemon
2016-12-21 04:40:14 +08:00
# Prevent open handles building up if we have connect to shared memory
2016-12-23 22:26:38 +08:00
zmMemInvalidate ( $ monitor ) ; # Close our file handle to the zmc process we are about to end
2017-11-23 23:29:05 +08:00
2016-08-22 23:41:26 +08:00
} # end foreach monitor
2017-11-23 09:03:37 +08:00
$ eventcounts_sth - > finish ( ) ;
2017-11-15 00:55:00 +08:00
my $ diskspace_sql = 'UPDATE Storage SET DiskSpace =(SELECT SUM(DiskSpace) FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL)' ;
2017-11-23 09:03:37 +08:00
my $ diskspace_sth = $ dbh - > prepare_cached ( $ diskspace_sql )
2017-11-23 23:29:05 +08:00
or Fatal ( "Can't prepare '$diskspace_sql': " . $ dbh - > errstr ( ) ) ;
2017-11-15 00:55:00 +08:00
foreach my $ Storage ( ZoneMinder::Storage - > find ( ) ) {
2017-11-23 23:29:05 +08:00
Debug ( "Updating disk space for $$Storage{Name} was $$Storage{DiskSpace}" ) ;
2017-11-15 00:55:00 +08:00
$ diskspace_sth - > execute ( $$ Storage { Id } ) or Error ( "Can't execute: " . $ diskspace_sth - > errstr ( ) ) ;
2017-11-23 23:29:05 +08:00
$ Storage - > load ( ) ;
Debug ( "Updated disk space for $$Storage{Name} to $$Storage{DiskSpace}" ) ;
2017-11-15 00:55:00 +08:00
}
$ diskspace_sth - > finish ( ) ;
2016-08-22 23:41:26 +08:00
sleep ( $ Config { ZM_WATCH_CHECK_INTERVAL } ) ;
2015-06-19 23:07:20 +08:00
} # end while (1)
2016-08-22 23:41:26 +08:00
2005-12-16 21:36:24 +08:00
Info ( "Watchdog exiting\n" ) ;
2005-12-16 18:05:29 +08:00
exit ( ) ;
2016-08-22 23:41:26 +08:00
1 ;
__END__