2005-12-16 18:05:29 +08:00
#!/usr/bin/perl -wT
#
# ==========================================================================
#
# ZoneMinder Audit 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
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
2015-04-10 17:27:45 +08:00
= head1 NAME
zmaudit . pl - ZoneMinder event file system and database consistency checker
= head1 SYNOPSIS
zmaudit . pl [ - r , - report | - i , - interactive ]
= head1 DESCRIPTION
This script checks for consistency between the event filesystem and
the database . If events are found in one and not the other they are
deleted ( optionally ) . Additionally any monitor event directories that
do not correspond to a database monitor are similarly disposed of .
However monitors in the database that don ' t have a directory are left
alone as this is valid if they are newly created and have no events
yet .
= head1 OPTIONS
- r , - - report - Just report don ' t actually do anything
- i , - - interactive - Ask before applying any changes
- c , - - continuous - Run continuously
- v , - - version - Print the installed version of ZoneMinder
= cut
2005-12-16 18:05:29 +08:00
use strict ;
use bytes ;
# ==========================================================================
#
# These are the elements you can edit to suit your installation
#
# ==========================================================================
2006-10-18 23:09:08 +08:00
use constant MAX_AGED_DIRS = > 10 ; # Number of event dirs to check age on
2005-12-16 18:05:29 +08:00
use constant RECOVER_TAG = > "(r)" ; # Tag to append to event name when recovered
use constant RECOVER_TEXT = > "Recovered." ; # Text to append to event notes when recovered
# ==========================================================================
#
# You shouldn't need to change anything from here downwards
#
# ==========================================================================
2009-06-08 17:11:56 +08:00
@ EXTRA_PERL_LIB @
2005-12-16 18:05:29 +08:00
use ZoneMinder ;
use DBI ;
use POSIX ;
2007-08-30 02:11:09 +08:00
use File::Find ;
2005-12-16 18:05:29 +08:00
use Time::HiRes qw/gettimeofday/ ;
use Getopt::Long ;
2015-04-10 17:27:45 +08:00
use autouse 'Pod::Usage' = > qw( pod2usage ) ;
2005-12-16 18:05:29 +08:00
2013-12-17 05:32:02 +08:00
use constant IMAGE_PATH = > $ Config { ZM_PATH_WEB } . '/' . $ Config { ZM_DIR_IMAGES } ;
2015-04-10 18:20:05 +08:00
use constant EVENT_PATH = > ( $ Config { ZM_DIR_EVENTS } =~ m | / | )
? $ Config { ZM_DIR_EVENTS }
: ( $ Config { ZM_PATH_WEB } . '/' . $ Config { ZM_DIR_EVENTS } )
;
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 ) } ;
my $ report = 0 ;
2006-01-12 07:56:46 +08:00
my $ interactive = 0 ;
2006-01-12 23:38:24 +08:00
my $ continuous = 0 ;
2015-01-07 10:08:41 +08:00
my $ version ;
2005-12-16 18:05:29 +08:00
2011-06-21 17:19:10 +08:00
logInit ( ) ;
logSetSignal ( ) ;
2005-12-16 18:05:29 +08:00
2015-04-10 17:27:45 +08:00
GetOptions (
'report' = > \ $ report ,
'interactive' = > \ $ interactive ,
'continuous' = > \ $ continuous ,
'version' = > \ $ version
) or pod2usage ( - exitstatus = > - 1 ) ;
2005-12-16 18:05:29 +08:00
2015-01-07 10:08:41 +08:00
if ( $ version ) {
2015-04-10 18:20:05 +08:00
print ( ZoneMinder::Base:: ZM_VERSION . "\n" ) ;
exit ( 0 ) ;
2015-01-07 10:08:41 +08:00
}
2006-01-12 23:38:24 +08:00
if ( ( $ report + $ interactive + $ continuous ) > 1 )
2005-12-16 18:05:29 +08:00
{
2015-01-07 10:08:41 +08:00
print ( STDERR "Error, only one option may be specified\n" ) ;
2015-04-10 17:27:45 +08:00
pod2usage ( - exitstatus = > - 1 ) ;
2005-12-16 18:05:29 +08:00
}
2007-09-07 23:39:44 +08:00
my $ dbh = zmDbConnect ( ) ;
2005-12-16 18:05:29 +08:00
2016-01-05 04:13:01 +08:00
require ZoneMinder::Monitor ;
2015-12-23 00:50:29 +08:00
require ZoneMinder::Storage ;
2015-12-23 01:35:25 +08:00
require ZoneMinder::Event ;
2006-01-12 07:56:46 +08:00
2006-01-18 05:29:44 +08:00
my $ max_image_age = 6 / 24 ; # 6 hours
2007-08-30 02:11:09 +08:00
my $ max_swap_age = 24 / 24 ; # 24 hours
2005-12-16 18:05:29 +08:00
my $ image_path = IMAGE_PATH ;
2011-04-19 21:01:45 +08:00
2015-12-23 00:50:29 +08:00
2011-04-19 21:01:45 +08:00
my $ loop = 1 ;
my $ cleaned = 0 ;
2014-10-15 21:43:00 +08:00
MAIN: while ( $ loop ) {
2015-04-10 18:20:05 +08:00
while ( ! ( $ dbh and $ dbh - > ping ( ) ) ) {
$ dbh = zmDbConnect ( ) ;
2015-10-06 21:30:10 +08:00
if ( $ continuous ) {
Error ( "Unable to connect to database" ) ;
# if we are running continuously, then just skip to the next
# interval, otherwise we are a one off run, so wait a second and
# retry until someone kills us.
sleep ( $ Config { ZM_AUDIT_CHECK_INTERVAL } ) ;
} else {
Fatal ( "Unable to connect to database" ) ;
} # end if
} # end while can't connect to the db
if ( $ continuous ) {
# if we are running continuously, then just skip to the next
# interval, otherwise we are a one off run, so wait a second and
# retry until someone kills us.
sleep ( $ Config { ZM_AUDIT_CHECK_INTERVAL } ) ;
} else {
sleep 1 ;
} # end if
2015-04-10 18:20:05 +08:00
2015-05-14 00:36:35 +08:00
if ( ! exists $ Config { ZM_AUDIT_MIN_AGE } ) {
Fatal ( "ZM_AUDIT_MIN_AGE is not set in config." ) ;
}
2016-04-20 03:19:37 +08:00
my % Monitors ;
2015-04-10 18:20:05 +08:00
my $ db_monitors ;
2016-04-20 03:19:37 +08:00
my $ monitorSelectSql = "select * from Monitors order by Id" ;
2015-04-10 18:20:05 +08:00
my $ monitorSelectSth = $ dbh - > prepare_cached ( $ monitorSelectSql )
or Fatal ( "Can't prepare '$monitorSelectSql': " . $ dbh - > errstr ( ) ) ;
2016-04-20 03:19:37 +08:00
2015-04-10 18:20:05 +08:00
my $ eventSelectSql = " SELECT Id , ( unix_timestamp ( ) - unix_timestamp ( StartTime ) ) as Age
FROM Events WHERE MonitorId = ? ORDER BY Id " ;
my $ eventSelectSth = $ dbh - > prepare_cached ( $ eventSelectSql )
or Fatal ( "Can't prepare '$eventSelectSql': " . $ dbh - > errstr ( ) ) ;
2011-06-21 17:19:10 +08:00
2011-06-28 19:05:03 +08:00
$ cleaned = 0 ;
2015-04-10 18:20:05 +08:00
my $ res = $ monitorSelectSth - > execute ( )
or Fatal ( "Can't execute: " . $ monitorSelectSth - > errstr ( ) ) ;
2011-06-21 17:19:10 +08:00
while ( my $ monitor = $ monitorSelectSth - > fetchrow_hashref ( ) )
{
2016-04-20 03:19:37 +08:00
$ Monitors { $$ monitor { Id } } = $ monitor ;
2011-06-21 17:19:10 +08:00
Debug ( "Found database monitor '$monitor->{Id}'" ) ;
my $ db_events = $ db_monitors - > { $ monitor - > { Id } } = { } ;
2015-04-10 18:20:05 +08:00
my $ res = $ eventSelectSth - > execute ( $ monitor - > { Id } )
or Fatal ( "Can't execute: " . $ eventSelectSth - > errstr ( ) ) ;
2011-06-21 17:19:10 +08:00
while ( my $ event = $ eventSelectSth - > fetchrow_hashref ( ) )
{
$ db_events - > { $ event - > { Id } } = $ event - > { Age } ;
}
Debug ( "Got " . int ( keys ( %$ db_events ) ) . " events\n" ) ;
}
2015-12-23 00:50:29 +08:00
my $ fs_monitors ;
2015-12-25 04:25:56 +08:00
foreach my $ Storage (
ZoneMinder::Storage - > find ( ( $ Config { ZM_SERVER_ID } ? ( ServerId = > $ Config { ZM_SERVER_ID } ) : ( ) ) ) ,
new ZoneMinder:: Storage ( ) ,
) {
Debug ( "Checking events in " . $ Storage - > Path ( ) ) ;
2015-12-23 00:50:29 +08:00
chdir ( $ Storage - > Path ( ) ) ;
2015-12-23 01:35:25 +08:00
# Please note that this glob will take all files beginning with a digit.
2015-12-23 00:50:29 +08:00
foreach my $ monitor ( glob ( "[0-9]*" ) )
{
2015-12-23 01:35:25 +08:00
next if $ monitor =~ /\D/ ;
2015-12-23 00:50:29 +08:00
Debug ( "Found filesystem monitor '$monitor'" ) ;
my $ fs_events = $ fs_monitors - > { $ monitor } = { } ;
( my $ monitor_dir ) = ( $ monitor =~ /^(.*)$/ ) ; # De-taint
if ( $ Config { ZM_USE_DEEP_STORAGE } )
{
foreach my $ day_dir ( glob ( "$monitor_dir/*/*/*" ) )
{
Debug ( "Checking $day_dir" ) ;
( $ day_dir ) = ( $ day_dir =~ /^(.*)$/ ) ; # De-taint
chdir ( $ day_dir ) ;
opendir ( DIR , "." )
or Fatal ( "Can't open directory '$day_dir': $!" ) ;
my @ event_links = sort { $ b <=> $ a } grep { - l $ _ } readdir ( DIR ) ;
closedir ( DIR ) ;
my $ count = 0 ;
foreach my $ event_link ( @ event_links )
{
2016-01-08 23:36:38 +08:00
if ( $ event_link =~ /[^\d\.]/ ) {
Warning ( "Non-event link found $event_link in $day_dir, skipping" ) ;
next ;
}
2015-12-23 00:50:29 +08:00
Debug ( "Checking link $event_link" ) ;
( my $ event = $ event_link ) =~ s/^.*\.// ;
my $ event_path = readlink ( $ event_link ) ;
if ( $ count + + > MAX_AGED_DIRS )
{
$ fs_events - > { $ event } = - 1 ;
}
else
{
if ( ! - e $ event_path )
{
aud_print ( "Event link $day_dir/$event_link does not point to valid target" ) ;
if ( confirm ( ) )
{
( $ event_link ) = ( $ event_link =~ /^(.*)$/ ) ; # De-taint
unlink ( $ event_link ) ;
$ cleaned = 1 ;
}
}
else
{
$ fs_events - > { $ event } = ( time ( ) - ( $^T - ( ( - M $ event_path ) * 24 * 60 * 60 ) ) ) ;
}
}
}
chdir ( $ Storage - > Path ( ) ) ;
}
}
else
{
chdir ( $ monitor_dir ) ;
opendir ( DIR , "." ) or Fatal ( "Can't open directory '$monitor_dir': $!" ) ;
my @ temp_events = sort { $ b <=> $ a } grep { - d $ _ && $ _ =~ /^\d+$/ } readdir ( DIR ) ;
closedir ( DIR ) ;
my $ count = 0 ;
foreach my $ event ( @ temp_events )
{
if ( $ count + + > MAX_AGED_DIRS )
{
$ fs_events - > { $ event } = - 1 ;
}
else
{
$ fs_events - > { $ event } = ( time ( ) - ( $^T - ( ( - M $ event ) * 24 * 60 * 60 ) ) ) ;
}
}
chdir ( $ Storage - > Path ( ) ) ;
} # if USE_DEEP_STORAGE
Debug ( "Got " . int ( keys ( %$ fs_events ) ) . " events\n" ) ;
} # end foreach monitor
redo MAIN if ( $ cleaned ) ;
$ cleaned = 0 ;
while ( my ( $ fs_monitor , $ fs_events ) = each ( %$ fs_monitors ) )
{
if ( my $ db_events = $ db_monitors - > { $ fs_monitor } )
{
if ( $ fs_events )
{
2015-12-23 04:08:38 +08:00
foreach my $ fs_event ( sort { $ a <=> $ b } keys %$ fs_events ) {
my $ age = $ fs_events - > { $ fs_event } ;
2015-12-23 00:50:29 +08:00
if ( ! defined ( $ db_events - > { $ fs_event } ) && ( $ age < 0 || ( $ age > $ Config { ZM_AUDIT_MIN_AGE } ) ) )
{
aud_print ( "Filesystem event '$fs_monitor/$fs_event' does not exist in database" ) ;
if ( confirm ( ) )
{
my $ Event = new ZoneMinder:: Event ( $ fs_event ) ;
2015-12-25 01:36:16 +08:00
# Must set these because the db event does not exist
2015-12-23 01:55:55 +08:00
$ Event - > MonitorId ( $ fs_monitor ) ;
2015-12-25 01:36:16 +08:00
$ Event - > StorageId ( $ Storage - > Id ( ) ) ;
2015-12-23 00:50:29 +08:00
$ Event - > delete_files ( ) ;
$ cleaned = 1 ;
2015-12-23 03:31:24 +08:00
delete $ fs_events - > { $ fs_event } ;
2015-12-23 00:50:29 +08:00
}
}
}
}
}
else
{
2015-12-30 21:08:01 +08:00
aud_print ( "Filesystem monitor '$fs_monitor' in $$Storage{Path} does not exist in database" ) ;
2015-12-23 00:50:29 +08:00
if ( confirm ( ) )
{
my $ command = "rm -rf $fs_monitor" ;
executeShellCommand ( $ command ) ;
$ cleaned = 1 ;
}
}
}
my $ monitor_links ;
foreach my $ link ( glob ( "*" ) )
{
next if ( ! - l $ link ) ;
next if ( - e $ link ) ;
aud_print ( "Filesystem monitor link '$link' does not point to valid monitor directory" ) ;
if ( confirm ( ) )
{
( $ link ) = ( $ link =~ /^(.*)$/ ) ; # De-taint
my $ command = "rm $link" ;
executeShellCommand ( $ command ) ;
$ cleaned = 1 ;
}
}
} # end foreach Storage Area
2011-04-19 21:01:45 +08:00
redo MAIN if ( $ cleaned ) ;
$ cleaned = 0 ;
2011-08-26 17:51:13 +08:00
my $ deleteMonitorSql = "delete low_priority from Monitors where Id = ?" ;
2015-04-10 18:20:05 +08:00
my $ deleteMonitorSth = $ dbh - > prepare_cached ( $ deleteMonitorSql )
or Fatal ( "Can't prepare '$deleteMonitorSql': " . $ dbh - > errstr ( ) ) ;
2011-08-26 17:51:13 +08:00
my $ deleteEventSql = "delete low_priority from Events where Id = ?" ;
2015-04-10 18:20:05 +08:00
my $ deleteEventSth = $ dbh - > prepare_cached ( $ deleteEventSql )
or Fatal ( "Can't prepare '$deleteEventSql': " . $ dbh - > errstr ( ) ) ;
2011-08-26 17:51:13 +08:00
my $ deleteFramesSql = "delete low_priority from Frames where EventId = ?" ;
2015-04-10 18:20:05 +08:00
my $ deleteFramesSth = $ dbh - > prepare_cached ( $ deleteFramesSql )
or Fatal ( "Can't prepare '$deleteFramesSql': " . $ dbh - > errstr ( ) ) ;
2011-08-26 17:51:13 +08:00
my $ deleteStatsSql = "delete low_priority from Stats where EventId = ?" ;
2015-04-10 18:20:05 +08:00
my $ deleteStatsSth = $ dbh - > prepare_cached ( $ deleteStatsSql )
or Fatal ( "Can't prepare '$deleteStatsSql': " . $ dbh - > errstr ( ) ) ;
2011-06-21 17:19:10 +08:00
while ( my ( $ db_monitor , $ db_events ) = each ( %$ db_monitors ) )
{
if ( my $ fs_events = $ fs_monitors - > { $ db_monitor } )
{
if ( $ db_events )
{
while ( my ( $ db_event , $ age ) = each ( %$ db_events ) )
{
2015-05-13 23:38:55 +08:00
if ( ! defined ( $ fs_events - > { $ db_event } ) ) {
if ( $ age > $ Config { ZM_AUDIT_MIN_AGE } ) {
aud_print ( "Database event '$db_monitor/$db_event' does not exist in filesystem" ) ;
if ( confirm ( ) ) {
my $ res = $ deleteEventSth - > execute ( $ db_event )
or Fatal ( "Can't execute: " . $ deleteEventSth - > errstr ( ) ) ;
$ res = $ deleteFramesSth - > execute ( $ db_event )
or Fatal ( "Can't execute: " . $ deleteFramesSth - > errstr ( ) ) ;
$ res = $ deleteStatsSth - > execute ( $ db_event )
or Fatal ( "Can't execute: " . $ deleteStatsSth - > errstr ( ) ) ;
$ cleaned = 1 ;
}
} else {
2015-12-25 04:33:46 +08:00
my $ Event = new ZoneMinder:: Event ( $ db_event ) ;
my $ Storage = $ Event - > Storage ( ) ;
2016-01-05 04:57:35 +08:00
aud_print ( "Database event '" . $ Storage - > Path ( ) . "/$db_monitor/$db_event' does not exist in filesystem but too young to delete.\n" ) ;
2015-05-13 23:38:55 +08:00
}
2011-06-21 17:19:10 +08:00
}
}
}
}
else
{
2016-01-05 04:13:01 +08:00
my $ Monitor = new ZoneMinder:: Monitor ( $ db_monitor ) ;
my $ Storage = $ Monitor - > Storage ( ) ;
aud_print ( "Database monitor '$db_monitor' does not exist in filesystem, should have been at " . $ Storage - > Path ( ) . "\n" ) ;
2011-06-21 17:19:10 +08:00
#if ( confirm() )
#{
# We don't actually do this in case it's new
2015-04-10 18:20:05 +08:00
#my $res = $deleteMonitorSth->execute( $db_monitor )
# or Fatal( "Can't execute: ".$deleteMonitorSth->errstr() );
2011-04-19 21:01:45 +08:00
#$cleaned = 1;
2011-06-21 17:19:10 +08:00
#}
}
2015-12-23 00:50:29 +08:00
} # end foreach db monitors
2011-04-19 21:01:45 +08:00
redo MAIN if ( $ cleaned ) ;
2005-12-16 18:05:29 +08:00
2011-04-19 21:01:45 +08:00
# Remove orphaned events (with no monitor)
$ cleaned = 0 ;
2015-04-10 18:20:05 +08:00
my $ selectOrphanedEventsSql = " SELECT Events . Id , Events . Name
FROM Events LEFT JOIN Monitors ON ( Events . MonitorId = Monitors . Id )
WHERE isnull ( Monitors . Id ) " ;
my $ selectOrphanedEventsSth = $ dbh - > prepare_cached ( $ selectOrphanedEventsSql )
or Fatal ( "Can't prepare '$selectOrphanedEventsSql': " . $ dbh - > errstr ( ) ) ;
$ res = $ selectOrphanedEventsSth - > execute ( )
or Fatal ( "Can't execute: " . $ selectOrphanedEventsSth - > errstr ( ) ) ;
2011-06-21 17:19:10 +08:00
while ( my $ event = $ selectOrphanedEventsSth - > fetchrow_hashref ( ) )
{
aud_print ( "Found orphaned event with no monitor '$event->{Id}'" ) ;
if ( confirm ( ) )
{
2015-04-10 18:20:05 +08:00
$ res = $ deleteEventSth - > execute ( $ event - > { Id } )
or Fatal ( "Can't execute: " . $ deleteEventSth - > errstr ( ) ) ;
2011-04-19 21:01:45 +08:00
$ cleaned = 1 ;
2011-06-21 17:19:10 +08:00
}
}
2011-04-19 21:01:45 +08:00
redo MAIN if ( $ cleaned ) ;
# Remove empty events (with no frames)
$ cleaned = 0 ;
2015-10-06 21:30:10 +08:00
my $ selectEmptyEventsSql = " SELECT E . Id AS Id , E . StartTime , F . EventId FROM Events as E LEFT JOIN Frames as F ON ( E . Id = F . EventId )
2015-05-13 23:38:55 +08:00
WHERE isnull ( F . EventId ) AND now ( ) - interval ".$Config{ZM_AUDIT_MIN_AGE}." second > E . StartTime " ;
2015-04-10 18:20:05 +08:00
my $ selectEmptyEventsSth = $ dbh - > prepare_cached ( $ selectEmptyEventsSql )
or Fatal ( "Can't prepare '$selectEmptyEventsSql': " . $ dbh - > errstr ( ) ) ;
$ res = $ selectEmptyEventsSth - > execute ( )
or Fatal ( "Can't execute: " . $ selectEmptyEventsSth - > errstr ( ) ) ;
2011-06-21 17:19:10 +08:00
while ( my $ event = $ selectEmptyEventsSth - > fetchrow_hashref ( ) )
{
aud_print ( "Found empty event with no frame records '$event->{Id}'" ) ;
if ( confirm ( ) )
{
2015-04-10 18:20:05 +08:00
$ res = $ deleteEventSth - > execute ( $ event - > { Id } )
or Fatal ( "Can't execute: " . $ deleteEventSth - > errstr ( ) ) ;
2011-04-19 21:01:45 +08:00
$ cleaned = 1 ;
2011-06-21 17:19:10 +08:00
}
}
2011-04-19 21:01:45 +08:00
redo MAIN if ( $ cleaned ) ;
2009-10-09 04:23:45 +08:00
# Remove orphaned frame records
2011-04-19 21:01:45 +08:00
$ cleaned = 0 ;
2015-04-10 18:20:05 +08:00
my $ selectOrphanedFramesSql = " SELECT DISTINCT EventId FROM Frames
WHERE EventId NOT IN ( SELECT Id FROM Events ) " ;
my $ selectOrphanedFramesSth = $ dbh - > prepare_cached ( $ selectOrphanedFramesSql )
or Fatal ( "Can't prepare '$selectOrphanedFramesSql': " . $ dbh - > errstr ( ) ) ;
$ res = $ selectOrphanedFramesSth - > execute ( )
or Fatal ( "Can't execute: " . $ selectOrphanedFramesSth - > errstr ( ) ) ;
2011-06-21 17:19:10 +08:00
while ( my $ frame = $ selectOrphanedFramesSth - > fetchrow_hashref ( ) )
{
aud_print ( "Found orphaned frame records for event '$frame->{EventId}'" ) ;
if ( confirm ( ) )
{
2015-04-10 18:20:05 +08:00
$ res = $ deleteFramesSth - > execute ( $ frame - > { EventId } )
or Fatal ( "Can't execute: " . $ deleteFramesSth - > errstr ( ) ) ;
2011-04-19 21:01:45 +08:00
$ cleaned = 1 ;
2011-06-21 17:19:10 +08:00
}
}
2011-04-19 21:01:45 +08:00
redo MAIN if ( $ cleaned ) ;
2005-12-16 18:05:29 +08:00
2009-10-09 04:23:45 +08:00
# Remove orphaned stats records
2011-04-19 21:01:45 +08:00
$ cleaned = 0 ;
2015-04-10 18:20:05 +08:00
my $ selectOrphanedStatsSql = " SELECT DISTINCT EventId FROM Stats
WHERE EventId NOT IN ( SELECT Id FROM Events ) " ;
my $ selectOrphanedStatsSth = $ dbh - > prepare_cached ( $ selectOrphanedStatsSql )
or Fatal ( "Can't prepare '$selectOrphanedStatsSql': " . $ dbh - > errstr ( ) ) ;
$ res = $ selectOrphanedStatsSth - > execute ( )
or Fatal ( "Can't execute: " . $ selectOrphanedStatsSth - > errstr ( ) ) ;
2011-06-21 17:19:10 +08:00
while ( my $ stat = $ selectOrphanedStatsSth - > fetchrow_hashref ( ) )
{
aud_print ( "Found orphaned statistic records for event '$stat->{EventId}'" ) ;
if ( confirm ( ) )
{
2015-04-10 18:20:05 +08:00
$ res = $ deleteStatsSth - > execute ( $ stat - > { EventId } )
or Fatal ( "Can't execute: " . $ deleteStatsSth - > errstr ( ) ) ;
2011-04-19 21:01:45 +08:00
$ cleaned = 1 ;
2011-06-21 17:19:10 +08:00
}
}
2011-04-19 21:01:45 +08:00
redo MAIN if ( $ cleaned ) ;
2005-12-16 18:05:29 +08:00
2011-06-21 17:19:10 +08:00
# New audit to close any events that were left open for longer than MIN_AGE seconds
2015-04-10 18:20:05 +08:00
my $ selectUnclosedEventsSql =
2016-04-20 22:00:13 +08:00
#"SELECT E.Id, ANY_VALUE(E.MonitorId),
#
#max(F.TimeStamp) as EndTime,
#unix_timestamp(max(F.TimeStamp)) - unix_timestamp(E.StartTime) as Length,
#max(F.FrameId) as Frames,
#count(if(F.Score>0,1,NULL)) as AlarmFrames,
#sum(F.Score) as TotScore,
#max(F.Score) as MaxScore
#FROM Events as E
#INNER JOIN Frames as F on E.Id = F.EventId
#WHERE isnull(E.Frames) or isnull(E.EndTime)
#GROUP BY E.Id HAVING EndTime < (now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second)"
#;
"SELECT *, unix_timestamp(StartTime) AS TimeStamp FROM Events WHERE EndTime IS NULL AND StartTime < (now() - interval " . $ Config { ZM_AUDIT_MIN_AGE } . " second)" ;
my $ selectFrameDataSql = " SELECT max ( TimeStamp ) as EndTime , unix_timestamp ( max ( TimeStamp ) ) AS EndTimeStamp , max ( FrameId ) as Frames ,
2015-04-10 18:20:05 +08:00
count ( if ( F . Score > 0 , 1 , NULL ) ) as AlarmFrames ,
sum ( F . Score ) as TotScore ,
2016-04-20 03:22:46 +08:00
max ( F . Score ) as MaxScore
2016-04-20 22:00:13 +08:00
FROM Frames WHERE EventId = ? " ;
my $ selectFrameDataSth = $ dbh - > prepare_cached ( $ selectFrameDataSql )
or Fatal ( "Can't prepare '$selectFrameDataSql': " . $ dbh - > errstr ( ) ) ;
2015-04-10 18:20:05 +08:00
my $ selectUnclosedEventsSth = $ dbh - > prepare_cached ( $ selectUnclosedEventsSql )
or Fatal ( "Can't prepare '$selectUnclosedEventsSql': " . $ dbh - > errstr ( ) ) ;
my $ updateUnclosedEventsSql =
" UPDATE low_priority Events
SET Name = ? ,
EndTime = ? ,
Length = ? ,
Frames = ? ,
AlarmFrames = ? ,
TotScore = ? ,
AvgScore = ? ,
MaxScore = ? ,
Notes = concat_ws ( ' ' , Notes , ? )
WHERE Id = ? "
;
my $ updateUnclosedEventsSth = $ dbh - > prepare_cached ( $ updateUnclosedEventsSql )
or Fatal ( "Can't prepare '$updateUnclosedEventsSql': " . $ dbh - > errstr ( ) ) ;
$ res = $ selectUnclosedEventsSth - > execute ( )
or Fatal ( "Can't execute: " . $ selectUnclosedEventsSth - > errstr ( ) ) ;
2011-06-21 17:19:10 +08:00
while ( my $ event = $ selectUnclosedEventsSth - > fetchrow_hashref ( ) )
{
aud_print ( "Found open event '$event->{Id}'" ) ;
if ( confirm ( 'close' , 'closing' ) )
{
2016-04-20 22:00:13 +08:00
$ res = $ selectFrameDataSth - > execute ( $ event - > { Id } ) ;
my $ frame = $ selectFrameDataSth - > fetchrow_hashref ( ) ;
if ( $ frame ) {
$ res = $ updateUnclosedEventsSth - > execute
(
sprintf ( "%s%d%s" ,
$ Monitors { $ event - > { MonitorId } } - > { EventPrefix } ,
$ event - > { Id } ,
RECOVER_TAG
) ,
$ frame - > { EndTime } ,
$ frame - > { EndTimeStamp } - $ event - > { TimeStamp } ,
$ frame - > { Frames } ,
$ frame - > { AlarmFrames } ,
$ frame - > { TotScore } ,
$ frame - > { AlarmFrames }
? int ( $ frame - > { TotScore } / $ frame - > { AlarmFrames } )
: 0
,
$ frame - > { MaxScore } ,
RECOVER_TEXT ,
$ event - > { Id }
) or Fatal ( "Can't execute: " . $ updateUnclosedEventsSth - > errstr ( ) ) ;
} else {
Error ( "SHOULD DELETE" ) ;
} # end if has frame data
2011-06-21 17:19:10 +08:00
}
}
2005-12-16 18:05:29 +08:00
2011-06-21 17:19:10 +08:00
# Now delete any old image files
if ( my @ old_files = grep { - M > $ max_image_age } <$image_path/*.{jpg,gif,wbmp}> )
{
aud_print ( "Deleting " . int ( @ old_files ) . " old images\n" ) ;
my $ untainted_old_files = join ( ";" , @ old_files ) ;
( $ untainted_old_files ) = ( $ untainted_old_files =~ /^(.*)$/ ) ;
2011-08-26 15:51:36 +08:00
unlink ( split ( /;/ , $ untainted_old_files ) ) ;
2011-06-21 17:19:10 +08:00
}
# Now delete any old swap files
2015-05-14 22:27:11 +08:00
( my $ swap_image_root ) = ( $ Config { ZM_PATH_SWAP } =~ /^(.*)$/ ) ; # De-taint
2011-04-19 21:01:45 +08:00
File::Find:: find ( { wanted = > \ & deleteSwapImage , untaint = > 1 } , $ swap_image_root ) ;
2007-08-30 02:11:09 +08:00
2011-06-21 17:19:10 +08:00
# Prune the Logs table if required
2013-12-17 05:32:02 +08:00
if ( $ Config { ZM_LOG_DATABASE_LIMIT } )
2011-06-21 17:19:10 +08:00
{
2013-12-17 05:32:02 +08:00
if ( $ Config { ZM_LOG_DATABASE_LIMIT } =~ /^\d+$/ )
2011-06-21 17:19:10 +08:00
{
# Number of rows
2015-04-10 18:20:05 +08:00
my $ selectLogRowCountSql = "SELECT count(*) as Rows from Logs" ;
my $ selectLogRowCountSth = $ dbh - > prepare_cached ( $ selectLogRowCountSql )
or Fatal ( "Can't prepare '$selectLogRowCountSql': " . $ dbh - > errstr ( ) ) ;
$ res = $ selectLogRowCountSth - > execute ( )
or Fatal ( "Can't execute: " . $ selectLogRowCountSth - > errstr ( ) ) ;
2011-06-21 17:19:10 +08:00
my $ row = $ selectLogRowCountSth - > fetchrow_hashref ( ) ;
my $ logRows = $ row - > { Rows } ;
2013-12-17 05:32:02 +08:00
if ( $ logRows > $ Config { ZM_LOG_DATABASE_LIMIT } )
2011-06-21 17:19:10 +08:00
{
2015-04-10 18:20:05 +08:00
my $ deleteLogByRowsSql = "DELETE low_priority FROM Logs ORDER BY TimeKey ASC LIMIT ?" ;
my $ deleteLogByRowsSth = $ dbh - > prepare_cached ( $ deleteLogByRowsSql )
or Fatal ( "Can't prepare '$deleteLogByRowsSql': " . $ dbh - > errstr ( ) ) ;
$ res = $ deleteLogByRowsSth - > execute ( $ logRows - $ Config { ZM_LOG_DATABASE_LIMIT } )
or Fatal ( "Can't execute: " . $ deleteLogByRowsSth - > errstr ( ) ) ;
if ( $ deleteLogByRowsSth - > rows ( ) )
{
aud_print ( "Deleted " . $ deleteLogByRowsSth - > rows ( )
. " log table entries by count\n" )
;
}
2011-06-21 17:19:10 +08:00
}
}
else
{
# Time of record
2015-04-10 18:20:05 +08:00
my $ deleteLogByTimeSql =
" DELETE low_priority FROM Logs
WHERE TimeKey < unix_timestamp ( now ( ) - interval ".$Config{ZM_LOG_DATABASE_LIMIT}." ) " ;
my $ deleteLogByTimeSth = $ dbh - > prepare_cached ( $ deleteLogByTimeSql )
or Fatal ( "Can't prepare '$deleteLogByTimeSql': " . $ dbh - > errstr ( ) ) ;
$ res = $ deleteLogByTimeSth - > execute ( )
or Fatal ( "Can't execute: " . $ deleteLogByTimeSth - > errstr ( ) ) ;
if ( $ deleteLogByTimeSth - > rows ( ) ) {
aud_print ( "Deleted " . $ deleteLogByTimeSth - > rows ( )
. " log table entries by time\n" )
;
}
2011-06-21 17:19:10 +08:00
}
2015-12-23 00:50:29 +08:00
} # end if ZM_LOG_DATABASE_LIMIT
2011-04-19 21:01:45 +08:00
$ loop = $ continuous ;
2007-08-30 02:11:09 +08:00
2013-12-17 05:32:02 +08:00
sleep ( $ Config { ZM_AUDIT_CHECK_INTERVAL } ) if $ continuous ;
2011-04-19 21:01:45 +08:00
} ;
2007-08-30 02:11:09 +08:00
2011-04-19 21:01:45 +08:00
exit ( 0 ) ;
2015-04-10 18:20:05 +08:00
sub aud_print
2011-04-19 21:01:45 +08:00
{
2011-06-21 17:19:10 +08:00
my $ string = shift ;
if ( ! $ continuous )
{
print ( $ string ) ;
}
else
{
Info ( $ string ) ;
}
2011-04-19 21:01:45 +08:00
}
2015-04-10 18:20:05 +08:00
sub confirm
2011-04-19 21:01:45 +08:00
{
2011-06-21 17:19:10 +08:00
my $ prompt = shift || "delete" ;
my $ action = shift || "deleting" ;
my $ yesno = 0 ;
if ( $ report )
{
print ( "\n" ) ;
}
elsif ( $ interactive )
{
print ( ", $prompt y/n: " ) ;
my $ char = < > ;
chomp ( $ char ) ;
if ( $ char eq 'q' )
{
exit ( 0 ) ;
}
if ( ! $ char )
{
$ char = 'y' ;
}
$ yesno = ( $ char =~ /[yY]/ ) ;
}
else
{
if ( ! $ continuous )
{
print ( ", $action\n" ) ;
}
else
{
Info ( $ action ) ;
}
$ yesno = 1 ;
}
return ( $ yesno ) ;
2011-04-19 21:01:45 +08:00
}
2015-04-10 18:20:05 +08:00
sub deleteSwapImage
2011-04-19 21:01:45 +08:00
{
my $ file = $ _ ;
if ( $ file !~ /^zmswap-/ )
{
return ;
2007-08-30 02:11:09 +08:00
}
2011-04-19 21:01:45 +08:00
# Ignore directories
if ( - d $ file )
{
return ;
}
if ( - M $ file > $ max_swap_age )
{
Debug ( "Deleting $file" ) ;
#unlink( $file );
}
}