From 62704601caae6b04a5edf1aee6de5feede229ea8 Mon Sep 17 00:00:00 2001 From: Isaac Date: Fri, 19 Jan 2018 19:22:43 +0100 Subject: [PATCH 01/56] We should return the value of DIskSpace --- scripts/ZoneMinder/lib/ZoneMinder/Event.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index c2d8f9850..957488d9e 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -449,6 +449,7 @@ sub DiskSpace { Warning("Event does not exist at $_[0]{Path}"); } } # end if ! defined DiskSpace + return $_[0]{DiskSpace}; } sub MoveTo { From 268268606356bbcea526f45a513fc0b469ee52b3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 19 Jan 2018 16:32:26 -0500 Subject: [PATCH 02/56] Must disconnect and reconnect before calling update script --- scripts/zmupdate.pl.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index 5e7b72411..9e8358e03 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -932,6 +932,8 @@ sub patchDB { my $dbh = shift; my $version = shift; + zmDbDisconnect(); + my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); my $command = 'mysql'; if ( defined($portOrSocket) ) { @@ -966,6 +968,8 @@ sub patchDB { die( "Command '$command' exited with status: $status\n" ); } print( "\nDatabase successfully upgraded to version $version.\n" ); + + $dbh = zmDbConnect(); my $sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $version ) or die( "Can't execute: ".$sth->errstr() ); From 1291835c4524c28013a1a493fa49aaa08e15bd93 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 19 Jan 2018 16:32:48 -0500 Subject: [PATCH 03/56] do some output to tell use what we are doing during update process --- db/zm_update-1.31.0.sql | 6 +++++- db/zm_update-1.31.1.sql | 1 + db/zm_update-1.31.2.sql | 1 + db/zm_update-1.31.3.sql | 10 ++++++++++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/db/zm_update-1.31.0.sql b/db/zm_update-1.31.0.sql index 7daadd974..1e20953d6 100644 --- a/db/zm_update-1.31.0.sql +++ b/db/zm_update-1.31.0.sql @@ -1,7 +1,7 @@ -- -- This updates a 1.29.0 database to 1.30.0 -- - +SELECT 'Checking for SaveJPEGs in Monitors'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS @@ -16,6 +16,7 @@ SET @s = (SELECT IF( PREPARE stmt FROM @s; EXECUTE stmt; +SELECT 'Checking for VideoWriter in Monitors'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS @@ -30,6 +31,7 @@ SET @s = (SELECT IF( PREPARE stmt FROM @s; EXECUTE stmt; +SELECT 'Checking for EncoderParameters in Monitors'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS @@ -44,6 +46,7 @@ SET @s = (SELECT IF( PREPARE stmt FROM @s; EXECUTE stmt; +SELECT 'Checking for DefaultVideo in Events'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS @@ -58,6 +61,7 @@ SET @s = (SELECT IF( PREPARE stmt FROM @s; EXECUTE stmt; +SELECT 'Checking for RecordAudio in Monitors'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS diff --git a/db/zm_update-1.31.1.sql b/db/zm_update-1.31.1.sql index 18d825091..399a100bb 100644 --- a/db/zm_update-1.31.1.sql +++ b/db/zm_update-1.31.1.sql @@ -3,6 +3,7 @@ -- -- Add StateId Column to Events. -- +SELECT 'Checkfor StateId IN Events'; SET @s = (SELECT IF( (SELECT COUNT(*) diff --git a/db/zm_update-1.31.2.sql b/db/zm_update-1.31.2.sql index d918db17b..3e40885a4 100644 --- a/db/zm_update-1.31.2.sql +++ b/db/zm_update-1.31.2.sql @@ -2,6 +2,7 @@ -- Update Filters table to have a Concurrent Column -- +SELECT 'Checking for Concurrent in Filters'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS diff --git a/db/zm_update-1.31.3.sql b/db/zm_update-1.31.3.sql index 0f1e6850c..733efcedd 100644 --- a/db/zm_update-1.31.3.sql +++ b/db/zm_update-1.31.3.sql @@ -2,6 +2,7 @@ -- This adds StorageAreas -- +SELECT 'Checking For Storage Table'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES @@ -24,6 +25,7 @@ EXECUTE stmt; -- Add StorageId column to Monitors -- +SELECT 'Checking For StorageId in Monitors'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS @@ -42,6 +44,7 @@ EXECUTE stmt; -- Add StorageId column to Eventss -- +SELECT 'Checking For StorageId in Events'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS @@ -56,6 +59,7 @@ SET @s = (SELECT IF( PREPARE stmt FROM @s; EXECUTE stmt; +SELECT 'Updating Monitors SETTING StorageId to default'; UPDATE Monitors SET StorageId = 0 WHERE StorageId IS NULL; ALTER TABLE Monitors MODIFY `StorageId` smallint(5) unsigned NOT NULL default 0; UPDATE Events SET StorageId = 0 WHERE StorageId IS NULL; @@ -78,6 +82,7 @@ EXECUTE stmt; -- -- Update Monitors table to have an Index on ServerId -- +SELECT 'Create Index For ServerId on Monitors'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS @@ -96,6 +101,7 @@ EXECUTE stmt; -- -- Update Server table to have an Index on Name -- +SELECT 'Create Index FOR Name on Servers'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS @@ -111,15 +117,19 @@ PREPARE stmt FROM @s; EXECUTE stmt; +SELECT 'ALTER TABLE Logs MODIFY Message TEXT NOT NULL'; -- ALTER TABLE Logs ALTER Message DROP DEFAULT; ALTER TABLE Logs MODIFY Message TEXT NOT NULL; +SELECT 'ALTER TABLE Config MODIFY DefaultValue TEXT'; ALTER TABLE Config MODIFY DefaultValue TEXT; -- -- Add an Id column and make it the primary key of the Filters table -- + +SELECT 'Check for Id column in Filter'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS From 43d72decaca48f85a85ee9136bdb31af9bf89ebd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 19 Jan 2018 18:24:59 -0500 Subject: [PATCH 04/56] Turn off extra debugging --- scripts/ZoneMinder/lib/ZoneMinder/Object.pm | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm index df7118f0f..0a341fdac 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm @@ -48,7 +48,7 @@ use vars qw/ $AUTOLOAD $log $dbh/; *dbh = \$ZoneMinder::Database::dbh; my $debug = 0; -use constant DEBUG_ALL=>1; +use constant DEBUG_ALL=>0; sub new { my ( $parent, $id, $data ) = @_; @@ -170,8 +170,6 @@ sub save { foreach my $k ( keys %$data ) { $log->debug("Object::save after set $k => $$data{$k} $$self{$k}"); } - } else { - $log->debug("No data after set"); } } #$debug = 0; From 6d46a02a9a931f68e6e403f26b20eeced1689aac Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 19 Jan 2018 18:25:24 -0500 Subject: [PATCH 05/56] DbReconnect if the db connection goes down --- scripts/zmstats.pl.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/zmstats.pl.in b/scripts/zmstats.pl.in index 13e643c7a..3ea850ef5 100644 --- a/scripts/zmstats.pl.in +++ b/scripts/zmstats.pl.in @@ -153,7 +153,10 @@ my $eventcounts_week_sth = $dbh->prepare_cached( $eventcounts_week_sql ); my $eventcounts_month_sth = $dbh->prepare_cached( $eventcounts_month_sql ); while( 1 ) { - $dbh->ping(); + while ( ! ( $dbh and $dbh->ping() ) ) { + Info("Reconnecting to db"); + $dbh = zmDbConnect(); + } $dbh->do('DELETE FROM Events_Hour WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 hour)'); $dbh->do('DELETE FROM Events_Day WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 day)'); From bd2da456f425e1d4661dd70f6edc84a616937999 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 19 Jan 2018 21:09:33 -0500 Subject: [PATCH 06/56] handle non-multi-server case when restarting monitors via API --- web/api/app/Controller/MonitorsController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index d06ce1dfe..77113e892 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -171,7 +171,7 @@ class MonitorsController extends AppController { $func = $Monitor['Function']; // We don't pass the request data as the monitor object because it may be a subset of the full monitor array $this->daemonControl( $this->Monitor->id, 'stop' ); - if ( ( $func != 'None' ) and ( $Monitor['ServerId'] == ZM_SERVER_ID ) ) { + if ( ( $func != 'None' ) and ( (!defined('ZM_SERVER_ID']) or ($Monitor['ServerId']==ZM_SERVER_ID) ) ) { $this->daemonControl( $this->Monitor->id, 'start' ); } } // end function edit From 181a94de82f7205dcf2c0cb9d960bd5f7ee55e56 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 21 Jan 2018 11:10:34 -0500 Subject: [PATCH 07/56] test for existence of port in url_parts --- web/skins/classic/views/console.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/console.php b/web/skins/classic/views/console.php index 4bbdf9157..a4731d649 100644 --- a/web/skins/classic/views/console.php +++ b/web/skins/classic/views/console.php @@ -232,7 +232,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { } elseif ( $monitor['Type'] == 'Ffmpeg' || $monitor['Type'] == 'Libvlc' ) { $url_parts = parse_url( $monitor['Path'] ); $source = $url_parts['host']. ( - ( $url_parts['port'] and ( $url_parts['port'] != '554' and $url_parts['port'] != '80' ) ) ? ':'.$url_parts['port'] : '' ); + ( isset($url_parts['port']) and ( $url_parts['port'] != '554' and $url_parts['port'] != '80' ) ) ? ':'.$url_parts['port'] : '' ); } if ( $source == '' ) { $source = 'Monitor ' . $monitor['Id']; From fedd67a7f625e672c709a16db89c6de2cd5ab6b4 Mon Sep 17 00:00:00 2001 From: Isaac Date: Sun, 21 Jan 2018 21:11:05 +0100 Subject: [PATCH 08/56] add to_string --- scripts/ZoneMinder/lib/ZoneMinder/Object.pm | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm index df7118f0f..e0dcef4d3 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm @@ -430,6 +430,13 @@ sub transform { return $value; } # end sub transform + +sub to_string { + my $type = ref($_[0]); + my $fields = eval '\%'.$type.'::fields'; + return $type . ': '. join(' ' , map { $_[0]{$_} ? "$_ => $_[0]{$_}" : () } keys %$fields ); +} + 1; __END__ From a67c34fcad314329d84da2ea32baace90836293a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 21 Jan 2018 17:14:32 -0500 Subject: [PATCH 09/56] Allow specifiying storagearea to audit --- scripts/zmaudit.pl.in | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index 87cea1af1..7ce797bc2 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -67,6 +67,7 @@ my $continuous = 0; my $monitor_id = 0; my $version; my $force = 0; +my $storage_id = undef; logInit(); @@ -76,6 +77,7 @@ GetOptions( interactive =>\$interactive, monitor_id =>\$monitor_id, report =>\$report, + storage_id =>\$storage_id, version =>\$version ) or pod2usage(-exitstatus => -1); @@ -194,9 +196,17 @@ MAIN: while( $loop ) { my $fs_monitors; - foreach my $Storage ( - ZoneMinder::Storage->find( ($Config{ZM_SERVER_ID} ? ( ServerId => $Config{ZM_SERVER_ID} ) : () ) ), - ) { + my @Storage_Areas; + if ( defined $storage_id ) { + @Storage_Areas = ZoneMinder::Storage->find( Id=>$storage_id ); + } elsif ( $Config{ZM_SERVER_ID} ) { + @Storage_Areas = ZoneMinder::Storage->find( ServerId => $Config{ZM_SERVER_ID} ); + } else { + @Storage_Areas = ZoneMinder::Storage->find(); + } + + + foreach my $Storage ( @Storage_Areas ) { Debug('Checking events in ' . $Storage->Path() ); if ( ! chdir( $Storage->Path() ) ) { Error( 'Unable to change dir to ' . $Storage->Path() ); From 18f0610d878e00a5ea6bc0b3416d617ac10d65f5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 21 Jan 2018 17:18:29 -0500 Subject: [PATCH 10/56] Merge zm_ffmpeg.cpp from zma_to_thread --- src/zm_ffmpeg.cpp | 185 +++++++++++++++++++++++++++++++++------------- src/zm_ffmpeg.h | 7 +- 2 files changed, 139 insertions(+), 53 deletions(-) diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index 43e9696f7..2db516cf4 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -26,9 +26,13 @@ void FFMPEGInit() { static bool bInit = false; - if(!bInit) { + if ( !bInit ) { + if ( logDebugging() ) + av_log_set_level( AV_LOG_DEBUG ); + else + av_log_set_level( AV_LOG_QUIET ); av_register_all(); - av_log_set_level(AV_LOG_DEBUG); + avformat_network_init(); bInit = true; } } @@ -106,26 +110,25 @@ static int parse_key_value_pair(AVDictionary **pm, const char **buf, return ret; } int av_dict_parse_string(AVDictionary **pm, const char *str, - const char *key_val_sep, const char *pairs_sep, - int flags) - { - if (!str) - return 0; - - /* ignore STRDUP flags */ - flags &= ~(AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); - - while (*str) { - int ret; - if ( (ret = parse_key_value_pair(pm, &str, key_val_sep, pairs_sep, flags)) < 0) - return ret; - - if (*str) - str++; - } - - return 0; + const char *key_val_sep, const char *pairs_sep, + int flags) { + if (!str) + return 0; + + /* ignore STRDUP flags */ + flags &= ~(AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); + + while (*str) { + int ret; + if ( (ret = parse_key_value_pair(pm, &str, key_val_sep, pairs_sep, flags)) < 0) + return ret; + + if (*str) + str++; } + + return 0; +} #endif #endif // HAVE_LIBAVUTIL @@ -188,30 +191,29 @@ int hacked_up_context2_for_older_ffmpeg(AVFormatContext **avctx, AVOutputFormat if (ret) { avformat_free_context(s); return ret; - } else { - s->oformat = oformat; -#if 1 - // This is some very wrong code, and I don't think it is neccessary - if (s->oformat->priv_data_size > 0) { - s->priv_data = av_mallocz(s->oformat->priv_data_size); - if (s->priv_data) { - if (s->oformat->priv_class) { - *(const AVClass**)s->priv_data= s->oformat->priv_class; - av_opt_set_defaults(s->priv_data); - } + } + + s->oformat = oformat; +#if 0 + if (s->oformat->priv_data_size > 0) { + if (s->oformat->priv_class) { + // This looks wrong, we just allocated priv_data and now we are losing the pointer to it.FIXME + *(const AVClass**)s->priv_data = s->oformat->priv_class; + av_opt_set_defaults(s->priv_data); } else { - av_log(s, AV_LOG_ERROR, "Out of memory\n"); - ret = AVERROR(ENOMEM); - return ret; - } - s->priv_data = NULL; + s->priv_data = av_mallocz(s->oformat->priv_data_size); + if ( ! s->priv_data) { + av_log(s, AV_LOG_ERROR, "Out of memory\n"); + ret = AVERROR(ENOMEM); + return ret; } + s->priv_data = NULL; + } #endif - if (filename) strncpy(s->filename, filename, sizeof(s->filename)-1); - *avctx = s; - return 0; - } + if (filename) strncpy(s->filename, filename, sizeof(s->filename)-1); + *avctx = s; + return 0; } static void zm_log_fps(double d, const char *postfix) { @@ -228,22 +230,32 @@ static void zm_log_fps(double d, const char *postfix) { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) void zm_dump_codecpar ( const AVCodecParameters *par ) { - Debug(1, "Dumping codecpar codec_type(%d) codec_id(%d) codec_tag(%d) width(%d) height(%d)", + Debug(1, "Dumping codecpar codec_type(%d) codec_id(%d) codec_tag(%d) width(%d) height(%d) bit_rate(%d) format(%d = %s)", par->codec_type, par->codec_id, par->codec_tag, par->width, - par->height + par->height, + par->bit_rate, + par->format, + ((AVPixelFormat)par->format == AV_PIX_FMT_NONE ? "none" : av_get_pix_fmt_name((AVPixelFormat)par->format)) ); } #endif void zm_dump_codec ( const AVCodecContext *codec ) { - Debug(1, "Dumping codecpar codec_type(%d) codec_id(%d) width(%d) height(%d)", + Debug(1, "Dumping codec_context codec_type(%d) codec_id(%d) width(%d) height(%d) timebase(%d/%d) format(%s)", codec->codec_type, codec->codec_id, codec->width, - codec->height + codec->height, + codec->time_base.num, + codec->time_base.den, +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + (codec->pix_fmt == AV_PIX_FMT_NONE ? "none" : av_get_pix_fmt_name(codec->pix_fmt)) +#else + "unsupported on avconv" +#endif ); } @@ -255,7 +267,6 @@ void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output) AVStream *st = ic->streams[i]; AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0); - avcodec_string(buf, sizeof(buf), st->codec, is_output); Debug(1, " Stream #%d:%d", index, i); /* the pid is an important information, so we display it */ @@ -264,15 +275,21 @@ void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output) Debug(1, "[0x%x]", st->id); if (lang) Debug(1, "(%s)", lang->value); - Debug(1, ", %d, %d/%d", st->codec_info_nb_frames, st->time_base.num, st->time_base.den); + Debug(1, ", frames:%d, timebase: %d/%d", st->codec_info_nb_frames, st->time_base.num, st->time_base.den); + avcodec_string(buf, sizeof(buf), st->codec, is_output); Debug(1, ": %s", buf); +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + AVCodecParameters *codec = st->codecpar; +#else + AVCodecContext *codec = st->codec; +#endif if (st->sample_aspect_ratio.num && // default - av_cmp_q(st->sample_aspect_ratio, st->codec->sample_aspect_ratio)) { + av_cmp_q(st->sample_aspect_ratio, codec->sample_aspect_ratio)) { AVRational display_aspect_ratio; av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den, - st->codec->width * (int64_t)st->sample_aspect_ratio.num, - st->codec->height * (int64_t)st->sample_aspect_ratio.den, + codec->width * (int64_t)st->sample_aspect_ratio.num, + codec->height * (int64_t)st->sample_aspect_ratio.den, 1024 * 1024); Debug(1, ", SAR %d:%d DAR %d:%d", st->sample_aspect_ratio.num, st->sample_aspect_ratio.den, @@ -373,3 +390,71 @@ bool is_audio_stream( AVStream * stream ) { } return false; } + +int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet ) { + int ret; +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + if ( (ret = avcodec_send_packet(context, &packet)) < 0 ) { + Error( "Unable to send packet %s, continuing", + av_make_error_string(ret).c_str() ); + return 0; + } + +#if HAVE_AVUTIL_HWCONTEXT_H + if ( hwaccel ) { + if ( (ret = avcodec_receive_frame(context, hwFrame)) < 0 ) { + Error( "Unable to receive frame %d: %s, continuing", streams[packet.stream_index].frame_count, + av_make_error_string(ret).c_str() ); + return 0; + } + if ( (ret = av_hwframe_transfer_data(frame, hwFrame, 0)) < 0 ) { + Error( "Unable to transfer frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, + av_make_error_string(ret).c_str() ); + return 0; + } + } else { +#endif + if ( (ret = avcodec_receive_frame(context, frame)) < 0 ) { + Error( "Unable to send packet %s, continuing", av_make_error_string(ret).c_str() ); + return 0; + } +#if HAVE_AVUTIL_HWCONTEXT_H + } +#endif + +# else + int frameComplete; + while ( !frameComplete ) { + if ( (ret = zm_avcodec_decode_video( context, frame, &frameComplete, &packet )) < 0 ) { + Error( "Unable to decode frame at frame: %s, continuing", + av_make_error_string(ret).c_str() ); + return 0; + } + } +#endif + return 1; +} // end int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet ) +void dumpPacket(AVPacket *pkt, const char *text) { + char b[10240]; + + snprintf(b, sizeof(b), + " pts: %" PRId64 ", dts: %" PRId64 + ", data: %p, size: %d, stream_index: %d, flags: %04x, keyframe(%d) pos: %" PRId64 + ", duration: %" +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + PRId64 +#else + "d" +#endif + "\n", + pkt->pts, + pkt->dts, + pkt->data, + pkt->size, + pkt->stream_index, + pkt->flags, + pkt->flags & AV_PKT_FLAG_KEY, + pkt->pos, + pkt->duration); + Debug(1, "%s:%d:%s: %s", __FILE__, __LINE__, text, b); +} diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index 10ad5260c..7e3cf9651 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -237,9 +237,8 @@ enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subp */ #ifdef __cplusplus - inline static const std::string av_make_error_string(int errnum) - { - char errbuf[AV_ERROR_MAX_STRING_SIZE]; + inline static const std::string av_make_error_string(int errnum) { + static char errbuf[AV_ERROR_MAX_STRING_SIZE]; #if LIBAVUTIL_VERSION_CHECK(50, 13, 0, 13, 0) av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE); #else @@ -327,4 +326,6 @@ int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt); bool is_video_stream( AVStream * stream ); bool is_audio_stream( AVStream * stream ); +int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet ); +void dumpPacket(AVPacket *,const char *text="DEBUG"); #endif // ZM_FFMPEG_H From 4127aa50f38ebf2cedb32609607673fde310a23b Mon Sep 17 00:00:00 2001 From: Isaac Date: Mon, 22 Jan 2018 03:26:25 +0100 Subject: [PATCH 11/56] simplify logic, improve debugging --- src/zm_ffmpeg_camera.cpp | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index bf7a6ab13..25e96e5a7 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -756,7 +756,6 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event while ( ! frameComplete ) { av_init_packet( &packet ); - Debug(4,"before read frame"); ret = av_read_frame( mFormatContext, &packet ); if ( ret < 0 ) { av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); @@ -775,11 +774,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event } int keyframe = packet.flags & AV_PKT_FLAG_KEY; - - Debug( 4, "Got packet from stream %d packet pts (%u) dts(%u), key?(%d)", - packet.stream_index, packet.pts, packet.dts, - keyframe - ); +dumpPacket(&packet); //Video recording if ( recording.tv_sec ) { @@ -896,28 +891,18 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event if ( keyframe ) { Debug(3, "Clearing queue"); packetqueue.clearQueue( monitor->GetPreEventCount(), mVideoStreamId ); + packetqueue.queuePacket( &packet ); + } else if ( packetqueue.size() ) { + // it's a keyframe or we already have something in the queue + packetqueue.queuePacket( &packet ); } -#if 0 -// Not sure this is valid. While a camera will PROBABLY always have an increasing pts... it doesn't have to. -// Also, I think there are integer wrap-around issues. - -else if ( packet.pts && video_last_pts > packet.pts ) { - Warning( "Clearing queue due to out of order pts packet.pts(%d) < video_last_pts(%d)"); - packetqueue.clearQueue(); - } -#endif - } - + } else if ( packet.stream_index == mAudioStreamId ) { // The following lines should ensure that the queue always begins with a video keyframe - if ( packet.stream_index == mAudioStreamId ) { //Debug(2, "Have audio packet, reocrd_audio is (%d) and packetqueue.size is (%d)", record_audio, packetqueue.size() ); if ( record_audio && packetqueue.size() ) { // if it's audio, and we are doing audio, and there is already something in the queue packetqueue.queuePacket( &packet ); } - } else if ( packet.stream_index == mVideoStreamId ) { - if ( keyframe || packetqueue.size() ) // it's a keyframe or we already have something in the queue - packetqueue.queuePacket( &packet ); } } // end if recording or not From 414930cb903fc47b03385d531e7e7c12fd68b159 Mon Sep 17 00:00:00 2001 From: Isaac Date: Mon, 22 Jan 2018 03:26:33 +0100 Subject: [PATCH 12/56] simplify logic, improve debugging --- src/zm_videostore.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 1b8ad0028..432a74011 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -707,18 +707,6 @@ bool VideoStore::setup_resampler() { #endif } // end bool VideoStore::setup_resampler() -void VideoStore::dumpPacket(AVPacket *pkt) { - char b[10240]; - - snprintf(b, sizeof(b), - " pts: %" PRId64 ", dts: %" PRId64 - ", data: %p, size: %d, sindex: %d, dflags: %04x, s-pos: %" PRId64 - ", c-duration: %d\n", - pkt->pts, pkt->dts, pkt->data, pkt->size, pkt->stream_index, - pkt->flags, pkt->pos, pkt->duration); - Debug(1, "%s:%d:DEBUG: %s", __FILE__, __LINE__, b); -} - int VideoStore::writeVideoFramePacket(AVPacket *ipkt) { av_init_packet(&opkt); From 41a05f5eef8de55d8d804076fece08bc90324430 Mon Sep 17 00:00:00 2001 From: Isaac Date: Mon, 22 Jan 2018 03:26:36 +0100 Subject: [PATCH 13/56] simplify logic, improve debugging --- src/zm_videostore.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/zm_videostore.h b/src/zm_videostore.h index c1a09fc35..6d2b15cd0 100644 --- a/src/zm_videostore.h +++ b/src/zm_videostore.h @@ -80,7 +80,6 @@ public: int writeVideoFramePacket( AVPacket *pkt ); int writeAudioFramePacket( AVPacket *pkt ); - void dumpPacket( AVPacket *pkt ); }; #endif //havelibav From 06c9266c62eba7e8113dd8566e44c3692973d795 Mon Sep 17 00:00:00 2001 From: Isaac Date: Mon, 22 Jan 2018 03:27:01 +0100 Subject: [PATCH 14/56] use snapshot.jpg more --- web/includes/Event.php | 10 ++++++---- web/includes/functions.php | 4 ++-- web/index.php | 6 +++++- web/skins/classic/views/events.php | 26 +++++++++----------------- 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/web/includes/Event.php b/web/includes/Event.php index ab3ca6687..53c808d6c 100644 --- a/web/includes/Event.php +++ b/web/includes/Event.php @@ -211,14 +211,16 @@ class Event { } function createListThumbnail( $overwrite=false ) { + if ( (!$this->SaveJPEGs()) and file_exists($this->Path().'/snapshot.jpg') ) { + $frame = null; + } else { # Load the frame with the highest score to use as a thumbnail if ( !($frame = dbFetchOne( 'SELECT * FROM Frames WHERE EventId=? AND Score=? ORDER BY FrameId LIMIT 1', NULL, array( $this->{'Id'}, $this->{'MaxScore'} ) )) ) { Error("Unable to find a Frame matching max score " . $this->{'MaxScore'} . ' for event ' . $this->{'Id'} ); // FIXME: What if somehow the db frame was lost or score was changed? Should probably try another search for any frame. return( false ); } - - $frameId = $frame['FrameId']; + } if ( ZM_WEB_LIST_THUMB_WIDTH ) { $thumbWidth = ZM_WEB_LIST_THUMB_WIDTH; @@ -259,7 +261,7 @@ class Event { if ( ( ! $frame ) and file_exists( $eventPath.'/snapshot.jpg' ) ) { # No frame specified, so look for a snapshot to use $captImage = 'snapshot.jpg'; - Debug("Frame not specified, using snapshot"); + Logger::Debug("Frame not specified, using snapshot"); } else { $captImage = sprintf( '%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyze.jpg', $frame['FrameId'] ); if ( ! file_exists( $eventPath.'/'.$captImage ) ) { @@ -282,7 +284,7 @@ class Event { exec( $command, $output, $retval ); Logger::Debug("Retval: $retval, output: " . implode("\n", $output)); } else { - Error("Can't create frame images from video becuase there is no video file for this event (".$Event->DefaultVideo() ); + Error("Can't create frame images from video because there is no video file for event ".$Event->Id().' at ' .$Event->Path() ); } } // end if capture file exists } // end if analyze file exists diff --git a/web/includes/functions.php b/web/includes/functions.php index a5df103da..51884f1d0 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -148,7 +148,7 @@ function getAuthUser( $auth ) { } // end getAuthUser($auth) function generateAuthHash( $useRemoteAddr ) { - if ( ZM_OPT_USE_AUTH and ZM_AUTH_RELAY == 'hashed' and isset($_SESSION['username']) and $_SESSION['passwordHash'] ) { + if ( ZM_OPT_USE_AUTH and ZM_AUTH_HASH_LOGINS and ZM_AUTH_RELAY == 'hashed' and isset($_SESSION['username']) and $_SESSION['passwordHash'] ) { # regenerate a hash at half the liftetime of a hash, an hour is 3600 so half is 1800 $time = time(); if ( ( ! isset($_SESSION['AuthHash']) ) or ( $_SESSION['AuthHashGeneratedAt'] < ( $time - ( ZM_AUTH_HASH_TTL * 1800 ) ) ) ) { @@ -165,7 +165,7 @@ function generateAuthHash( $useRemoteAddr ) { $backTrace = debug_backtrace(); $file = $backTrace[1]['file']; $line = $backTrace[1]['line']; - Warning("Session is not active. AuthHash will not be cached. called from $file:$line"); + Warning("Session is not active. AuthHash will not be cached. called from $file:$line. OldHash:" . $_SESSION['AuthHash'] . ' generated at ' . $_SESSION['AuthHashGeneratedAt'] . ' < ' . $time . ' - ( ' . ZM_AUTH_HASH_TTL . '* 1800 = ' . ZM_AUTH_HASH_TTL * 1800 ); } $_SESSION['AuthHash'] = $auth; $_SESSION['AuthHashGeneratedAt'] = $time; diff --git a/web/index.php b/web/index.php index eb406ceb3..c3f2269fe 100644 --- a/web/index.php +++ b/web/index.php @@ -174,13 +174,17 @@ foreach ( getSkinIncludes( 'skin.php' ) as $includeFile ) require_once $includeFile; if ( ZM_OPT_USE_AUTH && ZM_AUTH_HASH_LOGINS ) { + Logger::Debug("Useing hash"); if ( empty($user) && ! empty($_REQUEST['auth']) ) { if ( $authUser = getAuthUser( $_REQUEST['auth'] ) ) { userLogin( $authUser['Username'], $authUser['Password'], true ); } } else if ( ! empty($user) ) { + Logger::Debug("generating hash"); // generate it once here, while session is open. Value will be cached in session and return when called later on generateAuthHash( ZM_AUTH_HASH_IPS ); + } else { + Logger::Debug(" not generating hash"); } } @@ -203,7 +207,7 @@ if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' && $vie require_once( 'includes/actions.php' ); # If I put this here, it protects all views and popups, but it has to go after actions.php because actions.php does the actual logging in. -if ( ZM_OPT_USE_AUTH && ! isset($user) ) { +if ( ZM_OPT_USE_AUTH and ! isset($user) ) { Logger::Debug("Redirecting to login" ); $view = 'login'; $request = null; diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index 4be96d4ed..b4c7cbbb3 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -25,7 +25,6 @@ if ( !canView( 'Events' ) || (!empty($_REQUEST['execute']) && !canEdit('Events') require_once( 'includes/Event.php' ); - $countSql = 'SELECT count(E.Id) AS EventCount FROM Monitors AS M INNER JOIN Events AS E ON (M.Id = E.MonitorId) WHERE'; $eventsSql = 'SELECT E.*,M.Name AS MonitorName,M.DefaultScale FROM Monitors AS M INNER JOIN Events AS E on (M.Id = E.MonitorId) WHERE'; if ( $user['MonitorIds'] ) { @@ -57,10 +56,8 @@ else $limit = 0; $nEvents = dbFetchOne( $countSql, 'EventCount' ); -Warning("Number of events: $nEvents"); if ( !empty($limit) && $nEvents > $limit ) { $nEvents = $limit; -Logger::Debug("Number of events: $nEvents"); } $pages = (int)ceil($nEvents/ZM_WEB_EVENTS_PER_PAGE); if ( !empty($page) ) { @@ -210,27 +207,22 @@ while ( $event_row = dbFetchNext( $results ) ) { ?> SaveJPEGs() == 4 ) and file_exists($event->Path().'/snapshot.jpg') ) { - Logger::Debug("Using snapshot"); - $imgSrc = '?view=image&eid='.$event->Id().'&fid=snapshot&width='.$thumbData['Width'].'&height='.$thumbData['Height']; -} else { - Logger::Debug("Not Using snapshot" . $event->Path().'/snapshot.jpg' ); - $imgSrc = '?view=image&eid='.$event->Id().'&fid='.$thumbData['FrameId'].'&width='.$thumbData['Width'].'&height='.$thumbData['Height']; -} + if ( ( $event->SaveJPEGs() == 0 ) and file_exists($event->Path().'/snapshot.jpg') ) { + Logger::Debug("Using snapshot"); + $imgSrc = '?view=image&eid='.$event->Id().'&fid=snapshot&width='.$thumbData['Width'].'&height='.$thumbData['Height']; + } else { + Logger::Debug("Not Using snapshot" . $event->Path().'/snapshot.jpg' ); + $imgSrc = '?view=image&eid='.$event->Id().'&fid='.$thumbData['FrameId'].'&width='.$thumbData['Width'].'&height='.$thumbData['Height']; + } $streamSrc = $event->getStreamSrc( array( 'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single') ); $imgHtml = ''. validHtmlStr('Event '.$event->Id()) .''; - - echo makePopupLink( - '?view=frame&eid='.$event->Id().'&fid='.$thumbData['FrameId'], - 'zmImage', - array( 'image', reScale( $event->Width(), $scale ), reScale( $event->Height(), $scale ) ), - $imgHtml - ); + echo ''.$imgHtml.''; ?>   Date: Mon, 22 Jan 2018 03:27:44 +0100 Subject: [PATCH 15/56] dfebug improvements --- src/zm_packetqueue.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index 766882796..950cfd576 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -88,7 +88,7 @@ unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream ZMPacket *zm_packet = *it; AVPacket *av_packet = &(zm_packet->packet); - Debug(4, "Looking for keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep ); + Debug(5, "Looking for keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep ); // Want frames_to_keep video keyframes. Otherwise, we may not have enough if ( ( av_packet->stream_index == stream_id) && ( av_packet->flags & AV_PKT_FLAG_KEY ) ) { @@ -157,7 +157,7 @@ void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVi } } if ( it == pktQueue.rend() ) { - Debug(1, "Didn't find a keyframe packet keeping all" ); + Debug(1, "Didn't find a keyframe before event starttime. keeping all" ); return; } From c04fcfb23992c83e2d341c8250587def1130b51b Mon Sep 17 00:00:00 2001 From: Isaac Date: Mon, 22 Jan 2018 15:50:18 +0100 Subject: [PATCH 16/56] Need a few more pixels of width to fit all preset buttons --- web/skins/classic/css/base/views/control.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/css/base/views/control.css b/web/skins/classic/css/base/views/control.css index b3d620f5c..90757a146 100644 --- a/web/skins/classic/css/base/views/control.css +++ b/web/skins/classic/css/base/views/control.css @@ -1,7 +1,7 @@ .ptzControls { vertical-align: top; margin: 10px auto 0; - width: 500px; + width: 520px; } .ptzControls::after { From 493a2386c4a78d854fa06339c3f80597288006c9 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 22 Jan 2018 11:09:35 -0500 Subject: [PATCH 17/56] Only load Time from StartTime if it has a value. Parse Path to get LInkPath if no Time. --- scripts/ZoneMinder/lib/ZoneMinder/Event.pm | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index 957488d9e..4c9d85f75 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -90,8 +90,9 @@ sub Time { $_[0]{Time} = $_[1]; } if ( ! defined $_[0]{Time} ) { - - $_[0]{Time} = Date::Parse::str2time( $_[0]{StartTime} ); + if ( $_[0]{StartTime} ) { + $_[0]{Time} = Date::Parse::str2time( $_[0]{StartTime} ); + } } return $_[0]{Time}; } @@ -230,8 +231,15 @@ sub LinkPath { ), '.'.$$event{Id} ); + } elsif ( $$event{Path} ) { + if ( ( $$event{Path} =~ /^(\d+\/\d{4}\/\d{2}\/\d{2})/ ) ) { + $$event{LinkPath} = $1.'/.'.$$event{Id}; + } else { + Error("Unable to get LinkPath from Path for $$event{Id} $$event{Path}"); + $$event{LinkPath} = ''; + } } else { - Error("Event $$event{Id} has no value for Time(), unable to determine link path"); + Error("Event $$event{Id} $$event{Path} has no value for Time(), unable to determine link path"); $$event{LinkPath} = ''; } } # end if Scheme @@ -243,7 +251,7 @@ sub LinkPath { sub GenerateVideo { my ( $self, $rate, $fps, $scale, $size, $overwrite, $format ) = @_; - my $event_path = $self->getPath( ); + my $event_path = $self->Path( ); chdir( $event_path ); ( my $video_name = $self->{Name} ) =~ s/\s/_/g; From b317748796e0cd842bfdd6a0f7dedb7a90e88330 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 22 Jan 2018 11:09:49 -0500 Subject: [PATCH 18/56] Add Server loading --- scripts/ZoneMinder/lib/ZoneMinder/Storage.pm | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm b/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm index 34158b9e4..8fbe5ed80 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm @@ -30,6 +30,7 @@ use warnings; require ZoneMinder::Base; require ZoneMinder::Object; +require ZoneMinder::Server; use parent qw(Exporter ZoneMinder::Object); @@ -59,6 +60,10 @@ sub find { my @sql_filters; my @sql_values; + if ( exists $sql_filters{Id} ) { + push @sql_filters , ' Id=? '; + push @sql_values, $sql_filters{Id}; + } if ( exists $sql_filters{Name} ) { push @sql_filters , ' Name = ? '; push @sql_values, $sql_filters{Name}; @@ -108,6 +113,14 @@ sub Name { return $_[0]{Name}; } # end sub Path +sub Server { + my $self = shift; + if ( ! $$self{Server} ) { + $$self{Server} = new ZoneMinder::Server( $$self{ServerId} ); + } + return $$self{Server}; +} + 1; __END__ # Below is stub documentation for your module. You'd better edit it! From 94fc8013c6e99cc0c5fccf30fa6843473c79539f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 22 Jan 2018 11:10:31 -0500 Subject: [PATCH 19/56] use a smarter glob to pick up events that were Deep, when the storage area is now Medium. --- scripts/zmaudit.pl.in | 60 ++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index 7ce797bc2..26366d460 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -72,13 +72,13 @@ my $storage_id = undef; logInit(); GetOptions( - continuous =>\$continuous, - force =>\$force, - interactive =>\$interactive, - monitor_id =>\$monitor_id, - report =>\$report, - storage_id =>\$storage_id, - version =>\$version + continuous =>\$continuous, + force =>\$force, + interactive =>\$interactive, + 'monitor_id=i' =>\$monitor_id, + report =>\$report, + 'storage_id=i' =>\$storage_id, + version =>\$version ) or pod2usage(-exitstatus => -1); if ( $version ) { @@ -167,6 +167,21 @@ MAIN: while( $loop ) { } # end if } # end while can't connect to the db + my @Storage_Areas; + if ( defined $storage_id ) { + @Storage_Areas = ZoneMinder::Storage->find( Id=>$storage_id ); + if ( !@Storage_Areas ) { + Fatal("No Storage Area found with Id $storage_id"); + } + Info("Auditing Storage Area $Storage_Areas[0]{Id} $Storage_Areas[0]{Name} at $Storage_Areas[0]{Path}"); + } elsif ( $Config{ZM_SERVER_ID} ) { + @Storage_Areas = ZoneMinder::Storage->find( ServerId => $Config{ZM_SERVER_ID} ); + Info("Auditing All Storage Areas on Server " . $Storage_Areas[0]->Server()->Name()); + } else { + @Storage_Areas = ZoneMinder::Storage->find(); + Info("Auditing All Storage Areas"); + } + my %Monitors; my $db_monitors; my $monitorSelectSql = $monitor_id ? 'SELECT * FROM Monitors WHERE Id=?' : 'SELECT * FROM Monitors ORDER BY Id'; @@ -184,28 +199,18 @@ MAIN: while( $loop ) { while( my $monitor = $monitorSelectSth->fetchrow_hashref() ) { $Monitors{$$monitor{Id}} = $monitor; - Debug( "Found database monitor '$monitor->{Id}'" ); my $db_events = $db_monitors->{$monitor->{Id}} = {}; my $res = $eventSelectSth->execute( $monitor->{Id} ) or Fatal( "Can't execute: ".$eventSelectSth->errstr() ); while ( my $event = $eventSelectSth->fetchrow_hashref() ) { $db_events->{$event->{Id}} = $event->{Age}; } - Debug( 'Got '.int(keys(%$db_events))." events\n" ); + Debug( 'Got '.int(keys(%$db_events))." events for monitor $monitor->{Id}" ); } my $fs_monitors; - my @Storage_Areas; - if ( defined $storage_id ) { - @Storage_Areas = ZoneMinder::Storage->find( Id=>$storage_id ); - } elsif ( $Config{ZM_SERVER_ID} ) { - @Storage_Areas = ZoneMinder::Storage->find( ServerId => $Config{ZM_SERVER_ID} ); - } else { - @Storage_Areas = ZoneMinder::Storage->find(); - } - foreach my $Storage ( @Storage_Areas ) { Debug('Checking events in ' . $Storage->Path() ); if ( ! chdir( $Storage->Path() ) ) { @@ -224,8 +229,8 @@ MAIN: while( $loop ) { # De-taint ( my $monitor_dir ) = ( $monitor =~ /^(.*)$/ ); - if ( $$Storage{Scheme} eq 'Deep' ) { - foreach my $day_dir ( glob("$monitor_dir/*/*/*") ) { + #if ( $$Storage{Scheme} eq 'Deep' ) { + foreach my $day_dir ( glob("$monitor_dir/[0-9][0-9]/[0-9][0-9]/[0-9][0-9]") ) { Debug( "Checking day dir $day_dir" ); ( $day_dir ) = ( $day_dir =~ /^(.*)$/ ); # De-taint if ( ! chdir( $day_dir ) ) { @@ -262,6 +267,7 @@ MAIN: while( $loop ) { $$Event{Id} = $event; $$Event{Path} = join('/', $Storage->Path(), $day_dir,$event_path); $$Event{RelativePath} = join('/', $day_dir,$event_path); + $$Event{Scheme} = 'Deep'; $Event->MonitorId( $monitor_dir ); $Event->StorageId( $Storage->Id() ); $Event->DiskSpace( undef ); @@ -269,8 +275,8 @@ MAIN: while( $loop ) { } # end foreach event_link chdir( $Storage->Path() ); } # end foreach day dir - } elsif ( $$Storage{Scheme} eq 'Medium' ) { - foreach my $event_dir ( glob("$monitor_dir/*/*") ) { + + foreach my $event_dir ( glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") ) { next if ! -d $event_dir; my ( $date, $event_id ) = $event_dir =~ /^$monitor_dir\/(\d{4}\-\d{2}\-\d{2})\/(\d+)$/; if ( ! $event_id ) { @@ -280,11 +286,13 @@ MAIN: while( $loop ) { my $Event = $fs_events->{$event_id} = new ZoneMinder::Event(); $$Event{Id} = $event_id; $$Event{Path} = join('/', $Storage->Path(), $event_dir ); + $$Event{Scheme} = 'Medium'; $$Event{RelativePath} = $event_dir; $Event->MonitorId( $monitor_dir ); $Event->StorageId( $Storage->Id() ); } # end foreach event - } else { + + if ( ! $$Storage{Scheme} ) { if ( ! chdir( $monitor_dir ) ) { Error( "Can't chdir directory '$$Storage{Path}/$monitor_dir': $!" ); next; @@ -320,7 +328,6 @@ MAIN: while( $loop ) { my $Event = $fs_events->{$fs_event_id}; - if ( ! defined( $db_events->{$fs_event_id} ) ) { my $age = $Event->age(); @@ -417,9 +424,7 @@ MAIN: while( $loop ) { $cleaned = 1; } } else { - my $Storage = $Event->Storage(); - - aud_print( "Database event '".$Event->getPath()." monitor:$db_monitor event:$db_event' does not exist in filesystem but too young to delete age: $age > MIN $Config{ZM_AUDIT_MIN_AGE}.\n" ); + aud_print( "Database event '".$Event->Path()." monitor:$db_monitor event:$db_event' does not exist in filesystem but too young to delete age: $age > MIN $Config{ZM_AUDIT_MIN_AGE}.\n" ); } } # end if exists in filesystem } # end if ! in fs_events @@ -776,6 +781,7 @@ yet. -f, --force - Run even if pid file exists -i, --interactive - Ask before applying any changes -r, --report - Just report don't actually do anything + -s, --storage_id - Specify a storage area to audit instead of all -v, --version - Print the installed version of ZoneMinder =cut From 924fa80ea6e1a4b7ebe96e6aa459db5325f05b6e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 22 Jan 2018 11:42:07 -0500 Subject: [PATCH 20/56] When renaming a monitor, take change of storage area into account when creating dirs and symlinks --- web/includes/actions.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/web/includes/actions.php b/web/includes/actions.php index 384953940..54941ab9f 100644 --- a/web/includes/actions.php +++ b/web/includes/actions.php @@ -493,10 +493,17 @@ if ( canEdit( 'Monitors' ) ) { zmaControl( $monitor, 'stop' ); zmcControl( $monitor, 'stop' ); dbQuery( 'UPDATE Monitors SET '.implode( ', ', $changes ).' WHERE Id=?', array($mid) ); - if ( isset($changes['Name']) ) { + if ( isset($changes['Name']) or isset($changes['StorageId']) ) { + $OldStorage = new Storage( $monitor['StorageId'] ); $saferOldName = basename( $monitor['Name'] ); + if ( file_exists( $OldStorage->Path().'/'.$saferOldName ) ) + unlink( $OldStorage->Path().'/'.$saferOldName ); + + $NewStorage = new Storage( $_REQUEST['newMonitor']['StorageId'] ); + if ( ! file_exists( $NewStorage->Path().'/'.$mid ) ) + mkdir( $NewStorage->Path().'/'.$mid, 0755 ); $saferNewName = basename( $_REQUEST['newMonitor']['Name'] ); - rename( ZM_DIR_EVENTS.'/'.$saferOldName, ZM_DIR_EVENTS.'/'.$saferNewName); + symlink( $mid, $NewStorage->Path().'/'.$saferNewName ); } if ( isset($changes['Width']) || isset($changes['Height']) ) { $newW = $_REQUEST['newMonitor']['Width']; @@ -541,9 +548,10 @@ if ( canEdit( 'Monitors' ) ) { $zoneArea = $_REQUEST['newMonitor']['Width'] * $_REQUEST['newMonitor']['Height']; dbQuery( "insert into Zones set MonitorId = ?, Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = ?, Area=?, AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels=?, MaxAlarmPixels=?, FilterX = 3, FilterY = 3, MinFilterPixels=?, MaxFilterPixels=?, MinBlobPixels=?, MinBlobs = 1", array( $mid, sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 ), $zoneArea, intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*2)/100) ) ); //$view = 'none'; - mkdir( ZM_DIR_EVENTS.'/'.$mid, 0755 ); + $Storage = new Storage( $_REQUEST['newMonitor']['StorageId'] ); + mkdir( $Storage->Path().'/'.$mid, 0755 ); $saferName = basename($_REQUEST['newMonitor']['Name']); - symlink( $mid, ZM_DIR_EVENTS.'/'.$saferName ); + symlink( $mid, $Storage->Path().'/'.$saferName ); if ( isset($_COOKIE['zmGroup']) ) { dbQuery( 'INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($_COOKIE['zmGroup'],$mid) ); } From 97595051ced8b47e6f543cf2bc11e65059821265 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 22 Jan 2018 11:42:19 -0500 Subject: [PATCH 21/56] whitespace --- web/includes/database.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/web/includes/database.php b/web/includes/database.php index 29ec1fd77..6bc6c0264 100644 --- a/web/includes/database.php +++ b/web/includes/database.php @@ -134,12 +134,12 @@ function dbQuery( $sql, $params=NULL ) { } else { $result = $dbConn->query( $sql ); } -if ( defined('ZM_DB_DEBUG') ) { - if ( $params ) - Warning("SQL: $sql" . implode(',',$params) . ' rows: '.$result->rowCount() ); - else - Warning("SQL: $sql: rows:" . $result->rowCount() ); -} + if ( defined('ZM_DB_DEBUG') ) { + if ( $params ) + Warning("SQL: $sql" . implode(',',$params) . ' rows: '.$result->rowCount() ); + else + Warning("SQL: $sql: rows:" . $result->rowCount() ); + } } catch(PDOException $e) { Error( "SQL-ERR '".$e->getMessage()."', statement was '".$sql."' params:" . ($params?implode(',',$params):'') ); } From 8a27ea3a04a1b6278f22af1f9830bd72b4fc3e31 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 22 Jan 2018 11:42:52 -0500 Subject: [PATCH 22/56] Always populate the monitorNames object, and check for duplicate names when saving an existing Monitor as well as new monitors --- web/skins/classic/views/js/monitor.js.php | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/web/skins/classic/views/js/monitor.js.php b/web/skins/classic/views/js/monitor.js.php index 470bb92b8..7a2f17fd3 100644 --- a/web/skins/classic/views/js/monitor.js.php +++ b/web/skins/classic/views/js/monitor.js.php @@ -34,17 +34,14 @@ controlOptions[][] = ' - var monitorNames = new Object(); monitorNames[''] = true; function validateForm( form ) { @@ -52,7 +49,7 @@ function validateForm( form ) { if ( form.elements['newMonitor[Name]'].value.search( /[^\w\-\.\(\)\:\/ ]/ ) >= 0 ) errors[errors.length] = ""; - else if ( form.elements.mid.value == 0 && monitorNames[form.elements['newMonitor[Name]'].value] ) + else if ( monitorNames[form.elements['newMonitor[Name]'].value] ) errors[errors.length] = ""; if ( form.elements['newMonitor[AnalysisFPSLimit]'].value && !(parseFloat(form.elements['newMonitor[AnalysisFPSLimit]'].value) > 0 ) ) From 843eee1e13170bbb994c8218cdcf628e6a482f1b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 22 Jan 2018 14:05:38 -0500 Subject: [PATCH 23/56] handle case where zmstats and zmstats.pl both exist --- db/zm_update-1.31.23.sql | 59 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/db/zm_update-1.31.23.sql b/db/zm_update-1.31.23.sql index 1e4b6795c..9482561df 100644 --- a/db/zm_update-1.31.23.sql +++ b/db/zm_update-1.31.23.sql @@ -2,7 +2,11 @@ SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() AND table_name = 'Servers' AND column_name = 'zmstats.pl' - ) > 0, + ) > 0 AND ( SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'zmstats' + ) = 0 + , "ALTER TABLE Servers CHANGE COLUMN `zmstats.pl` `zmstats` BOOLEAN NOT NULL DEFAULT FALSE", "SELECT 'zmstats.pl has already been changed to zmstats'" )); @@ -13,8 +17,27 @@ EXECUTE stmt; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() AND table_name = 'Servers' - AND column_name = 'zmaudit.pl' + AND column_name = 'zmstats.pl' ) > 0, + "ALTER TABLE Servers DROP COLUMN `zmstats.pl`", + "SELECT 'zmstats.pl has already been removed'" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'zmaudit.pl' + ) > 0 + AND + ( SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'zmaudit' + ) = 0 + , "ALTER TABLE Servers CHANGE COLUMN `zmaudit.pl` `zmaudit` BOOLEAN NOT NULL DEFAULT FALSE", "SELECT 'zmaudit.pl has already been changed to zmaudit'" )); @@ -25,11 +48,41 @@ EXECUTE stmt; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() AND table_name = 'Servers' - AND column_name = 'zmtrigger.pl' + AND column_name = 'zmaudit.pl' ) > 0, + "ALTER TABLE Servers DROP COLUMN `zmaudit.pl`", + "SELECT 'zmaudit.pl has already been removed'" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'zmtrigger.pl' + ) > 0 + AND + ( SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'zmtrigger' + ) = 0 + , "ALTER TABLE Servers CHANGE COLUMN `zmtrigger.pl` `zmtrigger` BOOLEAN NOT NULL DEFAULT FALSE", "SELECT 'zmtrigger.pl has already been changed to zmtrigger'" )); PREPARE stmt FROM @s; EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'zmtrigger.pl' + ) > 0, + "ALTER TABLE Servers DROP COLUMN `zmtrigger.pl`", + "SELECT 'zmtrigger.pl has already been removed'" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; From e59ade5391c1e150db105bdf470950e6cb253204 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 22 Jan 2018 14:06:03 -0500 Subject: [PATCH 24/56] Only alter MonitorIds if it exists. --- db/zm_update-1.31.7.sql | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/db/zm_update-1.31.7.sql b/db/zm_update-1.31.7.sql index 6634caf5f..1221d9adb 100644 --- a/db/zm_update-1.31.7.sql +++ b/db/zm_update-1.31.7.sql @@ -1 +1,12 @@ -ALTER TABLE Groups MODIFY `MonitorIds` text NOT NULL; +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Groups' + AND column_name = 'MonitorIds' + ) > 0, + "ALTER TABLE Groups MODIFY `MonitorIds` text NOT NULL", + "SELECT 'Groups no longer has MonitorIds'" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + From d63594b5fa129969785d5493fe01cca75f0fb5e7 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 22 Jan 2018 14:06:52 -0500 Subject: [PATCH 25/56] don't do updates in a transaction so we don't hold locks --- scripts/zmupdate.pl.in | 78 ++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 30 deletions(-) diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index 9e8358e03..f428ea1f9 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -159,7 +159,7 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) { my $ua = LWP::UserAgent->new; $ua->agent( "ZoneMinder Update Agent/".ZM_VERSION ); if ( $Config{ZM_UPDATE_CHECK_PROXY} ) { - $ua->proxy( "http", $Config{ZM_UPDATE_CHECK_PROXY} ); + $ua->proxy( 'http', $Config{ZM_UPDATE_CHECK_PROXY} ); } my $req = HTTP::Request->new( GET=>'https://update.zoneminder.com/version.txt' ); my $res = $ua->request($req); @@ -224,7 +224,7 @@ if ( $zoneFix ) { } $sth->finish(); - $sql = "update Zones set MinAlarmPixels = ?, MaxAlarmPixels = ?, MinFilterPixels = ?, MaxFilterPixels = ?, MinBlobPixels = ?, MaxBlobPixels = ? where Id = ?"; + $sql = 'update Zones set MinAlarmPixels = ?, MaxAlarmPixels = ?, MinFilterPixels = ?, MaxFilterPixels = ?, MinBlobPixels = ?, MaxBlobPixels = ? where Id = ?'; $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); foreach my $zone ( @zones ) { my $zone_width = (($zone->{HiX}*$zone->{MonitorWidth})-($zone->{LoX}*$zone->{MonitorWidth}))/100; @@ -378,7 +378,7 @@ if ( $version ) { if ( $response =~ /^[yY]$/ ) { my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); - my $command = "mysqldump"; + my $command = 'mysqldump'; if ( defined($portOrSocket) ) { if ( $portOrSocket =~ /^\// ) { $command .= " -S".$portOrSocket; @@ -503,7 +503,7 @@ if ( $version ) { my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); while( my $monitor = $sth->fetchrow_hashref() ) { - my $sql = "update Events set Width = ?, Height = ? where MonitorId = ?"; + my $sql = 'update Events set Width = ?, Height = ? where MonitorId = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $monitor->{Width}, $monitor->{Height}, $monitor->{Id} ) or die( "Can't execute: ".$sth->errstr() ); } @@ -518,7 +518,7 @@ if ( $version ) { my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); my $sequence = 1; while( my $monitor = $sth->fetchrow_hashref() ) { - my $sql = "update Monitors set Sequence = ? where Id = ?"; + my $sql = 'update Monitors set Sequence = ? where Id = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $sequence++, $monitor->{Id} ) or die( "Can't execute: ".$sth->errstr() ); } @@ -536,7 +536,7 @@ if ( $version ) { push( @filters, $filter ); } $sth->finish(); - $sql = "update Filters set Query = ? where Name = ?"; + $sql = 'update Filters set Query = ? where Name = ?'; $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); foreach my $filter ( @filters ) { if ( $filter->{Query} =~ /op\d=&/ ) { @@ -569,7 +569,7 @@ if ( $version ) { no strict 'refs'; foreach my $zone ( @zones ) { # Create the coordinate strings - if ( $zone->{Units} eq "Pixels" ) { + if ( $zone->{Units} eq 'Pixels' ) { my $sql = "update Zones set NumCoords = 4, Coords = concat( LoX,',',LoY,' ',HiX,',',LoY,' ',HiX,',',HiY,' ',LoX,',',HiY ), Area = round( ((HiX-LoX)+1)*((HiY-LoY)+1) ) where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $zone->{Id} ) or die( "Can't execute: ".$sth->errstr() ); @@ -604,7 +604,7 @@ if ( $version ) { foreach my $defn ( split( /,/, $state->{Definition} ) ) { push( @new_defns, $defn.":1" ); } - my $sql = "update States set Definition = ? where Name = ?"; + my $sql = 'update States set Definition = ? where Name = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( join( ',', @new_defns ), $state->{Name} ) or die( "Can't execute: ".$sth->errstr() ); } @@ -684,7 +684,7 @@ if ( $version ) { $db_monitor->{LabelFormat} =~ s/\%\%s/%N/; $db_monitor->{LabelFormat} =~ s/\%\%s/%Q/; - my $sql = "update Monitors set LabelFormat = ? where Id = ?"; + my $sql = 'update Monitors set LabelFormat = ? where Id = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $db_monitor->{LabelFormat}, $db_monitor->{Id} ) or die( "Can't execute: ".$sth->errstr() ); } @@ -743,7 +743,7 @@ if ( $version ) { $i++; } $newQuery .= '}'; - foreach my $field ( "sort_field", "sort_asc", "limit" ) { + foreach my $field ( 'sort_field', 'sort_asc', 'limit' ) { if ( defined($filter->{$field}) ) { $newQuery .= 's:'.length($field).':"'.$field.'";'; $newQuery .= 's:'.length($filter->{$field}).':"'.$filter->{$field}.'";'; @@ -751,7 +751,7 @@ if ( $version ) { } $newQuery .= '}'; - my $sql = "update Filters set Query = ? where Name = ?"; + my $sql = 'update Filters set Query = ? where Name = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $newQuery, $dbFilter->{Name} ) or die( "Can't execute: ".$sth->errstr() ); } @@ -829,7 +829,7 @@ if ( $version ) { my $phpQuery = $dbFilter->{Query}; my $query = PHP::Serialization::unserialize( $phpQuery ); my $jsonQuery = jsonEncode( $query ); - my $sql = "update Filters set Query = ? where Name = ?"; + my $sql = 'update Filters set Query = ? where Name = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $jsonQuery, $dbFilter->{Name} ) or die( "Can't execute: ".$sth->errstr() ); }; @@ -851,7 +851,7 @@ if ( $version ) { # Copy the FTP specific values to the new general config my $fetchSql = "select * from Config where Name like 'ZM_UPLOAD_FTP_%'"; my $fetchSth = $dbh->prepare_cached( $fetchSql ) or die( "Can't prepare '$fetchSql': ".$dbh->errstr() ); - my $updateSql = "update Config set Value = ? where Name = ?"; + my $updateSql = 'update Config set Value = ? where Name = ?'; my $updateSth = $dbh->prepare_cached( $updateSql ) or die( "Can't prepare '$updateSql': ".$dbh->errstr() ); my $fetchRes = $fetchSth->execute() or die( "Can't execute: ".$fetchSth->errstr() ); while( my $config = $fetchSth->fetchrow_hashref() ) { @@ -881,7 +881,7 @@ if ( $version ) { if ( $version ge '1.26.0' ) { my @files; - $updateDir = $Config{ZM_PATH_DATA}."/db" if ! $updateDir; + $updateDir = $Config{ZM_PATH_DATA}.'/db' if ! $updateDir; opendir( my $dh, $updateDir ) || die "Can't open updateDir $!"; #@files = sort grep { (!/^\./) && /^zm_update\-[\d\.]+\.sql$/ && -f "$updateDir/$_" } readdir($dh); #PP - use perl version sort @@ -895,29 +895,28 @@ if ( $version ) { die "Should have found upgrade scripts at $updateDir\n"; } # end if - $dbh->{'AutoCommit'} = 0; foreach my $patch ( @files ) { my ( $v ) = $patch =~ /^zm_update\-([\d\.]+)\.sql$/; #PP make sure we use version compare if ( version->parse('v' . $v) > version->parse('v' . $version) ) { print( "Upgrading DB to $v from $version\n" ); patchDB( $dbh, $v ); - if ( $dbh->errstr() ) { - $dbh->rollback(); - die "Error: " . $dbh->errstr(). ". Rolled back.\n"; - } # end if error - } # end if + my $sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $version ) or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); + #patchDB_using_do( $dbh, $version, $updateDir.'/'.$patch ); + } # end if newer version } # end foreach patchfile - $dbh->{'AutoCommit'} = 1; $cascade = !undef; } # end if if ( $cascade ) { my $installed_version = ZM_VERSION; - my $sql = "update Config set Value = ? where Name = ?"; + my $sql = 'update Config set Value = ? where Name = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( "$installed_version", "ZM_DYN_DB_VERSION" ) or die( "Can't execute: ".$sth->errstr() ); - $res = $sth->execute( "$installed_version", "ZM_DYN_CURR_VERSION" ) or die( "Can't execute: ".$sth->errstr() ); + my $res = $sth->execute( "$installed_version", 'ZM_DYN_DB_VERSION' ) or die( "Can't execute: ".$sth->errstr() ); + $res = $sth->execute( "$installed_version", 'ZM_DYN_CURR_VERSION' ) or die( "Can't execute: ".$sth->errstr() ); $sth->finish(); } else { zmDbDisconnect(); @@ -928,11 +927,35 @@ if ( $version ) { zmDbDisconnect(); exit( 0 ); +sub patchDB_using_do { + my ( $dbh, $version, $file ) = @_; + + open( my $fh, '<', $file ) or die "Unable to open $file $!"; + $/ = undef; + my $sql = <$fh>; + close $fh; + if ( $sql ) { + $dbh->{'AutoCommit'} = 0; + $dbh->do($sql); + if ( $dbh->errstr() ) { + $dbh->rollback(); + die "Error: " . $dbh->errstr(). ". Rolled back.\n"; + } # end if error + + my $sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $version ) or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); + + $dbh->{'AutoCommit'} = 1; + } else { + Warning("Empty db update file at $file"); + } +} sub patchDB { my $dbh = shift; my $version = shift; - zmDbDisconnect(); my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); my $command = 'mysql'; @@ -969,11 +992,6 @@ sub patchDB { } print( "\nDatabase successfully upgraded to version $version.\n" ); - $dbh = zmDbConnect(); - my $sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $version ) or die( "Can't execute: ".$sth->errstr() ); - $sth->finish(); } sub migratePaths { From 2068a5c684d4420769a2ca3c59f8fef896259727 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 22 Jan 2018 15:25:51 -0500 Subject: [PATCH 26/56] Add object caching --- scripts/ZoneMinder/lib/ZoneMinder/Object.pm | 30 ++++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm index 4921b1f8b..baa77059a 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm @@ -42,17 +42,26 @@ use ZoneMinder::Config qw(:all); use ZoneMinder::Logger qw(:all); use ZoneMinder::Database qw(:all); -use vars qw/ $AUTOLOAD $log $dbh/; +use vars qw/ $AUTOLOAD $log $dbh %cache $no_cache/; *log = \$ZoneMinder::Logger::logger; *dbh = \$ZoneMinder::Database::dbh; my $debug = 0; +$no_cache = 0; use constant DEBUG_ALL=>0; +sub init_cache { + $no_cache = 0; + %cache = (); +} # end sub init_cache + sub new { my ( $parent, $id, $data ) = @_; + $cache{$parent} = {} if ! $cache{$parent}; + my $sub_cache = $cache{$parent}; + my $self = {}; bless $self, $parent; no strict 'refs'; @@ -61,10 +70,23 @@ sub new { Error( 'NO primary_key for type ' . $parent ); return; } # end if - if ( ( $$self{$primary_key} = $id ) or $data ) { -#$log->debug("loading $parent $id") if $debug or DEBUG_ALL; - $self->load( $data ); + + if ( $id and (!$no_cache) and $$sub_cache{$id} ) { + if ( $data ) { + # The reason to use load is if we have overriden it in the object, + $$sub_cache{$id}->load( $data ); + } + return $$sub_cache{$id}; } + + if ( ( $$self{$primary_key} = $id ) or $data ) { + #$log->debug("loading $parent $id") if $debug or DEBUG_ALL; + $self->load( $data ); + if ( !$no_cache ) { + $$sub_cache{$id} = $self; + } # end if + } # end if + return $self; } # end sub new From 37ff7c3779cb3adcd8287f08d0a234c06d84d95a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 22 Jan 2018 15:26:01 -0500 Subject: [PATCH 27/56] Add finding by Id to Event --- scripts/ZoneMinder/lib/ZoneMinder/Event.pm | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index 4c9d85f75..43bff549e 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -116,6 +116,10 @@ sub find { push @sql_filters , ' Name = ? '; push @sql_values, $sql_filters{Name}; } + if ( exists $sql_filters{Id} ) { + push @sql_filters , ' Id = ? '; + push @sql_values, $sql_filters{Id}; + } $sql .= ' WHERE ' . join(' AND ', @sql_filters ) if @sql_filters; $sql .= ' LIMIT ' . $sql_filters{limit} if $sql_filters{limit}; @@ -345,13 +349,6 @@ sub delete { } Info( "Deleting event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime}\n" ); $ZoneMinder::Database::dbh->ping(); -# Do it individually to avoid locking up the table for new events - my $sql = 'DELETE FROM Events WHERE Id=?'; - my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql ) - or Error( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() ); - my $res = $sth->execute( $event->{Id} ) - or Error( "Can't execute '$sql': ".$sth->errstr() ); - $sth->finish(); if ( ! $Config{ZM_OPT_FAST_DELETE} ) { my $sql = 'DELETE FROM Frames WHERE EventId=?'; @@ -372,6 +369,13 @@ sub delete { } else { Debug('Not deleting frames, stats and files for speed.'); } +# Do it individually to avoid locking up the table for new events + my $sql = 'DELETE FROM Events WHERE Id=?'; + my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql ) + or Error( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() ); + my $res = $sth->execute( $event->{Id} ) + or Error( "Can't execute '$sql': ".$sth->errstr() ); + $sth->finish(); } # end sub delete sub delete_files { From e60a86a06131d220252f95e4cebe2515ae79d891 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 22 Jan 2018 15:26:18 -0500 Subject: [PATCH 28/56] improve debug output --- scripts/zmaudit.pl.in | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index 26366d460..78373c2c8 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -55,7 +55,6 @@ use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|) ; use constant ZM_AUDIT_PID => '@ZM_RUNDIR@/zmaudit.pid'; -$| = 1; $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; @@ -121,6 +120,9 @@ sub HupHandler { } sub TermHandler { Info("Received TERM, exiting"); + Term(); +} +sub Term { unlink ZM_AUDIT_PID; exit( 0 ); } @@ -130,6 +132,8 @@ $SIG{INT} = \&TermHandler; my $dbh = zmDbConnect(); +$| = 1; + require ZoneMinder::Monitor; require ZoneMinder::Storage; require ZoneMinder::Event; @@ -390,8 +394,8 @@ MAIN: while( $loop ) { while ( my ( $db_event, $age ) = each( %$db_events ) ) { if ( ! defined( $fs_events->{$db_event} ) ) { - Debug("Event $db_event is not in fs."); my $Event = ZoneMinder::Event->find_one( Id=>$db_event ); + Debug("Event $db_event is not in fs. Should have been at ".$Event->Path()); if ( ! $Event ) { Debug("Event $db_event is no longer in db. Filter probably deleted it while we were auditing."); next; @@ -418,7 +422,7 @@ MAIN: while( $loop ) { Debug("Database event $$Event{Id} apparently exists at " . $Event->Path() ); } else { if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) { - aud_print( "Database event '$db_monitor/$db_event' does not exist at " . $Event->Path().' in filesystem' ); + aud_print( "Database event '$db_monitor/$db_event' does not exist at " . $Event->Path().' in filesystem, deleting' ); if ( confirm() ) { $Event->delete(); $cleaned = 1; @@ -666,7 +670,7 @@ $eventcounts_sth->finish(); sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} ) if $continuous; }; -term_handler(); +Term(); sub aud_print { my $string = shift; From 8fe0e1bdc0c2bb255e153fb116c80dd73516a726 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 22 Jan 2018 15:58:01 -0500 Subject: [PATCH 29/56] fix Debug => Logger::Debug --- web/skins/classic/views/events.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index b4c7cbbb3..cb6b4ea2c 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -65,9 +65,7 @@ if ( !empty($page) ) { $page = 1; else if ( $page > $pages ) $page = $pages; -} -if ( !empty($page) ) { $limitStart = (($page-1)*ZM_WEB_EVENTS_PER_PAGE); if ( empty( $limit ) ) { $limitAmount = ZM_WEB_EVENTS_PER_PAGE; @@ -222,7 +220,7 @@ while ( $event_row = dbFetchNext( $results ) ) {   +?> Date: Tue, 23 Jan 2018 09:07:40 -0500 Subject: [PATCH 30/56] Set Default Scheme --- src/zm_storage.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/zm_storage.cpp b/src/zm_storage.cpp index fde088db1..3f80e26aa 100644 --- a/src/zm_storage.cpp +++ b/src/zm_storage.cpp @@ -36,8 +36,8 @@ Storage::Storage() { } else { strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path)-1 ); } - scheme = DEEP; - scheme_str = "Deep"; + scheme = MEDIUM; + scheme_str = "Medium"; } Storage::Storage( MYSQL_ROW &dbrow ) { @@ -93,6 +93,8 @@ Storage::Storage( unsigned int p_id ) { } Debug(1,"No id passed to Storage constructor. Using default path %s instead", path ); strcpy(name, "Default"); + scheme = MEDIUM; + scheme_str = "Medium"; } } From 1b23f7b564e89ff9d83c439a2d7452ff1daeb1cf Mon Sep 17 00:00:00 2001 From: Isaac Date: Tue, 23 Jan 2018 17:34:49 +0100 Subject: [PATCH 31/56] turn off debug --- web/ajax/stream.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/ajax/stream.php b/web/ajax/stream.php index 1f0245a69..94b621ed4 100644 --- a/web/ajax/stream.php +++ b/web/ajax/stream.php @@ -55,7 +55,7 @@ $remSockFile = ZM_PATH_SOCKS.'/zms-'.sprintf('%06d',$_REQUEST['connkey']).'s.soc $max_socket_tries = 10; // FIXME This should not exceed web_ajax_timeout while ( !file_exists($remSockFile) && $max_socket_tries-- ) { //sometimes we are too fast for our own good, if it hasn't been setup yet give it a second. - Logger::Debug("$remSockFile does not exist, waiting, current " . (time() - $start_time) . ' seconds' ); + //Logger::Debug("$remSockFile does not exist, waiting, current " . (time() - $start_time) . ' seconds' ); usleep(200000); } From 83371d0f21d836df0899b6d91c04a44da0c21ffb Mon Sep 17 00:00:00 2001 From: Isaac Date: Tue, 23 Jan 2018 17:42:57 +0100 Subject: [PATCH 32/56] Fix Error => Debug --- scripts/zmdc.pl.in | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/zmdc.pl.in b/scripts/zmdc.pl.in index e7198fdd7..623769d81 100644 --- a/scripts/zmdc.pl.in +++ b/scripts/zmdc.pl.in @@ -185,7 +185,7 @@ use Sys::CpuLoad; my $attempts = 0; while( !connect( CLIENT, $saddr ) ) { $attempts++; - Error("Waiting for zmdc.pl server process at " . SOCK_FILE.", attempt $attempts" ); + Debug("Waiting for zmdc.pl server process at ".SOCK_FILE.", attempt $attempts" ); Fatal( "Can't connect: $!" ) if ($attempts > MAX_CONNECT_DELAY); usleep(200000); } # end while @@ -566,13 +566,19 @@ sub restart { my $command = $daemon; $command .= ' '.join( ' ', ( @args ) ) if @args; + dPrint ( ZoneMinder::Logger::WARNING, "Restarting $command\n"); my $process = $cmd_hash{$command}; if ( $process ) { + dPrint ( ZoneMinder::Logger::WARNING, "Have process" ); if ( $process->{pid} ) { + dPrint ( ZoneMinder::Logger::WARNING, "Have process pid " .$process->{pid} ); my $cpid = $process->{pid}; if ( defined($pid_hash{$cpid}) ) { + dPrint ( ZoneMinder::Logger::WARNING, "Have process pid hash " .$process->{pid} ); _stop( 0, $process ); return; + } else { + dPrint ( ZoneMinder::Logger::WARNING, "Not sending stop" ); } } } From abb2cfbe125f70ef8306c3af5b4b2664e4c54924 Mon Sep 17 00:00:00 2001 From: Isaac Date: Tue, 23 Jan 2018 18:14:55 +0100 Subject: [PATCH 33/56] fix zm_av_packet_ref for old ffmpeg. Need to set the size and flags as well as copying the mem. --- src/zm_ffmpeg.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index 2db516cf4..5ddd8ee82 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -354,8 +354,10 @@ int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt) { #if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100) #else unsigned int zm_av_packet_ref( AVPacket *dst, AVPacket *src ) { - dst->data = reinterpret_cast(new uint64_t[(src->size + FF_INPUT_BUFFER_PADDING_SIZE)/sizeof(uint64_t) + 1]); + dst->size = (src->size + FF_INPUT_BUFFER_PADDING_SIZE)/sizeof(uint64_t) + 1; + dst->data = reinterpret_cast(new uint64_t[dst->size]); memcpy(dst->data, src->data, src->size ); + dst->flags = src->flags; return 0; } #endif From 66fd02a29eb8d0cb1010d501e8de8899ae12f4f3 Mon Sep 17 00:00:00 2001 From: Isaac Date: Tue, 23 Jan 2018 18:15:29 +0100 Subject: [PATCH 34/56] Use dumpPacket --- src/zm_videostore.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 432a74011..d530c6716 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -814,10 +814,7 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) { AVPacket safepkt; memcpy(&safepkt, &opkt, sizeof(AVPacket)); - Debug(1, - "writing video packet keyframe(%d) pts(%d) dts(%d) duration(%d) " - "ipkt.duration(%d)", - opkt.flags & AV_PKT_FLAG_KEY, opkt.pts, opkt.dts, duration, ipkt->duration); + dumpPacket( &opkt, "writing video packet" ); if ((opkt.data == NULL) || (opkt.size < 1)) { Warning("%s:%d: Mangled AVPacket: discarding frame", __FILE__, __LINE__); dumpPacket(ipkt); @@ -839,7 +836,7 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) { Warning( "%s:%d: Writing frame [av_interleaved_write_frame()] failed: %s(%d) " " ", - __FILE__, __LINE__, av_make_error_string(ret).c_str(), (ret)); + __FILE__, __LINE__, av_make_error_string(ret).c_str(), ret); dumpPacket(&safepkt); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) zm_dump_codecpar(video_in_stream->codecpar); From 8bd1e4cec6dc75c730f5718e18c458c0f8da46fe Mon Sep 17 00:00:00 2001 From: Isaac Date: Tue, 23 Jan 2018 18:15:59 +0100 Subject: [PATCH 35/56] Only show groups filter if there are groups --- web/skins/classic/views/_monitor_filters.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/skins/classic/views/_monitor_filters.php b/web/skins/classic/views/_monitor_filters.php index eb58fccf6..d2564f7a5 100644 --- a/web/skins/classic/views/_monitor_filters.php +++ b/web/skins/classic/views/_monitor_filters.php @@ -46,6 +46,9 @@ foreach ( $storage_areas as $S ) { ?>
+ + Date: Tue, 23 Jan 2018 18:16:24 +0100 Subject: [PATCH 36/56] bump version to 1.31.27 for new event_update trigger --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index fca4bdfac..060b0a197 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.31.26 +1.31.27 From de08e3114ee73ee5eca7040e47ffbae7e7faccc8 Mon Sep 17 00:00:00 2001 From: Isaac Date: Tue, 23 Jan 2018 18:16:37 +0100 Subject: [PATCH 37/56] more and less debug --- web/skins/classic/views/events.php | 2 +- web/views/image.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index cb6b4ea2c..7218b67f6 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -206,7 +206,7 @@ while ( $event_row = dbFetchNext( $results ) ) { SaveJPEGs() == 0 ) and file_exists($event->Path().'/snapshot.jpg') ) { - Logger::Debug("Using snapshot"); + Logger::Debug("Using snapshot" . $event->Path().'/snapshot.jpg' ); $imgSrc = '?view=image&eid='.$event->Id().'&fid=snapshot&width='.$thumbData['Width'].'&height='.$thumbData['Height']; } else { Logger::Debug("Not Using snapshot" . $event->Path().'/snapshot.jpg' ); diff --git a/web/views/image.php b/web/views/image.php index 6214c7b44..eb1bd4743 100644 --- a/web/views/image.php +++ b/web/views/image.php @@ -66,7 +66,6 @@ if ( empty($_REQUEST['path']) ) { $Frame = new Frame(); $Frame->FrameId('snapshot'); $path = $Event->Path().'/snapshot.jpg'; -Warning("Path to snapshot: $path"); } else { $show = empty($_REQUEST['show']) ? 'capture' : $_REQUEST['show']; From fd2d2a0d77147d834fb6bde50d5e052d62521ca2 Mon Sep 17 00:00:00 2001 From: Isaac Date: Tue, 23 Jan 2018 18:43:59 +0100 Subject: [PATCH 38/56] ad update to update events_update_trigger to dix totaldiskspace --- db/zm_update-1.31.27.sql | 100 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 db/zm_update-1.31.27.sql diff --git a/db/zm_update-1.31.27.sql b/db/zm_update-1.31.27.sql new file mode 100644 index 000000000..6500c1efd --- /dev/null +++ b/db/zm_update-1.31.27.sql @@ -0,0 +1,100 @@ +delimiter // +DROP TRIGGER IF EXISTS event_update_trigger; + +CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events +FOR EACH ROW +BEGIN + declare diff BIGINT default 0; + + set diff = NEW.DiskSpace - OLD.DiskSpace; + IF ( NEW.StorageId = OLD.StorageID ) THEN + IF ( diff ) THEN + call update_storage_stats(OLD.StorageId, diff); + END IF; + ELSE + IF ( NEW.DiskSpace ) THEN + call update_storage_stats(NEW.StorageId, NEW.DiskSpace); + END IF; + IF ( OLD.DiskSpace ) THEN + call update_storage_stats(OLD.StorageId, -OLD.DiskSpace); + END IF; + END IF; + + UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + IF ( NEW.Archived != OLD.Archived ) THEN + IF ( NEW.Archived ) THEN + INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace); + UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId; + ELSEIF ( OLD.Archived ) THEN + DELETE FROM Events_Archived WHERE EventId=OLD.Id; + UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)-1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) WHERE Id=OLD.MonitorId; + ELSE + IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN + UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Monitors SET + ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) + WHERE Id=OLD.MonitorId; + END IF; + END IF; + END IF; + + IF ( + ( (OLD.DiskSpace IS NOT NULL) AND (NEW.DiskSpace IS NULL) ) + OR + ( (OLD.DiskSpace IS NULL) AND (NEW.DiskSpace IS NOT NULL) ) + OR ( OLD.DiskSpace != NEW.DiskSpace ) + ) THEN + UPDATE Monitors SET TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=OLD.MonitorId; + END IF; + +END; + +// + +DROP TRIGGER IF EXISTS event_insert_trigger; + +create trigger event_insert_trigger after insert on Events +for each row + begin + + INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + UPDATE Monitors SET TotalEvents = COALESCE(TotalEvents,0)+1 WHERE Id=NEW.MonitorId; +end; +// + +UPDATE Monitors INNER JOIN ( + SELECT MonitorId, + COUNT(Id) AS TotalEvents, + SUM(DiskSpace) AS TotalEventDiskSpace, + SUM(IF(Archived,1,0)) AS ArchivedEvents, + SUM(IF(Archived,DiskSpace,0)) AS ArchivedEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),1,0)) AS HourEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),DiskSpace,0)) AS HourEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),1,0)) AS DayEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),DiskSpace,0)) AS DayEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),1,0)) AS WeekEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),DiskSpace,0)) AS WeekEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),1,0)) AS MonthEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),DiskSpace,0)) AS MonthEventDiskSpace + FROM Events GROUP BY MonitorId + ) AS E ON E.MonitorId=Monitors.Id SET + Monitors.TotalEvents = E.TotalEvents, + Monitors.TotalEventDiskSpace = E.TotalEventDiskSpace, + Monitors.ArchivedEvents = E.ArchivedEvents, + Monitors.ArchivedEventDiskSpace = E.ArchivedEventDiskSpace, + Monitors.HourEvents = E.HourEvents, + Monitors.HourEventDiskSpace = E.HourEventDiskSpace, + Monitors.DayEvents = E.DayEvents, + Monitors.DayEventDiskSpace = E.DayEventDiskSpace, + Monitors.WeekEvents = E.WeekEvents, + Monitors.WeekEventDiskSpace = E.WeekEventDiskSpace, + Monitors.MonthEvents = E.MonthEvents, + Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace// + + From 447a0975c71ff6fdb7fd5f2c96b5ff960fbdaa09 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 23 Jan 2018 13:15:48 -0800 Subject: [PATCH 39/56] fix bracket --- web/api/app/Controller/MonitorsController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index 77113e892..c372463c1 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -171,7 +171,7 @@ class MonitorsController extends AppController { $func = $Monitor['Function']; // We don't pass the request data as the monitor object because it may be a subset of the full monitor array $this->daemonControl( $this->Monitor->id, 'stop' ); - if ( ( $func != 'None' ) and ( (!defined('ZM_SERVER_ID']) or ($Monitor['ServerId']==ZM_SERVER_ID) ) ) { + if ( ( $func != 'None' ) and ( (!defined('ZM_SERVER_ID')) or ($Monitor['ServerId']==ZM_SERVER_ID) ) ) { $this->daemonControl( $this->Monitor->id, 'start' ); } } // end function edit From 933259f9a576de784b1825e7917ea4ca765f1fc9 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 23 Jan 2018 13:15:48 -0800 Subject: [PATCH 40/56] fix bracket --- web/api/app/Controller/MonitorsController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index 77113e892..c372463c1 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -171,7 +171,7 @@ class MonitorsController extends AppController { $func = $Monitor['Function']; // We don't pass the request data as the monitor object because it may be a subset of the full monitor array $this->daemonControl( $this->Monitor->id, 'stop' ); - if ( ( $func != 'None' ) and ( (!defined('ZM_SERVER_ID']) or ($Monitor['ServerId']==ZM_SERVER_ID) ) ) { + if ( ( $func != 'None' ) and ( (!defined('ZM_SERVER_ID')) or ($Monitor['ServerId']==ZM_SERVER_ID) ) ) { $this->daemonControl( $this->Monitor->id, 'start' ); } } // end function edit From 0742217599e5812687f22052668bfa41c44ab3a2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 23 Jan 2018 13:28:29 -0800 Subject: [PATCH 41/56] Only populate monitorNames if query is valid --- web/skins/classic/views/js/monitor.js.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/skins/classic/views/js/monitor.js.php b/web/skins/classic/views/js/monitor.js.php index 7a2f17fd3..16c7ee438 100644 --- a/web/skins/classic/views/js/monitor.js.php +++ b/web/skins/classic/views/js/monitor.js.php @@ -37,11 +37,13 @@ controlOptions[][] = ' monitorNames[''] = true; function validateForm( form ) { From fdf1ef795f32b1ab4692b09c040c44ebb3f4e98f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 23 Jan 2018 13:28:46 -0800 Subject: [PATCH 42/56] Add default value for newStorage['StorageId'] --- web/skins/classic/views/storage.php | 1 + 1 file changed, 1 insertion(+) diff --git a/web/skins/classic/views/storage.php b/web/skins/classic/views/storage.php index 8768e445c..4b041f0f1 100644 --- a/web/skins/classic/views/storage.php +++ b/web/skins/classic/views/storage.php @@ -35,6 +35,7 @@ if ( $_REQUEST['id'] ) { $newStorage['Path'] = ''; $newStorage['Type'] = 'local'; $newStorage['Scheme'] = 'Medium'; + $newStorage['StorageId'] = ''; } $type_options = array( 'local' => translate('Local'), 's3fs' => translate('s3fs') ); From ea4937b75e3c2eaf5c0ea23149dac9955a7f40cb Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 23 Jan 2018 17:47:24 -0500 Subject: [PATCH 43/56] put back missing s --- web/skins/classic/views/js/monitor.js.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/monitor.js.php b/web/skins/classic/views/js/monitor.js.php index 16c7ee438..8871de840 100644 --- a/web/skins/classic/views/js/monitor.js.php +++ b/web/skins/classic/views/js/monitor.js.php @@ -36,7 +36,7 @@ controlOptions[][] = ' From 9f89ccfa32ad06c19827ee7d16b933d04eb16682 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 24 Jan 2018 07:46:56 -0500 Subject: [PATCH 44/56] revert issue with AUTH_HASH_LOGINS --- web/includes/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/includes/functions.php b/web/includes/functions.php index 51884f1d0..b2afc8681 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -148,7 +148,7 @@ function getAuthUser( $auth ) { } // end getAuthUser($auth) function generateAuthHash( $useRemoteAddr ) { - if ( ZM_OPT_USE_AUTH and ZM_AUTH_HASH_LOGINS and ZM_AUTH_RELAY == 'hashed' and isset($_SESSION['username']) and $_SESSION['passwordHash'] ) { + if ( ZM_OPT_USE_AUTH and ZM_AUTH_RELAY == 'hashed' and isset($_SESSION['username']) and $_SESSION['passwordHash'] ) { # regenerate a hash at half the liftetime of a hash, an hour is 3600 so half is 1800 $time = time(); if ( ( ! isset($_SESSION['AuthHash']) ) or ( $_SESSION['AuthHashGeneratedAt'] < ( $time - ( ZM_AUTH_HASH_TTL * 1800 ) ) ) ) { From c7ecd580c1fc9512ebecc08f2f604934b0af52f1 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 24 Jan 2018 10:27:01 -0500 Subject: [PATCH 45/56] Fix #81 by testing for non-numeric characters in LIMIT. Change type of input to number, making it harder to enter non-numeric characters --- web/skins/classic/views/filter.php | 2 +- web/skins/classic/views/js/filter.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/views/filter.php b/web/skins/classic/views/filter.php index d01204d91..9d4d6094e 100644 --- a/web/skins/classic/views/filter.php +++ b/web/skins/classic/views/filter.php @@ -334,7 +334,7 @@ echo htmlSelect( 'filter[Query][sort_asc]', $sort_dirns, $filter->sort_asc() ); - + diff --git a/web/skins/classic/views/js/filter.js b/web/skins/classic/views/js/filter.js index 4f574507d..c8e6749a4 100644 --- a/web/skins/classic/views/js/filter.js +++ b/web/skins/classic/views/js/filter.js @@ -16,6 +16,11 @@ function validateForm ( form ) { alert( errorBrackets ); return false; } + var numbers_reg = /\D/; + if ( numbers_reg.test( form.elements['filter[Query][limit]'].value ) ) { + alert( "There appear to be non-numeric characters in your limit. Limit must be a positive integer value or empty." ); + return false; + } return true; } From a271f1776d35e0ed352ab4f96d76765fb71118db Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 24 Jan 2018 10:35:22 -0500 Subject: [PATCH 46/56] Fix #80 don't escape NULL value when building SQL --- web/includes/functions.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/web/includes/functions.php b/web/includes/functions.php index b2afc8681..2214d1cff 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -1236,31 +1236,38 @@ function parseFilter( &$filter, $saveToSession=false, $querySep='&' ) { case 'ServerId': if ( $value == 'ZM_SERVER_ID' ) { $value = ZM_SERVER_ID; + } else if ( $value == 'NULL' ) { + } else { $value = dbEscape($value); } break; case 'StorageId': $StorageArea = new Storage( $value ); - $value = dbEscape($value); + if ( $value != 'NULL' ) + $value = dbEscape($value); break; case 'DateTime': case 'StartDateTime': case 'EndDateTime': - $value = "'".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."'"; + if ( $value != 'NULL' ) + $value = "'".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."'"; break; case 'Date': case 'StartDate': case 'EndDate': - $value = "to_days( '".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."' )"; + if ( $value != 'NULL' ) + $value = "to_days( '".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."' )"; break; case 'Time': case 'StartTime': case 'EndTime': + if ( $value != 'NULL' ) $value = "extract( hour_second from '".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."' )"; break; default : - $value = dbEscape($value); + if ( $value != 'NULL' ) + $value = dbEscape($value); break; } $valueList[] = $value; From cf4ac74d02beeae8104159862371811ca1d6ead0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 24 Jan 2018 10:45:42 -0500 Subject: [PATCH 47/56] If failed to delete from Frames or Stats, return instead of deleting the event. --- scripts/ZoneMinder/lib/ZoneMinder/Event.pm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index 43bff549e..530aa4f5c 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -357,6 +357,9 @@ sub delete { my $res = $sth->execute( $event->{Id} ) or Error( "Can't execute '$sql': ".$sth->errstr() ); $sth->finish(); + if ( $ZoneMinder::Database::dbh->errstr() ) { + return; + } $sql = 'DELETE FROM Stats WHERE EventId=?'; $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql ) @@ -364,6 +367,9 @@ sub delete { $res = $sth->execute( $event->{Id} ) or Error( "Can't execute '$sql': ".$sth->errstr() ); $sth->finish(); + if ( $ZoneMinder::Database::dbh->errstr() ) { + return; + } $event->delete_files( ); } else { From 50fc4a2d94c2ed5e9406105b0673e1dd3dfbd554 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 24 Jan 2018 11:51:11 -0500 Subject: [PATCH 48/56] Use a memory table called Monitor_Status to store FPS and Status info for Monitors. This is to reduce locking and updates on the main Monitors table. --- db/zm_create.sql.in | 11 ++-- db/zm_update-1.31.28.sql | 57 ++++++++++++++++++++ src/zm_ffmpeg_camera.cpp | 7 +-- src/zm_monitor.cpp | 4 +- src/zmc.cpp | 2 +- version | 2 +- web/skins/classic/views/_monitor_filters.php | 2 +- web/skins/classic/views/console.php | 13 ++++- 8 files changed, 86 insertions(+), 12 deletions(-) create mode 100644 db/zm_update-1.31.28.sql diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 92d133e0f..2ff2de208 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -608,9 +608,6 @@ CREATE TABLE `Monitors` ( `WebColour` varchar(32) NOT NULL default 'red', `Exif` tinyint(1) unsigned NOT NULL default '0', `Sequence` smallint(5) unsigned default NULL, - `Status` enum('Unknown','NotRunning','Running','NoSignal','Signal') NOT NULL default 'Unknown', - `CaptureFPS` DECIMAL(10,2) NOT NULL default 0, - `AnalysisFPS` DECIMAL(5,2) NOT NULL default 0, `TotalEvents` int(10) unsigned, `TotalEventDiskSpace` bigint unsigned, `HourEvents` int(10) unsigned, @@ -629,6 +626,14 @@ CREATE TABLE `Monitors` ( CREATE INDEX `Monitors_ServerId_idx` ON `Monitors` (`ServerId`); +DROP TABLE IF EXISTS `Monitor_Status`; +CREATE TABLE `Monitor_Status` ( + `Id` int(10) unsigned NOT NULL, + `Status` enum('Unknown','NotRunning','Running','NoSignal','Signal') NOT NULL default 'Unknown', + `CaptureFPS` DECIMAL(10,2) NOT NULL default 0, + `AnalysisFPS` DECIMAL(5,2) NOT NULL default 0, + PRIMARY KEY (`Id`) +) ENGINE=MEMORY; -- -- Table structure for table `States` -- PP - Added IsActive to track custom run states diff --git a/db/zm_update-1.31.28.sql b/db/zm_update-1.31.28.sql new file mode 100644 index 000000000..ce540b545 --- /dev/null +++ b/db/zm_update-1.31.28.sql @@ -0,0 +1,57 @@ +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitor_Status' + ) > 0 + , + "SELECT 'Monitor_Status Already exists'", + " +CREATE TABLE `Monitor_Status` ( + `Id` int(10) unsigned NOT NULL, + `Status` enum('Unknown','NotRunning','Running','NoSignal','Signal') NOT NULL default 'Unknown', + `CaptureFPS` DECIMAL(10,2) NOT NULL default 0, + `AnalysisFPS` DECIMAL(5,2) NOT NULL default 0, + PRIMARY KEY (`Id`) +) ENGINE=MEMORY" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'Status' + ) > 0 + , + "ALTER TABLE Monitors DROP COLUMN Status" + "SELECT 'Monitor Status already removed.'", + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'CaptureFPS' + ) > 0 + , + "ALTER TABLE Monitors DROP COLUMN CaptureFPS" + "SELECT 'Monitor CaptureFPS already removed.'", + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'AnalysisFPS' + ) > 0 + , + "ALTER TABLE Monitors DROP COLUMN AnalysisFPS" + "SELECT 'Monitor AnalysisFPS already removed.'", + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 25e96e5a7..069d68b63 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -738,7 +738,8 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event int ret; static char errbuf[AV_ERROR_MAX_STRING_SIZE]; - // If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread. + // If the reopen thread has a value, but mCanCapture != 0, then we have just reopened + // the connection to the ffmpeg device, and we can clean up the thread. if ( mReopenThread != 0 ) { void *retval = 0; @@ -751,7 +752,6 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event mReopenThread = 0; } - int frameComplete = false; while ( ! frameComplete ) { av_init_packet( &packet ); @@ -774,7 +774,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event } int keyframe = packet.flags & AV_PKT_FLAG_KEY; -dumpPacket(&packet); + dumpPacket(&packet); //Video recording if ( recording.tv_sec ) { @@ -836,6 +836,7 @@ dumpPacket(&packet); startTime, this->getMonitor()); } // end if record_audio + if ( ! videoStore->open() ) { delete videoStore; videoStore = NULL; diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index dc097d1ab..980a41645 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1205,7 +1205,7 @@ bool Monitor::Analyse() { fps = double(fps_report_interval)/(now.tv_sec - last_fps_time); Info( "%s: %d - Analysing at %.2f fps", name, image_count, fps ); static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "UPDATE Monitors SET AnalysisFPS = '%.2lf' WHERE Id = '%d'", fps, id ); + snprintf( sql, sizeof(sql), "INSERT INTO Monitor_Status (Id,AnalysisFPS) VALUES (%d, %.2lf) ON DUPLICATE KEY UPDATE AnalysisFPS = %.2lf", id, fps, fps ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); } @@ -3022,7 +3022,7 @@ Debug(4, "Return from Capture (%d)", captureResult); Info( "%s: images:%d - Capturing at %.2lf fps", name, image_count, fps ); last_fps_time = now; static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "UPDATE Monitors SET CaptureFPS='%.2lf' WHERE Id=%d", fps, id ); + snprintf( sql, sizeof(sql), "INSERT INTO Monitor_Status (Id,CaptureFPS) VALUES (%d, %.2lf) ON DUPLICATE KEY UPDATE CaptureFPS = %.2lf", id, fps, fps ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); } diff --git a/src/zmc.cpp b/src/zmc.cpp index a58e04a86..5169b0699 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -223,7 +223,7 @@ int main(int argc, char *argv[]) { Info("Starting Capture version %s", ZM_VERSION); static char sql[ZM_SQL_SML_BUFSIZ]; for ( int i = 0; i < n_monitors; i ++ ) { - snprintf( sql, sizeof(sql), "UPDATE Monitors SET Status = 'Running' WHERE Id = '%d'", monitors[i]->Id() ); + snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (Id, Status ) VALUES ('%d','Running')", monitors[i]->Id() ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); } diff --git a/version b/version index 060b0a197..9e60186a4 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.31.27 +1.31.28 diff --git a/web/skins/classic/views/_monitor_filters.php b/web/skins/classic/views/_monitor_filters.php index d2564f7a5..7633200ea 100644 --- a/web/skins/classic/views/_monitor_filters.php +++ b/web/skins/classic/views/_monitor_filters.php @@ -89,7 +89,7 @@ if ( ! is_array( $selected_monitor_ids ) ) { $values += $ids; } - $sql = 'SELECT * FROM Monitors' . ( count($conditions) ? ' WHERE ' . implode(' AND ', $conditions ) : '' ).' ORDER BY Sequence ASC'; + $sql = 'SELECT *,S.Status AS Status, S.CaptureFPS AS CaptureFPS FROM Monitors AS M LEFT JOIN Monitor_Status AS S ON S.Id=M.Id ' . ( count($conditions) ? ' WHERE ' . implode(' AND ', $conditions ) : '' ).' ORDER BY Sequence ASC'; $monitors = dbFetchAll( $sql, null, $values ); $displayMonitors = array(); $monitors_dropdown = array(); diff --git a/web/skins/classic/views/console.php b/web/skins/classic/views/console.php index a4731d649..d3350ff96 100644 --- a/web/skins/classic/views/console.php +++ b/web/skins/classic/views/console.php @@ -215,7 +215,18 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { ' : '>') . $monitor['Name'] ?> '.translate('Fn'.$monitor['Function']).( empty($monitor['Enabled']) ? ', disabled' : '' ) .'', canEdit( 'Monitors' ) ) ?>
- + From b821d665bfc07d536e89fe4036aca38e17c66ceb Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 24 Jan 2018 12:16:18 -0500 Subject: [PATCH 49/56] Fix commas --- db/zm_update-1.31.28.sql | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/db/zm_update-1.31.28.sql b/db/zm_update-1.31.28.sql index ce540b545..096b020c9 100644 --- a/db/zm_update-1.31.28.sql +++ b/db/zm_update-1.31.28.sql @@ -23,8 +23,8 @@ SET @s = (SELECT IF( AND column_name = 'Status' ) > 0 , - "ALTER TABLE Monitors DROP COLUMN Status" - "SELECT 'Monitor Status already removed.'", + "ALTER TABLE Monitors DROP COLUMN Status", + "SELECT 'Monitor Status already removed.'" )); PREPARE stmt FROM @s; @@ -36,8 +36,8 @@ SET @s = (SELECT IF( AND column_name = 'CaptureFPS' ) > 0 , - "ALTER TABLE Monitors DROP COLUMN CaptureFPS" - "SELECT 'Monitor CaptureFPS already removed.'", + "ALTER TABLE Monitors DROP COLUMN CaptureFPS", + "SELECT 'Monitor CaptureFPS already removed.'" )); PREPARE stmt FROM @s; @@ -49,8 +49,8 @@ SET @s = (SELECT IF( AND column_name = 'AnalysisFPS' ) > 0 , - "ALTER TABLE Monitors DROP COLUMN AnalysisFPS" - "SELECT 'Monitor AnalysisFPS already removed.'", + "ALTER TABLE Monitors DROP COLUMN AnalysisFPS", + "SELECT 'Monitor AnalysisFPS already removed.'" )); PREPARE stmt FROM @s; From 1800c6fcf0a691195a9aac156b3302c289590b51 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 24 Jan 2018 12:16:34 -0500 Subject: [PATCH 50/56] Add locking around Event deletion --- scripts/ZoneMinder/lib/ZoneMinder/Event.pm | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index 530aa4f5c..766436a84 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -350,6 +350,9 @@ sub delete { Info( "Deleting event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime}\n" ); $ZoneMinder::Database::dbh->ping(); + $ZoneMinder::Database::dbh->begin_work(); + $event->lock_and_load(); + if ( ! $Config{ZM_OPT_FAST_DELETE} ) { my $sql = 'DELETE FROM Frames WHERE EventId=?'; my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql ) @@ -358,6 +361,7 @@ sub delete { or Error( "Can't execute '$sql': ".$sth->errstr() ); $sth->finish(); if ( $ZoneMinder::Database::dbh->errstr() ) { + $ZoneMinder::Database::dbh->commit(); return; } @@ -368,6 +372,7 @@ sub delete { or Error( "Can't execute '$sql': ".$sth->errstr() ); $sth->finish(); if ( $ZoneMinder::Database::dbh->errstr() ) { + $ZoneMinder::Database::dbh->commit(); return; } @@ -382,6 +387,7 @@ sub delete { my $res = $sth->execute( $event->{Id} ) or Error( "Can't execute '$sql': ".$sth->errstr() ); $sth->finish(); + $ZoneMinder::Database::dbh->commit(); } # end sub delete sub delete_files { @@ -520,7 +526,10 @@ sub MoveTo { } } } - return $error if $error; + if ( $error ) { + $ZoneMinder::Database::dbh->commit(); + return $error; + } my @files = glob("$OldPath/*"); for my $file (@files) { From e4c900a8688960310f74b59de8944dc005ed4a57 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 24 Jan 2018 12:15:35 -0500 Subject: [PATCH 51/56] Use a LEFT JOIN on Storage WHEN Running Filters --- scripts/ZoneMinder/lib/ZoneMinder/Filter.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm index ed5779b2b..cd8b76a62 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -140,7 +140,7 @@ sub Sql { M.DefaultScale FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId - INNER JOIN Storage as S on S.Id = E.StorageId + LEFT JOIN Storage as S on S.Id = E.StorageId '; $self->{Sql} = ''; From c930a7cce650acd6c490bba4eb07b68cbde1d19c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 24 Jan 2018 15:29:49 -0500 Subject: [PATCH 52/56] fix crash --- scripts/zmaudit.pl.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index 78373c2c8..519e80851 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -395,11 +395,11 @@ MAIN: while( $loop ) { while ( my ( $db_event, $age ) = each( %$db_events ) ) { if ( ! defined( $fs_events->{$db_event} ) ) { my $Event = ZoneMinder::Event->find_one( Id=>$db_event ); - Debug("Event $db_event is not in fs. Should have been at ".$Event->Path()); if ( ! $Event ) { Debug("Event $db_event is no longer in db. Filter probably deleted it while we were auditing."); next; } + Debug("Event $db_event is not in fs. Should have been at ".$Event->Path()); if ( ! $Event->StartTime() ) { Info("Event $$Event{Id} has no start time. deleting it."); if ( confirm() ) { From e364a541d4f34fea9cf64e7cf7fda5c572f62e13 Mon Sep 17 00:00:00 2001 From: Isaac Date: Wed, 24 Jan 2018 22:21:43 +0100 Subject: [PATCH 53/56] Whitespace and use ZM_DIR_EXPORT instead of ZM_DIR_TMP --- web/views/archive.php | 59 +++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/web/views/archive.php b/web/views/archive.php index ce4e1b274..9238a925e 100644 --- a/web/views/archive.php +++ b/web/views/archive.php @@ -18,45 +18,44 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -if ( !canView( 'Events' ) ) -{ - $view = "error"; - return; +if ( !canView( 'Events' ) ) { + $view = 'error'; + return; } $archivetype = $_REQUEST['type']; if ( $archivetype ) { - switch ($archivetype) { - case "tar": - $mimetype = "gzip"; - $file_ext = "tar.gz"; - break; - case "zip": - $mimetype = "zip"; - $file_ext = "zip"; - break; - default: - $mimetype = NULL; - $file_ext = NULL; - } + switch ($archivetype) { + case 'tar': + $mimetype = 'gzip'; + $file_ext = 'tar.gz'; + break; + case 'zip': + $mimetype = 'zip'; + $file_ext = 'zip'; + break; + default: + $mimetype = NULL; + $file_ext = NULL; + } - if ( $mimetype ) { - $filename = "zmExport.$file_ext"; - $filename_path = ZM_DIR_TEMP."/".$filename; - if ( is_readable($filename_path) ) { - header( "Content-type: application/$mimetype" ); - header( "Content-Disposition: attachment; filename=$filename"); - set_time_limit(0); - readfile( $filename_path ); - } else { - Error("$filename_path does not exist or is not readable."); - } + if ( $mimetype ) { + $filename = "zmExport.$file_ext"; + $filename_path = ZM_DIR_EXPORTS.'/'.$filename; + if ( is_readable($filename_path) ) { + header( "Content-type: application/$mimetype" ); + header( "Content-Disposition: attachment; filename=$filename"); + set_time_limit(0); + readfile( $filename_path ); } else { - Error("Unsupported archive type specified. Supported archives are tar and zip"); + Error("$filename_path does not exist or is not readable."); } + } else { + Error("Unsupported archive type specified. Supported archives are tar and zip"); + } } else { - Error("No archive type given to archive.php. Please specify a tar or zip archive."); + Error("No archive type given to archive.php. Please specify a tar or zip archive."); } ?> From 5865bbfb1290657b577788f03f7595100108787e Mon Sep 17 00:00:00 2001 From: Isaac Date: Wed, 24 Jan 2018 23:07:21 +0100 Subject: [PATCH 54/56] turn off debugging --- web/index.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web/index.php b/web/index.php index c3f2269fe..3ebc58a98 100644 --- a/web/index.php +++ b/web/index.php @@ -174,17 +174,13 @@ foreach ( getSkinIncludes( 'skin.php' ) as $includeFile ) require_once $includeFile; if ( ZM_OPT_USE_AUTH && ZM_AUTH_HASH_LOGINS ) { - Logger::Debug("Useing hash"); if ( empty($user) && ! empty($_REQUEST['auth']) ) { if ( $authUser = getAuthUser( $_REQUEST['auth'] ) ) { userLogin( $authUser['Username'], $authUser['Password'], true ); } } else if ( ! empty($user) ) { - Logger::Debug("generating hash"); // generate it once here, while session is open. Value will be cached in session and return when called later on generateAuthHash( ZM_AUTH_HASH_IPS ); - } else { - Logger::Debug(" not generating hash"); } } From e4c28150fe066a17b739e50672b5c0a8732ff423 Mon Sep 17 00:00:00 2001 From: Isaac Date: Wed, 24 Jan 2018 23:07:57 +0100 Subject: [PATCH 55/56] cleanup, code style, quotes. Include jquery source instead of trying to link to it --- .../classic/includes/export_functions.php | 391 ++++++++---------- 1 file changed, 171 insertions(+), 220 deletions(-) diff --git a/web/skins/classic/includes/export_functions.php b/web/skins/classic/includes/export_functions.php index f384594da..8a53394fa 100644 --- a/web/skins/classic/includes/export_functions.php +++ b/web/skins/classic/includes/export_functions.php @@ -71,7 +71,9 @@ html ul.tabs li.active, html ul.tabs li.active a:hover { } --> - +